Blame view

net/netfilter/nf_conntrack_expect.c 17.5 KB
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /* 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>
   *
   * 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   Patrick McHardy   [NETFILTER]: nf_c...
22
  #include <linux/jhash.h>
457c4cbc5   Eric W. Biederman   [NET]: Make /proc...
23
  #include <net/net_namespace.h>
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
24
25
26
27
28
29
  
  #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   Patrick McHardy   netfilter: nf_con...
30
  #include <net/netfilter/nf_conntrack_zones.h>
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
31

a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
32
33
  unsigned int nf_ct_expect_hsize __read_mostly;
  EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
f264a7df0   Patrick McHardy   [NETFILTER]: nf_c...
34
  unsigned int nf_ct_expect_max __read_mostly;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
35

e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
36
  static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
37

bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
38
  static HLIST_HEAD(nf_ct_userspace_expect_list);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
39
  /* nf_conntrack_expect helper functions */
ebbf41df4   Pablo Neira Ayuso   netfilter: ctnetl...
40
41
  void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
  				u32 pid, int report)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
42
43
  {
  	struct nf_conn_help *master_help = nfct_help(exp->master);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
44
  	struct net *net = nf_ct_exp_net(exp);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
45

77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
46
  	NF_CT_ASSERT(!timer_pending(&exp->timeout));
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
47
  	hlist_del_rcu(&exp->hnode);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
48
  	net->ct.expect_count--;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
49

b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
50
  	hlist_del(&exp->lnode);
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
51
52
  	if (!(exp->flags & NF_CT_EXPECT_USERSPACE))
  		master_help->expecting[exp->class]--;
ebbf41df4   Pablo Neira Ayuso   netfilter: ctnetl...
53
  	nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report);
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
54
  	nf_ct_expect_put(exp);
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
55

0d55af879   Alexey Dobriyan   netfilter: netns ...
56
  	NF_CT_STAT_INC(net, expect_delete);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
57
  }
ebbf41df4   Pablo Neira Ayuso   netfilter: ctnetl...
58
  EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
59

6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
60
  static void nf_ct_expectation_timed_out(unsigned long ul_expect)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
61
62
  {
  	struct nf_conntrack_expect *exp = (void *)ul_expect;
f8ba1affa   Patrick McHardy   [NETFILTER]: nf_c...
63
  	spin_lock_bh(&nf_conntrack_lock);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
64
  	nf_ct_unlink_expect(exp);
f8ba1affa   Patrick McHardy   [NETFILTER]: nf_c...
65
  	spin_unlock_bh(&nf_conntrack_lock);
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
66
  	nf_ct_expect_put(exp);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
67
  }
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
68
69
  static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
  {
34498825c   Patrick McHardy   [NETFILTER]: non-...
70
  	unsigned int hash;
f682cefa5   Changli Gao   netfilter: fix th...
71
72
  	if (unlikely(!nf_conntrack_hash_rnd)) {
  		init_nf_conntrack_hash_rnd();
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
73
  	}
34498825c   Patrick McHardy   [NETFILTER]: non-...
74
  	hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
75
  		      (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
f682cefa5   Changli Gao   netfilter: fix th...
76
  		       (__force __u16)tuple->dst.u.all) ^ nf_conntrack_hash_rnd);
34498825c   Patrick McHardy   [NETFILTER]: non-...
77
  	return ((u64)hash * nf_ct_expect_hsize) >> 32;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
78
  }
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
79
  struct nf_conntrack_expect *
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
80
81
  __nf_ct_expect_find(struct net *net, u16 zone,
  		    const struct nf_conntrack_tuple *tuple)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
82
83
  {
  	struct nf_conntrack_expect *i;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
84
85
  	struct hlist_node *n;
  	unsigned int h;
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
86
  	if (!net->ct.expect_count)
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
87
  		return NULL;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
88

a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
89
  	h = nf_ct_expect_dst_hash(tuple);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
90
  	hlist_for_each_entry_rcu(i, n, &net->ct.expect_hash[h], hnode) {
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
91
92
  		if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
  		    nf_ct_zone(i->master) == zone)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
93
94
95
96
  			return i;
  	}
  	return NULL;
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
97
  EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
