blob: 527651a53a45ded66c97162f4e072ca55a7d1efa [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>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/types.h>
13#include <linux/netfilter.h>
14#include <linux/skbuff.h>
15#include <linux/proc_fs.h>
16#include <linux/seq_file.h>
17#include <linux/stddef.h>
18#include <linux/slab.h>
19#include <linux/err.h>
20#include <linux/percpu.h>
21#include <linux/kernel.h>
Patrick McHardya71c0852007-07-07 22:33:47 -070022#include <linux/jhash.h>
Paul Gortmakerd9b93842011-09-18 13:21:27 -040023#include <linux/moduleparam.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040024#include <linux/export.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020025#include <net/net_namespace.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010026
27#include <net/netfilter/nf_conntrack.h>
28#include <net/netfilter/nf_conntrack_core.h>
29#include <net/netfilter/nf_conntrack_expect.h>
30#include <net/netfilter/nf_conntrack_helper.h>
31#include <net/netfilter/nf_conntrack_tuple.h>
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010032#include <net/netfilter/nf_conntrack_zones.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010033
Patrick McHardya71c0852007-07-07 22:33:47 -070034unsigned int nf_ct_expect_hsize __read_mostly;
35EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
36
Patrick McHardyf264a7d2007-07-07 22:36:24 -070037unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070038
Patrick McHardye9c1b082007-07-07 22:32:53 -070039static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010040
41/* nf_conntrack_expect helper functions */
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020042void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
43 u32 pid, int report)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010044{
45 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020046 struct net *net = nf_ct_exp_net(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010047
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +010048 NF_CT_ASSERT(master_help);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010049 NF_CT_ASSERT(!timer_pending(&exp->timeout));
50
Patrick McHardy7d0742d2008-01-31 04:38:19 -080051 hlist_del_rcu(&exp->hnode);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020052 net->ct.expect_count--;
Patrick McHardya71c0852007-07-07 22:33:47 -070053
Patrick McHardyb5605802007-07-07 22:35:56 -070054 hlist_del(&exp->lnode);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +010055 master_help->expecting[exp->class]--;
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020056
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020057 nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report);
Patrick McHardy68236452007-07-07 22:30:49 -070058 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070059
Alexey Dobriyan0d55af82008-10-08 11:35:07 +020060 NF_CT_STAT_INC(net, expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010061}
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020062EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010063
Patrick McHardy68236452007-07-07 22:30:49 -070064static void nf_ct_expectation_timed_out(unsigned long ul_expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010065{
66 struct nf_conntrack_expect *exp = (void *)ul_expect;
67
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080068 spin_lock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010069 nf_ct_unlink_expect(exp);
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080070 spin_unlock_bh(&nf_conntrack_lock);
Patrick McHardy68236452007-07-07 22:30:49 -070071 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010072}
73
Patrick McHardya71c0852007-07-07 22:33:47 -070074static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
75{
Patrick McHardy34498822007-12-17 22:45:52 -080076 unsigned int hash;
77
Changli Gaof682cef2011-01-05 04:23:23 +000078 if (unlikely(!nf_conntrack_hash_rnd)) {
79 init_nf_conntrack_hash_rnd();
Patrick McHardya71c0852007-07-07 22:33:47 -070080 }
81
Patrick McHardy34498822007-12-17 22:45:52 -080082 hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
Patrick McHardya71c0852007-07-07 22:33:47 -070083 (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
Changli Gaof682cef2011-01-05 04:23:23 +000084 (__force __u16)tuple->dst.u.all) ^ nf_conntrack_hash_rnd);
Patrick McHardy34498822007-12-17 22:45:52 -080085 return ((u64)hash * nf_ct_expect_hsize) >> 32;
Patrick McHardya71c0852007-07-07 22:33:47 -070086}
87
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010088struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010089__nf_ct_expect_find(struct net *net, u16 zone,
90 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010091{
92 struct nf_conntrack_expect *i;
Patrick McHardya71c0852007-07-07 22:33:47 -070093 struct hlist_node *n;
94 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010095
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020096 if (!net->ct.expect_count)
Patrick McHardya71c0852007-07-07 22:33:47 -070097 return NULL;
98
99 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200100 hlist_for_each_entry_rcu(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100101 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
102 nf_ct_zone(i->master) == zone)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100103 return i;
104 }
105 return NULL;
106}
Patrick McHardy68236452007-07-07 22:30:49 -0700107EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100108
109/* Just find a expectation corresponding to a tuple. */
110struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100111nf_ct_expect_find_get(struct net *net, u16 zone,
112 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100113{
114 struct nf_conntrack_expect *i;
115
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800116 rcu_read_lock();
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100117 i = __nf_ct_expect_find(net, zone, tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800118 if (i && !atomic_inc_not_zero(&i->use))
119 i = NULL;
120 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100121
122 return i;
123}
Patrick McHardy68236452007-07-07 22:30:49 -0700124EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100125
126/* If an expectation for this connection is found, it gets delete from
127 * global list then returned. */
128struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100129nf_ct_find_expectation(struct net *net, u16 zone,
130 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100131{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700132 struct nf_conntrack_expect *i, *exp = NULL;
133 struct hlist_node *n;
134 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);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200140 hlist_for_each_entry(i, n, &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
159 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
160 atomic_inc(&exp->use);
161 return exp;
162 } else if (del_timer(&exp->timeout)) {
163 nf_ct_unlink_expect(exp);
164 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100165 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800166
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100167 return NULL;
168}
169
170/* delete all expectations for this conntrack */
171void nf_ct_remove_expectations(struct nf_conn *ct)
172{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100173 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700174 struct nf_conntrack_expect *exp;
175 struct hlist_node *n, *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100176
177 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700178 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100179 return;
180
Patrick McHardyb5605802007-07-07 22:35:56 -0700181 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
182 if (del_timer(&exp->timeout)) {
183 nf_ct_unlink_expect(exp);
184 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800185 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100186 }
187}
Patrick McHardy13b18332006-12-02 22:11:25 -0800188EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100189
190/* Would two expected things clash? */
191static inline int expect_clash(const struct nf_conntrack_expect *a,
192 const struct nf_conntrack_expect *b)
193{
194 /* Part covered by intersection of masks must be unequal,
195 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700196 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100197 int count;
198
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100199 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100200
201 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
202 intersect_mask.src.u3.all[count] =
203 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
204 }
205
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100206 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
207}
208
209static inline int expect_matches(const struct nf_conntrack_expect *a,
210 const struct nf_conntrack_expect *b)
211{
Joe Perchesf64f9e72009-11-29 16:55:45 -0800212 return a->master == b->master && a->class == b->class &&
213 nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100214 nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
215 nf_ct_zone(a->master) == nf_ct_zone(b->master);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100216}
217
218/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700219void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100220{
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800221 spin_lock_bh(&nf_conntrack_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700222 if (del_timer(&exp->timeout)) {
223 nf_ct_unlink_expect(exp);
224 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100225 }
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800226 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100227}
Patrick McHardy68236452007-07-07 22:30:49 -0700228EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100229
230/* We don't increase the master conntrack refcount for non-fulfilled
231 * conntracks. During the conntrack destruction, the expectations are
232 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700233struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100234{
235 struct nf_conntrack_expect *new;
236
Patrick McHardy68236452007-07-07 22:30:49 -0700237 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100238 if (!new)
239 return NULL;
240
241 new->master = me;
242 atomic_set(&new->use, 1);
243 return new;
244}
Patrick McHardy68236452007-07-07 22:30:49 -0700245EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100246
Patrick McHardy6002f2662008-03-25 20:09:15 -0700247void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200248 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700249 const union nf_inet_addr *saddr,
250 const union nf_inet_addr *daddr,
251 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800252{
253 int len;
254
255 if (family == AF_INET)
256 len = 4;
257 else
258 len = 16;
259
260 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700261 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800262 exp->expectfn = NULL;
263 exp->helper = NULL;
264 exp->tuple.src.l3num = family;
265 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800266
267 if (saddr) {
268 memcpy(&exp->tuple.src.u3, saddr, len);
269 if (sizeof(exp->tuple.src.u3) > len)
270 /* address needs to be cleared for nf_ct_tuple_equal */
271 memset((void *)&exp->tuple.src.u3 + len, 0x00,
272 sizeof(exp->tuple.src.u3) - len);
273 memset(&exp->mask.src.u3, 0xFF, len);
274 if (sizeof(exp->mask.src.u3) > len)
275 memset((void *)&exp->mask.src.u3 + len, 0x00,
276 sizeof(exp->mask.src.u3) - len);
277 } else {
278 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
279 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
280 }
281
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800282 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100283 exp->tuple.src.u.all = *src;
284 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800285 } else {
286 exp->tuple.src.u.all = 0;
287 exp->mask.src.u.all = 0;
288 }
289
Patrick McHardyd4156e82007-07-07 22:31:32 -0700290 memcpy(&exp->tuple.dst.u3, daddr, len);
291 if (sizeof(exp->tuple.dst.u3) > len)
292 /* address needs to be cleared for nf_ct_tuple_equal */
293 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
294 sizeof(exp->tuple.dst.u3) - len);
295
Al Viroa34c4582007-07-26 17:33:19 +0100296 exp->tuple.dst.u.all = *dst;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800297}
Patrick McHardy68236452007-07-07 22:30:49 -0700298EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800299
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800300static void nf_ct_expect_free_rcu(struct rcu_head *head)
301{
302 struct nf_conntrack_expect *exp;
303
304 exp = container_of(head, struct nf_conntrack_expect, rcu);
305 kmem_cache_free(nf_ct_expect_cachep, exp);
306}
307
Patrick McHardy68236452007-07-07 22:30:49 -0700308void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100309{
310 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800311 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100312}
Patrick McHardy68236452007-07-07 22:30:49 -0700313EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100314
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100315static int nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100316{
317 struct nf_conn_help *master_help = nfct_help(exp->master);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100318 struct nf_conntrack_helper *helper;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200319 struct net *net = nf_ct_exp_net(exp);
Patrick McHardya71c0852007-07-07 22:33:47 -0700320 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100321
Eric Dumazet3bfd45f2010-11-16 10:19:18 +0100322 /* two references : one for hash insert, one for the timer */
323 atomic_add(2, &exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700324
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100325 hlist_add_head(&exp->lnode, &master_help->expectations);
326 master_help->expecting[exp->class]++;
Patrick McHardya71c0852007-07-07 22:33:47 -0700327
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200328 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
329 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100330
Patrick McHardy68236452007-07-07 22:30:49 -0700331 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
332 (unsigned long)exp);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100333 helper = rcu_dereference_protected(master_help->helper,
334 lockdep_is_held(&nf_conntrack_lock));
335 if (helper) {
336 exp->timeout.expires = jiffies +
337 helper->expect_policy[exp->class].timeout * HZ;
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200338 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100339 add_timer(&exp->timeout);
340
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200341 NF_CT_STAT_INC(net, expect_create);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100342 return 0;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100343}
344
345/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700346static void evict_oldest_expect(struct nf_conn *master,
347 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100348{
Patrick McHardyb5605802007-07-07 22:35:56 -0700349 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700350 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700351 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100352
Patrick McHardy6002f2662008-03-25 20:09:15 -0700353 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
354 if (exp->class == new->class)
355 last = exp;
356 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700357
Patrick McHardy6002f2662008-03-25 20:09:15 -0700358 if (last && del_timer(&last->timeout)) {
359 nf_ct_unlink_expect(last);
360 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100361 }
362}
363
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100364static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100365{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700366 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100367 struct nf_conntrack_expect *i;
368 struct nf_conn *master = expect->master;
369 struct nf_conn_help *master_help = nfct_help(master);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100370 struct nf_conntrack_helper *helper;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200371 struct net *net = nf_ct_exp_net(expect);
Pablo Neira Ayuso2614f862012-08-16 02:25:24 +0200372 struct hlist_node *n, *next;
Patrick McHardya71c0852007-07-07 22:33:47 -0700373 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200374 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100375
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100376 if (!master_help) {
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700377 ret = -ESHUTDOWN;
378 goto out;
379 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700380 h = nf_ct_expect_dst_hash(&expect->tuple);
Pablo Neira Ayuso2614f862012-08-16 02:25:24 +0200381 hlist_for_each_entry_safe(i, n, next, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100382 if (expect_matches(i, expect)) {
Pablo Neira Ayuso2614f862012-08-16 02:25:24 +0200383 if (del_timer(&i->timeout)) {
384 nf_ct_unlink_expect(i);
385 nf_ct_expect_put(i);
386 break;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100387 }
388 } else if (expect_clash(i, expect)) {
389 ret = -EBUSY;
390 goto out;
391 }
392 }
393 /* Will be over limit? */
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100394 helper = rcu_dereference_protected(master_help->helper,
395 lockdep_is_held(&nf_conntrack_lock));
396 if (helper) {
397 p = &helper->expect_policy[expect->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200398 if (p->max_expected &&
399 master_help->expecting[expect->class] >= p->max_expected) {
400 evict_oldest_expect(master, expect);
401 if (master_help->expecting[expect->class]
402 >= p->max_expected) {
403 ret = -EMFILE;
404 goto out;
405 }
Patrick McHardy6002f2662008-03-25 20:09:15 -0700406 }
407 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100408
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200409 if (net->ct.expect_count >= nf_ct_expect_max) {
Joe Perchese87cc472012-05-13 21:56:26 +0000410 net_warn_ratelimited("nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700411 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700412 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100413out:
414 return ret;
415}
416
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100417int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
418 u32 pid, int report)
419{
420 int ret;
421
422 spin_lock_bh(&nf_conntrack_lock);
423 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200424 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100425 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200426
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100427 ret = nf_ct_expect_insert(expect);
428 if (ret < 0)
429 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200430 spin_unlock_bh(&nf_conntrack_lock);
431 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
432 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100433out:
434 spin_unlock_bh(&nf_conntrack_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100435 return ret;
436}
437EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
438
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200439#ifdef CONFIG_NF_CONNTRACK_PROCFS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700440struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200441 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700442 unsigned int bucket;
443};
444
445static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100446{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200447 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700448 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800449 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100450
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700451 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100452 n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800453 if (n)
454 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100455 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700456 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100457}
458
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700459static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
460 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100461{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200462 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700463 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100464
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100465 head = rcu_dereference(hlist_next_rcu(head));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700466 while (head == NULL) {
467 if (++st->bucket >= nf_ct_expect_hsize)
468 return NULL;
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100469 head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700470 }
471 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100472}
473
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700474static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
475{
476 struct hlist_node *head = ct_expect_get_first(seq);
477
478 if (head)
479 while (pos && (head = ct_expect_get_next(seq, head)))
480 pos--;
481 return pos ? NULL : head;
482}
483
484static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800485 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700486{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800487 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700488 return ct_expect_get_idx(seq, *pos);
489}
490
491static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
492{
493 (*pos)++;
494 return ct_expect_get_next(seq, v);
495}
496
497static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800498 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100499{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800500 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100501}
502
503static int exp_seq_show(struct seq_file *s, void *v)
504{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700505 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100506 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700507 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700508 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700509
510 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100511
512 if (expect->timeout.function)
513 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
514 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
515 else
516 seq_printf(s, "- ");
517 seq_printf(s, "l3proto = %u proto=%u ",
518 expect->tuple.src.l3num,
519 expect->tuple.dst.protonum);
520 print_tuple(s, &expect->tuple,
521 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100522 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100523 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700524
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700525 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
526 seq_printf(s, "PERMANENT");
527 delim = ",";
528 }
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200529 if (expect->flags & NF_CT_EXPECT_INACTIVE) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700530 seq_printf(s, "%sINACTIVE", delim);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200531 delim = ",";
532 }
533 if (expect->flags & NF_CT_EXPECT_USERSPACE)
534 seq_printf(s, "%sUSERSPACE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700535
Patrick McHardyb87921b2010-02-11 12:22:48 +0100536 helper = rcu_dereference(nfct_help(expect->master)->helper);
537 if (helper) {
538 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
539 if (helper->expect_policy[expect->class].name)
540 seq_printf(s, "/%s",
541 helper->expect_policy[expect->class].name);
542 }
543
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100544 return seq_putc(s, '\n');
545}
546
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700547static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100548 .start = exp_seq_start,
549 .next = exp_seq_next,
550 .stop = exp_seq_stop,
551 .show = exp_seq_show
552};
553
554static int exp_open(struct inode *inode, struct file *file)
555{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200556 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700557 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100558}
559
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700560static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100561 .owner = THIS_MODULE,
562 .open = exp_open,
563 .read = seq_read,
564 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200565 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100566};
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200567#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700568
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200569static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700570{
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200571#ifdef CONFIG_NF_CONNTRACK_PROCFS
Patrick McHardye9c1b082007-07-07 22:32:53 -0700572 struct proc_dir_entry *proc;
573
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200574 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700575 if (!proc)
576 return -ENOMEM;
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200577#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700578 return 0;
579}
580
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200581static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700582{
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200583#ifdef CONFIG_NF_CONNTRACK_PROCFS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200584 proc_net_remove(net, "nf_conntrack_expect");
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200585#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700586}
587
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800588module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700589
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200590int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700591{
Patrick McHardya71c0852007-07-07 22:33:47 -0700592 int err = -ENOMEM;
593
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200594 if (net_eq(net, &init_net)) {
595 if (!nf_ct_expect_hsize) {
Patrick McHardyd696c7b2010-02-08 11:18:07 -0800596 nf_ct_expect_hsize = net->ct.htable_size / 256;
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200597 if (!nf_ct_expect_hsize)
598 nf_ct_expect_hsize = 1;
599 }
600 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700601 }
602
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200603 net->ct.expect_count = 0;
Patrick McHardyd862a662011-01-14 15:45:56 +0100604 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200605 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700606 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700607
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200608 if (net_eq(net, &init_net)) {
609 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
Patrick McHardye9c1b082007-07-07 22:32:53 -0700610 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900611 0, 0, NULL);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200612 if (!nf_ct_expect_cachep)
613 goto err2;
614 }
Patrick McHardye9c1b082007-07-07 22:32:53 -0700615
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200616 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700617 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700618 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700619
620 return 0;
621
Patrick McHardya71c0852007-07-07 22:33:47 -0700622err3:
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200623 if (net_eq(net, &init_net))
624 kmem_cache_destroy(nf_ct_expect_cachep);
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700625err2:
Patrick McHardyd862a662011-01-14 15:45:56 +0100626 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700627err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700628 return err;
629}
630
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200631void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700632{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200633 exp_proc_remove(net);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200634 if (net_eq(net, &init_net)) {
635 rcu_barrier(); /* Wait for call_rcu() before destroy */
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200636 kmem_cache_destroy(nf_ct_expect_cachep);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200637 }
Patrick McHardyd862a662011-01-14 15:45:56 +0100638 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700639}