blob: b45a4223cb058a47ae2863a4166cd5085e587ca6 [file] [log] [blame]
Martin Josefsson77ab9cf2006-11-29 02:34:58 +01001/* Expectation handling for nf_conntrack. */
2
3/* (C) 1999-2001 Paul `Rusty' Russell
4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
5 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
Patrick McHardyf229f6c2013-04-06 15:24:29 +02006 * (c) 2005-2012 Patrick McHardy <kaber@trash.net>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +01007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/types.h>
14#include <linux/netfilter.h>
15#include <linux/skbuff.h>
16#include <linux/proc_fs.h>
17#include <linux/seq_file.h>
18#include <linux/stddef.h>
19#include <linux/slab.h>
20#include <linux/err.h>
21#include <linux/percpu.h>
22#include <linux/kernel.h>
Patrick McHardya71c0852007-07-07 22:33:47 -070023#include <linux/jhash.h>
Paul Gortmakerd9b93842011-09-18 13:21:27 -040024#include <linux/moduleparam.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040025#include <linux/export.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020026#include <net/net_namespace.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010027
28#include <net/netfilter/nf_conntrack.h>
29#include <net/netfilter/nf_conntrack_core.h>
30#include <net/netfilter/nf_conntrack_expect.h>
31#include <net/netfilter/nf_conntrack_helper.h>
32#include <net/netfilter/nf_conntrack_tuple.h>
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010033#include <net/netfilter/nf_conntrack_zones.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010034
Patrick McHardya71c0852007-07-07 22:33:47 -070035unsigned int nf_ct_expect_hsize __read_mostly;
36EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
37
Patrick McHardyf264a7d2007-07-07 22:36:24 -070038unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070039
Patrick McHardye9c1b082007-07-07 22:32:53 -070040static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010041
42/* nf_conntrack_expect helper functions */
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020043void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
Patrick McHardyec464e52013-04-17 06:47:08 +000044 u32 portid, int report)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010045{
46 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020047 struct net *net = nf_ct_exp_net(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010048
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +010049 NF_CT_ASSERT(master_help);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010050 NF_CT_ASSERT(!timer_pending(&exp->timeout));
51
Patrick McHardy7d0742d2008-01-31 04:38:19 -080052 hlist_del_rcu(&exp->hnode);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020053 net->ct.expect_count--;
Patrick McHardya71c0852007-07-07 22:33:47 -070054
Patrick McHardyb5605802007-07-07 22:35:56 -070055 hlist_del(&exp->lnode);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +010056 master_help->expecting[exp->class]--;
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020057
Patrick McHardyec464e52013-04-17 06:47:08 +000058 nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report);
Patrick McHardy68236452007-07-07 22:30:49 -070059 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070060
Alexey Dobriyan0d55af82008-10-08 11:35:07 +020061 NF_CT_STAT_INC(net, expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010062}
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020063EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010064
Patrick McHardy68236452007-07-07 22:30:49 -070065static void nf_ct_expectation_timed_out(unsigned long ul_expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010066{
67 struct nf_conntrack_expect *exp = (void *)ul_expect;
68
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +010069 spin_lock_bh(&nf_conntrack_expect_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010070 nf_ct_unlink_expect(exp);
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +010071 spin_unlock_bh(&nf_conntrack_expect_lock);
Patrick McHardy68236452007-07-07 22:30:49 -070072 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010073}
74
Patrick McHardya71c0852007-07-07 22:33:47 -070075static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
76{
Patrick McHardy34498822007-12-17 22:45:52 -080077 unsigned int hash;
78
Changli Gaof682cef2011-01-05 04:23:23 +000079 if (unlikely(!nf_conntrack_hash_rnd)) {
80 init_nf_conntrack_hash_rnd();
Patrick McHardya71c0852007-07-07 22:33:47 -070081 }
82
Patrick McHardy34498822007-12-17 22:45:52 -080083 hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
Patrick McHardya71c0852007-07-07 22:33:47 -070084 (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
Changli Gaof682cef2011-01-05 04:23:23 +000085 (__force __u16)tuple->dst.u.all) ^ nf_conntrack_hash_rnd);
Daniel Borkmann8fc54f62014-08-23 20:58:54 +020086
87 return reciprocal_scale(hash, nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -070088}
89
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010090struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010091__nf_ct_expect_find(struct net *net, u16 zone,
92 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010093{
94 struct nf_conntrack_expect *i;
Patrick McHardya71c0852007-07-07 22:33:47 -070095 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010096
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020097 if (!net->ct.expect_count)
Patrick McHardya71c0852007-07-07 22:33:47 -070098 return NULL;
99
100 h = nf_ct_expect_dst_hash(tuple);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800101 hlist_for_each_entry_rcu(i, &net->ct.expect_hash[h], hnode) {
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100102 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
103 nf_ct_zone(i->master) == zone)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100104 return i;
105 }
106 return NULL;
107}
Patrick McHardy68236452007-07-07 22:30:49 -0700108EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100109
110/* Just find a expectation corresponding to a tuple. */
111struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100112nf_ct_expect_find_get(struct net *net, u16 zone,
113 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100114{
115 struct nf_conntrack_expect *i;
116
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800117 rcu_read_lock();
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100118 i = __nf_ct_expect_find(net, zone, tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800119 if (i && !atomic_inc_not_zero(&i->use))
120 i = NULL;
121 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100122
123 return i;
124}
Patrick McHardy68236452007-07-07 22:30:49 -0700125EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100126
127/* If an expectation for this connection is found, it gets delete from
128 * global list then returned. */
129struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100130nf_ct_find_expectation(struct net *net, u16 zone,
131 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100132{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700133 struct nf_conntrack_expect *i, *exp = NULL;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700134 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100135
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200136 if (!net->ct.expect_count)
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700137 return NULL;
138
139 h = nf_ct_expect_dst_hash(tuple);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800140 hlist_for_each_entry(i, &net->ct.expect_hash[h], hnode) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700141 if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100142 nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
143 nf_ct_zone(i->master) == zone) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700144 exp = i;
145 break;
146 }
147 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800148 if (!exp)
149 return NULL;
150
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100151 /* If master is not in hash table yet (ie. packet hasn't left
152 this machine yet), how can other end know about expected?
153 Hence these are not the droids you are looking for (if
154 master ct never got confirmed, we'd hold a reference to it
155 and weird things would happen to future packets). */
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800156 if (!nf_ct_is_confirmed(exp->master))
157 return NULL;
158
Jesper Dangaard Brouere1b207d2014-03-03 14:45:39 +0100159 /* Avoid race with other CPUs, that for exp->master ct, is
160 * about to invoke ->destroy(), or nf_ct_delete() via timeout
161 * or early_drop().
162 *
163 * The atomic_inc_not_zero() check tells: If that fails, we
164 * know that the ct is being destroyed. If it succeeds, we
165 * can be sure the ct cannot disappear underneath.
166 */
167 if (unlikely(nf_ct_is_dying(exp->master) ||
168 !atomic_inc_not_zero(&exp->master->ct_general.use)))
169 return NULL;
170
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800171 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
172 atomic_inc(&exp->use);
173 return exp;
174 } else if (del_timer(&exp->timeout)) {
175 nf_ct_unlink_expect(exp);
176 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100177 }
Jesper Dangaard Brouere1b207d2014-03-03 14:45:39 +0100178 /* Undo exp->master refcnt increase, if del_timer() failed */
179 nf_ct_put(exp->master);
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800180
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100181 return NULL;
182}
183
184/* delete all expectations for this conntrack */
185void nf_ct_remove_expectations(struct nf_conn *ct)
186{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100187 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700188 struct nf_conntrack_expect *exp;
Sasha Levinb67bfe02013-02-27 17:06:00 -0800189 struct hlist_node *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100190
191 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700192 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100193 return;
194
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100195 spin_lock_bh(&nf_conntrack_expect_lock);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800196 hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
Patrick McHardyb5605802007-07-07 22:35:56 -0700197 if (del_timer(&exp->timeout)) {
198 nf_ct_unlink_expect(exp);
199 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800200 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100201 }
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100202 spin_unlock_bh(&nf_conntrack_expect_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100203}
Patrick McHardy13b18332006-12-02 22:11:25 -0800204EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100205
206/* Would two expected things clash? */
207static inline int expect_clash(const struct nf_conntrack_expect *a,
208 const struct nf_conntrack_expect *b)
209{
210 /* Part covered by intersection of masks must be unequal,
211 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700212 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100213 int count;
214
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100215 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100216
217 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
218 intersect_mask.src.u3.all[count] =
219 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
220 }
221
Joe Stringer4b318142015-07-21 21:37:31 -0700222 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask) &&
223 nf_ct_zone(a->master) == nf_ct_zone(b->master);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100224}
225
226static inline int expect_matches(const struct nf_conntrack_expect *a,
227 const struct nf_conntrack_expect *b)
228{
Joe Perchesf64f9e72009-11-29 16:55:45 -0800229 return a->master == b->master && a->class == b->class &&
230 nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100231 nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
232 nf_ct_zone(a->master) == nf_ct_zone(b->master);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100233}
234
235/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700236void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100237{
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100238 spin_lock_bh(&nf_conntrack_expect_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700239 if (del_timer(&exp->timeout)) {
240 nf_ct_unlink_expect(exp);
241 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100242 }
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100243 spin_unlock_bh(&nf_conntrack_expect_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100244}
Patrick McHardy68236452007-07-07 22:30:49 -0700245EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100246
247/* We don't increase the master conntrack refcount for non-fulfilled
248 * conntracks. During the conntrack destruction, the expectations are
249 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700250struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100251{
252 struct nf_conntrack_expect *new;
253
Patrick McHardy68236452007-07-07 22:30:49 -0700254 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100255 if (!new)
256 return NULL;
257
258 new->master = me;
259 atomic_set(&new->use, 1);
260 return new;
261}
Patrick McHardy68236452007-07-07 22:30:49 -0700262EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100263
Patrick McHardy6002f2662008-03-25 20:09:15 -0700264void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200265 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700266 const union nf_inet_addr *saddr,
267 const union nf_inet_addr *daddr,
268 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800269{
270 int len;
271
272 if (family == AF_INET)
273 len = 4;
274 else
275 len = 16;
276
277 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700278 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800279 exp->expectfn = NULL;
280 exp->helper = NULL;
281 exp->tuple.src.l3num = family;
282 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800283
284 if (saddr) {
285 memcpy(&exp->tuple.src.u3, saddr, len);
286 if (sizeof(exp->tuple.src.u3) > len)
287 /* address needs to be cleared for nf_ct_tuple_equal */
288 memset((void *)&exp->tuple.src.u3 + len, 0x00,
289 sizeof(exp->tuple.src.u3) - len);
290 memset(&exp->mask.src.u3, 0xFF, len);
291 if (sizeof(exp->mask.src.u3) > len)
292 memset((void *)&exp->mask.src.u3 + len, 0x00,
293 sizeof(exp->mask.src.u3) - len);
294 } else {
295 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
296 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
297 }
298
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800299 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100300 exp->tuple.src.u.all = *src;
301 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800302 } else {
303 exp->tuple.src.u.all = 0;
304 exp->mask.src.u.all = 0;
305 }
306
Patrick McHardyd4156e82007-07-07 22:31:32 -0700307 memcpy(&exp->tuple.dst.u3, daddr, len);
308 if (sizeof(exp->tuple.dst.u3) > len)
309 /* address needs to be cleared for nf_ct_tuple_equal */
310 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
311 sizeof(exp->tuple.dst.u3) - len);
312
Al Viroa34c4582007-07-26 17:33:19 +0100313 exp->tuple.dst.u.all = *dst;
Pablo Neira Ayusof09eca82013-07-09 20:16:39 +0200314
315#ifdef CONFIG_NF_NAT_NEEDED
316 memset(&exp->saved_addr, 0, sizeof(exp->saved_addr));
317 memset(&exp->saved_proto, 0, sizeof(exp->saved_proto));
318#endif
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800319}
Patrick McHardy68236452007-07-07 22:30:49 -0700320EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800321
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800322static void nf_ct_expect_free_rcu(struct rcu_head *head)
323{
324 struct nf_conntrack_expect *exp;
325
326 exp = container_of(head, struct nf_conntrack_expect, rcu);
327 kmem_cache_free(nf_ct_expect_cachep, exp);
328}
329
Patrick McHardy68236452007-07-07 22:30:49 -0700330void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100331{
332 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800333 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100334}
Patrick McHardy68236452007-07-07 22:30:49 -0700335EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100336
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100337static int nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100338{
339 struct nf_conn_help *master_help = nfct_help(exp->master);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100340 struct nf_conntrack_helper *helper;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200341 struct net *net = nf_ct_exp_net(exp);
Patrick McHardya71c0852007-07-07 22:33:47 -0700342 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100343
Eric Dumazet3bfd45f2010-11-16 10:19:18 +0100344 /* two references : one for hash insert, one for the timer */
345 atomic_add(2, &exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700346
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100347 hlist_add_head(&exp->lnode, &master_help->expectations);
348 master_help->expecting[exp->class]++;
Patrick McHardya71c0852007-07-07 22:33:47 -0700349
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200350 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
351 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100352
Patrick McHardy68236452007-07-07 22:30:49 -0700353 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
354 (unsigned long)exp);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100355 helper = rcu_dereference_protected(master_help->helper,
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100356 lockdep_is_held(&nf_conntrack_expect_lock));
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100357 if (helper) {
358 exp->timeout.expires = jiffies +
359 helper->expect_policy[exp->class].timeout * HZ;
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200360 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100361 add_timer(&exp->timeout);
362
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200363 NF_CT_STAT_INC(net, expect_create);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100364 return 0;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100365}
366
367/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700368static void evict_oldest_expect(struct nf_conn *master,
369 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100370{
Patrick McHardyb5605802007-07-07 22:35:56 -0700371 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700372 struct nf_conntrack_expect *exp, *last = NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100373
Sasha Levinb67bfe02013-02-27 17:06:00 -0800374 hlist_for_each_entry(exp, &master_help->expectations, lnode) {
Patrick McHardy6002f2662008-03-25 20:09:15 -0700375 if (exp->class == new->class)
376 last = exp;
377 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700378
Patrick McHardy6002f2662008-03-25 20:09:15 -0700379 if (last && del_timer(&last->timeout)) {
380 nf_ct_unlink_expect(last);
381 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100382 }
383}
384
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100385static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100386{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700387 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100388 struct nf_conntrack_expect *i;
389 struct nf_conn *master = expect->master;
390 struct nf_conn_help *master_help = nfct_help(master);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100391 struct nf_conntrack_helper *helper;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200392 struct net *net = nf_ct_exp_net(expect);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800393 struct hlist_node *next;
Patrick McHardya71c0852007-07-07 22:33:47 -0700394 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200395 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100396
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100397 if (!master_help) {
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700398 ret = -ESHUTDOWN;
399 goto out;
400 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700401 h = nf_ct_expect_dst_hash(&expect->tuple);
Sasha Levinb67bfe02013-02-27 17:06:00 -0800402 hlist_for_each_entry_safe(i, next, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100403 if (expect_matches(i, expect)) {
Pablo Neira Ayuso2614f862012-08-16 02:25:24 +0200404 if (del_timer(&i->timeout)) {
405 nf_ct_unlink_expect(i);
406 nf_ct_expect_put(i);
407 break;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100408 }
409 } else if (expect_clash(i, expect)) {
410 ret = -EBUSY;
411 goto out;
412 }
413 }
414 /* Will be over limit? */
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100415 helper = rcu_dereference_protected(master_help->helper,
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100416 lockdep_is_held(&nf_conntrack_expect_lock));
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100417 if (helper) {
418 p = &helper->expect_policy[expect->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200419 if (p->max_expected &&
420 master_help->expecting[expect->class] >= p->max_expected) {
421 evict_oldest_expect(master, expect);
422 if (master_help->expecting[expect->class]
423 >= p->max_expected) {
424 ret = -EMFILE;
425 goto out;
426 }
Patrick McHardy6002f2662008-03-25 20:09:15 -0700427 }
428 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100429
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200430 if (net->ct.expect_count >= nf_ct_expect_max) {
Joe Perchese87cc472012-05-13 21:56:26 +0000431 net_warn_ratelimited("nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700432 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700433 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100434out:
435 return ret;
436}
437
Jesper Dangaard Brouerb476b722014-03-03 14:44:54 +0100438int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
Patrick McHardyec464e52013-04-17 06:47:08 +0000439 u32 portid, int report)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100440{
441 int ret;
442
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100443 spin_lock_bh(&nf_conntrack_expect_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100444 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200445 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100446 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200447
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100448 ret = nf_ct_expect_insert(expect);
449 if (ret < 0)
450 goto out;
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100451 spin_unlock_bh(&nf_conntrack_expect_lock);
Patrick McHardyec464e52013-04-17 06:47:08 +0000452 nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200453 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100454out:
Jesper Dangaard Brouerca7433d2014-03-03 14:46:01 +0100455 spin_unlock_bh(&nf_conntrack_expect_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100456 return ret;
457}
458EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
459
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200460#ifdef CONFIG_NF_CONNTRACK_PROCFS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700461struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200462 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700463 unsigned int bucket;
464};
465
466static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100467{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200468 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700469 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800470 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100471
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700472 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100473 n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800474 if (n)
475 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100476 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700477 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100478}
479
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700480static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
481 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100482{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200483 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700484 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100485
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100486 head = rcu_dereference(hlist_next_rcu(head));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700487 while (head == NULL) {
488 if (++st->bucket >= nf_ct_expect_hsize)
489 return NULL;
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100490 head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700491 }
492 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100493}
494
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700495static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
496{
497 struct hlist_node *head = ct_expect_get_first(seq);
498
499 if (head)
500 while (pos && (head = ct_expect_get_next(seq, head)))
501 pos--;
502 return pos ? NULL : head;
503}
504
505static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800506 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700507{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800508 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700509 return ct_expect_get_idx(seq, *pos);
510}
511
512static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
513{
514 (*pos)++;
515 return ct_expect_get_next(seq, v);
516}
517
518static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800519 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100520{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800521 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100522}
523
524static int exp_seq_show(struct seq_file *s, void *v)
525{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700526 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100527 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700528 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700529 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700530
531 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100532
533 if (expect->timeout.function)
534 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
535 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
536 else
537 seq_printf(s, "- ");
538 seq_printf(s, "l3proto = %u proto=%u ",
539 expect->tuple.src.l3num,
540 expect->tuple.dst.protonum);
541 print_tuple(s, &expect->tuple,
542 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100543 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100544 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700545
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700546 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
547 seq_printf(s, "PERMANENT");
548 delim = ",";
549 }
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200550 if (expect->flags & NF_CT_EXPECT_INACTIVE) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700551 seq_printf(s, "%sINACTIVE", delim);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200552 delim = ",";
553 }
554 if (expect->flags & NF_CT_EXPECT_USERSPACE)
555 seq_printf(s, "%sUSERSPACE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700556
Patrick McHardyb87921b2010-02-11 12:22:48 +0100557 helper = rcu_dereference(nfct_help(expect->master)->helper);
558 if (helper) {
559 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
560 if (helper->expect_policy[expect->class].name)
561 seq_printf(s, "/%s",
562 helper->expect_policy[expect->class].name);
563 }
564
Joe Perches1ca9e412015-03-16 11:25:17 -0700565 seq_putc(s, '\n');
566
567 return 0;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100568}
569
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700570static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100571 .start = exp_seq_start,
572 .next = exp_seq_next,
573 .stop = exp_seq_stop,
574 .show = exp_seq_show
575};
576
577static int exp_open(struct inode *inode, struct file *file)
578{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200579 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700580 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100581}
582
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700583static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100584 .owner = THIS_MODULE,
585 .open = exp_open,
586 .read = seq_read,
587 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200588 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100589};
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200590#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700591
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200592static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700593{
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200594#ifdef CONFIG_NF_CONNTRACK_PROCFS
Patrick McHardye9c1b082007-07-07 22:32:53 -0700595 struct proc_dir_entry *proc;
596
Gao fengd4beaa62013-02-18 01:34:54 +0000597 proc = proc_create("nf_conntrack_expect", 0440, net->proc_net,
598 &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700599 if (!proc)
600 return -ENOMEM;
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200601#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700602 return 0;
603}
604
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200605static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700606{
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200607#ifdef CONFIG_NF_CONNTRACK_PROCFS
Gao fengece31ff2013-02-18 01:34:56 +0000608 remove_proc_entry("nf_conntrack_expect", net->proc_net);
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200609#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700610}
611
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800612module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700613
Gao feng83b4dbe2013-01-21 22:10:25 +0000614int nf_conntrack_expect_pernet_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700615{
Patrick McHardya71c0852007-07-07 22:33:47 -0700616 int err = -ENOMEM;
617
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200618 net->ct.expect_count = 0;
Patrick McHardyd862a662011-01-14 15:45:56 +0100619 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200620 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700621 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700622
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200623 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700624 if (err < 0)
Gao feng83b4dbe2013-01-21 22:10:25 +0000625 goto err2;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700626
627 return 0;
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700628err2:
Patrick McHardyd862a662011-01-14 15:45:56 +0100629 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700630err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700631 return err;
632}
633
Gao feng83b4dbe2013-01-21 22:10:25 +0000634void nf_conntrack_expect_pernet_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700635{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200636 exp_proc_remove(net);
Patrick McHardyd862a662011-01-14 15:45:56 +0100637 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700638}
Gao feng83b4dbe2013-01-21 22:10:25 +0000639
640int nf_conntrack_expect_init(void)
641{
642 if (!nf_ct_expect_hsize) {
643 nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
644 if (!nf_ct_expect_hsize)
645 nf_ct_expect_hsize = 1;
646 }
647 nf_ct_expect_max = nf_ct_expect_hsize * 4;
648 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
649 sizeof(struct nf_conntrack_expect),
650 0, 0, NULL);
651 if (!nf_ct_expect_cachep)
652 return -ENOMEM;
653 return 0;
654}
655
656void nf_conntrack_expect_fini(void)
657{
658 rcu_barrier(); /* Wait for call_rcu() before destroy */
659 kmem_cache_destroy(nf_ct_expect_cachep);
660}