Blame view
net/netfilter/nf_conntrack_expect.c
19.3 KB
77ab9cff0 [NETFILTER]: nf_c... |
1 2 3 4 5 |
/* Expectation handling for nf_conntrack. */ /* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> |
f229f6ce4 netfilter: add my... |
6 |
* (c) 2005-2012 Patrick McHardy <kaber@trash.net> |
77ab9cff0 [NETFILTER]: nf_c... |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/types.h> #include <linux/netfilter.h> #include <linux/skbuff.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/stddef.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/percpu.h> #include <linux/kernel.h> |
a71c08556 [NETFILTER]: nf_c... |
23 |
#include <linux/jhash.h> |
d9b938421 net: add modulepa... |
24 |
#include <linux/moduleparam.h> |
bc3b2d7fb net: Add export.h... |
25 |
#include <linux/export.h> |
457c4cbc5 [NET]: Make /proc... |
26 |
#include <net/net_namespace.h> |
a9a083c38 netfilter: conntr... |
27 |
#include <net/netns/hash.h> |
77ab9cff0 [NETFILTER]: nf_c... |
28 29 30 31 32 33 |
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_tuple.h> |
5d0aa2ccd netfilter: nf_con... |
34 |
#include <net/netfilter/nf_conntrack_zones.h> |
77ab9cff0 [NETFILTER]: nf_c... |
35 |
|
a71c08556 [NETFILTER]: nf_c... |
36 37 |
unsigned int nf_ct_expect_hsize __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_expect_hsize); |
0a93aaedc netfilter: conntr... |
38 39 |
struct hlist_head *nf_ct_expect_hash __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_expect_hash); |
f264a7df0 [NETFILTER]: nf_c... |
40 |
unsigned int nf_ct_expect_max __read_mostly; |
a71c08556 [NETFILTER]: nf_c... |
41 |
|
e9c1b084e [NETFILTER]: nf_c... |
42 |
static struct kmem_cache *nf_ct_expect_cachep __read_mostly; |
7001c6d10 netfilter: conntr... |
43 |
static unsigned int nf_ct_expect_hashrnd __read_mostly; |
77ab9cff0 [NETFILTER]: nf_c... |
44 45 |
/* nf_conntrack_expect helper functions */ |
ebbf41df4 netfilter: ctnetl... |
46 |
void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, |
ec464e5dc netfilter: rename... |
47 |
u32 portid, int report) |
77ab9cff0 [NETFILTER]: nf_c... |
48 49 |
{ struct nf_conn_help *master_help = nfct_help(exp->master); |
9b03f38d0 netfilter: netns ... |
50 |
struct net *net = nf_ct_exp_net(exp); |
77ab9cff0 [NETFILTER]: nf_c... |
51 |
|
44d6e2f27 net: Replace NF_C... |
52 53 |
WARN_ON(!master_help); WARN_ON(timer_pending(&exp->timeout)); |
77ab9cff0 [NETFILTER]: nf_c... |
54 |
|
7d0742da1 [NETFILTER]: nf_c... |
55 |
hlist_del_rcu(&exp->hnode); |
9b03f38d0 netfilter: netns ... |
56 |
net->ct.expect_count--; |
a71c08556 [NETFILTER]: nf_c... |
57 |
|
7cddd967b netfilter: nf_ct_... |
58 |
hlist_del_rcu(&exp->lnode); |
3d058d7bc netfilter: rework... |
59 |
master_help->expecting[exp->class]--; |
bc01befdc netfilter: ctnetl... |
60 |
|
ec464e5dc netfilter: rename... |
61 |
nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report); |
6823645d6 [NETFILTER]: nf_c... |
62 |
nf_ct_expect_put(exp); |
b560580a1 [NETFILTER]: nf_c... |
63 |
|
0d55af879 netfilter: netns ... |
64 |
NF_CT_STAT_INC(net, expect_delete); |
77ab9cff0 [NETFILTER]: nf_c... |
65 |
} |
ebbf41df4 netfilter: ctnetl... |
66 |
EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report); |
77ab9cff0 [NETFILTER]: nf_c... |
67 |
|
6823645d6 [NETFILTER]: nf_c... |
68 |
static void nf_ct_expectation_timed_out(unsigned long ul_expect) |
77ab9cff0 [NETFILTER]: nf_c... |
69 70 |
{ struct nf_conntrack_expect *exp = (void *)ul_expect; |
ca7433df3 netfilter: conntr... |
71 |
spin_lock_bh(&nf_conntrack_expect_lock); |
77ab9cff0 [NETFILTER]: nf_c... |
72 |
nf_ct_unlink_expect(exp); |
ca7433df3 netfilter: conntr... |
73 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
6823645d6 [NETFILTER]: nf_c... |
74 |
nf_ct_expect_put(exp); |
77ab9cff0 [NETFILTER]: nf_c... |
75 |
} |
a9a083c38 netfilter: conntr... |
76 |
static unsigned int nf_ct_expect_dst_hash(const struct net *n, const struct nf_conntrack_tuple *tuple) |
a71c08556 [NETFILTER]: nf_c... |
77 |
{ |
a9a083c38 netfilter: conntr... |
78 |
unsigned int hash, seed; |
34498825c [NETFILTER]: non-... |
79 |
|
7001c6d10 netfilter: conntr... |
80 |
get_random_once(&nf_ct_expect_hashrnd, sizeof(nf_ct_expect_hashrnd)); |
a71c08556 [NETFILTER]: nf_c... |
81 |
|
a9a083c38 netfilter: conntr... |
82 |
seed = nf_ct_expect_hashrnd ^ net_hash_mix(n); |
34498825c [NETFILTER]: non-... |
83 |
hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all), |
a71c08556 [NETFILTER]: nf_c... |
84 |
(((tuple->dst.protonum ^ tuple->src.l3num) << 16) | |
a9a083c38 netfilter: conntr... |
85 |
(__force __u16)tuple->dst.u.all) ^ seed); |
8fc54f689 net: use reciproc... |
86 87 |
return reciprocal_scale(hash, nf_ct_expect_hsize); |
a71c08556 [NETFILTER]: nf_c... |
88 |
} |
03d7dc5cd netfilter: conntr... |
89 90 91 92 93 94 95 96 97 98 |
static bool nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_expect *i, const struct nf_conntrack_zone *zone, const struct net *net) { return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) && net_eq(net, nf_ct_net(i->master)) && nf_ct_zone_equal_any(i->master, zone); } |
ec0e3f011 netfilter: nf_ct_... |
99 100 101 102 103 104 105 106 107 108 |
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp) { if (del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); nf_ct_expect_put(exp); return true; } return false; } EXPORT_SYMBOL_GPL(nf_ct_remove_expect); |
77ab9cff0 [NETFILTER]: nf_c... |
109 |
struct nf_conntrack_expect * |
308ac9143 netfilter: nf_con... |
110 111 |
__nf_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, |
5d0aa2ccd netfilter: nf_con... |
112 |
const struct nf_conntrack_tuple *tuple) |
77ab9cff0 [NETFILTER]: nf_c... |
113 114 |
{ struct nf_conntrack_expect *i; |
a71c08556 [NETFILTER]: nf_c... |
115 |
unsigned int h; |
9b03f38d0 netfilter: netns ... |
116 |
if (!net->ct.expect_count) |
a71c08556 [NETFILTER]: nf_c... |
117 |
return NULL; |
77ab9cff0 [NETFILTER]: nf_c... |
118 |
|
a9a083c38 netfilter: conntr... |
119 |
h = nf_ct_expect_dst_hash(net, tuple); |
0a93aaedc netfilter: conntr... |
120 |
hlist_for_each_entry_rcu(i, &nf_ct_expect_hash[h], hnode) { |
03d7dc5cd netfilter: conntr... |
121 |
if (nf_ct_exp_equal(tuple, i, zone, net)) |
77ab9cff0 [NETFILTER]: nf_c... |
122 123 124 125 |
return i; } return NULL; } |
6823645d6 [NETFILTER]: nf_c... |
126 |
EXPORT_SYMBOL_GPL(__nf_ct_expect_find); |
77ab9cff0 [NETFILTER]: nf_c... |
127 128 129 |
/* Just find a expectation corresponding to a tuple. */ struct nf_conntrack_expect * |
308ac9143 netfilter: nf_con... |
130 131 |
nf_ct_expect_find_get(struct net *net, const struct nf_conntrack_zone *zone, |
5d0aa2ccd netfilter: nf_con... |
132 |
const struct nf_conntrack_tuple *tuple) |
77ab9cff0 [NETFILTER]: nf_c... |
133 134 |
{ struct nf_conntrack_expect *i; |
7d0742da1 [NETFILTER]: nf_c... |
135 |
rcu_read_lock(); |
5d0aa2ccd netfilter: nf_con... |
136 |
i = __nf_ct_expect_find(net, zone, tuple); |
b54ab92b8 netfilter: refcou... |
137 |
if (i && !refcount_inc_not_zero(&i->use)) |
7d0742da1 [NETFILTER]: nf_c... |
138 139 |
i = NULL; rcu_read_unlock(); |
77ab9cff0 [NETFILTER]: nf_c... |
140 141 142 |
return i; } |
6823645d6 [NETFILTER]: nf_c... |
143 |
EXPORT_SYMBOL_GPL(nf_ct_expect_find_get); |
77ab9cff0 [NETFILTER]: nf_c... |
144 145 146 147 |
/* If an expectation for this connection is found, it gets delete from * global list then returned. */ struct nf_conntrack_expect * |
308ac9143 netfilter: nf_con... |
148 149 |
nf_ct_find_expectation(struct net *net, const struct nf_conntrack_zone *zone, |
5d0aa2ccd netfilter: nf_con... |
150 |
const struct nf_conntrack_tuple *tuple) |
77ab9cff0 [NETFILTER]: nf_c... |
151 |
{ |
359b9ab61 [NETFILTER]: nf_c... |
152 |
struct nf_conntrack_expect *i, *exp = NULL; |
359b9ab61 [NETFILTER]: nf_c... |
153 |
unsigned int h; |
9b03f38d0 netfilter: netns ... |
154 |
if (!net->ct.expect_count) |
359b9ab61 [NETFILTER]: nf_c... |
155 |
return NULL; |
ece006416 [NETFILTER]: nf_c... |
156 |
|
a9a083c38 netfilter: conntr... |
157 |
h = nf_ct_expect_dst_hash(net, tuple); |
0a93aaedc netfilter: conntr... |
158 |
hlist_for_each_entry(i, &nf_ct_expect_hash[h], hnode) { |
359b9ab61 [NETFILTER]: nf_c... |
159 |
if (!(i->flags & NF_CT_EXPECT_INACTIVE) && |
03d7dc5cd netfilter: conntr... |
160 |
nf_ct_exp_equal(tuple, i, zone, net)) { |
359b9ab61 [NETFILTER]: nf_c... |
161 162 163 164 |
exp = i; break; } } |
ece006416 [NETFILTER]: nf_c... |
165 166 |
if (!exp) return NULL; |
77ab9cff0 [NETFILTER]: nf_c... |
167 |
|
77ab9cff0 [NETFILTER]: nf_c... |
168 169 170 171 172 |
/* If master is not in hash table yet (ie. packet hasn't left this machine yet), how can other end know about expected? Hence these are not the droids you are looking for (if master ct never got confirmed, we'd hold a reference to it and weird things would happen to future packets). */ |
ece006416 [NETFILTER]: nf_c... |
173 174 |
if (!nf_ct_is_confirmed(exp->master)) return NULL; |
e1b207dac netfilter: avoid ... |
175 176 177 178 179 180 181 182 183 184 185 |
/* Avoid race with other CPUs, that for exp->master ct, is * about to invoke ->destroy(), or nf_ct_delete() via timeout * or early_drop(). * * The atomic_inc_not_zero() check tells: If that fails, we * know that the ct is being destroyed. If it succeeds, we * can be sure the ct cannot disappear underneath. */ if (unlikely(nf_ct_is_dying(exp->master) || !atomic_inc_not_zero(&exp->master->ct_general.use))) return NULL; |
ece006416 [NETFILTER]: nf_c... |
186 |
if (exp->flags & NF_CT_EXPECT_PERMANENT) { |
b54ab92b8 netfilter: refcou... |
187 |
refcount_inc(&exp->use); |
ece006416 [NETFILTER]: nf_c... |
188 189 190 191 |
return exp; } else if (del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); return exp; |
77ab9cff0 [NETFILTER]: nf_c... |
192 |
} |
e1b207dac netfilter: avoid ... |
193 194 |
/* Undo exp->master refcnt increase, if del_timer() failed */ nf_ct_put(exp->master); |
ece006416 [NETFILTER]: nf_c... |
195 |
|
77ab9cff0 [NETFILTER]: nf_c... |
196 197 198 199 200 201 |
return NULL; } /* delete all expectations for this conntrack */ void nf_ct_remove_expectations(struct nf_conn *ct) { |
77ab9cff0 [NETFILTER]: nf_c... |
202 |
struct nf_conn_help *help = nfct_help(ct); |
b560580a1 [NETFILTER]: nf_c... |
203 |
struct nf_conntrack_expect *exp; |
b67bfe0d4 hlist: drop the n... |
204 |
struct hlist_node *next; |
77ab9cff0 [NETFILTER]: nf_c... |
205 206 |
/* Optimization: most connection never expect any others. */ |
6002f266b [NETFILTER]: nf_c... |
207 |
if (!help) |
77ab9cff0 [NETFILTER]: nf_c... |
208 |
return; |
ca7433df3 netfilter: conntr... |
209 |
spin_lock_bh(&nf_conntrack_expect_lock); |
b67bfe0d4 hlist: drop the n... |
210 |
hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { |
ec0e3f011 netfilter: nf_ct_... |
211 |
nf_ct_remove_expect(exp); |
77ab9cff0 [NETFILTER]: nf_c... |
212 |
} |
ca7433df3 netfilter: conntr... |
213 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
77ab9cff0 [NETFILTER]: nf_c... |
214 |
} |
13b183391 [NETFILTER]: nf_c... |
215 |
EXPORT_SYMBOL_GPL(nf_ct_remove_expectations); |
77ab9cff0 [NETFILTER]: nf_c... |
216 217 218 219 220 221 222 |
/* Would two expected things clash? */ static inline int expect_clash(const struct nf_conntrack_expect *a, const struct nf_conntrack_expect *b) { /* Part covered by intersection of masks must be unequal, otherwise they clash */ |
d4156e8cd [NETFILTER]: nf_c... |
223 |
struct nf_conntrack_tuple_mask intersect_mask; |
77ab9cff0 [NETFILTER]: nf_c... |
224 |
int count; |
77ab9cff0 [NETFILTER]: nf_c... |
225 |
intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all; |
77ab9cff0 [NETFILTER]: nf_c... |
226 227 228 229 230 |
for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){ intersect_mask.src.u3.all[count] = a->mask.src.u3.all[count] & b->mask.src.u3.all[count]; } |
4b31814d2 netfilter: nf_con... |
231 |
return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask) && |
03d7dc5cd netfilter: conntr... |
232 |
net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) && |
deedb5903 netfilter: nf_con... |
233 |
nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master)); |
77ab9cff0 [NETFILTER]: nf_c... |
234 235 236 237 238 |
} static inline int expect_matches(const struct nf_conntrack_expect *a, const struct nf_conntrack_expect *b) { |
f64f9e719 net: Move && and ... |
239 |
return a->master == b->master && a->class == b->class && |
308ac9143 netfilter: nf_con... |
240 241 |
nf_ct_tuple_equal(&a->tuple, &b->tuple) && nf_ct_tuple_mask_equal(&a->mask, &b->mask) && |
03d7dc5cd netfilter: conntr... |
242 |
net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) && |
deedb5903 netfilter: nf_con... |
243 |
nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master)); |
77ab9cff0 [NETFILTER]: nf_c... |
244 245 246 |
} /* Generally a bad idea to call this: could have matched already. */ |
6823645d6 [NETFILTER]: nf_c... |
247 |
void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) |
77ab9cff0 [NETFILTER]: nf_c... |
248 |
{ |
ca7433df3 netfilter: conntr... |
249 |
spin_lock_bh(&nf_conntrack_expect_lock); |
ec0e3f011 netfilter: nf_ct_... |
250 |
nf_ct_remove_expect(exp); |
ca7433df3 netfilter: conntr... |
251 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
77ab9cff0 [NETFILTER]: nf_c... |
252 |
} |
6823645d6 [NETFILTER]: nf_c... |
253 |
EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); |
77ab9cff0 [NETFILTER]: nf_c... |
254 255 256 257 |
/* We don't increase the master conntrack refcount for non-fulfilled * conntracks. During the conntrack destruction, the expectations are * always killed before the conntrack itself */ |
6823645d6 [NETFILTER]: nf_c... |
258 |
struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) |
77ab9cff0 [NETFILTER]: nf_c... |
259 260 |
{ struct nf_conntrack_expect *new; |
6823645d6 [NETFILTER]: nf_c... |
261 |
new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC); |
77ab9cff0 [NETFILTER]: nf_c... |
262 263 264 265 |
if (!new) return NULL; new->master = me; |
b54ab92b8 netfilter: refcou... |
266 |
refcount_set(&new->use, 1); |
77ab9cff0 [NETFILTER]: nf_c... |
267 268 |
return new; } |
6823645d6 [NETFILTER]: nf_c... |
269 |
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc); |
77ab9cff0 [NETFILTER]: nf_c... |
270 |
|
6002f266b [NETFILTER]: nf_c... |
271 |
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, |
76108cea0 netfilter: Use un... |
272 |
u_int8_t family, |
1d9d75225 [NETFILTER]: nf_c... |
273 274 275 |
const union nf_inet_addr *saddr, const union nf_inet_addr *daddr, u_int8_t proto, const __be16 *src, const __be16 *dst) |
d6a9b6500 [NETFILTER]: nf_c... |
276 277 278 279 280 281 282 283 284 |
{ int len; if (family == AF_INET) len = 4; else len = 16; exp->flags = 0; |
6002f266b [NETFILTER]: nf_c... |
285 |
exp->class = class; |
d6a9b6500 [NETFILTER]: nf_c... |
286 287 288 289 |
exp->expectfn = NULL; exp->helper = NULL; exp->tuple.src.l3num = family; exp->tuple.dst.protonum = proto; |
d6a9b6500 [NETFILTER]: nf_c... |
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
if (saddr) { memcpy(&exp->tuple.src.u3, saddr, len); if (sizeof(exp->tuple.src.u3) > len) /* address needs to be cleared for nf_ct_tuple_equal */ memset((void *)&exp->tuple.src.u3 + len, 0x00, sizeof(exp->tuple.src.u3) - len); memset(&exp->mask.src.u3, 0xFF, len); if (sizeof(exp->mask.src.u3) > len) memset((void *)&exp->mask.src.u3 + len, 0x00, sizeof(exp->mask.src.u3) - len); } else { memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3)); memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3)); } |
d6a9b6500 [NETFILTER]: nf_c... |
305 |
if (src) { |
a34c45896 netfilter endian ... |
306 307 |
exp->tuple.src.u.all = *src; exp->mask.src.u.all = htons(0xFFFF); |
d6a9b6500 [NETFILTER]: nf_c... |
308 309 310 311 |
} else { exp->tuple.src.u.all = 0; exp->mask.src.u.all = 0; } |
d4156e8cd [NETFILTER]: nf_c... |
312 313 314 315 316 |
memcpy(&exp->tuple.dst.u3, daddr, len); if (sizeof(exp->tuple.dst.u3) > len) /* address needs to be cleared for nf_ct_tuple_equal */ memset((void *)&exp->tuple.dst.u3 + len, 0x00, sizeof(exp->tuple.dst.u3) - len); |
a34c45896 netfilter endian ... |
317 |
exp->tuple.dst.u.all = *dst; |
f09eca8db netfilter: ctnetl... |
318 319 320 321 322 |
#ifdef CONFIG_NF_NAT_NEEDED memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); #endif |
d6a9b6500 [NETFILTER]: nf_c... |
323 |
} |
6823645d6 [NETFILTER]: nf_c... |
324 |
EXPORT_SYMBOL_GPL(nf_ct_expect_init); |
d6a9b6500 [NETFILTER]: nf_c... |
325 |
|
7d0742da1 [NETFILTER]: nf_c... |
326 327 328 329 330 331 332 |
static void nf_ct_expect_free_rcu(struct rcu_head *head) { struct nf_conntrack_expect *exp; exp = container_of(head, struct nf_conntrack_expect, rcu); kmem_cache_free(nf_ct_expect_cachep, exp); } |
6823645d6 [NETFILTER]: nf_c... |
333 |
void nf_ct_expect_put(struct nf_conntrack_expect *exp) |
77ab9cff0 [NETFILTER]: nf_c... |
334 |
{ |
b54ab92b8 netfilter: refcou... |
335 |
if (refcount_dec_and_test(&exp->use)) |
7d0742da1 [NETFILTER]: nf_c... |
336 |
call_rcu(&exp->rcu, nf_ct_expect_free_rcu); |
77ab9cff0 [NETFILTER]: nf_c... |
337 |
} |
6823645d6 [NETFILTER]: nf_c... |
338 |
EXPORT_SYMBOL_GPL(nf_ct_expect_put); |
77ab9cff0 [NETFILTER]: nf_c... |
339 |
|
4dee62b1b netfilter: nf_ct_... |
340 |
static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) |
77ab9cff0 [NETFILTER]: nf_c... |
341 342 |
{ struct nf_conn_help *master_help = nfct_help(exp->master); |
3d058d7bc netfilter: rework... |
343 |
struct nf_conntrack_helper *helper; |
9b03f38d0 netfilter: netns ... |
344 |
struct net *net = nf_ct_exp_net(exp); |
a9a083c38 netfilter: conntr... |
345 |
unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); |
77ab9cff0 [NETFILTER]: nf_c... |
346 |
|
3bfd45f93 netfilter: nf_con... |
347 |
/* two references : one for hash insert, one for the timer */ |
b54ab92b8 netfilter: refcou... |
348 |
refcount_add(2, &exp->use); |
b560580a1 [NETFILTER]: nf_c... |
349 |
|
6823645d6 [NETFILTER]: nf_c... |
350 351 |
setup_timer(&exp->timeout, nf_ct_expectation_timed_out, (unsigned long)exp); |
3d058d7bc netfilter: rework... |
352 |
helper = rcu_dereference_protected(master_help->helper, |
ca7433df3 netfilter: conntr... |
353 |
lockdep_is_held(&nf_conntrack_expect_lock)); |
3d058d7bc netfilter: rework... |
354 355 356 |
if (helper) { exp->timeout.expires = jiffies + helper->expect_policy[exp->class].timeout * HZ; |
bc01befdc netfilter: ctnetl... |
357 |
} |
77ab9cff0 [NETFILTER]: nf_c... |
358 |
add_timer(&exp->timeout); |
56a97e701 netfilter: expect... |
359 360 361 362 363 |
hlist_add_head_rcu(&exp->lnode, &master_help->expectations); master_help->expecting[exp->class]++; hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]); net->ct.expect_count++; |
0d55af879 netfilter: netns ... |
364 |
NF_CT_STAT_INC(net, expect_create); |
77ab9cff0 [NETFILTER]: nf_c... |
365 366 367 |
} /* Race with expectations being used means we could have none to find; OK. */ |
6002f266b [NETFILTER]: nf_c... |
368 369 |
static void evict_oldest_expect(struct nf_conn *master, struct nf_conntrack_expect *new) |
77ab9cff0 [NETFILTER]: nf_c... |
370 |
{ |
b560580a1 [NETFILTER]: nf_c... |
371 |
struct nf_conn_help *master_help = nfct_help(master); |
6002f266b [NETFILTER]: nf_c... |
372 |
struct nf_conntrack_expect *exp, *last = NULL; |
77ab9cff0 [NETFILTER]: nf_c... |
373 |
|
b67bfe0d4 hlist: drop the n... |
374 |
hlist_for_each_entry(exp, &master_help->expectations, lnode) { |
6002f266b [NETFILTER]: nf_c... |
375 376 377 |
if (exp->class == new->class) last = exp; } |
b560580a1 [NETFILTER]: nf_c... |
378 |
|
ec0e3f011 netfilter: nf_ct_... |
379 380 |
if (last) nf_ct_remove_expect(last); |
77ab9cff0 [NETFILTER]: nf_c... |
381 |
} |
19abb7b09 netfilter: ctnetl... |
382 |
static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) |
77ab9cff0 [NETFILTER]: nf_c... |
383 |
{ |
6002f266b [NETFILTER]: nf_c... |
384 |
const struct nf_conntrack_expect_policy *p; |
77ab9cff0 [NETFILTER]: nf_c... |
385 386 387 |
struct nf_conntrack_expect *i; struct nf_conn *master = expect->master; struct nf_conn_help *master_help = nfct_help(master); |
3d058d7bc netfilter: rework... |
388 |
struct nf_conntrack_helper *helper; |
9b03f38d0 netfilter: netns ... |
389 |
struct net *net = nf_ct_exp_net(expect); |
b67bfe0d4 hlist: drop the n... |
390 |
struct hlist_node *next; |
a71c08556 [NETFILTER]: nf_c... |
391 |
unsigned int h; |
4b86c459c netfilter: nf_ct_... |
392 |
int ret = 0; |
77ab9cff0 [NETFILTER]: nf_c... |
393 |
|
3d058d7bc netfilter: rework... |
394 |
if (!master_help) { |
3c158f7f5 [NETFILTER]: nf_c... |
395 396 397 |
ret = -ESHUTDOWN; goto out; } |
a9a083c38 netfilter: conntr... |
398 |
h = nf_ct_expect_dst_hash(net, &expect->tuple); |
0a93aaedc netfilter: conntr... |
399 |
hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { |
77ab9cff0 [NETFILTER]: nf_c... |
400 |
if (expect_matches(i, expect)) { |
36ac344e1 netfilter: expect... |
401 |
if (nf_ct_remove_expect(i)) |
2614f8649 netfilter: nf_ct_... |
402 |
break; |
77ab9cff0 [NETFILTER]: nf_c... |
403 404 405 406 407 408 |
} else if (expect_clash(i, expect)) { ret = -EBUSY; goto out; } } /* Will be over limit? */ |
3d058d7bc netfilter: rework... |
409 |
helper = rcu_dereference_protected(master_help->helper, |
ca7433df3 netfilter: conntr... |
410 |
lockdep_is_held(&nf_conntrack_expect_lock)); |
3d058d7bc netfilter: rework... |
411 412 |
if (helper) { p = &helper->expect_policy[expect->class]; |
bc01befdc netfilter: ctnetl... |
413 414 415 416 417 418 419 420 |
if (p->max_expected && master_help->expecting[expect->class] >= p->max_expected) { evict_oldest_expect(master, expect); if (master_help->expecting[expect->class] >= p->max_expected) { ret = -EMFILE; goto out; } |
6002f266b [NETFILTER]: nf_c... |
421 422 |
} } |
77ab9cff0 [NETFILTER]: nf_c... |
423 |
|
9b03f38d0 netfilter: netns ... |
424 |
if (net->ct.expect_count >= nf_ct_expect_max) { |
e87cc4728 net: Convert net_... |
425 426 |
net_warn_ratelimited("nf_conntrack: expectation table full "); |
f264a7df0 [NETFILTER]: nf_c... |
427 |
ret = -EMFILE; |
f264a7df0 [NETFILTER]: nf_c... |
428 |
} |
19abb7b09 netfilter: ctnetl... |
429 430 431 |
out: return ret; } |
b476b72a0 netfilter: trivia... |
432 |
int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, |
ec464e5dc netfilter: rename... |
433 |
u32 portid, int report) |
19abb7b09 netfilter: ctnetl... |
434 435 |
{ int ret; |
ca7433df3 netfilter: conntr... |
436 |
spin_lock_bh(&nf_conntrack_expect_lock); |
19abb7b09 netfilter: ctnetl... |
437 |
ret = __nf_ct_expect_check(expect); |
4b86c459c netfilter: nf_ct_... |
438 |
if (ret < 0) |
19abb7b09 netfilter: ctnetl... |
439 |
goto out; |
f264a7df0 [NETFILTER]: nf_c... |
440 |
|
4dee62b1b netfilter: nf_ct_... |
441 |
nf_ct_expect_insert(expect); |
ca7433df3 netfilter: conntr... |
442 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
ec464e5dc netfilter: rename... |
443 |
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); |
7fb668ac7 netfilter: nf_ct_... |
444 |
return 0; |
19abb7b09 netfilter: ctnetl... |
445 |
out: |
ca7433df3 netfilter: conntr... |
446 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
19abb7b09 netfilter: ctnetl... |
447 448 449 |
return ret; } EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); |
ac7b84839 netfilter: expect... |
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), void *data) { struct nf_conntrack_expect *exp; const struct hlist_node *next; unsigned int i; spin_lock_bh(&nf_conntrack_expect_lock); for (i = 0; i < nf_ct_expect_hsize; i++) { hlist_for_each_entry_safe(exp, next, &nf_ct_expect_hash[i], hnode) { if (iter(exp, data) && del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); nf_ct_expect_put(exp); } } } spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_destroy); void nf_ct_expect_iterate_net(struct net *net, bool (*iter)(struct nf_conntrack_expect *e, void *data), void *data, u32 portid, int report) { struct nf_conntrack_expect *exp; const struct hlist_node *next; unsigned int i; spin_lock_bh(&nf_conntrack_expect_lock); for (i = 0; i < nf_ct_expect_hsize; i++) { hlist_for_each_entry_safe(exp, next, &nf_ct_expect_hash[i], hnode) { if (!net_eq(nf_ct_exp_net(exp), net)) continue; if (iter(exp, data) && del_timer(&exp->timeout)) { nf_ct_unlink_expect_report(exp, portid, report); nf_ct_expect_put(exp); } } } spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_net); |
54b07dca6 netfilter: provid... |
503 |
#ifdef CONFIG_NF_CONNTRACK_PROCFS |
5d08ad440 [NETFILTER]: nf_c... |
504 |
struct ct_expect_iter_state { |
dc5129f8d netfilter: netns ... |
505 |
struct seq_net_private p; |
5d08ad440 [NETFILTER]: nf_c... |
506 507 508 509 |
unsigned int bucket; }; static struct hlist_node *ct_expect_get_first(struct seq_file *seq) |
77ab9cff0 [NETFILTER]: nf_c... |
510 |
{ |
5d08ad440 [NETFILTER]: nf_c... |
511 |
struct ct_expect_iter_state *st = seq->private; |
7d0742da1 [NETFILTER]: nf_c... |
512 |
struct hlist_node *n; |
77ab9cff0 [NETFILTER]: nf_c... |
513 |
|
5d08ad440 [NETFILTER]: nf_c... |
514 |
for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) { |
0a93aaedc netfilter: conntr... |
515 |
n = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket])); |
7d0742da1 [NETFILTER]: nf_c... |
516 517 |
if (n) return n; |
5d08ad440 [NETFILTER]: nf_c... |
518 519 520 |
} return NULL; } |
77ab9cff0 [NETFILTER]: nf_c... |
521 |
|
5d08ad440 [NETFILTER]: nf_c... |
522 523 524 525 |
static struct hlist_node *ct_expect_get_next(struct seq_file *seq, struct hlist_node *head) { struct ct_expect_iter_state *st = seq->private; |
77ab9cff0 [NETFILTER]: nf_c... |
526 |
|
0e60ebe04 netfilter: add __... |
527 |
head = rcu_dereference(hlist_next_rcu(head)); |
5d08ad440 [NETFILTER]: nf_c... |
528 529 |
while (head == NULL) { if (++st->bucket >= nf_ct_expect_hsize) |
77ab9cff0 [NETFILTER]: nf_c... |
530 |
return NULL; |
0a93aaedc netfilter: conntr... |
531 |
head = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket])); |
77ab9cff0 [NETFILTER]: nf_c... |
532 |
} |
5d08ad440 [NETFILTER]: nf_c... |
533 |
return head; |
77ab9cff0 [NETFILTER]: nf_c... |
534 |
} |
5d08ad440 [NETFILTER]: nf_c... |
535 |
static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos) |
77ab9cff0 [NETFILTER]: nf_c... |
536 |
{ |
5d08ad440 [NETFILTER]: nf_c... |
537 |
struct hlist_node *head = ct_expect_get_first(seq); |
77ab9cff0 [NETFILTER]: nf_c... |
538 |
|
5d08ad440 [NETFILTER]: nf_c... |
539 540 541 542 543 |
if (head) while (pos && (head = ct_expect_get_next(seq, head))) pos--; return pos ? NULL : head; } |
77ab9cff0 [NETFILTER]: nf_c... |
544 |
|
5d08ad440 [NETFILTER]: nf_c... |
545 |
static void *exp_seq_start(struct seq_file *seq, loff_t *pos) |
7d0742da1 [NETFILTER]: nf_c... |
546 |
__acquires(RCU) |
5d08ad440 [NETFILTER]: nf_c... |
547 |
{ |
7d0742da1 [NETFILTER]: nf_c... |
548 |
rcu_read_lock(); |
5d08ad440 [NETFILTER]: nf_c... |
549 550 |
return ct_expect_get_idx(seq, *pos); } |
77ab9cff0 [NETFILTER]: nf_c... |
551 |
|
5d08ad440 [NETFILTER]: nf_c... |
552 553 554 555 |
static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos) { (*pos)++; return ct_expect_get_next(seq, v); |
77ab9cff0 [NETFILTER]: nf_c... |
556 |
} |
5d08ad440 [NETFILTER]: nf_c... |
557 |
static void exp_seq_stop(struct seq_file *seq, void *v) |
7d0742da1 [NETFILTER]: nf_c... |
558 |
__releases(RCU) |
77ab9cff0 [NETFILTER]: nf_c... |
559 |
{ |
7d0742da1 [NETFILTER]: nf_c... |
560 |
rcu_read_unlock(); |
77ab9cff0 [NETFILTER]: nf_c... |
561 562 563 564 |
} static int exp_seq_show(struct seq_file *s, void *v) { |
5d08ad440 [NETFILTER]: nf_c... |
565 |
struct nf_conntrack_expect *expect; |
b87921bdf netfilter: nf_con... |
566 |
struct nf_conntrack_helper *helper; |
5d08ad440 [NETFILTER]: nf_c... |
567 |
struct hlist_node *n = v; |
359b9ab61 [NETFILTER]: nf_c... |
568 |
char *delim = ""; |
5d08ad440 [NETFILTER]: nf_c... |
569 570 |
expect = hlist_entry(n, struct nf_conntrack_expect, hnode); |
77ab9cff0 [NETFILTER]: nf_c... |
571 572 573 574 575 |
if (expect->timeout.function) seq_printf(s, "%ld ", timer_pending(&expect->timeout) ? (long)(expect->timeout.expires - jiffies)/HZ : 0); else |
cdec26858 netfilter: Use se... |
576 |
seq_puts(s, "- "); |
77ab9cff0 [NETFILTER]: nf_c... |
577 578 579 580 581 |
seq_printf(s, "l3proto = %u proto=%u ", expect->tuple.src.l3num, expect->tuple.dst.protonum); print_tuple(s, &expect->tuple, __nf_ct_l3proto_find(expect->tuple.src.l3num), |
605dcad6c [NETFILTER]: nf_c... |
582 |
__nf_ct_l4proto_find(expect->tuple.src.l3num, |
77ab9cff0 [NETFILTER]: nf_c... |
583 |
expect->tuple.dst.protonum)); |
4bb119eab [NETFILTER]: nf_c... |
584 |
|
359b9ab61 [NETFILTER]: nf_c... |
585 |
if (expect->flags & NF_CT_EXPECT_PERMANENT) { |
cdec26858 netfilter: Use se... |
586 |
seq_puts(s, "PERMANENT"); |
359b9ab61 [NETFILTER]: nf_c... |
587 588 |
delim = ","; } |
bc01befdc netfilter: ctnetl... |
589 |
if (expect->flags & NF_CT_EXPECT_INACTIVE) { |
359b9ab61 [NETFILTER]: nf_c... |
590 |
seq_printf(s, "%sINACTIVE", delim); |
bc01befdc netfilter: ctnetl... |
591 592 593 594 |
delim = ","; } if (expect->flags & NF_CT_EXPECT_USERSPACE) seq_printf(s, "%sUSERSPACE", delim); |
4bb119eab [NETFILTER]: nf_c... |
595 |
|
b87921bdf netfilter: nf_con... |
596 597 598 |
helper = rcu_dereference(nfct_help(expect->master)->helper); if (helper) { seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name); |
b173a28f6 netfilter: nf_ct_... |
599 |
if (helper->expect_policy[expect->class].name[0]) |
b87921bdf netfilter: nf_con... |
600 601 602 |
seq_printf(s, "/%s", helper->expect_policy[expect->class].name); } |
1ca9e4177 netfilter: Remove... |
603 604 605 606 |
seq_putc(s, ' '); return 0; |
77ab9cff0 [NETFILTER]: nf_c... |
607 |
} |
56b3d975b [NET]: Make all i... |
608 |
static const struct seq_operations exp_seq_ops = { |
77ab9cff0 [NETFILTER]: nf_c... |
609 610 611 612 613 614 615 616 |
.start = exp_seq_start, .next = exp_seq_next, .stop = exp_seq_stop, .show = exp_seq_show }; static int exp_open(struct inode *inode, struct file *file) { |
dc5129f8d netfilter: netns ... |
617 |
return seq_open_net(inode, file, &exp_seq_ops, |
e2da59133 [NETFILTER]: Make... |
618 |
sizeof(struct ct_expect_iter_state)); |
77ab9cff0 [NETFILTER]: nf_c... |
619 |
} |
5d08ad440 [NETFILTER]: nf_c... |
620 |
static const struct file_operations exp_file_ops = { |
77ab9cff0 [NETFILTER]: nf_c... |
621 622 623 624 |
.owner = THIS_MODULE, .open = exp_open, .read = seq_read, .llseek = seq_lseek, |
dc5129f8d netfilter: netns ... |
625 |
.release = seq_release_net, |
77ab9cff0 [NETFILTER]: nf_c... |
626 |
}; |
54b07dca6 netfilter: provid... |
627 |
#endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084e [NETFILTER]: nf_c... |
628 |
|
dc5129f8d netfilter: netns ... |
629 |
static int exp_proc_init(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
630 |
{ |
54b07dca6 netfilter: provid... |
631 |
#ifdef CONFIG_NF_CONNTRACK_PROCFS |
e9c1b084e [NETFILTER]: nf_c... |
632 |
struct proc_dir_entry *proc; |
f13f2aeed netfilter: Set /p... |
633 634 |
kuid_t root_uid; kgid_t root_gid; |
e9c1b084e [NETFILTER]: nf_c... |
635 |
|
d4beaa66a net: proc: change... |
636 637 |
proc = proc_create("nf_conntrack_expect", 0440, net->proc_net, &exp_file_ops); |
e9c1b084e [NETFILTER]: nf_c... |
638 639 |
if (!proc) return -ENOMEM; |
f13f2aeed netfilter: Set /p... |
640 641 642 643 644 |
root_uid = make_kuid(net->user_ns, 0); root_gid = make_kgid(net->user_ns, 0); if (uid_valid(root_uid) && gid_valid(root_gid)) proc_set_user(proc, root_uid, root_gid); |
54b07dca6 netfilter: provid... |
645 |
#endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084e [NETFILTER]: nf_c... |
646 647 |
return 0; } |
dc5129f8d netfilter: netns ... |
648 |
static void exp_proc_remove(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
649 |
{ |
54b07dca6 netfilter: provid... |
650 |
#ifdef CONFIG_NF_CONNTRACK_PROCFS |
ece31ffd5 net: proc: change... |
651 |
remove_proc_entry("nf_conntrack_expect", net->proc_net); |
54b07dca6 netfilter: provid... |
652 |
#endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084e [NETFILTER]: nf_c... |
653 |
} |
13ccdfc2a netfilter: nf_con... |
654 |
module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400); |
a71c08556 [NETFILTER]: nf_c... |
655 |
|
83b4dbe19 netfilter: nf_ct_... |
656 |
int nf_conntrack_expect_pernet_init(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
657 |
{ |
9b03f38d0 netfilter: netns ... |
658 |
net->ct.expect_count = 0; |
0a93aaedc netfilter: conntr... |
659 |
return exp_proc_init(net); |
e9c1b084e [NETFILTER]: nf_c... |
660 |
} |
83b4dbe19 netfilter: nf_ct_... |
661 |
void nf_conntrack_expect_pernet_fini(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
662 |
{ |
dc5129f8d netfilter: netns ... |
663 |
exp_proc_remove(net); |
e9c1b084e [NETFILTER]: nf_c... |
664 |
} |
83b4dbe19 netfilter: nf_ct_... |
665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
int nf_conntrack_expect_init(void) { if (!nf_ct_expect_hsize) { nf_ct_expect_hsize = nf_conntrack_htable_size / 256; if (!nf_ct_expect_hsize) nf_ct_expect_hsize = 1; } nf_ct_expect_max = nf_ct_expect_hsize * 4; nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect", sizeof(struct nf_conntrack_expect), 0, 0, NULL); if (!nf_ct_expect_cachep) return -ENOMEM; |
0a93aaedc netfilter: conntr... |
679 680 681 682 683 684 |
nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0); if (!nf_ct_expect_hash) { kmem_cache_destroy(nf_ct_expect_cachep); return -ENOMEM; } |
83b4dbe19 netfilter: nf_ct_... |
685 686 687 688 689 690 691 |
return 0; } void nf_conntrack_expect_fini(void) { rcu_barrier(); /* Wait for call_rcu() before destroy */ kmem_cache_destroy(nf_ct_expect_cachep); |
0a93aaedc netfilter: conntr... |
692 |
nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_hsize); |
83b4dbe19 netfilter: nf_ct_... |
693 |
} |