Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
77ab9cff MJ |
2 | /* Expectation handling for nf_conntrack. */ |
3 | ||
4 | /* (C) 1999-2001 Paul `Rusty' Russell | |
5 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | |
6 | * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> | |
f229f6ce | 7 | * (c) 2005-2012 Patrick McHardy <kaber@trash.net> |
77ab9cff MJ |
8 | */ |
9 | ||
10 | #include <linux/types.h> | |
11 | #include <linux/netfilter.h> | |
12 | #include <linux/skbuff.h> | |
13 | #include <linux/proc_fs.h> | |
14 | #include <linux/seq_file.h> | |
15 | #include <linux/stddef.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/percpu.h> | |
19 | #include <linux/kernel.h> | |
dd6d2910 | 20 | #include <linux/siphash.h> |
d9b93842 | 21 | #include <linux/moduleparam.h> |
bc3b2d7f | 22 | #include <linux/export.h> |
457c4cbc | 23 | #include <net/net_namespace.h> |
a9a083c3 | 24 | #include <net/netns/hash.h> |
77ab9cff MJ |
25 | |
26 | #include <net/netfilter/nf_conntrack.h> | |
27 | #include <net/netfilter/nf_conntrack_core.h> | |
40d102cd | 28 | #include <net/netfilter/nf_conntrack_ecache.h> |
77ab9cff MJ |
29 | #include <net/netfilter/nf_conntrack_expect.h> |
30 | #include <net/netfilter/nf_conntrack_helper.h> | |
40d102cd | 31 | #include <net/netfilter/nf_conntrack_l4proto.h> |
77ab9cff | 32 | #include <net/netfilter/nf_conntrack_tuple.h> |
5d0aa2cc | 33 | #include <net/netfilter/nf_conntrack_zones.h> |
77ab9cff | 34 | |
a71c0855 PM |
35 | unsigned int nf_ct_expect_hsize __read_mostly; |
36 | EXPORT_SYMBOL_GPL(nf_ct_expect_hsize); | |
37 | ||
0a93aaed FW |
38 | struct hlist_head *nf_ct_expect_hash __read_mostly; |
39 | EXPORT_SYMBOL_GPL(nf_ct_expect_hash); | |
40 | ||
f264a7df | 41 | unsigned int nf_ct_expect_max __read_mostly; |
a71c0855 | 42 | |
e9c1b084 | 43 | static struct kmem_cache *nf_ct_expect_cachep __read_mostly; |
49ecc2e9 | 44 | static siphash_aligned_key_t nf_ct_expect_hashrnd; |
77ab9cff MJ |
45 | |
46 | /* nf_conntrack_expect helper functions */ | |
ebbf41df | 47 | void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, |
ec464e5d | 48 | u32 portid, int report) |
77ab9cff MJ |
49 | { |
50 | struct nf_conn_help *master_help = nfct_help(exp->master); | |
9b03f38d | 51 | struct net *net = nf_ct_exp_net(exp); |
f6f2e580 | 52 | struct nf_conntrack_net *cnet; |
77ab9cff | 53 | |
44d6e2f2 VR |
54 | WARN_ON(!master_help); |
55 | WARN_ON(timer_pending(&exp->timeout)); | |
77ab9cff | 56 | |
7d0742da | 57 | hlist_del_rcu(&exp->hnode); |
f6f2e580 | 58 | |
0418b989 | 59 | cnet = nf_ct_pernet(net); |
f6f2e580 | 60 | cnet->expect_count--; |
a71c0855 | 61 | |
7cddd967 | 62 | hlist_del_rcu(&exp->lnode); |
3d058d7b | 63 | master_help->expecting[exp->class]--; |
bc01befd | 64 | |
ec464e5d | 65 | nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report); |
6823645d | 66 | nf_ct_expect_put(exp); |
b560580a | 67 | |
0d55af87 | 68 | NF_CT_STAT_INC(net, expect_delete); |
77ab9cff | 69 | } |
ebbf41df | 70 | EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report); |
77ab9cff | 71 | |
e99e88a9 | 72 | static void nf_ct_expectation_timed_out(struct timer_list *t) |
77ab9cff | 73 | { |
e99e88a9 | 74 | struct nf_conntrack_expect *exp = from_timer(exp, t, timeout); |
77ab9cff | 75 | |
ca7433df | 76 | spin_lock_bh(&nf_conntrack_expect_lock); |
77ab9cff | 77 | nf_ct_unlink_expect(exp); |
ca7433df | 78 | spin_unlock_bh(&nf_conntrack_expect_lock); |
6823645d | 79 | nf_ct_expect_put(exp); |
77ab9cff MJ |
80 | } |
81 | ||
a9a083c3 | 82 | static unsigned int nf_ct_expect_dst_hash(const struct net *n, const struct nf_conntrack_tuple *tuple) |
a71c0855 | 83 | { |
dd6d2910 FW |
84 | struct { |
85 | union nf_inet_addr dst_addr; | |
86 | u32 net_mix; | |
87 | u16 dport; | |
88 | u8 l3num; | |
89 | u8 protonum; | |
90 | } __aligned(SIPHASH_ALIGNMENT) combined; | |
91 | u32 hash; | |
34498825 | 92 | |
7001c6d1 | 93 | get_random_once(&nf_ct_expect_hashrnd, sizeof(nf_ct_expect_hashrnd)); |
a71c0855 | 94 | |
dd6d2910 | 95 | memset(&combined, 0, sizeof(combined)); |
a9a083c3 | 96 | |
dd6d2910 FW |
97 | combined.dst_addr = tuple->dst.u3; |
98 | combined.net_mix = net_hash_mix(n); | |
99 | combined.dport = (__force __u16)tuple->dst.u.all; | |
100 | combined.l3num = tuple->src.l3num; | |
101 | combined.protonum = tuple->dst.protonum; | |
102 | ||
103 | hash = siphash(&combined, sizeof(combined), &nf_ct_expect_hashrnd); | |
8fc54f68 DB |
104 | |
105 | return reciprocal_scale(hash, nf_ct_expect_hsize); | |
a71c0855 PM |
106 | } |
107 | ||
03d7dc5c FW |
108 | static bool |
109 | nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple, | |
110 | const struct nf_conntrack_expect *i, | |
111 | const struct nf_conntrack_zone *zone, | |
112 | const struct net *net) | |
113 | { | |
114 | return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) && | |
115 | net_eq(net, nf_ct_net(i->master)) && | |
116 | nf_ct_zone_equal_any(i->master, zone); | |
117 | } | |
118 | ||
ec0e3f01 GF |
119 | bool nf_ct_remove_expect(struct nf_conntrack_expect *exp) |
120 | { | |
121 | if (del_timer(&exp->timeout)) { | |
122 | nf_ct_unlink_expect(exp); | |
123 | nf_ct_expect_put(exp); | |
124 | return true; | |
125 | } | |
126 | return false; | |
127 | } | |
128 | EXPORT_SYMBOL_GPL(nf_ct_remove_expect); | |
129 | ||
77ab9cff | 130 | struct nf_conntrack_expect * |
308ac914 DB |
131 | __nf_ct_expect_find(struct net *net, |
132 | const struct nf_conntrack_zone *zone, | |
5d0aa2cc | 133 | const struct nf_conntrack_tuple *tuple) |
77ab9cff | 134 | { |
0418b989 | 135 | struct nf_conntrack_net *cnet = nf_ct_pernet(net); |
77ab9cff | 136 | struct nf_conntrack_expect *i; |
a71c0855 PM |
137 | unsigned int h; |
138 | ||
f6f2e580 | 139 | if (!cnet->expect_count) |
a71c0855 | 140 | return NULL; |
77ab9cff | 141 | |
a9a083c3 | 142 | h = nf_ct_expect_dst_hash(net, tuple); |
0a93aaed | 143 | hlist_for_each_entry_rcu(i, &nf_ct_expect_hash[h], hnode) { |
03d7dc5c | 144 | if (nf_ct_exp_equal(tuple, i, zone, net)) |
77ab9cff MJ |
145 | return i; |
146 | } | |
147 | return NULL; | |
148 | } | |
6823645d | 149 | EXPORT_SYMBOL_GPL(__nf_ct_expect_find); |
77ab9cff MJ |
150 | |
151 | /* Just find a expectation corresponding to a tuple. */ | |
152 | struct nf_conntrack_expect * | |
308ac914 DB |
153 | nf_ct_expect_find_get(struct net *net, |
154 | const struct nf_conntrack_zone *zone, | |
5d0aa2cc | 155 | const struct nf_conntrack_tuple *tuple) |
77ab9cff MJ |
156 | { |
157 | struct nf_conntrack_expect *i; | |
158 | ||
7d0742da | 159 | rcu_read_lock(); |
5d0aa2cc | 160 | i = __nf_ct_expect_find(net, zone, tuple); |
b54ab92b | 161 | if (i && !refcount_inc_not_zero(&i->use)) |
7d0742da PM |
162 | i = NULL; |
163 | rcu_read_unlock(); | |
77ab9cff MJ |
164 | |
165 | return i; | |
166 | } | |
6823645d | 167 | EXPORT_SYMBOL_GPL(nf_ct_expect_find_get); |
77ab9cff MJ |
168 | |
169 | /* If an expectation for this connection is found, it gets delete from | |
170 | * global list then returned. */ | |
171 | struct nf_conntrack_expect * | |
308ac914 DB |
172 | nf_ct_find_expectation(struct net *net, |
173 | const struct nf_conntrack_zone *zone, | |
4914109a | 174 | const struct nf_conntrack_tuple *tuple, bool unlink) |
77ab9cff | 175 | { |
0418b989 | 176 | struct nf_conntrack_net *cnet = nf_ct_pernet(net); |
359b9ab6 | 177 | struct nf_conntrack_expect *i, *exp = NULL; |
359b9ab6 PM |
178 | unsigned int h; |
179 | ||
f6f2e580 | 180 | if (!cnet->expect_count) |
359b9ab6 | 181 | return NULL; |
ece00641 | 182 | |
a9a083c3 | 183 | h = nf_ct_expect_dst_hash(net, tuple); |
0a93aaed | 184 | hlist_for_each_entry(i, &nf_ct_expect_hash[h], hnode) { |
359b9ab6 | 185 | if (!(i->flags & NF_CT_EXPECT_INACTIVE) && |
03d7dc5c | 186 | nf_ct_exp_equal(tuple, i, zone, net)) { |
359b9ab6 PM |
187 | exp = i; |
188 | break; | |
189 | } | |
190 | } | |
ece00641 YK |
191 | if (!exp) |
192 | return NULL; | |
77ab9cff | 193 | |
77ab9cff MJ |
194 | /* If master is not in hash table yet (ie. packet hasn't left |
195 | this machine yet), how can other end know about expected? | |
196 | Hence these are not the droids you are looking for (if | |
197 | master ct never got confirmed, we'd hold a reference to it | |
198 | and weird things would happen to future packets). */ | |
ece00641 YK |
199 | if (!nf_ct_is_confirmed(exp->master)) |
200 | return NULL; | |
201 | ||
e1b207da JDB |
202 | /* Avoid race with other CPUs, that for exp->master ct, is |
203 | * about to invoke ->destroy(), or nf_ct_delete() via timeout | |
204 | * or early_drop(). | |
205 | * | |
71977437 | 206 | * The refcount_inc_not_zero() check tells: If that fails, we |
e1b207da JDB |
207 | * know that the ct is being destroyed. If it succeeds, we |
208 | * can be sure the ct cannot disappear underneath. | |
209 | */ | |
210 | if (unlikely(nf_ct_is_dying(exp->master) || | |
71977437 | 211 | !refcount_inc_not_zero(&exp->master->ct_general.use))) |
e1b207da JDB |
212 | return NULL; |
213 | ||
4914109a | 214 | if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) { |
b54ab92b | 215 | refcount_inc(&exp->use); |
ece00641 YK |
216 | return exp; |
217 | } else if (del_timer(&exp->timeout)) { | |
218 | nf_ct_unlink_expect(exp); | |
219 | return exp; | |
77ab9cff | 220 | } |
e1b207da JDB |
221 | /* Undo exp->master refcnt increase, if del_timer() failed */ |
222 | nf_ct_put(exp->master); | |
ece00641 | 223 | |
77ab9cff MJ |
224 | return NULL; |
225 | } | |
226 | ||
227 | /* delete all expectations for this conntrack */ | |
228 | void nf_ct_remove_expectations(struct nf_conn *ct) | |
229 | { | |
77ab9cff | 230 | struct nf_conn_help *help = nfct_help(ct); |
b560580a | 231 | struct nf_conntrack_expect *exp; |
b67bfe0d | 232 | struct hlist_node *next; |
77ab9cff MJ |
233 | |
234 | /* Optimization: most connection never expect any others. */ | |
6002f266 | 235 | if (!help) |
77ab9cff MJ |
236 | return; |
237 | ||
ca7433df | 238 | spin_lock_bh(&nf_conntrack_expect_lock); |
b67bfe0d | 239 | hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { |
ec0e3f01 | 240 | nf_ct_remove_expect(exp); |
77ab9cff | 241 | } |
ca7433df | 242 | spin_unlock_bh(&nf_conntrack_expect_lock); |
77ab9cff | 243 | } |
13b18339 | 244 | EXPORT_SYMBOL_GPL(nf_ct_remove_expectations); |
77ab9cff MJ |
245 | |
246 | /* Would two expected things clash? */ | |
247 | static inline int expect_clash(const struct nf_conntrack_expect *a, | |
248 | const struct nf_conntrack_expect *b) | |
249 | { | |
250 | /* Part covered by intersection of masks must be unequal, | |
251 | otherwise they clash */ | |
d4156e8c | 252 | struct nf_conntrack_tuple_mask intersect_mask; |
77ab9cff MJ |
253 | int count; |
254 | ||
77ab9cff | 255 | intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all; |
77ab9cff MJ |
256 | |
257 | for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){ | |
258 | intersect_mask.src.u3.all[count] = | |
259 | a->mask.src.u3.all[count] & b->mask.src.u3.all[count]; | |
260 | } | |
261 | ||
4b31814d | 262 | return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask) && |
03d7dc5c | 263 | net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) && |
deedb590 | 264 | nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master)); |
77ab9cff MJ |
265 | } |
266 | ||
267 | static inline int expect_matches(const struct nf_conntrack_expect *a, | |
268 | const struct nf_conntrack_expect *b) | |
269 | { | |
3c00fb0b | 270 | return nf_ct_tuple_equal(&a->tuple, &b->tuple) && |
308ac914 | 271 | nf_ct_tuple_mask_equal(&a->mask, &b->mask) && |
03d7dc5c | 272 | net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) && |
deedb590 | 273 | nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master)); |
77ab9cff MJ |
274 | } |
275 | ||
3c00fb0b | 276 | static bool master_matches(const struct nf_conntrack_expect *a, |
277 | const struct nf_conntrack_expect *b, | |
278 | unsigned int flags) | |
279 | { | |
280 | if (flags & NF_CT_EXP_F_SKIP_MASTER) | |
281 | return true; | |
282 | ||
283 | return a->master == b->master; | |
284 | } | |
285 | ||
77ab9cff | 286 | /* Generally a bad idea to call this: could have matched already. */ |
6823645d | 287 | void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) |
77ab9cff | 288 | { |
ca7433df | 289 | spin_lock_bh(&nf_conntrack_expect_lock); |
ec0e3f01 | 290 | nf_ct_remove_expect(exp); |
ca7433df | 291 | spin_unlock_bh(&nf_conntrack_expect_lock); |
77ab9cff | 292 | } |
6823645d | 293 | EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); |
77ab9cff MJ |
294 | |
295 | /* We don't increase the master conntrack refcount for non-fulfilled | |
296 | * conntracks. During the conntrack destruction, the expectations are | |
297 | * always killed before the conntrack itself */ | |
6823645d | 298 | struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) |
77ab9cff MJ |
299 | { |
300 | struct nf_conntrack_expect *new; | |
301 | ||
6823645d | 302 | new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC); |
77ab9cff MJ |
303 | if (!new) |
304 | return NULL; | |
305 | ||
306 | new->master = me; | |
b54ab92b | 307 | refcount_set(&new->use, 1); |
77ab9cff MJ |
308 | return new; |
309 | } | |
6823645d | 310 | EXPORT_SYMBOL_GPL(nf_ct_expect_alloc); |
77ab9cff | 311 | |
6002f266 | 312 | void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, |
76108cea | 313 | u_int8_t family, |
1d9d7522 PM |
314 | const union nf_inet_addr *saddr, |
315 | const union nf_inet_addr *daddr, | |
316 | u_int8_t proto, const __be16 *src, const __be16 *dst) | |
d6a9b650 PM |
317 | { |
318 | int len; | |
319 | ||
320 | if (family == AF_INET) | |
321 | len = 4; | |
322 | else | |
323 | len = 16; | |
324 | ||
325 | exp->flags = 0; | |
6002f266 | 326 | exp->class = class; |
d6a9b650 PM |
327 | exp->expectfn = NULL; |
328 | exp->helper = NULL; | |
329 | exp->tuple.src.l3num = family; | |
330 | exp->tuple.dst.protonum = proto; | |
d6a9b650 PM |
331 | |
332 | if (saddr) { | |
333 | memcpy(&exp->tuple.src.u3, saddr, len); | |
334 | if (sizeof(exp->tuple.src.u3) > len) | |
335 | /* address needs to be cleared for nf_ct_tuple_equal */ | |
336 | memset((void *)&exp->tuple.src.u3 + len, 0x00, | |
337 | sizeof(exp->tuple.src.u3) - len); | |
338 | memset(&exp->mask.src.u3, 0xFF, len); | |
339 | if (sizeof(exp->mask.src.u3) > len) | |
340 | memset((void *)&exp->mask.src.u3 + len, 0x00, | |
341 | sizeof(exp->mask.src.u3) - len); | |
342 | } else { | |
343 | memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3)); | |
344 | memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3)); | |
345 | } | |
346 | ||
d6a9b650 | 347 | if (src) { |
a34c4589 AV |
348 | exp->tuple.src.u.all = *src; |
349 | exp->mask.src.u.all = htons(0xFFFF); | |
d6a9b650 PM |
350 | } else { |
351 | exp->tuple.src.u.all = 0; | |
352 | exp->mask.src.u.all = 0; | |
353 | } | |
354 | ||
d4156e8c PM |
355 | memcpy(&exp->tuple.dst.u3, daddr, len); |
356 | if (sizeof(exp->tuple.dst.u3) > len) | |
357 | /* address needs to be cleared for nf_ct_tuple_equal */ | |
358 | memset((void *)&exp->tuple.dst.u3 + len, 0x00, | |
359 | sizeof(exp->tuple.dst.u3) - len); | |
360 | ||
a34c4589 | 361 | exp->tuple.dst.u.all = *dst; |
f09eca8d | 362 | |
4806e975 | 363 | #if IS_ENABLED(CONFIG_NF_NAT) |
f09eca8d PNA |
364 | memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); |
365 | memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); | |
366 | #endif | |
d6a9b650 | 367 | } |
6823645d | 368 | EXPORT_SYMBOL_GPL(nf_ct_expect_init); |
d6a9b650 | 369 | |
7d0742da PM |
370 | static void nf_ct_expect_free_rcu(struct rcu_head *head) |
371 | { | |
372 | struct nf_conntrack_expect *exp; | |
373 | ||
374 | exp = container_of(head, struct nf_conntrack_expect, rcu); | |
375 | kmem_cache_free(nf_ct_expect_cachep, exp); | |
376 | } | |
377 | ||
6823645d | 378 | void nf_ct_expect_put(struct nf_conntrack_expect *exp) |
77ab9cff | 379 | { |
b54ab92b | 380 | if (refcount_dec_and_test(&exp->use)) |
7d0742da | 381 | call_rcu(&exp->rcu, nf_ct_expect_free_rcu); |
77ab9cff | 382 | } |
6823645d | 383 | EXPORT_SYMBOL_GPL(nf_ct_expect_put); |
77ab9cff | 384 | |
4dee62b1 | 385 | static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) |
77ab9cff | 386 | { |
f6f2e580 | 387 | struct nf_conntrack_net *cnet; |
77ab9cff | 388 | struct nf_conn_help *master_help = nfct_help(exp->master); |
3d058d7b | 389 | struct nf_conntrack_helper *helper; |
9b03f38d | 390 | struct net *net = nf_ct_exp_net(exp); |
a9a083c3 | 391 | unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); |
77ab9cff | 392 | |
3bfd45f9 | 393 | /* two references : one for hash insert, one for the timer */ |
b54ab92b | 394 | refcount_add(2, &exp->use); |
b560580a | 395 | |
e99e88a9 | 396 | timer_setup(&exp->timeout, nf_ct_expectation_timed_out, 0); |
3d058d7b | 397 | helper = rcu_dereference_protected(master_help->helper, |
ca7433df | 398 | lockdep_is_held(&nf_conntrack_expect_lock)); |
3d058d7b PNA |
399 | if (helper) { |
400 | exp->timeout.expires = jiffies + | |
401 | helper->expect_policy[exp->class].timeout * HZ; | |
bc01befd | 402 | } |
77ab9cff MJ |
403 | add_timer(&exp->timeout); |
404 | ||
56a97e70 FW |
405 | hlist_add_head_rcu(&exp->lnode, &master_help->expectations); |
406 | master_help->expecting[exp->class]++; | |
407 | ||
408 | hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]); | |
0418b989 | 409 | cnet = nf_ct_pernet(net); |
f6f2e580 | 410 | cnet->expect_count++; |
56a97e70 | 411 | |
0d55af87 | 412 | NF_CT_STAT_INC(net, expect_create); |
77ab9cff MJ |
413 | } |
414 | ||
415 | /* Race with expectations being used means we could have none to find; OK. */ | |
6002f266 PM |
416 | static void evict_oldest_expect(struct nf_conn *master, |
417 | struct nf_conntrack_expect *new) | |
77ab9cff | 418 | { |
b560580a | 419 | struct nf_conn_help *master_help = nfct_help(master); |
6002f266 | 420 | struct nf_conntrack_expect *exp, *last = NULL; |
77ab9cff | 421 | |
b67bfe0d | 422 | hlist_for_each_entry(exp, &master_help->expectations, lnode) { |
6002f266 PM |
423 | if (exp->class == new->class) |
424 | last = exp; | |
425 | } | |
b560580a | 426 | |
ec0e3f01 GF |
427 | if (last) |
428 | nf_ct_remove_expect(last); | |
77ab9cff MJ |
429 | } |
430 | ||
3c00fb0b | 431 | static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, |
432 | unsigned int flags) | |
77ab9cff | 433 | { |
6002f266 | 434 | const struct nf_conntrack_expect_policy *p; |
77ab9cff | 435 | struct nf_conntrack_expect *i; |
f6f2e580 | 436 | struct nf_conntrack_net *cnet; |
77ab9cff MJ |
437 | struct nf_conn *master = expect->master; |
438 | struct nf_conn_help *master_help = nfct_help(master); | |
3d058d7b | 439 | struct nf_conntrack_helper *helper; |
9b03f38d | 440 | struct net *net = nf_ct_exp_net(expect); |
b67bfe0d | 441 | struct hlist_node *next; |
a71c0855 | 442 | unsigned int h; |
4b86c459 | 443 | int ret = 0; |
77ab9cff | 444 | |
3d058d7b | 445 | if (!master_help) { |
3c158f7f PM |
446 | ret = -ESHUTDOWN; |
447 | goto out; | |
448 | } | |
a9a083c3 | 449 | h = nf_ct_expect_dst_hash(net, &expect->tuple); |
0a93aaed | 450 | hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { |
3c00fb0b | 451 | if (master_matches(i, expect, flags) && |
452 | expect_matches(i, expect)) { | |
453 | if (i->class != expect->class || | |
454 | i->master != expect->master) | |
876c2731 FW |
455 | return -EALREADY; |
456 | ||
36ac344e | 457 | if (nf_ct_remove_expect(i)) |
2614f864 | 458 | break; |
77ab9cff MJ |
459 | } else if (expect_clash(i, expect)) { |
460 | ret = -EBUSY; | |
461 | goto out; | |
462 | } | |
463 | } | |
464 | /* Will be over limit? */ | |
3d058d7b | 465 | helper = rcu_dereference_protected(master_help->helper, |
ca7433df | 466 | lockdep_is_held(&nf_conntrack_expect_lock)); |
3d058d7b PNA |
467 | if (helper) { |
468 | p = &helper->expect_policy[expect->class]; | |
bc01befd PNA |
469 | if (p->max_expected && |
470 | master_help->expecting[expect->class] >= p->max_expected) { | |
471 | evict_oldest_expect(master, expect); | |
472 | if (master_help->expecting[expect->class] | |
473 | >= p->max_expected) { | |
474 | ret = -EMFILE; | |
475 | goto out; | |
476 | } | |
6002f266 PM |
477 | } |
478 | } | |
77ab9cff | 479 | |
0418b989 | 480 | cnet = nf_ct_pernet(net); |
f6f2e580 | 481 | if (cnet->expect_count >= nf_ct_expect_max) { |
e87cc472 | 482 | net_warn_ratelimited("nf_conntrack: expectation table full\n"); |
f264a7df | 483 | ret = -EMFILE; |
f264a7df | 484 | } |
19abb7b0 PNA |
485 | out: |
486 | return ret; | |
487 | } | |
488 | ||
b476b72a | 489 | int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, |
3c00fb0b | 490 | u32 portid, int report, unsigned int flags) |
19abb7b0 PNA |
491 | { |
492 | int ret; | |
493 | ||
ca7433df | 494 | spin_lock_bh(&nf_conntrack_expect_lock); |
3c00fb0b | 495 | ret = __nf_ct_expect_check(expect, flags); |
4b86c459 | 496 | if (ret < 0) |
19abb7b0 | 497 | goto out; |
f264a7df | 498 | |
4dee62b1 GF |
499 | nf_ct_expect_insert(expect); |
500 | ||
ca7433df | 501 | spin_unlock_bh(&nf_conntrack_expect_lock); |
ec464e5d | 502 | nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); |
7fb668ac | 503 | return 0; |
19abb7b0 | 504 | out: |
ca7433df | 505 | spin_unlock_bh(&nf_conntrack_expect_lock); |
19abb7b0 PNA |
506 | return ret; |
507 | } | |
508 | EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); | |
509 | ||
ac7b8483 FW |
510 | void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), |
511 | void *data) | |
512 | { | |
513 | struct nf_conntrack_expect *exp; | |
514 | const struct hlist_node *next; | |
515 | unsigned int i; | |
516 | ||
517 | spin_lock_bh(&nf_conntrack_expect_lock); | |
518 | ||
519 | for (i = 0; i < nf_ct_expect_hsize; i++) { | |
520 | hlist_for_each_entry_safe(exp, next, | |
521 | &nf_ct_expect_hash[i], | |
522 | hnode) { | |
523 | if (iter(exp, data) && del_timer(&exp->timeout)) { | |
524 | nf_ct_unlink_expect(exp); | |
525 | nf_ct_expect_put(exp); | |
526 | } | |
527 | } | |
528 | } | |
529 | ||
530 | spin_unlock_bh(&nf_conntrack_expect_lock); | |
531 | } | |
532 | EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_destroy); | |
533 | ||
534 | void nf_ct_expect_iterate_net(struct net *net, | |
535 | bool (*iter)(struct nf_conntrack_expect *e, void *data), | |
536 | void *data, | |
537 | u32 portid, int report) | |
538 | { | |
539 | struct nf_conntrack_expect *exp; | |
540 | const struct hlist_node *next; | |
541 | unsigned int i; | |
542 | ||
543 | spin_lock_bh(&nf_conntrack_expect_lock); | |
544 | ||
545 | for (i = 0; i < nf_ct_expect_hsize; i++) { | |
546 | hlist_for_each_entry_safe(exp, next, | |
547 | &nf_ct_expect_hash[i], | |
548 | hnode) { | |
549 | ||
550 | if (!net_eq(nf_ct_exp_net(exp), net)) | |
551 | continue; | |
552 | ||
553 | if (iter(exp, data) && del_timer(&exp->timeout)) { | |
554 | nf_ct_unlink_expect_report(exp, portid, report); | |
555 | nf_ct_expect_put(exp); | |
556 | } | |
557 | } | |
558 | } | |
559 | ||
560 | spin_unlock_bh(&nf_conntrack_expect_lock); | |
561 | } | |
562 | EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_net); | |
563 | ||
54b07dca | 564 | #ifdef CONFIG_NF_CONNTRACK_PROCFS |
5d08ad44 | 565 | struct ct_expect_iter_state { |
dc5129f8 | 566 | struct seq_net_private p; |
5d08ad44 PM |
567 | unsigned int bucket; |
568 | }; | |
569 | ||
570 | static struct hlist_node *ct_expect_get_first(struct seq_file *seq) | |
77ab9cff | 571 | { |
5d08ad44 | 572 | struct ct_expect_iter_state *st = seq->private; |
7d0742da | 573 | struct hlist_node *n; |
77ab9cff | 574 | |
5d08ad44 | 575 | for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) { |
0a93aaed | 576 | n = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket])); |
7d0742da PM |
577 | if (n) |
578 | return n; | |
5d08ad44 PM |
579 | } |
580 | return NULL; | |
581 | } | |
77ab9cff | 582 | |
5d08ad44 PM |
583 | static struct hlist_node *ct_expect_get_next(struct seq_file *seq, |
584 | struct hlist_node *head) | |
585 | { | |
586 | struct ct_expect_iter_state *st = seq->private; | |
77ab9cff | 587 | |
0e60ebe0 | 588 | head = rcu_dereference(hlist_next_rcu(head)); |
5d08ad44 PM |
589 | while (head == NULL) { |
590 | if (++st->bucket >= nf_ct_expect_hsize) | |
77ab9cff | 591 | return NULL; |
0a93aaed | 592 | head = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket])); |
77ab9cff | 593 | } |
5d08ad44 | 594 | return head; |
77ab9cff MJ |
595 | } |
596 | ||
5d08ad44 | 597 | static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos) |
77ab9cff | 598 | { |
5d08ad44 | 599 | struct hlist_node *head = ct_expect_get_first(seq); |
77ab9cff | 600 | |
5d08ad44 PM |
601 | if (head) |
602 | while (pos && (head = ct_expect_get_next(seq, head))) | |
603 | pos--; | |
604 | return pos ? NULL : head; | |
605 | } | |
77ab9cff | 606 | |
5d08ad44 | 607 | static void *exp_seq_start(struct seq_file *seq, loff_t *pos) |
7d0742da | 608 | __acquires(RCU) |
5d08ad44 | 609 | { |
7d0742da | 610 | rcu_read_lock(); |
5d08ad44 PM |
611 | return ct_expect_get_idx(seq, *pos); |
612 | } | |
77ab9cff | 613 | |
5d08ad44 PM |
614 | static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
615 | { | |
616 | (*pos)++; | |
617 | return ct_expect_get_next(seq, v); | |
77ab9cff MJ |
618 | } |
619 | ||
5d08ad44 | 620 | static void exp_seq_stop(struct seq_file *seq, void *v) |
7d0742da | 621 | __releases(RCU) |
77ab9cff | 622 | { |
7d0742da | 623 | rcu_read_unlock(); |
77ab9cff MJ |
624 | } |
625 | ||
626 | static int exp_seq_show(struct seq_file *s, void *v) | |
627 | { | |
5d08ad44 | 628 | struct nf_conntrack_expect *expect; |
b87921bd | 629 | struct nf_conntrack_helper *helper; |
5d08ad44 | 630 | struct hlist_node *n = v; |
359b9ab6 | 631 | char *delim = ""; |
5d08ad44 PM |
632 | |
633 | expect = hlist_entry(n, struct nf_conntrack_expect, hnode); | |
77ab9cff MJ |
634 | |
635 | if (expect->timeout.function) | |
636 | seq_printf(s, "%ld ", timer_pending(&expect->timeout) | |
637 | ? (long)(expect->timeout.expires - jiffies)/HZ : 0); | |
638 | else | |
cdec2685 | 639 | seq_puts(s, "- "); |
77ab9cff MJ |
640 | seq_printf(s, "l3proto = %u proto=%u ", |
641 | expect->tuple.src.l3num, | |
642 | expect->tuple.dst.protonum); | |
643 | print_tuple(s, &expect->tuple, | |
4a60dc74 | 644 | nf_ct_l4proto_find(expect->tuple.dst.protonum)); |
4bb119ea | 645 | |
359b9ab6 | 646 | if (expect->flags & NF_CT_EXPECT_PERMANENT) { |
cdec2685 | 647 | seq_puts(s, "PERMANENT"); |
359b9ab6 PM |
648 | delim = ","; |
649 | } | |
bc01befd | 650 | if (expect->flags & NF_CT_EXPECT_INACTIVE) { |
359b9ab6 | 651 | seq_printf(s, "%sINACTIVE", delim); |
bc01befd PNA |
652 | delim = ","; |
653 | } | |
654 | if (expect->flags & NF_CT_EXPECT_USERSPACE) | |
655 | seq_printf(s, "%sUSERSPACE", delim); | |
4bb119ea | 656 | |
b87921bd PM |
657 | helper = rcu_dereference(nfct_help(expect->master)->helper); |
658 | if (helper) { | |
659 | seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name); | |
b173a28f | 660 | if (helper->expect_policy[expect->class].name[0]) |
b87921bd PM |
661 | seq_printf(s, "/%s", |
662 | helper->expect_policy[expect->class].name); | |
663 | } | |
664 | ||
1ca9e417 JP |
665 | seq_putc(s, '\n'); |
666 | ||
667 | return 0; | |
77ab9cff MJ |
668 | } |
669 | ||
56b3d975 | 670 | static const struct seq_operations exp_seq_ops = { |
77ab9cff MJ |
671 | .start = exp_seq_start, |
672 | .next = exp_seq_next, | |
673 | .stop = exp_seq_stop, | |
674 | .show = exp_seq_show | |
675 | }; | |
54b07dca | 676 | #endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084 | 677 | |
dc5129f8 | 678 | static int exp_proc_init(struct net *net) |
e9c1b084 | 679 | { |
54b07dca | 680 | #ifdef CONFIG_NF_CONNTRACK_PROCFS |
e9c1b084 | 681 | struct proc_dir_entry *proc; |
f13f2aee PW |
682 | kuid_t root_uid; |
683 | kgid_t root_gid; | |
e9c1b084 | 684 | |
c3506372 CH |
685 | proc = proc_create_net("nf_conntrack_expect", 0440, net->proc_net, |
686 | &exp_seq_ops, sizeof(struct ct_expect_iter_state)); | |
e9c1b084 PM |
687 | if (!proc) |
688 | return -ENOMEM; | |
f13f2aee PW |
689 | |
690 | root_uid = make_kuid(net->user_ns, 0); | |
691 | root_gid = make_kgid(net->user_ns, 0); | |
692 | if (uid_valid(root_uid) && gid_valid(root_gid)) | |
693 | proc_set_user(proc, root_uid, root_gid); | |
54b07dca | 694 | #endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084 PM |
695 | return 0; |
696 | } | |
697 | ||
dc5129f8 | 698 | static void exp_proc_remove(struct net *net) |
e9c1b084 | 699 | { |
54b07dca | 700 | #ifdef CONFIG_NF_CONNTRACK_PROCFS |
ece31ffd | 701 | remove_proc_entry("nf_conntrack_expect", net->proc_net); |
54b07dca | 702 | #endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084 PM |
703 | } |
704 | ||
13ccdfc2 | 705 | module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400); |
a71c0855 | 706 | |
83b4dbe1 | 707 | int nf_conntrack_expect_pernet_init(struct net *net) |
e9c1b084 | 708 | { |
0a93aaed | 709 | return exp_proc_init(net); |
e9c1b084 PM |
710 | } |
711 | ||
83b4dbe1 | 712 | void nf_conntrack_expect_pernet_fini(struct net *net) |
e9c1b084 | 713 | { |
dc5129f8 | 714 | exp_proc_remove(net); |
e9c1b084 | 715 | } |
83b4dbe1 G |
716 | |
717 | int nf_conntrack_expect_init(void) | |
718 | { | |
719 | if (!nf_ct_expect_hsize) { | |
720 | nf_ct_expect_hsize = nf_conntrack_htable_size / 256; | |
721 | if (!nf_ct_expect_hsize) | |
722 | nf_ct_expect_hsize = 1; | |
723 | } | |
724 | nf_ct_expect_max = nf_ct_expect_hsize * 4; | |
aa23cfe6 | 725 | nf_ct_expect_cachep = KMEM_CACHE(nf_conntrack_expect, 0); |
83b4dbe1 G |
726 | if (!nf_ct_expect_cachep) |
727 | return -ENOMEM; | |
0a93aaed FW |
728 | |
729 | nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0); | |
730 | if (!nf_ct_expect_hash) { | |
731 | kmem_cache_destroy(nf_ct_expect_cachep); | |
732 | return -ENOMEM; | |
733 | } | |
734 | ||
83b4dbe1 G |
735 | return 0; |
736 | } | |
737 | ||
738 | void nf_conntrack_expect_fini(void) | |
739 | { | |
740 | rcu_barrier(); /* Wait for call_rcu() before destroy */ | |
741 | kmem_cache_destroy(nf_ct_expect_cachep); | |
285189c7 | 742 | kvfree(nf_ct_expect_hash); |
83b4dbe1 | 743 | } |