blob: acb29ccaa41fd46624754adb794b5d9389a45c3c [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>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020023#include <net/net_namespace.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010024
25#include <net/netfilter/nf_conntrack.h>
26#include <net/netfilter/nf_conntrack_core.h>
27#include <net/netfilter/nf_conntrack_expect.h>
28#include <net/netfilter/nf_conntrack_helper.h>
29#include <net/netfilter/nf_conntrack_tuple.h>
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010030#include <net/netfilter/nf_conntrack_zones.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010031
Patrick McHardya71c0852007-07-07 22:33:47 -070032unsigned int nf_ct_expect_hsize __read_mostly;
33EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
34
35static unsigned int nf_ct_expect_hash_rnd __read_mostly;
Patrick McHardyf264a7d2007-07-07 22:36:24 -070036unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070037static int nf_ct_expect_hash_rnd_initted __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 */
42void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
43{
44 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020045 struct net *net = nf_ct_exp_net(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010046
47 NF_CT_ASSERT(master_help);
48 NF_CT_ASSERT(!timer_pending(&exp->timeout));
49
Patrick McHardy7d0742d2008-01-31 04:38:19 -080050 hlist_del_rcu(&exp->hnode);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020051 net->ct.expect_count--;
Patrick McHardya71c0852007-07-07 22:33:47 -070052
Patrick McHardyb5605802007-07-07 22:35:56 -070053 hlist_del(&exp->lnode);
Patrick McHardy6002f2662008-03-25 20:09:15 -070054 master_help->expecting[exp->class]--;
Patrick McHardy68236452007-07-07 22:30:49 -070055 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070056
Alexey Dobriyan0d55af82008-10-08 11:35:07 +020057 NF_CT_STAT_INC(net, expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010058}
Patrick McHardy13b18332006-12-02 22:11:25 -080059EXPORT_SYMBOL_GPL(nf_ct_unlink_expect);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010060
Patrick McHardy68236452007-07-07 22:30:49 -070061static void nf_ct_expectation_timed_out(unsigned long ul_expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010062{
63 struct nf_conntrack_expect *exp = (void *)ul_expect;
64
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080065 spin_lock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010066 nf_ct_unlink_expect(exp);
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080067 spin_unlock_bh(&nf_conntrack_lock);
Patrick McHardy68236452007-07-07 22:30:49 -070068 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010069}
70
Patrick McHardya71c0852007-07-07 22:33:47 -070071static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
72{
Patrick McHardy34498822007-12-17 22:45:52 -080073 unsigned int hash;
74
Patrick McHardya71c0852007-07-07 22:33:47 -070075 if (unlikely(!nf_ct_expect_hash_rnd_initted)) {
Hagen Paul Pfeiferaf07d242009-02-20 10:48:06 +010076 get_random_bytes(&nf_ct_expect_hash_rnd,
77 sizeof(nf_ct_expect_hash_rnd));
Patrick McHardya71c0852007-07-07 22:33:47 -070078 nf_ct_expect_hash_rnd_initted = 1;
79 }
80
Patrick McHardy34498822007-12-17 22:45:52 -080081 hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
Patrick McHardya71c0852007-07-07 22:33:47 -070082 (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
Patrick McHardy34498822007-12-17 22:45:52 -080083 (__force __u16)tuple->dst.u.all) ^ nf_ct_expect_hash_rnd);
84 return ((u64)hash * nf_ct_expect_hsize) >> 32;
Patrick McHardya71c0852007-07-07 22:33:47 -070085}
86
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010087struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010088__nf_ct_expect_find(struct net *net, u16 zone,
89 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010090{
91 struct nf_conntrack_expect *i;
Patrick McHardya71c0852007-07-07 22:33:47 -070092 struct hlist_node *n;
93 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010094
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020095 if (!net->ct.expect_count)
Patrick McHardya71c0852007-07-07 22:33:47 -070096 return NULL;
97
98 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020099 hlist_for_each_entry_rcu(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100100 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
101 nf_ct_zone(i->master) == zone)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100102 return i;
103 }
104 return NULL;
105}
Patrick McHardy68236452007-07-07 22:30:49 -0700106EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100107
108/* Just find a expectation corresponding to a tuple. */
109struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100110nf_ct_expect_find_get(struct net *net, u16 zone,
111 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100112{
113 struct nf_conntrack_expect *i;
114
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800115 rcu_read_lock();
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100116 i = __nf_ct_expect_find(net, zone, tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800117 if (i && !atomic_inc_not_zero(&i->use))
118 i = NULL;
119 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100120
121 return i;
122}
Patrick McHardy68236452007-07-07 22:30:49 -0700123EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100124
125/* If an expectation for this connection is found, it gets delete from
126 * global list then returned. */
127struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100128nf_ct_find_expectation(struct net *net, u16 zone,
129 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100130{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700131 struct nf_conntrack_expect *i, *exp = NULL;
132 struct hlist_node *n;
133 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100134
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200135 if (!net->ct.expect_count)
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700136 return NULL;
137
138 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200139 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700140 if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100141 nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
142 nf_ct_zone(i->master) == zone) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700143 exp = i;
144 break;
145 }
146 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800147 if (!exp)
148 return NULL;
149
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100150 /* If master is not in hash table yet (ie. packet hasn't left
151 this machine yet), how can other end know about expected?
152 Hence these are not the droids you are looking for (if
153 master ct never got confirmed, we'd hold a reference to it
154 and weird things would happen to future packets). */
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800155 if (!nf_ct_is_confirmed(exp->master))
156 return NULL;
157
158 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
159 atomic_inc(&exp->use);
160 return exp;
161 } else if (del_timer(&exp->timeout)) {
162 nf_ct_unlink_expect(exp);
163 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100164 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800165
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100166 return NULL;
167}
168
169/* delete all expectations for this conntrack */
170void nf_ct_remove_expectations(struct nf_conn *ct)
171{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100172 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700173 struct nf_conntrack_expect *exp;
174 struct hlist_node *n, *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100175
176 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700177 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100178 return;
179
Patrick McHardyb5605802007-07-07 22:35:56 -0700180 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
181 if (del_timer(&exp->timeout)) {
182 nf_ct_unlink_expect(exp);
183 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800184 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100185 }
186}
Patrick McHardy13b18332006-12-02 22:11:25 -0800187EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100188
189/* Would two expected things clash? */
190static inline int expect_clash(const struct nf_conntrack_expect *a,
191 const struct nf_conntrack_expect *b)
192{
193 /* Part covered by intersection of masks must be unequal,
194 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700195 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100196 int count;
197
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100198 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100199
200 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
201 intersect_mask.src.u3.all[count] =
202 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
203 }
204
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100205 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
206}
207
208static inline int expect_matches(const struct nf_conntrack_expect *a,
209 const struct nf_conntrack_expect *b)
210{
Joe Perchesf64f9e72009-11-29 16:55:45 -0800211 return a->master == b->master && a->class == b->class &&
212 nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100213 nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
214 nf_ct_zone(a->master) == nf_ct_zone(b->master);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100215}
216
217/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700218void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100219{
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800220 spin_lock_bh(&nf_conntrack_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700221 if (del_timer(&exp->timeout)) {
222 nf_ct_unlink_expect(exp);
223 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100224 }
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800225 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100226}
Patrick McHardy68236452007-07-07 22:30:49 -0700227EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100228
229/* We don't increase the master conntrack refcount for non-fulfilled
230 * conntracks. During the conntrack destruction, the expectations are
231 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700232struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100233{
234 struct nf_conntrack_expect *new;
235
Patrick McHardy68236452007-07-07 22:30:49 -0700236 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100237 if (!new)
238 return NULL;
239
240 new->master = me;
241 atomic_set(&new->use, 1);
242 return new;
243}
Patrick McHardy68236452007-07-07 22:30:49 -0700244EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100245
Patrick McHardy6002f2662008-03-25 20:09:15 -0700246void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200247 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700248 const union nf_inet_addr *saddr,
249 const union nf_inet_addr *daddr,
250 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800251{
252 int len;
253
254 if (family == AF_INET)
255 len = 4;
256 else
257 len = 16;
258
259 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700260 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800261 exp->expectfn = NULL;
262 exp->helper = NULL;
263 exp->tuple.src.l3num = family;
264 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800265
266 if (saddr) {
267 memcpy(&exp->tuple.src.u3, saddr, len);
268 if (sizeof(exp->tuple.src.u3) > len)
269 /* address needs to be cleared for nf_ct_tuple_equal */
270 memset((void *)&exp->tuple.src.u3 + len, 0x00,
271 sizeof(exp->tuple.src.u3) - len);
272 memset(&exp->mask.src.u3, 0xFF, len);
273 if (sizeof(exp->mask.src.u3) > len)
274 memset((void *)&exp->mask.src.u3 + len, 0x00,
275 sizeof(exp->mask.src.u3) - len);
276 } else {
277 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
278 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
279 }
280
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800281 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100282 exp->tuple.src.u.all = *src;
283 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800284 } else {
285 exp->tuple.src.u.all = 0;
286 exp->mask.src.u.all = 0;
287 }
288
Patrick McHardyd4156e82007-07-07 22:31:32 -0700289 memcpy(&exp->tuple.dst.u3, daddr, len);
290 if (sizeof(exp->tuple.dst.u3) > len)
291 /* address needs to be cleared for nf_ct_tuple_equal */
292 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
293 sizeof(exp->tuple.dst.u3) - len);
294
Al Viroa34c4582007-07-26 17:33:19 +0100295 exp->tuple.dst.u.all = *dst;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800296}
Patrick McHardy68236452007-07-07 22:30:49 -0700297EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800298
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800299static void nf_ct_expect_free_rcu(struct rcu_head *head)
300{
301 struct nf_conntrack_expect *exp;
302
303 exp = container_of(head, struct nf_conntrack_expect, rcu);
304 kmem_cache_free(nf_ct_expect_cachep, exp);
305}
306
Patrick McHardy68236452007-07-07 22:30:49 -0700307void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100308{
309 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800310 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100311}
Patrick McHardy68236452007-07-07 22:30:49 -0700312EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100313
Patrick McHardy68236452007-07-07 22:30:49 -0700314static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100315{
316 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200317 struct net *net = nf_ct_exp_net(exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700318 const struct nf_conntrack_expect_policy *p;
Patrick McHardya71c0852007-07-07 22:33:47 -0700319 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100320
321 atomic_inc(&exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700322
323 hlist_add_head(&exp->lnode, &master_help->expectations);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700324 master_help->expecting[exp->class]++;
Patrick McHardya71c0852007-07-07 22:33:47 -0700325
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200326 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
327 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100328
Patrick McHardy68236452007-07-07 22:30:49 -0700329 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
330 (unsigned long)exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700331 p = &master_help->helper->expect_policy[exp->class];
332 exp->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100333 add_timer(&exp->timeout);
334
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100335 atomic_inc(&exp->use);
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200336 NF_CT_STAT_INC(net, expect_create);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100337}
338
339/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700340static void evict_oldest_expect(struct nf_conn *master,
341 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100342{
Patrick McHardyb5605802007-07-07 22:35:56 -0700343 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700344 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700345 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100346
Patrick McHardy6002f2662008-03-25 20:09:15 -0700347 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
348 if (exp->class == new->class)
349 last = exp;
350 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700351
Patrick McHardy6002f2662008-03-25 20:09:15 -0700352 if (last && del_timer(&last->timeout)) {
353 nf_ct_unlink_expect(last);
354 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100355 }
356}
357
358static inline int refresh_timer(struct nf_conntrack_expect *i)
359{
360 struct nf_conn_help *master_help = nfct_help(i->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700361 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100362
363 if (!del_timer(&i->timeout))
364 return 0;
365
Patrick McHardy6002f2662008-03-25 20:09:15 -0700366 p = &master_help->helper->expect_policy[i->class];
367 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100368 add_timer(&i->timeout);
369 return 1;
370}
371
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100372static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100373{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700374 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100375 struct nf_conntrack_expect *i;
376 struct nf_conn *master = expect->master;
377 struct nf_conn_help *master_help = nfct_help(master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200378 struct net *net = nf_ct_exp_net(expect);
Patrick McHardya71c0852007-07-07 22:33:47 -0700379 struct hlist_node *n;
380 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200381 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100382
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700383 if (!master_help->helper) {
384 ret = -ESHUTDOWN;
385 goto out;
386 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700387 h = nf_ct_expect_dst_hash(&expect->tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200388 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100389 if (expect_matches(i, expect)) {
390 /* Refresh timer: if it's dying, ignore.. */
391 if (refresh_timer(i)) {
392 ret = 0;
393 goto out;
394 }
395 } else if (expect_clash(i, expect)) {
396 ret = -EBUSY;
397 goto out;
398 }
399 }
400 /* Will be over limit? */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700401 p = &master_help->helper->expect_policy[expect->class];
402 if (p->max_expected &&
403 master_help->expecting[expect->class] >= p->max_expected) {
404 evict_oldest_expect(master, expect);
405 if (master_help->expecting[expect->class] >= p->max_expected) {
406 ret = -EMFILE;
407 goto out;
408 }
409 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100410
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200411 if (net->ct.expect_count >= nf_ct_expect_max) {
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700412 if (net_ratelimit())
413 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700414 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700415 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700416 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100417out:
418 return ret;
419}
420
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100421int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
422 u32 pid, int report)
423{
424 int ret;
425
426 spin_lock_bh(&nf_conntrack_lock);
427 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200428 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100429 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200430
431 ret = 0;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100432 nf_ct_expect_insert(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200433 spin_unlock_bh(&nf_conntrack_lock);
434 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
435 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100436out:
437 spin_unlock_bh(&nf_conntrack_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100438 return ret;
439}
440EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
441
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100442#ifdef CONFIG_PROC_FS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700443struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200444 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700445 unsigned int bucket;
446};
447
448static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100449{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200450 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700451 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800452 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100453
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700454 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200455 n = rcu_dereference(net->ct.expect_hash[st->bucket].first);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800456 if (n)
457 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100458 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700459 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100460}
461
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700462static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
463 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100464{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200465 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700466 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100467
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800468 head = rcu_dereference(head->next);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700469 while (head == NULL) {
470 if (++st->bucket >= nf_ct_expect_hsize)
471 return NULL;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200472 head = rcu_dereference(net->ct.expect_hash[st->bucket].first);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700473 }
474 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100475}
476
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700477static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
478{
479 struct hlist_node *head = ct_expect_get_first(seq);
480
481 if (head)
482 while (pos && (head = ct_expect_get_next(seq, head)))
483 pos--;
484 return pos ? NULL : head;
485}
486
487static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800488 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700489{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800490 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700491 return ct_expect_get_idx(seq, *pos);
492}
493
494static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
495{
496 (*pos)++;
497 return ct_expect_get_next(seq, v);
498}
499
500static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800501 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100502{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800503 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100504}
505
506static int exp_seq_show(struct seq_file *s, void *v)
507{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700508 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100509 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700510 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700511 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700512
513 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100514
515 if (expect->timeout.function)
516 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
517 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
518 else
519 seq_printf(s, "- ");
520 seq_printf(s, "l3proto = %u proto=%u ",
521 expect->tuple.src.l3num,
522 expect->tuple.dst.protonum);
523 print_tuple(s, &expect->tuple,
524 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100525 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100526 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700527
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700528 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
529 seq_printf(s, "PERMANENT");
530 delim = ",";
531 }
532 if (expect->flags & NF_CT_EXPECT_INACTIVE)
533 seq_printf(s, "%sINACTIVE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700534
Patrick McHardyb87921b2010-02-11 12:22:48 +0100535 helper = rcu_dereference(nfct_help(expect->master)->helper);
536 if (helper) {
537 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
538 if (helper->expect_policy[expect->class].name)
539 seq_printf(s, "/%s",
540 helper->expect_policy[expect->class].name);
541 }
542
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100543 return seq_putc(s, '\n');
544}
545
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700546static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100547 .start = exp_seq_start,
548 .next = exp_seq_next,
549 .stop = exp_seq_stop,
550 .show = exp_seq_show
551};
552
553static int exp_open(struct inode *inode, struct file *file)
554{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200555 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700556 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100557}
558
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700559static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100560 .owner = THIS_MODULE,
561 .open = exp_open,
562 .read = seq_read,
563 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200564 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100565};
566#endif /* CONFIG_PROC_FS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700567
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200568static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700569{
570#ifdef CONFIG_PROC_FS
571 struct proc_dir_entry *proc;
572
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200573 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700574 if (!proc)
575 return -ENOMEM;
576#endif /* CONFIG_PROC_FS */
577 return 0;
578}
579
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200580static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700581{
582#ifdef CONFIG_PROC_FS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200583 proc_net_remove(net, "nf_conntrack_expect");
Patrick McHardye9c1b082007-07-07 22:32:53 -0700584#endif /* CONFIG_PROC_FS */
585}
586
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800587module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700588
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200589int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700590{
Patrick McHardya71c0852007-07-07 22:33:47 -0700591 int err = -ENOMEM;
592
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200593 if (net_eq(net, &init_net)) {
594 if (!nf_ct_expect_hsize) {
Patrick McHardyd696c7b2010-02-08 11:18:07 -0800595 nf_ct_expect_hsize = net->ct.htable_size / 256;
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200596 if (!nf_ct_expect_hsize)
597 nf_ct_expect_hsize = 1;
598 }
599 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700600 }
601
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200602 net->ct.expect_count = 0;
603 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
Eric Dumazetea781f12009-03-25 21:05:46 +0100604 &net->ct.expect_vmalloc, 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:
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200626 nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
Patrick McHardya71c0852007-07-07 22:33:47 -0700627 nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700628err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700629 return err;
630}
631
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200632void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700633{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200634 exp_proc_remove(net);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200635 if (net_eq(net, &init_net)) {
636 rcu_barrier(); /* Wait for call_rcu() before destroy */
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200637 kmem_cache_destroy(nf_ct_expect_cachep);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200638 }
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200639 nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
Patrick McHardya71c0852007-07-07 22:33:47 -0700640 nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700641}