98
99
100
  
  /* Just find a expectation corresponding to a tuple. */
  struct nf_conntrack_expect *
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
101
102
  nf_ct_expect_find_get(struct net *net, u16 zone,
  		      const struct nf_conntrack_tuple *tuple)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
103
104
  {
  	struct nf_conntrack_expect *i;
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
105
  	rcu_read_lock();
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
106
  	i = __nf_ct_expect_find(net, zone, tuple);
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
107
108
109
  	if (i && !atomic_inc_not_zero(&i->use))
  		i = NULL;
  	rcu_read_unlock();
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
110
111
112
  
  	return i;
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
113
  EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
114
115
116
117
  
  /* If an expectation for this connection is found, it gets delete from
   * global list then returned. */
  struct nf_conntrack_expect *
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
118
119
  nf_ct_find_expectation(struct net *net, u16 zone,
  		       const struct nf_conntrack_tuple *tuple)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
120
  {
359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
121
122
123
  	struct nf_conntrack_expect *i, *exp = NULL;
  	struct hlist_node *n;
  	unsigned int h;
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
124
  	if (!net->ct.expect_count)
359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
125
  		return NULL;
ece006416   Yasuyuki Kozakai   [NETFILTER]: nf_c...
126

359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
127
  	h = nf_ct_expect_dst_hash(tuple);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
128
  	hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
129
  		if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
130
131
  		    nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
  		    nf_ct_zone(i->master) == zone) {
359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
132
133
134
135
  			exp = i;
  			break;
  		}
  	}
ece006416   Yasuyuki Kozakai   [NETFILTER]: nf_c...
136
137
  	if (!exp)
  		return NULL;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
138

77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
139
140
141
142
143
  	/* 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   Yasuyuki Kozakai   [NETFILTER]: nf_c...
144
145
146
147
148
149
150
151
152
  	if (!nf_ct_is_confirmed(exp->master))
  		return NULL;
  
  	if (exp->flags & NF_CT_EXPECT_PERMANENT) {
  		atomic_inc(&exp->use);
  		return exp;
  	} else if (del_timer(&exp->timeout)) {
  		nf_ct_unlink_expect(exp);
  		return exp;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
153
  	}
ece006416   Yasuyuki Kozakai   [NETFILTER]: nf_c...
154

77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
155
156
157
158
159
160
  	return NULL;
  }
  
  /* delete all expectations for this conntrack */
  void nf_ct_remove_expectations(struct nf_conn *ct)
  {
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
161
  	struct nf_conn_help *help = nfct_help(ct);
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
162
163
  	struct nf_conntrack_expect *exp;
  	struct hlist_node *n, *next;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
164
165
  
  	/* Optimization: most connection never expect any others. */
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
166
  	if (!help)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
167
  		return;
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
168
169
170
171
  	hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
  		if (del_timer(&exp->timeout)) {
  			nf_ct_unlink_expect(exp);
  			nf_ct_expect_put(exp);
601e68e10   YOSHIFUJI Hideaki   [NETFILTER]: Fix ...
172
  		}
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
173
174
  	}
  }
13b183391   Patrick McHardy   [NETFILTER]: nf_c...
175
  EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
176
177
178
179
180
181
182
  
  /* 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   Patrick McHardy   [NETFILTER]: nf_c...
183
  	struct nf_conntrack_tuple_mask intersect_mask;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
184
  	int count;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
185
  	intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
186
187
188
189
190
  
  	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];
  	}
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
191
192
193
194
195
196
  	return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
  }
  
  static inline int expect_matches(const struct nf_conntrack_expect *a,
  				 const struct nf_conntrack_expect *b)
  {
f64f9e719   Joe Perches   net: Move && and ...
197
198
  	return a->master == b->master && a->class == b->class &&
  		nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
5d0aa2ccd   Patrick McHardy   netfilter: nf_con...
199
200
  		nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
  		nf_ct_zone(a->master) == nf_ct_zone(b->master);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
201
202
203
  }
  
  /* Generally a bad idea to call this: could have matched already. */
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
204
  void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
