1 /*
2  * connection tracking event cache.
3  */
4 
5 #ifndef _NF_CONNTRACK_ECACHE_H
6 #define _NF_CONNTRACK_ECACHE_H
7 #include <net/netfilter/nf_conntrack.h>
8 
9 #include <net/net_namespace.h>
10 #include <net/netfilter/nf_conntrack_expect.h>
11 #include <linux/netfilter/nf_conntrack_common.h>
12 #include <linux/netfilter/nf_conntrack_tuple_common.h>
13 #include <net/netfilter/nf_conntrack_extend.h>
14 
15 struct nf_conntrack_ecache {
16 	unsigned long cache;	/* bitops want long */
17 	unsigned long missed;	/* missed events */
18 	u16 ctmask;		/* bitmask of ct events to be delivered */
19 	u16 expmask;		/* bitmask of expect events to be delivered */
20 	u32 portid;		/* netlink portid of destroyer */
21 };
22 
23 static inline struct nf_conntrack_ecache *
nf_ct_ecache_find(const struct nf_conn * ct)24 nf_ct_ecache_find(const struct nf_conn *ct)
25 {
26 #ifdef CONFIG_NF_CONNTRACK_EVENTS
27 	return nf_ct_ext_find(ct, NF_CT_EXT_ECACHE);
28 #else
29 	return NULL;
30 #endif
31 }
32 
33 static inline struct nf_conntrack_ecache *
nf_ct_ecache_ext_add(struct nf_conn * ct,u16 ctmask,u16 expmask,gfp_t gfp)34 nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp)
35 {
36 #ifdef CONFIG_NF_CONNTRACK_EVENTS
37 	struct net *net = nf_ct_net(ct);
38 	struct nf_conntrack_ecache *e;
39 
40 	if (!ctmask && !expmask && net->ct.sysctl_events) {
41 		ctmask = ~0;
42 		expmask = ~0;
43 	}
44 	if (!ctmask && !expmask)
45 		return NULL;
46 
47 	e = nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
48 	if (e) {
49 		e->ctmask  = ctmask;
50 		e->expmask = expmask;
51 	}
52 	return e;
53 #else
54 	return NULL;
55 #endif
56 };
57 
58 #ifdef CONFIG_NF_CONNTRACK_EVENTS
59 /* This structure is passed to event handler */
60 struct nf_ct_event {
61 	struct nf_conn *ct;
62 	u32 portid;
63 	int report;
64 };
65 
66 struct nf_ct_event_notifier {
67 	int (*fcn)(unsigned int events, struct nf_ct_event *item);
68 };
69 
70 int nf_conntrack_register_notifier(struct net *net,
71 				   struct nf_ct_event_notifier *nb);
72 void nf_conntrack_unregister_notifier(struct net *net,
73 				      struct nf_ct_event_notifier *nb);
74 
75 void nf_ct_deliver_cached_events(struct nf_conn *ct);
76 
77 static inline void
nf_conntrack_event_cache(enum ip_conntrack_events event,struct nf_conn * ct)78 nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
79 {
80 	struct net *net = nf_ct_net(ct);
81 	struct nf_conntrack_ecache *e;
82 
83 	if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb))
84 		return;
85 
86 	e = nf_ct_ecache_find(ct);
87 	if (e == NULL)
88 		return;
89 
90 	set_bit(event, &e->cache);
91 }
92 
93 static inline int
nf_conntrack_eventmask_report(unsigned int eventmask,struct nf_conn * ct,u32 portid,int report)94 nf_conntrack_eventmask_report(unsigned int eventmask,
95 			      struct nf_conn *ct,
96 			      u32 portid,
97 			      int report)
98 {
99 	int ret = 0;
100 	struct net *net = nf_ct_net(ct);
101 	struct nf_ct_event_notifier *notify;
102 	struct nf_conntrack_ecache *e;
103 
104 	rcu_read_lock();
105 	notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
106 	if (notify == NULL)
107 		goto out_unlock;
108 
109 	e = nf_ct_ecache_find(ct);
110 	if (e == NULL)
111 		goto out_unlock;
112 
113 	if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) {
114 		struct nf_ct_event item = {
115 			.ct 	= ct,
116 			.portid	= e->portid ? e->portid : portid,
117 			.report = report
118 		};
119 		/* This is a resent of a destroy event? If so, skip missed */
120 		unsigned long missed = e->portid ? 0 : e->missed;
121 
122 		if (!((eventmask | missed) & e->ctmask))
123 			goto out_unlock;
124 
125 		ret = notify->fcn(eventmask | missed, &item);
126 		if (unlikely(ret < 0 || missed)) {
127 			spin_lock_bh(&ct->lock);
128 			if (ret < 0) {
129 				/* This is a destroy event that has been
130 				 * triggered by a process, we store the PORTID
131 				 * to include it in the retransmission. */
132 				if (eventmask & (1 << IPCT_DESTROY) &&
133 				    e->portid == 0 && portid != 0)
134 					e->portid = portid;
135 				else
136 					e->missed |= eventmask;
137 			} else
138 				e->missed &= ~missed;
139 			spin_unlock_bh(&ct->lock);
140 		}
141 	}
142 out_unlock:
143 	rcu_read_unlock();
144 	return ret;
145 }
146 
147 static inline int
nf_conntrack_event_report(enum ip_conntrack_events event,struct nf_conn * ct,u32 portid,int report)148 nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct,
149 			  u32 portid, int report)
150 {
151 	return nf_conntrack_eventmask_report(1 << event, ct, portid, report);
152 }
153 
154 static inline int
nf_conntrack_event(enum ip_conntrack_events event,struct nf_conn * ct)155 nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct)
156 {
157 	return nf_conntrack_eventmask_report(1 << event, ct, 0, 0);
158 }
159 
160 struct nf_exp_event {
161 	struct nf_conntrack_expect *exp;
162 	u32 portid;
163 	int report;
164 };
165 
166 struct nf_exp_event_notifier {
167 	int (*fcn)(unsigned int events, struct nf_exp_event *item);
168 };
169 
170 int nf_ct_expect_register_notifier(struct net *net,
171 				   struct nf_exp_event_notifier *nb);
172 void nf_ct_expect_unregister_notifier(struct net *net,
173 				      struct nf_exp_event_notifier *nb);
174 
175 static inline void
nf_ct_expect_event_report(enum ip_conntrack_expect_events event,struct nf_conntrack_expect * exp,u32 portid,int report)176 nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
177 			  struct nf_conntrack_expect *exp,
178 			  u32 portid,
179 			  int report)
180 {
181 	struct net *net = nf_ct_exp_net(exp);
182 	struct nf_exp_event_notifier *notify;
183 	struct nf_conntrack_ecache *e;
184 
185 	rcu_read_lock();
186 	notify = rcu_dereference(net->ct.nf_expect_event_cb);
187 	if (notify == NULL)
188 		goto out_unlock;
189 
190 	e = nf_ct_ecache_find(exp->master);
191 	if (e == NULL)
192 		goto out_unlock;
193 
194 	if (e->expmask & (1 << event)) {
195 		struct nf_exp_event item = {
196 			.exp	= exp,
197 			.portid	= portid,
198 			.report = report
199 		};
200 		notify->fcn(1 << event, &item);
201 	}
202 out_unlock:
203 	rcu_read_unlock();
204 }
205 
206 static inline void
nf_ct_expect_event(enum ip_conntrack_expect_events event,struct nf_conntrack_expect * exp)207 nf_ct_expect_event(enum ip_conntrack_expect_events event,
208 		   struct nf_conntrack_expect *exp)
209 {
210 	nf_ct_expect_event_report(event, exp, 0, 0);
211 }
212 
213 int nf_conntrack_ecache_pernet_init(struct net *net);
214 void nf_conntrack_ecache_pernet_fini(struct net *net);
215 
216 int nf_conntrack_ecache_init(void);
217 void nf_conntrack_ecache_fini(void);
218 
nf_conntrack_ecache_delayed_work(struct net * net)219 static inline void nf_conntrack_ecache_delayed_work(struct net *net)
220 {
221 	if (!delayed_work_pending(&net->ct.ecache_dwork)) {
222 		schedule_delayed_work(&net->ct.ecache_dwork, HZ);
223 		net->ct.ecache_dwork_pending = true;
224 	}
225 }
226 
nf_conntrack_ecache_work(struct net * net)227 static inline void nf_conntrack_ecache_work(struct net *net)
228 {
229 	if (net->ct.ecache_dwork_pending) {
230 		net->ct.ecache_dwork_pending = false;
231 		mod_delayed_work(system_wq, &net->ct.ecache_dwork, 0);
232 	}
233 }
234 #else /* CONFIG_NF_CONNTRACK_EVENTS */
nf_conntrack_event_cache(enum ip_conntrack_events event,struct nf_conn * ct)235 static inline void nf_conntrack_event_cache(enum ip_conntrack_events event,
236 					    struct nf_conn *ct) {}
nf_conntrack_eventmask_report(unsigned int eventmask,struct nf_conn * ct,u32 portid,int report)237 static inline int nf_conntrack_eventmask_report(unsigned int eventmask,
238 						struct nf_conn *ct,
239 						u32 portid,
240 						int report) { return 0; }
nf_conntrack_event(enum ip_conntrack_events event,struct nf_conn * ct)241 static inline int nf_conntrack_event(enum ip_conntrack_events event,
242 				     struct nf_conn *ct) { return 0; }
nf_conntrack_event_report(enum ip_conntrack_events event,struct nf_conn * ct,u32 portid,int report)243 static inline int nf_conntrack_event_report(enum ip_conntrack_events event,
244 					    struct nf_conn *ct,
245 					    u32 portid,
246 					    int report) { return 0; }
nf_ct_deliver_cached_events(const struct nf_conn * ct)247 static inline void nf_ct_deliver_cached_events(const struct nf_conn *ct) {}
nf_ct_expect_event(enum ip_conntrack_expect_events event,struct nf_conntrack_expect * exp)248 static inline void nf_ct_expect_event(enum ip_conntrack_expect_events event,
249 				      struct nf_conntrack_expect *exp) {}
nf_ct_expect_event_report(enum ip_conntrack_expect_events e,struct nf_conntrack_expect * exp,u32 portid,int report)250 static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e,
251 					     struct nf_conntrack_expect *exp,
252  					     u32 portid,
253  					     int report) {}
254 
nf_conntrack_ecache_pernet_init(struct net * net)255 static inline int nf_conntrack_ecache_pernet_init(struct net *net)
256 {
257 	return 0;
258 }
259 
nf_conntrack_ecache_pernet_fini(struct net * net)260 static inline void nf_conntrack_ecache_pernet_fini(struct net *net)
261 {
262 }
263 
nf_conntrack_ecache_init(void)264 static inline int nf_conntrack_ecache_init(void)
265 {
266 	return 0;
267 }
268 
nf_conntrack_ecache_fini(void)269 static inline void nf_conntrack_ecache_fini(void)
270 {
271 }
272 
nf_conntrack_ecache_delayed_work(struct net * net)273 static inline void nf_conntrack_ecache_delayed_work(struct net *net)
274 {
275 }
276 
nf_conntrack_ecache_work(struct net * net)277 static inline void nf_conntrack_ecache_work(struct net *net)
278 {
279 }
280 #endif /* CONFIG_NF_CONNTRACK_EVENTS */
281 
282 #endif /*_NF_CONNTRACK_ECACHE_H*/
283 
284