Blame view
net/netfilter/nf_conntrack_expect.c
19.2 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
77ab9cff0 [NETFILTER]: nf_c... |
2 3 4 5 6 |
/* 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... |
7 |
* (c) 2005-2012 Patrick McHardy <kaber@trash.net> |
77ab9cff0 [NETFILTER]: nf_c... |
8 9 10 11 12 13 14 15 16 17 18 19 |
*/ #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... |
20 |
#include <linux/jhash.h> |
d9b938421 net: add modulepa... |
21 |
#include <linux/moduleparam.h> |
bc3b2d7fb net: Add export.h... |
22 |
#include <linux/export.h> |
457c4cbc5 [NET]: Make /proc... |
23 |
#include <net/net_namespace.h> |
a9a083c38 netfilter: conntr... |
24 |
#include <net/netns/hash.h> |
77ab9cff0 [NETFILTER]: nf_c... |
25 26 27 |
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> |
40d102cde netfilter: update... |
28 |
#include <net/netfilter/nf_conntrack_ecache.h> |
77ab9cff0 [NETFILTER]: nf_c... |
29 30 |
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_helper.h> |
40d102cde netfilter: update... |
31 |
#include <net/netfilter/nf_conntrack_l4proto.h> |
77ab9cff0 [NETFILTER]: nf_c... |
32 |
#include <net/netfilter/nf_conntrack_tuple.h> |
5d0aa2ccd netfilter: nf_con... |
33 |
#include <net/netfilter/nf_conntrack_zones.h> |
77ab9cff0 [NETFILTER]: nf_c... |
34 |
|
a71c08556 [NETFILTER]: nf_c... |
35 36 |
unsigned int nf_ct_expect_hsize __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_expect_hsize); |
0a93aaedc netfilter: conntr... |
37 38 |
struct hlist_head *nf_ct_expect_hash __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_expect_hash); |
f264a7df0 [NETFILTER]: nf_c... |
39 |
unsigned int nf_ct_expect_max __read_mostly; |
a71c08556 [NETFILTER]: nf_c... |
40 |
|
e9c1b084e [NETFILTER]: nf_c... |
41 |
static struct kmem_cache *nf_ct_expect_cachep __read_mostly; |
7001c6d10 netfilter: conntr... |
42 |
static unsigned int nf_ct_expect_hashrnd __read_mostly; |
77ab9cff0 [NETFILTER]: nf_c... |
43 44 |
/* nf_conntrack_expect helper functions */ |
ebbf41df4 netfilter: ctnetl... |
45 |
void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, |
ec464e5dc netfilter: rename... |
46 |
u32 portid, int report) |
77ab9cff0 [NETFILTER]: nf_c... |
47 48 |
{ struct nf_conn_help *master_help = nfct_help(exp->master); |
9b03f38d0 netfilter: netns ... |
49 |
struct net *net = nf_ct_exp_net(exp); |
77ab9cff0 [NETFILTER]: nf_c... |
50 |
|
44d6e2f27 net: Replace NF_C... |
51 52 |
WARN_ON(!master_help); WARN_ON(timer_pending(&exp->timeout)); |
77ab9cff0 [NETFILTER]: nf_c... |
53 |
|
7d0742da1 [NETFILTER]: nf_c... |
54 |
hlist_del_rcu(&exp->hnode); |
9b03f38d0 netfilter: netns ... |
55 |
net->ct.expect_count--; |
a71c08556 [NETFILTER]: nf_c... |
56 |
|
7cddd967b netfilter: nf_ct_... |
57 |
hlist_del_rcu(&exp->lnode); |
3d058d7bc netfilter: rework... |
58 |
master_help->expecting[exp->class]--; |
bc01befdc netfilter: ctnetl... |
59 |
|
ec464e5dc netfilter: rename... |
60 |
nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report); |
6823645d6 [NETFILTER]: nf_c... |
61 |
nf_ct_expect_put(exp); |
b560580a1 [NETFILTER]: nf_c... |
62 |
|
0d55af879 netfilter: netns ... |
63 |
NF_CT_STAT_INC(net, expect_delete); |
77ab9cff0 [NETFILTER]: nf_c... |
64 |
} |
ebbf41df4 netfilter: ctnetl... |
65 |
EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report); |
77ab9cff0 [NETFILTER]: nf_c... |
66 |
|
e99e88a9d treewide: setup_t... |
67 |
static void nf_ct_expectation_timed_out(struct timer_list *t) |
77ab9cff0 [NETFILTER]: nf_c... |
68 |
{ |
e99e88a9d treewide: setup_t... |
69 |
struct nf_conntrack_expect *exp = from_timer(exp, t, timeout); |
77ab9cff0 [NETFILTER]: nf_c... |
70 |
|
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) { |
3c00fb0bf netfilter: nf_con... |
239 |
return nf_ct_tuple_equal(&a->tuple, &b->tuple) && |
308ac9143 netfilter: nf_con... |
240 |
nf_ct_tuple_mask_equal(&a->mask, &b->mask) && |
03d7dc5cd netfilter: conntr... |
241 |
net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) && |
deedb5903 netfilter: nf_con... |
242 |
nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master)); |
77ab9cff0 [NETFILTER]: nf_c... |
243 |
} |
3c00fb0bf netfilter: nf_con... |
244 245 246 247 248 249 250 251 252 |
static bool master_matches(const struct nf_conntrack_expect *a, const struct nf_conntrack_expect *b, unsigned int flags) { if (flags & NF_CT_EXP_F_SKIP_MASTER) return true; return a->master == b->master; } |
77ab9cff0 [NETFILTER]: nf_c... |
253 |
/* Generally a bad idea to call this: could have matched already. */ |
6823645d6 [NETFILTER]: nf_c... |
254 |
void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) |
77ab9cff0 [NETFILTER]: nf_c... |
255 |
{ |
ca7433df3 netfilter: conntr... |
256 |
spin_lock_bh(&nf_conntrack_expect_lock); |
ec0e3f011 netfilter: nf_ct_... |
257 |
nf_ct_remove_expect(exp); |
ca7433df3 netfilter: conntr... |
258 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
77ab9cff0 [NETFILTER]: nf_c... |
259 |
} |
6823645d6 [NETFILTER]: nf_c... |
260 |
EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); |
77ab9cff0 [NETFILTER]: nf_c... |
261 262 263 264 |
/* 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... |
265 |
struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) |
77ab9cff0 [NETFILTER]: nf_c... |
266 267 |
{ struct nf_conntrack_expect *new; |
6823645d6 [NETFILTER]: nf_c... |
268 |
new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC); |
77ab9cff0 [NETFILTER]: nf_c... |
269 270 271 272 |
if (!new) return NULL; new->master = me; |
b54ab92b8 netfilter: refcou... |
273 |
refcount_set(&new->use, 1); |
77ab9cff0 [NETFILTER]: nf_c... |
274 275 |
return new; } |
6823645d6 [NETFILTER]: nf_c... |
276 |
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc); |
77ab9cff0 [NETFILTER]: nf_c... |
277 |
|
6002f266b [NETFILTER]: nf_c... |
278 |
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, |
76108cea0 netfilter: Use un... |
279 |
u_int8_t family, |
1d9d75225 [NETFILTER]: nf_c... |
280 281 282 |
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... |
283 284 285 286 287 288 289 290 291 |
{ int len; if (family == AF_INET) len = 4; else len = 16; exp->flags = 0; |
6002f266b [NETFILTER]: nf_c... |
292 |
exp->class = class; |
d6a9b6500 [NETFILTER]: nf_c... |
293 294 295 296 |
exp->expectfn = NULL; exp->helper = NULL; exp->tuple.src.l3num = family; exp->tuple.dst.protonum = proto; |
d6a9b6500 [NETFILTER]: nf_c... |
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
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... |
312 |
if (src) { |
a34c45896 netfilter endian ... |
313 314 |
exp->tuple.src.u.all = *src; exp->mask.src.u.all = htons(0xFFFF); |
d6a9b6500 [NETFILTER]: nf_c... |
315 316 317 318 |
} else { exp->tuple.src.u.all = 0; exp->mask.src.u.all = 0; } |
d4156e8cd [NETFILTER]: nf_c... |
319 320 321 322 323 |
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 ... |
324 |
exp->tuple.dst.u.all = *dst; |
f09eca8db netfilter: ctnetl... |
325 |
|
4806e9757 netfilter: replac... |
326 |
#if IS_ENABLED(CONFIG_NF_NAT) |
f09eca8db netfilter: ctnetl... |
327 328 329 |
memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); #endif |
d6a9b6500 [NETFILTER]: nf_c... |
330 |
} |
6823645d6 [NETFILTER]: nf_c... |
331 |
EXPORT_SYMBOL_GPL(nf_ct_expect_init); |
d6a9b6500 [NETFILTER]: nf_c... |
332 |
|
7d0742da1 [NETFILTER]: nf_c... |
333 334 335 336 337 338 339 |
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... |
340 |
void nf_ct_expect_put(struct nf_conntrack_expect *exp) |
77ab9cff0 [NETFILTER]: nf_c... |
341 |
{ |
b54ab92b8 netfilter: refcou... |
342 |
if (refcount_dec_and_test(&exp->use)) |
7d0742da1 [NETFILTER]: nf_c... |
343 |
call_rcu(&exp->rcu, nf_ct_expect_free_rcu); |
77ab9cff0 [NETFILTER]: nf_c... |
344 |
} |
6823645d6 [NETFILTER]: nf_c... |
345 |
EXPORT_SYMBOL_GPL(nf_ct_expect_put); |
77ab9cff0 [NETFILTER]: nf_c... |
346 |
|
4dee62b1b netfilter: nf_ct_... |
347 |
static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) |
77ab9cff0 [NETFILTER]: nf_c... |
348 349 |
{ struct nf_conn_help *master_help = nfct_help(exp->master); |
3d058d7bc netfilter: rework... |
350 |
struct nf_conntrack_helper *helper; |
9b03f38d0 netfilter: netns ... |
351 |
struct net *net = nf_ct_exp_net(exp); |
a9a083c38 netfilter: conntr... |
352 |
unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); |
77ab9cff0 [NETFILTER]: nf_c... |
353 |
|
3bfd45f93 netfilter: nf_con... |
354 |
/* two references : one for hash insert, one for the timer */ |
b54ab92b8 netfilter: refcou... |
355 |
refcount_add(2, &exp->use); |
b560580a1 [NETFILTER]: nf_c... |
356 |
|
e99e88a9d treewide: setup_t... |
357 |
timer_setup(&exp->timeout, nf_ct_expectation_timed_out, 0); |
3d058d7bc netfilter: rework... |
358 |
helper = rcu_dereference_protected(master_help->helper, |
ca7433df3 netfilter: conntr... |
359 |
lockdep_is_held(&nf_conntrack_expect_lock)); |
3d058d7bc netfilter: rework... |
360 361 362 |
if (helper) { exp->timeout.expires = jiffies + helper->expect_policy[exp->class].timeout * HZ; |
bc01befdc netfilter: ctnetl... |
363 |
} |
77ab9cff0 [NETFILTER]: nf_c... |
364 |
add_timer(&exp->timeout); |
56a97e701 netfilter: expect... |
365 366 367 368 369 |
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 ... |
370 |
NF_CT_STAT_INC(net, expect_create); |
77ab9cff0 [NETFILTER]: nf_c... |
371 372 373 |
} /* Race with expectations being used means we could have none to find; OK. */ |
6002f266b [NETFILTER]: nf_c... |
374 375 |
static void evict_oldest_expect(struct nf_conn *master, struct nf_conntrack_expect *new) |
77ab9cff0 [NETFILTER]: nf_c... |
376 |
{ |
b560580a1 [NETFILTER]: nf_c... |
377 |
struct nf_conn_help *master_help = nfct_help(master); |
6002f266b [NETFILTER]: nf_c... |
378 |
struct nf_conntrack_expect *exp, *last = NULL; |
77ab9cff0 [NETFILTER]: nf_c... |
379 |
|
b67bfe0d4 hlist: drop the n... |
380 |
hlist_for_each_entry(exp, &master_help->expectations, lnode) { |
6002f266b [NETFILTER]: nf_c... |
381 382 383 |
if (exp->class == new->class) last = exp; } |
b560580a1 [NETFILTER]: nf_c... |
384 |
|
ec0e3f011 netfilter: nf_ct_... |
385 386 |
if (last) nf_ct_remove_expect(last); |
77ab9cff0 [NETFILTER]: nf_c... |
387 |
} |
3c00fb0bf netfilter: nf_con... |
388 389 |
static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, unsigned int flags) |
77ab9cff0 [NETFILTER]: nf_c... |
390 |
{ |
6002f266b [NETFILTER]: nf_c... |
391 |
const struct nf_conntrack_expect_policy *p; |
77ab9cff0 [NETFILTER]: nf_c... |
392 393 394 |
struct nf_conntrack_expect *i; struct nf_conn *master = expect->master; struct nf_conn_help *master_help = nfct_help(master); |
3d058d7bc netfilter: rework... |
395 |
struct nf_conntrack_helper *helper; |
9b03f38d0 netfilter: netns ... |
396 |
struct net *net = nf_ct_exp_net(expect); |
b67bfe0d4 hlist: drop the n... |
397 |
struct hlist_node *next; |
a71c08556 [NETFILTER]: nf_c... |
398 |
unsigned int h; |
4b86c459c netfilter: nf_ct_... |
399 |
int ret = 0; |
77ab9cff0 [NETFILTER]: nf_c... |
400 |
|
3d058d7bc netfilter: rework... |
401 |
if (!master_help) { |
3c158f7f5 [NETFILTER]: nf_c... |
402 403 404 |
ret = -ESHUTDOWN; goto out; } |
a9a083c38 netfilter: conntr... |
405 |
h = nf_ct_expect_dst_hash(net, &expect->tuple); |
0a93aaedc netfilter: conntr... |
406 |
hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { |
3c00fb0bf netfilter: nf_con... |
407 408 409 410 |
if (master_matches(i, expect, flags) && expect_matches(i, expect)) { if (i->class != expect->class || i->master != expect->master) |
876c27314 netfilter: nf_con... |
411 |
return -EALREADY; |
36ac344e1 netfilter: expect... |
412 |
if (nf_ct_remove_expect(i)) |
2614f8649 netfilter: nf_ct_... |
413 |
break; |
77ab9cff0 [NETFILTER]: nf_c... |
414 415 416 417 418 419 |
} else if (expect_clash(i, expect)) { ret = -EBUSY; goto out; } } /* Will be over limit? */ |
3d058d7bc netfilter: rework... |
420 |
helper = rcu_dereference_protected(master_help->helper, |
ca7433df3 netfilter: conntr... |
421 |
lockdep_is_held(&nf_conntrack_expect_lock)); |
3d058d7bc netfilter: rework... |
422 423 |
if (helper) { p = &helper->expect_policy[expect->class]; |
bc01befdc netfilter: ctnetl... |
424 425 426 427 428 429 430 431 |
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... |
432 433 |
} } |
77ab9cff0 [NETFILTER]: nf_c... |
434 |
|
9b03f38d0 netfilter: netns ... |
435 |
if (net->ct.expect_count >= nf_ct_expect_max) { |
e87cc4728 net: Convert net_... |
436 437 |
net_warn_ratelimited("nf_conntrack: expectation table full "); |
f264a7df0 [NETFILTER]: nf_c... |
438 |
ret = -EMFILE; |
f264a7df0 [NETFILTER]: nf_c... |
439 |
} |
19abb7b09 netfilter: ctnetl... |
440 441 442 |
out: return ret; } |
b476b72a0 netfilter: trivia... |
443 |
int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, |
3c00fb0bf netfilter: nf_con... |
444 |
u32 portid, int report, unsigned int flags) |
19abb7b09 netfilter: ctnetl... |
445 446 |
{ int ret; |
ca7433df3 netfilter: conntr... |
447 |
spin_lock_bh(&nf_conntrack_expect_lock); |
3c00fb0bf netfilter: nf_con... |
448 |
ret = __nf_ct_expect_check(expect, flags); |
4b86c459c netfilter: nf_ct_... |
449 |
if (ret < 0) |
19abb7b09 netfilter: ctnetl... |
450 |
goto out; |
f264a7df0 [NETFILTER]: nf_c... |
451 |
|
4dee62b1b netfilter: nf_ct_... |
452 |
nf_ct_expect_insert(expect); |
ca7433df3 netfilter: conntr... |
453 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
ec464e5dc netfilter: rename... |
454 |
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); |
7fb668ac7 netfilter: nf_ct_... |
455 |
return 0; |
19abb7b09 netfilter: ctnetl... |
456 |
out: |
ca7433df3 netfilter: conntr... |
457 |
spin_unlock_bh(&nf_conntrack_expect_lock); |
19abb7b09 netfilter: ctnetl... |
458 459 460 |
return ret; } EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); |
ac7b84839 netfilter: expect... |
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 503 504 505 506 507 508 509 510 511 512 513 |
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... |
514 |
#ifdef CONFIG_NF_CONNTRACK_PROCFS |
5d08ad440 [NETFILTER]: nf_c... |
515 |
struct ct_expect_iter_state { |
dc5129f8d netfilter: netns ... |
516 |
struct seq_net_private p; |
5d08ad440 [NETFILTER]: nf_c... |
517 518 519 520 |
unsigned int bucket; }; static struct hlist_node *ct_expect_get_first(struct seq_file *seq) |
77ab9cff0 [NETFILTER]: nf_c... |
521 |
{ |
5d08ad440 [NETFILTER]: nf_c... |
522 |
struct ct_expect_iter_state *st = seq->private; |
7d0742da1 [NETFILTER]: nf_c... |
523 |
struct hlist_node *n; |
77ab9cff0 [NETFILTER]: nf_c... |
524 |
|
5d08ad440 [NETFILTER]: nf_c... |
525 |
for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) { |
0a93aaedc netfilter: conntr... |
526 |
n = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket])); |
7d0742da1 [NETFILTER]: nf_c... |
527 528 |
if (n) return n; |
5d08ad440 [NETFILTER]: nf_c... |
529 530 531 |
} return NULL; } |
77ab9cff0 [NETFILTER]: nf_c... |
532 |
|
5d08ad440 [NETFILTER]: nf_c... |
533 534 535 536 |
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... |
537 |
|
0e60ebe04 netfilter: add __... |
538 |
head = rcu_dereference(hlist_next_rcu(head)); |
5d08ad440 [NETFILTER]: nf_c... |
539 540 |
while (head == NULL) { if (++st->bucket >= nf_ct_expect_hsize) |
77ab9cff0 [NETFILTER]: nf_c... |
541 |
return NULL; |
0a93aaedc netfilter: conntr... |
542 |
head = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket])); |
77ab9cff0 [NETFILTER]: nf_c... |
543 |
} |
5d08ad440 [NETFILTER]: nf_c... |
544 |
return head; |
77ab9cff0 [NETFILTER]: nf_c... |
545 |
} |
5d08ad440 [NETFILTER]: nf_c... |
546 |
static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos) |
77ab9cff0 [NETFILTER]: nf_c... |
547 |
{ |
5d08ad440 [NETFILTER]: nf_c... |
548 |
struct hlist_node *head = ct_expect_get_first(seq); |
77ab9cff0 [NETFILTER]: nf_c... |
549 |
|
5d08ad440 [NETFILTER]: nf_c... |
550 551 552 553 554 |
if (head) while (pos && (head = ct_expect_get_next(seq, head))) pos--; return pos ? NULL : head; } |
77ab9cff0 [NETFILTER]: nf_c... |
555 |
|
5d08ad440 [NETFILTER]: nf_c... |
556 |
static void *exp_seq_start(struct seq_file *seq, loff_t *pos) |
7d0742da1 [NETFILTER]: nf_c... |
557 |
__acquires(RCU) |
5d08ad440 [NETFILTER]: nf_c... |
558 |
{ |
7d0742da1 [NETFILTER]: nf_c... |
559 |
rcu_read_lock(); |
5d08ad440 [NETFILTER]: nf_c... |
560 561 |
return ct_expect_get_idx(seq, *pos); } |
77ab9cff0 [NETFILTER]: nf_c... |
562 |
|
5d08ad440 [NETFILTER]: nf_c... |
563 564 565 566 |
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... |
567 |
} |
5d08ad440 [NETFILTER]: nf_c... |
568 |
static void exp_seq_stop(struct seq_file *seq, void *v) |
7d0742da1 [NETFILTER]: nf_c... |
569 |
__releases(RCU) |
77ab9cff0 [NETFILTER]: nf_c... |
570 |
{ |
7d0742da1 [NETFILTER]: nf_c... |
571 |
rcu_read_unlock(); |
77ab9cff0 [NETFILTER]: nf_c... |
572 573 574 575 |
} static int exp_seq_show(struct seq_file *s, void *v) { |
5d08ad440 [NETFILTER]: nf_c... |
576 |
struct nf_conntrack_expect *expect; |
b87921bdf netfilter: nf_con... |
577 |
struct nf_conntrack_helper *helper; |
5d08ad440 [NETFILTER]: nf_c... |
578 |
struct hlist_node *n = v; |
359b9ab61 [NETFILTER]: nf_c... |
579 |
char *delim = ""; |
5d08ad440 [NETFILTER]: nf_c... |
580 581 |
expect = hlist_entry(n, struct nf_conntrack_expect, hnode); |
77ab9cff0 [NETFILTER]: nf_c... |
582 583 584 585 586 |
if (expect->timeout.function) seq_printf(s, "%ld ", timer_pending(&expect->timeout) ? (long)(expect->timeout.expires - jiffies)/HZ : 0); else |
cdec26858 netfilter: Use se... |
587 |
seq_puts(s, "- "); |
77ab9cff0 [NETFILTER]: nf_c... |
588 589 590 591 |
seq_printf(s, "l3proto = %u proto=%u ", expect->tuple.src.l3num, expect->tuple.dst.protonum); print_tuple(s, &expect->tuple, |
4a60dc748 netfilter: conntr... |
592 |
nf_ct_l4proto_find(expect->tuple.dst.protonum)); |
4bb119eab [NETFILTER]: nf_c... |
593 |
|
359b9ab61 [NETFILTER]: nf_c... |
594 |
if (expect->flags & NF_CT_EXPECT_PERMANENT) { |
cdec26858 netfilter: Use se... |
595 |
seq_puts(s, "PERMANENT"); |
359b9ab61 [NETFILTER]: nf_c... |
596 597 |
delim = ","; } |
bc01befdc netfilter: ctnetl... |
598 |
if (expect->flags & NF_CT_EXPECT_INACTIVE) { |
359b9ab61 [NETFILTER]: nf_c... |
599 |
seq_printf(s, "%sINACTIVE", delim); |
bc01befdc netfilter: ctnetl... |
600 601 602 603 |
delim = ","; } if (expect->flags & NF_CT_EXPECT_USERSPACE) seq_printf(s, "%sUSERSPACE", delim); |
4bb119eab [NETFILTER]: nf_c... |
604 |
|
b87921bdf netfilter: nf_con... |
605 606 607 |
helper = rcu_dereference(nfct_help(expect->master)->helper); if (helper) { seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name); |
b173a28f6 netfilter: nf_ct_... |
608 |
if (helper->expect_policy[expect->class].name[0]) |
b87921bdf netfilter: nf_con... |
609 610 611 |
seq_printf(s, "/%s", helper->expect_policy[expect->class].name); } |
1ca9e4177 netfilter: Remove... |
612 613 614 615 |
seq_putc(s, ' '); return 0; |
77ab9cff0 [NETFILTER]: nf_c... |
616 |
} |
56b3d975b [NET]: Make all i... |
617 |
static const struct seq_operations exp_seq_ops = { |
77ab9cff0 [NETFILTER]: nf_c... |
618 619 620 621 622 |
.start = exp_seq_start, .next = exp_seq_next, .stop = exp_seq_stop, .show = exp_seq_show }; |
54b07dca6 netfilter: provid... |
623 |
#endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084e [NETFILTER]: nf_c... |
624 |
|
dc5129f8d netfilter: netns ... |
625 |
static int exp_proc_init(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
626 |
{ |
54b07dca6 netfilter: provid... |
627 |
#ifdef CONFIG_NF_CONNTRACK_PROCFS |
e9c1b084e [NETFILTER]: nf_c... |
628 |
struct proc_dir_entry *proc; |
f13f2aeed netfilter: Set /p... |
629 630 |
kuid_t root_uid; kgid_t root_gid; |
e9c1b084e [NETFILTER]: nf_c... |
631 |
|
c35063722 proc: introduce p... |
632 633 |
proc = proc_create_net("nf_conntrack_expect", 0440, net->proc_net, &exp_seq_ops, sizeof(struct ct_expect_iter_state)); |
e9c1b084e [NETFILTER]: nf_c... |
634 635 |
if (!proc) return -ENOMEM; |
f13f2aeed netfilter: Set /p... |
636 637 638 639 640 |
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... |
641 |
#endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084e [NETFILTER]: nf_c... |
642 643 |
return 0; } |
dc5129f8d netfilter: netns ... |
644 |
static void exp_proc_remove(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
645 |
{ |
54b07dca6 netfilter: provid... |
646 |
#ifdef CONFIG_NF_CONNTRACK_PROCFS |
ece31ffd5 net: proc: change... |
647 |
remove_proc_entry("nf_conntrack_expect", net->proc_net); |
54b07dca6 netfilter: provid... |
648 |
#endif /* CONFIG_NF_CONNTRACK_PROCFS */ |
e9c1b084e [NETFILTER]: nf_c... |
649 |
} |
13ccdfc2a netfilter: nf_con... |
650 |
module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400); |
a71c08556 [NETFILTER]: nf_c... |
651 |
|
83b4dbe19 netfilter: nf_ct_... |
652 |
int nf_conntrack_expect_pernet_init(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
653 |
{ |
9b03f38d0 netfilter: netns ... |
654 |
net->ct.expect_count = 0; |
0a93aaedc netfilter: conntr... |
655 |
return exp_proc_init(net); |
e9c1b084e [NETFILTER]: nf_c... |
656 |
} |
83b4dbe19 netfilter: nf_ct_... |
657 |
void nf_conntrack_expect_pernet_fini(struct net *net) |
e9c1b084e [NETFILTER]: nf_c... |
658 |
{ |
dc5129f8d netfilter: netns ... |
659 |
exp_proc_remove(net); |
e9c1b084e [NETFILTER]: nf_c... |
660 |
} |
83b4dbe19 netfilter: nf_ct_... |
661 662 663 664 665 666 667 668 669 670 671 672 673 674 |
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... |
675 676 677 678 679 680 |
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_... |
681 682 683 684 685 686 687 |
return 0; } void nf_conntrack_expect_fini(void) { rcu_barrier(); /* Wait for call_rcu() before destroy */ kmem_cache_destroy(nf_ct_expect_cachep); |
285189c78 netfilter: use kv... |
688 |
kvfree(nf_ct_expect_hash); |
83b4dbe19 netfilter: nf_ct_... |
689 |
} |