205
  {
f8ba1affa   Patrick McHardy   [NETFILTER]: nf_c...
206
  	spin_lock_bh(&nf_conntrack_lock);
4e1d4e6c5   Patrick McHardy   [NETFILTER]: nf_c...
207
208
209
  	if (del_timer(&exp->timeout)) {
  		nf_ct_unlink_expect(exp);
  		nf_ct_expect_put(exp);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
210
  	}
f8ba1affa   Patrick McHardy   [NETFILTER]: nf_c...
211
  	spin_unlock_bh(&nf_conntrack_lock);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
212
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
213
  EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
214
215
216
217
  
  /* 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   Patrick McHardy   [NETFILTER]: nf_c...
218
  struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
219
220
  {
  	struct nf_conntrack_expect *new;
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
221
  	new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
222
223
224
225
226
227
228
  	if (!new)
  		return NULL;
  
  	new->master = me;
  	atomic_set(&new->use, 1);
  	return new;
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
229
  EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
230

6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
231
  void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
76108cea0   Jan Engelhardt   netfilter: Use un...
232
  		       u_int8_t family,
1d9d75225   Patrick McHardy   [NETFILTER]: nf_c...
233
234
235
  		       const union nf_inet_addr *saddr,
  		       const union nf_inet_addr *daddr,
  		       u_int8_t proto, const __be16 *src, const __be16 *dst)
d6a9b6500   Patrick McHardy   [NETFILTER]: nf_c...
236
237
238
239
240
241
242
243
244
  {
  	int len;
  
  	if (family == AF_INET)
  		len = 4;
  	else
  		len = 16;
  
  	exp->flags = 0;
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
245
  	exp->class = class;
d6a9b6500   Patrick McHardy   [NETFILTER]: nf_c...
246
247
248
249
  	exp->expectfn = NULL;
  	exp->helper = NULL;
  	exp->tuple.src.l3num = family;
  	exp->tuple.dst.protonum = proto;
d6a9b6500   Patrick McHardy   [NETFILTER]: nf_c...
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  
  	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   Patrick McHardy   [NETFILTER]: nf_c...
265
  	if (src) {
a34c45896   Al Viro   netfilter endian ...
266
267
  		exp->tuple.src.u.all = *src;
  		exp->mask.src.u.all = htons(0xFFFF);
d6a9b6500   Patrick McHardy   [NETFILTER]: nf_c...
268
269
270
271
  	} else {
  		exp->tuple.src.u.all = 0;
  		exp->mask.src.u.all = 0;
  	}
d4156e8cd   Patrick McHardy   [NETFILTER]: nf_c...
272
273
274
275
276
  	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   Al Viro   netfilter endian ...
277
  	exp->tuple.dst.u.all = *dst;
d6a9b6500   Patrick McHardy   [NETFILTER]: nf_c...
278
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
279
  EXPORT_SYMBOL_GPL(nf_ct_expect_init);
d6a9b6500   Patrick McHardy   [NETFILTER]: nf_c...
280

7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
281
282
283
284
285
286
287
  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   Patrick McHardy   [NETFILTER]: nf_c...
288
  void nf_ct_expect_put(struct nf_conntrack_expect *exp)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
289
290
  {
  	if (atomic_dec_and_test(&exp->use))
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
291
  		call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
292
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
293
  EXPORT_SYMBOL_GPL(nf_ct_expect_put);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
294

6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
295
  static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
296
297
  {
  	struct nf_conn_help *master_help = nfct_help(exp->master);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
298
  	struct net *net = nf_ct_exp_net(exp);
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
299
  	const struct nf_conntrack_expect_policy *p;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
300
  	unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
301

3bfd45f93   Eric Dumazet   netfilter: nf_con...
302
303
  	/* two references : one for hash insert, one for the timer */
  	atomic_add(2, &exp->use);
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
304

bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
305
306
307
308
309
  	if (master_help) {
  		hlist_add_head(&exp->lnode, &master_help->expectations);
  		master_help->expecting[exp->class]++;
  	} else if (exp->flags & NF_CT_EXPECT_USERSPACE)
  		hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
310

9b03f38d0   Alexey Dobriyan   netfilter: netns ...
311
312
  	hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
  	net->ct.expect_count++;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
313

6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
314
315
  	setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
  		    (unsigned long)exp);
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
316
  	if (master_help) {
c5d277d29   Eric Dumazet   netfilter: rcu sp...
317
318
319
320
  		p = &rcu_dereference_protected(
  				master_help->helper,
  				lockdep_is_held(&nf_conntrack_lock)
  				)->expect_policy[exp->class];
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
321
322
  		exp->timeout.expires = jiffies + p->timeout * HZ;
  	}
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
323
  	add_timer(&exp->timeout);
