Commit | Line | Data |
---|---|---|
f6180121 MJ |
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 | ||
6058fa6b | 9 | #include <net/net_namespace.h> |
f6180121 | 10 | #include <net/netfilter/nf_conntrack_expect.h> |
a0891aa6 PNA |
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> | |
f6180121 | 14 | |
a0891aa6 | 15 | struct nf_conntrack_ecache { |
0cebe4b4 PM |
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 */ | |
15e47304 | 20 | u32 portid; /* netlink portid of destroyer */ |
a0891aa6 | 21 | }; |
6bfea198 | 22 | |
a0891aa6 PNA |
23 | static inline struct nf_conntrack_ecache * |
24 | nf_ct_ecache_find(const struct nf_conn *ct) | |
25 | { | |
e0e76c83 | 26 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
a0891aa6 | 27 | return nf_ct_ext_find(ct, NF_CT_EXT_ECACHE); |
e0e76c83 CG |
28 | #else |
29 | return NULL; | |
30 | #endif | |
a0891aa6 | 31 | } |
6bfea198 | 32 | |
a0891aa6 | 33 | static inline struct nf_conntrack_ecache * |
0cebe4b4 | 34 | nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp) |
a0891aa6 | 35 | { |
e0e76c83 | 36 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
a0891aa6 | 37 | struct net *net = nf_ct_net(ct); |
0cebe4b4 | 38 | struct nf_conntrack_ecache *e; |
6bfea198 | 39 | |
0cebe4b4 PM |
40 | if (!ctmask && !expmask && net->ct.sysctl_events) { |
41 | ctmask = ~0; | |
42 | expmask = ~0; | |
43 | } | |
44 | if (!ctmask && !expmask) | |
a0891aa6 | 45 | return NULL; |
6bfea198 | 46 | |
0cebe4b4 PM |
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; | |
e0e76c83 CG |
53 | #else |
54 | return NULL; | |
55 | #endif | |
6bfea198 PNA |
56 | }; |
57 | ||
f6180121 | 58 | #ifdef CONFIG_NF_CONNTRACK_EVENTS |
19abb7b0 PNA |
59 | /* This structure is passed to event handler */ |
60 | struct nf_ct_event { | |
61 | struct nf_conn *ct; | |
15e47304 | 62 | u32 portid; |
19abb7b0 PNA |
63 | int report; |
64 | }; | |
65 | ||
e34d5c1a PNA |
66 | struct nf_ct_event_notifier { |
67 | int (*fcn)(unsigned int events, struct nf_ct_event *item); | |
68 | }; | |
69 | ||
4e77be46 JP |
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); | |
f6180121 | 74 | |
4e77be46 | 75 | void nf_ct_deliver_cached_events(struct nf_conn *ct); |
f6180121 MJ |
76 | |
77 | static inline void | |
a71996fc | 78 | nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) |
f6180121 | 79 | { |
70e9942f | 80 | struct net *net = nf_ct_net(ct); |
a0891aa6 PNA |
81 | struct nf_conntrack_ecache *e; |
82 | ||
6bd0405b | 83 | if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) |
a0891aa6 PNA |
84 | return; |
85 | ||
86 | e = nf_ct_ecache_find(ct); | |
87 | if (e == NULL) | |
88 | return; | |
89 | ||
90 | set_bit(event, &e->cache); | |
f6180121 MJ |
91 | } |
92 | ||
dd7669a9 | 93 | static inline int |
a0891aa6 PNA |
94 | nf_conntrack_eventmask_report(unsigned int eventmask, |
95 | struct nf_conn *ct, | |
15e47304 | 96 | u32 portid, |
a0891aa6 | 97 | int report) |
f6180121 | 98 | { |
dd7669a9 | 99 | int ret = 0; |
70e9942f | 100 | struct net *net = nf_ct_net(ct); |
e34d5c1a | 101 | struct nf_ct_event_notifier *notify; |
dd7669a9 | 102 | struct nf_conntrack_ecache *e; |
e34d5c1a PNA |
103 | |
104 | rcu_read_lock(); | |
70e9942f | 105 | notify = rcu_dereference(net->ct.nf_conntrack_event_cb); |
e34d5c1a PNA |
106 | if (notify == NULL) |
107 | goto out_unlock; | |
108 | ||
dd7669a9 PNA |
109 | e = nf_ct_ecache_find(ct); |
110 | if (e == NULL) | |
111 | goto out_unlock; | |
112 | ||
e34d5c1a PNA |
113 | if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) { |
114 | struct nf_ct_event item = { | |
115 | .ct = ct, | |
15e47304 | 116 | .portid = e->portid ? e->portid : portid, |
e34d5c1a PNA |
117 | .report = report |
118 | }; | |
dd7669a9 | 119 | /* This is a resent of a destroy event? If so, skip missed */ |
15e47304 | 120 | unsigned long missed = e->portid ? 0 : e->missed; |
dd7669a9 | 121 | |
0cebe4b4 PM |
122 | if (!((eventmask | missed) & e->ctmask)) |
123 | goto out_unlock; | |
124 | ||
dd7669a9 PNA |
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 | |
15e47304 | 130 | * triggered by a process, we store the PORTID |
dd7669a9 PNA |
131 | * to include it in the retransmission. */ |
132 | if (eventmask & (1 << IPCT_DESTROY) && | |
15e47304 EB |
133 | e->portid == 0 && portid != 0) |
134 | e->portid = portid; | |
dd7669a9 PNA |
135 | else |
136 | e->missed |= eventmask; | |
137 | } else | |
138 | e->missed &= ~missed; | |
139 | spin_unlock_bh(&ct->lock); | |
140 | } | |
e34d5c1a PNA |
141 | } |
142 | out_unlock: | |
143 | rcu_read_unlock(); | |
dd7669a9 | 144 | return ret; |
f6180121 MJ |
145 | } |
146 | ||
dd7669a9 | 147 | static inline int |
a0891aa6 | 148 | nf_conntrack_event_report(enum ip_conntrack_events event, struct nf_conn *ct, |
15e47304 | 149 | u32 portid, int report) |
a0891aa6 | 150 | { |
15e47304 | 151 | return nf_conntrack_eventmask_report(1 << event, ct, portid, report); |
a0891aa6 PNA |
152 | } |
153 | ||
dd7669a9 | 154 | static inline int |
19abb7b0 PNA |
155 | nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) |
156 | { | |
dd7669a9 | 157 | return nf_conntrack_eventmask_report(1 << event, ct, 0, 0); |
19abb7b0 PNA |
158 | } |
159 | ||
160 | struct nf_exp_event { | |
161 | struct nf_conntrack_expect *exp; | |
15e47304 | 162 | u32 portid; |
19abb7b0 PNA |
163 | int report; |
164 | }; | |
165 | ||
e34d5c1a PNA |
166 | struct nf_exp_event_notifier { |
167 | int (*fcn)(unsigned int events, struct nf_exp_event *item); | |
168 | }; | |
169 | ||
4e77be46 JP |
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); | |
010c7d6f | 174 | |
19abb7b0 PNA |
175 | static inline void |
176 | nf_ct_expect_event_report(enum ip_conntrack_expect_events event, | |
177 | struct nf_conntrack_expect *exp, | |
15e47304 | 178 | u32 portid, |
19abb7b0 PNA |
179 | int report) |
180 | { | |
70e9942f | 181 | struct net *net = nf_ct_exp_net(exp); |
e34d5c1a | 182 | struct nf_exp_event_notifier *notify; |
0cebe4b4 | 183 | struct nf_conntrack_ecache *e; |
e34d5c1a PNA |
184 | |
185 | rcu_read_lock(); | |
70e9942f | 186 | notify = rcu_dereference(net->ct.nf_expect_event_cb); |
e34d5c1a PNA |
187 | if (notify == NULL) |
188 | goto out_unlock; | |
189 | ||
0cebe4b4 PM |
190 | e = nf_ct_ecache_find(exp->master); |
191 | if (e == NULL) | |
a0891aa6 PNA |
192 | goto out_unlock; |
193 | ||
0cebe4b4 | 194 | if (e->expmask & (1 << event)) { |
e34d5c1a PNA |
195 | struct nf_exp_event item = { |
196 | .exp = exp, | |
15e47304 | 197 | .portid = portid, |
e34d5c1a PNA |
198 | .report = report |
199 | }; | |
a0891aa6 | 200 | notify->fcn(1 << event, &item); |
e34d5c1a PNA |
201 | } |
202 | out_unlock: | |
203 | rcu_read_unlock(); | |
19abb7b0 PNA |
204 | } |
205 | ||
f6180121 | 206 | static inline void |
6823645d PM |
207 | nf_ct_expect_event(enum ip_conntrack_expect_events event, |
208 | struct nf_conntrack_expect *exp) | |
f6180121 | 209 | { |
19abb7b0 | 210 | nf_ct_expect_event_report(event, exp, 0, 0); |
f6180121 MJ |
211 | } |
212 | ||
4e77be46 JP |
213 | int nf_conntrack_ecache_pernet_init(struct net *net); |
214 | void nf_conntrack_ecache_pernet_fini(struct net *net); | |
6058fa6b | 215 | |
4e77be46 JP |
216 | int nf_conntrack_ecache_init(void); |
217 | void nf_conntrack_ecache_fini(void); | |
f6180121 | 218 | |
9500507c FW |
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 | ||
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 */ | |
f6180121 | 235 | static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, |
64f1b653 | 236 | struct nf_conn *ct) {} |
dd7669a9 PNA |
237 | static inline int nf_conntrack_eventmask_report(unsigned int eventmask, |
238 | struct nf_conn *ct, | |
15e47304 | 239 | u32 portid, |
dd7669a9 PNA |
240 | int report) { return 0; } |
241 | static inline int nf_conntrack_event(enum ip_conntrack_events event, | |
242 | struct nf_conn *ct) { return 0; } | |
243 | static inline int nf_conntrack_event_report(enum ip_conntrack_events event, | |
244 | struct nf_conn *ct, | |
15e47304 | 245 | u32 portid, |
dd7669a9 | 246 | int report) { return 0; } |
f6180121 | 247 | static inline void nf_ct_deliver_cached_events(const struct nf_conn *ct) {} |
6823645d PM |
248 | static inline void nf_ct_expect_event(enum ip_conntrack_expect_events event, |
249 | struct nf_conntrack_expect *exp) {} | |
19abb7b0 PNA |
250 | static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e, |
251 | struct nf_conntrack_expect *exp, | |
15e47304 | 252 | u32 portid, |
19abb7b0 | 253 | int report) {} |
6058fa6b | 254 | |
3fe0f943 | 255 | static inline int nf_conntrack_ecache_pernet_init(struct net *net) |
6058fa6b AD |
256 | { |
257 | return 0; | |
bb21c95e | 258 | } |
6058fa6b | 259 | |
3fe0f943 G |
260 | static inline void nf_conntrack_ecache_pernet_fini(struct net *net) |
261 | { | |
262 | } | |
263 | ||
264 | static inline int nf_conntrack_ecache_init(void) | |
265 | { | |
266 | return 0; | |
267 | } | |
268 | ||
269 | static inline void nf_conntrack_ecache_fini(void) | |
6058fa6b AD |
270 | { |
271 | } | |
9500507c FW |
272 | |
273 | static inline void nf_conntrack_ecache_delayed_work(struct net *net) | |
274 | { | |
275 | } | |
276 | ||
277 | static inline void nf_conntrack_ecache_work(struct net *net) | |
278 | { | |
279 | } | |
f6180121 MJ |
280 | #endif /* CONFIG_NF_CONNTRACK_EVENTS */ |
281 | ||
282 | #endif /*_NF_CONNTRACK_ECACHE_H*/ | |
283 |