0d55af879   Alexey Dobriyan   netfilter: netns ...
324
  	NF_CT_STAT_INC(net, expect_create);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
325
326
327
  }
  
  /* Race with expectations being used means we could have none to find; OK. */
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
328
329
  static void evict_oldest_expect(struct nf_conn *master,
  				struct nf_conntrack_expect *new)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
330
  {
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
331
  	struct nf_conn_help *master_help = nfct_help(master);
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
332
  	struct nf_conntrack_expect *exp, *last = NULL;
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
333
  	struct hlist_node *n;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
334

6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
335
336
337
338
  	hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
  		if (exp->class == new->class)
  			last = exp;
  	}
b560580a1   Patrick McHardy   [NETFILTER]: nf_c...
339

6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
340
341
342
  	if (last && del_timer(&last->timeout)) {
  		nf_ct_unlink_expect(last);
  		nf_ct_expect_put(last);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
343
344
345
346
347
348
  	}
  }
  
  static inline int refresh_timer(struct nf_conntrack_expect *i)
  {
  	struct nf_conn_help *master_help = nfct_help(i->master);
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
349
  	const struct nf_conntrack_expect_policy *p;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
350
351
352
  
  	if (!del_timer(&i->timeout))
  		return 0;
c5d277d29   Eric Dumazet   netfilter: rcu sp...
353
354
355
356
  	p = &rcu_dereference_protected(
  		master_help->helper,
  		lockdep_is_held(&nf_conntrack_lock)
  		)->expect_policy[i->class];
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
357
  	i->timeout.expires = jiffies + p->timeout * HZ;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
358
359
360
  	add_timer(&i->timeout);
  	return 1;
  }
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
361
  static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
362
  {
6002f266b   Patrick McHardy   [NETFILTER]: nf_c...
363
  	const struct nf_conntrack_expect_policy *p;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
364
365
366
  	struct nf_conntrack_expect *i;
  	struct nf_conn *master = expect->master;
  	struct nf_conn_help *master_help = nfct_help(master);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
367
  	struct net *net = nf_ct_exp_net(expect);
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
368
369
  	struct hlist_node *n;
  	unsigned int h;
83731671d   Pablo Neira Ayuso   netfilter: ctnetl...
370
  	int ret = 1;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
371

bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
372
373
374
  	/* Don't allow expectations created from kernel-space with no helper */
  	if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
  	    (!master_help || (master_help && !master_help->helper))) {
3c158f7f5   Patrick McHarrdy   [NETFILTER]: nf_c...
375
376
377
  		ret = -ESHUTDOWN;
  		goto out;
  	}
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
378
  	h = nf_ct_expect_dst_hash(&expect->tuple);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
379
  	hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
380
381
382
383
384
385
386
387
388
389
390
391
  		if (expect_matches(i, expect)) {
  			/* Refresh timer: if it's dying, ignore.. */
  			if (refresh_timer(i)) {
  				ret = 0;
  				goto out;
  			}
  		} else if (expect_clash(i, expect)) {
  			ret = -EBUSY;
  			goto out;
  		}
  	}
  	/* Will be over limit? */
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
392
  	if (master_help) {
c5d277d29   Eric Dumazet   netfilter: rcu sp...
393
394
395
396
  		p = &rcu_dereference_protected(
  			master_help->helper,
  			lockdep_is_held(&nf_conntrack_lock)
  			)->expect_policy[expect->class];
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
397
398
399
400
401
402
403
404
  		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   Patrick McHardy   [NETFILTER]: nf_c...
405
406
  		}
  	}
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
407

9b03f38d0   Alexey Dobriyan   netfilter: netns ...
408
  	if (net->ct.expect_count >= nf_ct_expect_max) {
f264a7df0   Patrick McHardy   [NETFILTER]: nf_c...
409
410
  		if (net_ratelimit())
  			printk(KERN_WARNING
3d89e9cf3   Alexey Dobriyan   [NETFILTER]: nf_c...
411
412
  			       "nf_conntrack: expectation table full
  ");
f264a7df0   Patrick McHardy   [NETFILTER]: nf_c...
413
  		ret = -EMFILE;
f264a7df0   Patrick McHardy   [NETFILTER]: nf_c...
414
  	}
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
415
416
417
  out:
  	return ret;
  }
83731671d   Pablo Neira Ayuso   netfilter: ctnetl...
418
419
  int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, 
  				u32 pid, int report)
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
420
421
422
423
424
  {
  	int ret;
  
  	spin_lock_bh(&nf_conntrack_lock);
  	ret = __nf_ct_expect_check(expect);
83731671d   Pablo Neira Ayuso   netfilter: ctnetl...
425
  	if (ret <= 0)
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
426
  		goto out;
f264a7df0   Patrick McHardy   [NETFILTER]: nf_c...
427

83731671d   Pablo Neira Ayuso   netfilter: ctnetl...
428
  	ret = 0;
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
429
  	nf_ct_expect_insert(expect);
f8ba1affa   Patrick McHardy   [NETFILTER]: nf_c...
430
  	spin_unlock_bh(&nf_conntrack_lock);
83731671d   Pablo Neira Ayuso   netfilter: ctnetl...
431
  	nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
432
  	return ret;
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
433
434
  out:
  	spin_unlock_bh(&nf_conntrack_lock);
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
435
436
437
  	return ret;
  }
  EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  void nf_ct_remove_userspace_expectations(void)
  {
  	struct nf_conntrack_expect *exp;
  	struct hlist_node *n, *next;
  
  	hlist_for_each_entry_safe(exp, n, next,
  				  &nf_ct_userspace_expect_list, lnode) {
  		if (del_timer(&exp->timeout)) {
  			nf_ct_unlink_expect(exp);
  			nf_ct_expect_put(exp);
  		}
  	}
  }
  EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
452
  #ifdef CONFIG_PROC_FS
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
453
  struct ct_expect_iter_state {
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
454
  	struct seq_net_private p;
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
455
456
457
458
  	unsigned int bucket;
  };
  
  static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
459
  {
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
460
  	struct net *net = seq_file_net(seq);
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
461
  	struct ct_expect_iter_state *st = seq->private;
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
462
  	struct hlist_node *n;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
463

5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
464
  	for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
0e60ebe04   Eric Dumazet   netfilter: add __...
465
  		n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
466
467
  		if (n)
  			return n;
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
468
469
470
  	}
  	return NULL;
  }
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
471

5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
472
473
474
  static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
  					     struct hlist_node *head)
  {
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
475
  	struct net *net = seq_file_net(seq);
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
476
  	struct ct_expect_iter_state *st = seq->private;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
477

0e60ebe04   Eric Dumazet   netfilter: add __...
478
  	head = rcu_dereference(hlist_next_rcu(head));
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
479
480
  	while (head == NULL) {
  		if (++st->bucket >= nf_ct_expect_hsize)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
481
  			return NULL;
0e60ebe04   Eric Dumazet   netfilter: add __...
482
  		head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
483
  	}
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
484
  	return head;
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
485
  }
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
486
  static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
487
  {
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
488
  	struct hlist_node *head = ct_expect_get_first(seq);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
489

5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
490
491
492
493
494
  	if (head)
  		while (pos && (head = ct_expect_get_next(seq, head)))
  			pos--;
  	return pos ? NULL : head;
  }
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
495

5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
496
  static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
497
  	__acquires(RCU)
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
498
  {
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
499
  	rcu_read_lock();
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
500
501
  	return ct_expect_get_idx(seq, *pos);
  }
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
502

5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
503
504
505
506
  static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	(*pos)++;
  	return ct_expect_get_next(seq, v);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
507
  }
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
508
  static void exp_seq_stop(struct seq_file *seq, void *v)
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
509
  	__releases(RCU)
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
510
  {
7d0742da1   Patrick McHardy   [NETFILTER]: nf_c...
511
  	rcu_read_unlock();
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
512
513
514
515
  }
  
  static int exp_seq_show(struct seq_file *s, void *v)
  {
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
516
  	struct nf_conntrack_expect *expect;
b87921bdf   Patrick McHardy   netfilter: nf_con...
517
  	struct nf_conntrack_helper *helper;
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
518
  	struct hlist_node *n = v;
359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
519
  	char *delim = "";
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
520
521
  
  	expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
522
523
524
525
526
527
528
529
530
531
532
  
  	if (expect->timeout.function)
  		seq_printf(s, "%ld ", timer_pending(&expect->timeout)
  			   ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
  	else
  		seq_printf(s, "- ");
  	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   Martin Josefsson   [NETFILTER]: nf_c...
533
  		    __nf_ct_l4proto_find(expect->tuple.src.l3num,
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
534
  				       expect->tuple.dst.protonum));
4bb119eab   Patrick McHardy   [NETFILTER]: nf_c...
535

359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
536
537
538
539
  	if (expect->flags & NF_CT_EXPECT_PERMANENT) {
  		seq_printf(s, "PERMANENT");
  		delim = ",";
  	}
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
540
  	if (expect->flags & NF_CT_EXPECT_INACTIVE) {
359b9ab61   Patrick McHardy   [NETFILTER]: nf_c...
541
  		seq_printf(s, "%sINACTIVE", delim);
bc01befdc   Pablo Neira Ayuso   netfilter: ctnetl...
542
543
544
545
  		delim = ",";
  	}
  	if (expect->flags & NF_CT_EXPECT_USERSPACE)
  		seq_printf(s, "%sUSERSPACE", delim);
4bb119eab   Patrick McHardy   [NETFILTER]: nf_c...
546

b87921bdf   Patrick McHardy   netfilter: nf_con...
547
548
549
550
551
552
553
  	helper = rcu_dereference(nfct_help(expect->master)->helper);
  	if (helper) {
  		seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
  		if (helper->expect_policy[expect->class].name)
  			seq_printf(s, "/%s",
  				   helper->expect_policy[expect->class].name);
  	}
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
554
555
556
  	return seq_putc(s, '
  ');
  }
56b3d975b   Philippe De Muyter   [NET]: Make all i...
557
  static const struct seq_operations exp_seq_ops = {
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
558
559
560
561
562
563
564
565
  	.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   Alexey Dobriyan   netfilter: netns ...
566
  	return seq_open_net(inode, file, &exp_seq_ops,
e2da59133   Pavel Emelyanov   [NETFILTER]: Make...
567
  			sizeof(struct ct_expect_iter_state));
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
568
  }
5d08ad440   Patrick McHardy   [NETFILTER]: nf_c...
569
  static const struct file_operations exp_file_ops = {
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
570
571
572
573
  	.owner   = THIS_MODULE,
  	.open    = exp_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
574
  	.release = seq_release_net,
77ab9cff0   Martin Josefsson   [NETFILTER]: nf_c...
575
576
  };
  #endif /* CONFIG_PROC_FS */
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
577

dc5129f8d   Alexey Dobriyan   netfilter: netns ...
578
  static int exp_proc_init(struct net *net)
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
579
580
581
  {
  #ifdef CONFIG_PROC_FS
  	struct proc_dir_entry *proc;
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
582
  	proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
583
584
585
586
587
  	if (!proc)
  		return -ENOMEM;
  #endif /* CONFIG_PROC_FS */
  	return 0;
  }
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
588
  static void exp_proc_remove(struct net *net)
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
589
590
  {
  #ifdef CONFIG_PROC_FS
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
591
  	proc_net_remove(net, "nf_conntrack_expect");
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
592
593
  #endif /* CONFIG_PROC_FS */
  }
13ccdfc2a   Alexey Dobriyan   netfilter: nf_con...
594
  module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
595

9b03f38d0   Alexey Dobriyan   netfilter: netns ...
596
  int nf_conntrack_expect_init(struct net *net)
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
597
  {
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
598
  	int err = -ENOMEM;
08f6547d2   Alexey Dobriyan   netfilter: netns ...
599
600
  	if (net_eq(net, &init_net)) {
  		if (!nf_ct_expect_hsize) {
d696c7bda   Patrick McHardy   netfilter: nf_con...
601
  			nf_ct_expect_hsize = net->ct.htable_size / 256;
08f6547d2   Alexey Dobriyan   netfilter: netns ...
602
603
604
605
  			if (!nf_ct_expect_hsize)
  				nf_ct_expect_hsize = 1;
  		}
  		nf_ct_expect_max = nf_ct_expect_hsize * 4;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
606
  	}
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
607
  	net->ct.expect_count = 0;
d862a6622   Patrick McHardy   netfilter: nf_con...
608
  	net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
609
  	if (net->ct.expect_hash == NULL)
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
610
  		goto err1;
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
611

08f6547d2   Alexey Dobriyan   netfilter: netns ...
612
613
  	if (net_eq(net, &init_net)) {
  		nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
614
  					sizeof(struct nf_conntrack_expect),
20c2df83d   Paul Mundt   mm: Remove slab d...
615
  					0, 0, NULL);
08f6547d2   Alexey Dobriyan   netfilter: netns ...
616
617
618
  		if (!nf_ct_expect_cachep)
  			goto err2;
  	}
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
619

dc5129f8d   Alexey Dobriyan   netfilter: netns ...
620
  	err = exp_proc_init(net);
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
621
  	if (err < 0)
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
622
  		goto err3;
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
623
624
  
  	return 0;
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
625
  err3:
08f6547d2   Alexey Dobriyan   netfilter: netns ...
626
627
  	if (net_eq(net, &init_net))
  		kmem_cache_destroy(nf_ct_expect_cachep);
12293bf91   Alexey Dobriyan   netfilter: nf_con...
628
  err2:
d862a6622   Patrick McHardy   netfilter: nf_con...
629
  	nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
a71c08556   Patrick McHardy   [NETFILTER]: nf_c...
630
  err1:
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
631
632
  	return err;
  }
9b03f38d0   Alexey Dobriyan   netfilter: netns ...
633
  void nf_conntrack_expect_fini(struct net *net)
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
634
  {
dc5129f8d   Alexey Dobriyan   netfilter: netns ...
635
  	exp_proc_remove(net);
308ff823e   Jesper Dangaard Brouer   nf_conntrack: Use...
636
637
  	if (net_eq(net, &init_net)) {
  		rcu_barrier(); /* Wait for call_rcu() before destroy */
08f6547d2   Alexey Dobriyan   netfilter: netns ...
638
  		kmem_cache_destroy(nf_ct_expect_cachep);
308ff823e   Jesper Dangaard Brouer   nf_conntrack: Use...
639
  	}
d862a6622   Patrick McHardy   netfilter: nf_con...
640
  	nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
e9c1b084e   Patrick McHardy   [NETFILTER]: nf_c...
641
  }