blob: 990fa12f2ee503eab3524a2550dff6372e3a02c1 [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>
30
Patrick McHardya71c0852007-07-07 22:33:47 -070031struct hlist_head *nf_ct_expect_hash __read_mostly;
32EXPORT_SYMBOL_GPL(nf_ct_expect_hash);
33
34unsigned int nf_ct_expect_hsize __read_mostly;
35EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
36
37static unsigned int nf_ct_expect_hash_rnd __read_mostly;
38static unsigned int nf_ct_expect_count;
Patrick McHardyf264a7d2007-07-07 22:36:24 -070039unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070040static int nf_ct_expect_hash_rnd_initted __read_mostly;
41static int nf_ct_expect_vmalloc;
42
Patrick McHardye9c1b082007-07-07 22:32:53 -070043static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010044
45/* nf_conntrack_expect helper functions */
46void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
47{
48 struct nf_conn_help *master_help = nfct_help(exp->master);
49
50 NF_CT_ASSERT(master_help);
51 NF_CT_ASSERT(!timer_pending(&exp->timeout));
52
Patrick McHardy7d0742d2008-01-31 04:38:19 -080053 hlist_del_rcu(&exp->hnode);
Patrick McHardya71c0852007-07-07 22:33:47 -070054 nf_ct_expect_count--;
55
Patrick McHardyb5605802007-07-07 22:35:56 -070056 hlist_del(&exp->lnode);
Patrick McHardy6002f2662008-03-25 20:09:15 -070057 master_help->expecting[exp->class]--;
Patrick McHardy68236452007-07-07 22:30:49 -070058 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070059
60 NF_CT_STAT_INC(expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010061}
Patrick McHardy13b18332006-12-02 22:11:25 -080062EXPORT_SYMBOL_GPL(nf_ct_unlink_expect);
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
Patrick McHardya71c0852007-07-07 22:33:47 -070078 if (unlikely(!nf_ct_expect_hash_rnd_initted)) {
79 get_random_bytes(&nf_ct_expect_hash_rnd, 4);
80 nf_ct_expect_hash_rnd_initted = 1;
81 }
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) |
Patrick McHardy34498822007-12-17 22:45:52 -080085 (__force __u16)tuple->dst.u.all) ^ nf_ct_expect_hash_rnd);
86 return ((u64)hash * nf_ct_expect_hsize) >> 32;
Patrick McHardya71c0852007-07-07 22:33:47 -070087}
88
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010089struct nf_conntrack_expect *
Patrick McHardy68236452007-07-07 22:30:49 -070090__nf_ct_expect_find(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
Patrick McHardya71c0852007-07-07 22:33:47 -070096 if (!nf_ct_expect_count)
97 return NULL;
98
99 h = nf_ct_expect_dst_hash(tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800100 hlist_for_each_entry_rcu(i, n, &nf_ct_expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100101 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask))
102 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 McHardy68236452007-07-07 22:30:49 -0700110nf_ct_expect_find_get(const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100111{
112 struct nf_conntrack_expect *i;
113
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800114 rcu_read_lock();
Patrick McHardy68236452007-07-07 22:30:49 -0700115 i = __nf_ct_expect_find(tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800116 if (i && !atomic_inc_not_zero(&i->use))
117 i = NULL;
118 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100119
120 return i;
121}
Patrick McHardy68236452007-07-07 22:30:49 -0700122EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100123
124/* If an expectation for this connection is found, it gets delete from
125 * global list then returned. */
126struct nf_conntrack_expect *
Patrick McHardy68236452007-07-07 22:30:49 -0700127nf_ct_find_expectation(const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100128{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700129 struct nf_conntrack_expect *i, *exp = NULL;
130 struct hlist_node *n;
131 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100132
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700133 if (!nf_ct_expect_count)
134 return NULL;
135
136 h = nf_ct_expect_dst_hash(tuple);
137 hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) {
138 if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
139 nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) {
140 exp = i;
141 break;
142 }
143 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800144 if (!exp)
145 return NULL;
146
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100147 /* If master is not in hash table yet (ie. packet hasn't left
148 this machine yet), how can other end know about expected?
149 Hence these are not the droids you are looking for (if
150 master ct never got confirmed, we'd hold a reference to it
151 and weird things would happen to future packets). */
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800152 if (!nf_ct_is_confirmed(exp->master))
153 return NULL;
154
155 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
156 atomic_inc(&exp->use);
157 return exp;
158 } else if (del_timer(&exp->timeout)) {
159 nf_ct_unlink_expect(exp);
160 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100161 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800162
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100163 return NULL;
164}
165
166/* delete all expectations for this conntrack */
167void nf_ct_remove_expectations(struct nf_conn *ct)
168{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100169 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700170 struct nf_conntrack_expect *exp;
171 struct hlist_node *n, *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100172
173 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700174 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100175 return;
176
Patrick McHardyb5605802007-07-07 22:35:56 -0700177 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
178 if (del_timer(&exp->timeout)) {
179 nf_ct_unlink_expect(exp);
180 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800181 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100182 }
183}
Patrick McHardy13b18332006-12-02 22:11:25 -0800184EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100185
186/* Would two expected things clash? */
187static inline int expect_clash(const struct nf_conntrack_expect *a,
188 const struct nf_conntrack_expect *b)
189{
190 /* Part covered by intersection of masks must be unequal,
191 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700192 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100193 int count;
194
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100195 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100196
197 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
198 intersect_mask.src.u3.all[count] =
199 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
200 }
201
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100202 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
203}
204
205static inline int expect_matches(const struct nf_conntrack_expect *a,
206 const struct nf_conntrack_expect *b)
207{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700208 return a->master == b->master && a->class == b->class
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100209 && nf_ct_tuple_equal(&a->tuple, &b->tuple)
Patrick McHardyd4156e82007-07-07 22:31:32 -0700210 && nf_ct_tuple_mask_equal(&a->mask, &b->mask);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100211}
212
213/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700214void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100215{
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800216 spin_lock_bh(&nf_conntrack_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700217 if (del_timer(&exp->timeout)) {
218 nf_ct_unlink_expect(exp);
219 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100220 }
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800221 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100222}
Patrick McHardy68236452007-07-07 22:30:49 -0700223EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100224
225/* We don't increase the master conntrack refcount for non-fulfilled
226 * conntracks. During the conntrack destruction, the expectations are
227 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700228struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100229{
230 struct nf_conntrack_expect *new;
231
Patrick McHardy68236452007-07-07 22:30:49 -0700232 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100233 if (!new)
234 return NULL;
235
236 new->master = me;
237 atomic_set(&new->use, 1);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800238 INIT_RCU_HEAD(&new->rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100239 return new;
240}
Patrick McHardy68236452007-07-07 22:30:49 -0700241EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100242
Patrick McHardy6002f2662008-03-25 20:09:15 -0700243void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200244 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700245 const union nf_inet_addr *saddr,
246 const union nf_inet_addr *daddr,
247 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800248{
249 int len;
250
251 if (family == AF_INET)
252 len = 4;
253 else
254 len = 16;
255
256 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700257 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800258 exp->expectfn = NULL;
259 exp->helper = NULL;
260 exp->tuple.src.l3num = family;
261 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800262
263 if (saddr) {
264 memcpy(&exp->tuple.src.u3, saddr, len);
265 if (sizeof(exp->tuple.src.u3) > len)
266 /* address needs to be cleared for nf_ct_tuple_equal */
267 memset((void *)&exp->tuple.src.u3 + len, 0x00,
268 sizeof(exp->tuple.src.u3) - len);
269 memset(&exp->mask.src.u3, 0xFF, len);
270 if (sizeof(exp->mask.src.u3) > len)
271 memset((void *)&exp->mask.src.u3 + len, 0x00,
272 sizeof(exp->mask.src.u3) - len);
273 } else {
274 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
275 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
276 }
277
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800278 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100279 exp->tuple.src.u.all = *src;
280 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800281 } else {
282 exp->tuple.src.u.all = 0;
283 exp->mask.src.u.all = 0;
284 }
285
Patrick McHardyd4156e82007-07-07 22:31:32 -0700286 memcpy(&exp->tuple.dst.u3, daddr, len);
287 if (sizeof(exp->tuple.dst.u3) > len)
288 /* address needs to be cleared for nf_ct_tuple_equal */
289 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
290 sizeof(exp->tuple.dst.u3) - len);
291
Al Viroa34c4582007-07-26 17:33:19 +0100292 exp->tuple.dst.u.all = *dst;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800293}
Patrick McHardy68236452007-07-07 22:30:49 -0700294EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800295
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800296static void nf_ct_expect_free_rcu(struct rcu_head *head)
297{
298 struct nf_conntrack_expect *exp;
299
300 exp = container_of(head, struct nf_conntrack_expect, rcu);
301 kmem_cache_free(nf_ct_expect_cachep, exp);
302}
303
Patrick McHardy68236452007-07-07 22:30:49 -0700304void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100305{
306 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800307 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100308}
Patrick McHardy68236452007-07-07 22:30:49 -0700309EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100310
Patrick McHardy68236452007-07-07 22:30:49 -0700311static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100312{
313 struct nf_conn_help *master_help = nfct_help(exp->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700314 const struct nf_conntrack_expect_policy *p;
Patrick McHardya71c0852007-07-07 22:33:47 -0700315 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100316
317 atomic_inc(&exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700318
319 hlist_add_head(&exp->lnode, &master_help->expectations);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700320 master_help->expecting[exp->class]++;
Patrick McHardya71c0852007-07-07 22:33:47 -0700321
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800322 hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
Patrick McHardya71c0852007-07-07 22:33:47 -0700323 nf_ct_expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100324
Patrick McHardy68236452007-07-07 22:30:49 -0700325 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
326 (unsigned long)exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700327 p = &master_help->helper->expect_policy[exp->class];
328 exp->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100329 add_timer(&exp->timeout);
330
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100331 atomic_inc(&exp->use);
332 NF_CT_STAT_INC(expect_create);
333}
334
335/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700336static void evict_oldest_expect(struct nf_conn *master,
337 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100338{
Patrick McHardyb5605802007-07-07 22:35:56 -0700339 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700340 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700341 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100342
Patrick McHardy6002f2662008-03-25 20:09:15 -0700343 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
344 if (exp->class == new->class)
345 last = exp;
346 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700347
Patrick McHardy6002f2662008-03-25 20:09:15 -0700348 if (last && del_timer(&last->timeout)) {
349 nf_ct_unlink_expect(last);
350 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100351 }
352}
353
354static inline int refresh_timer(struct nf_conntrack_expect *i)
355{
356 struct nf_conn_help *master_help = nfct_help(i->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700357 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100358
359 if (!del_timer(&i->timeout))
360 return 0;
361
Patrick McHardy6002f2662008-03-25 20:09:15 -0700362 p = &master_help->helper->expect_policy[i->class];
363 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100364 add_timer(&i->timeout);
365 return 1;
366}
367
Patrick McHardy68236452007-07-07 22:30:49 -0700368int nf_ct_expect_related(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100369{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700370 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100371 struct nf_conntrack_expect *i;
372 struct nf_conn *master = expect->master;
373 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardya71c0852007-07-07 22:33:47 -0700374 struct hlist_node *n;
375 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100376 int ret;
377
378 NF_CT_ASSERT(master_help);
379
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800380 spin_lock_bh(&nf_conntrack_lock);
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700381 if (!master_help->helper) {
382 ret = -ESHUTDOWN;
383 goto out;
384 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700385 h = nf_ct_expect_dst_hash(&expect->tuple);
386 hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100387 if (expect_matches(i, expect)) {
388 /* Refresh timer: if it's dying, ignore.. */
389 if (refresh_timer(i)) {
390 ret = 0;
391 goto out;
392 }
393 } else if (expect_clash(i, expect)) {
394 ret = -EBUSY;
395 goto out;
396 }
397 }
398 /* Will be over limit? */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700399 p = &master_help->helper->expect_policy[expect->class];
400 if (p->max_expected &&
401 master_help->expecting[expect->class] >= p->max_expected) {
402 evict_oldest_expect(master, expect);
403 if (master_help->expecting[expect->class] >= p->max_expected) {
404 ret = -EMFILE;
405 goto out;
406 }
407 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100408
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700409 if (nf_ct_expect_count >= nf_ct_expect_max) {
410 if (net_ratelimit())
411 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700412 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700413 ret = -EMFILE;
414 goto out;
415 }
416
Patrick McHardy68236452007-07-07 22:30:49 -0700417 nf_ct_expect_insert(expect);
418 nf_ct_expect_event(IPEXP_NEW, expect);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100419 ret = 0;
420out:
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800421 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100422 return ret;
423}
Patrick McHardy68236452007-07-07 22:30:49 -0700424EXPORT_SYMBOL_GPL(nf_ct_expect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100425
426#ifdef CONFIG_PROC_FS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700427struct ct_expect_iter_state {
428 unsigned int bucket;
429};
430
431static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100432{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700433 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800434 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100435
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700436 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800437 n = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
438 if (n)
439 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100440 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700441 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100442}
443
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700444static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
445 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100446{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700447 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100448
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800449 head = rcu_dereference(head->next);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700450 while (head == NULL) {
451 if (++st->bucket >= nf_ct_expect_hsize)
452 return NULL;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800453 head = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700454 }
455 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100456}
457
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700458static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
459{
460 struct hlist_node *head = ct_expect_get_first(seq);
461
462 if (head)
463 while (pos && (head = ct_expect_get_next(seq, head)))
464 pos--;
465 return pos ? NULL : head;
466}
467
468static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800469 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700470{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800471 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700472 return ct_expect_get_idx(seq, *pos);
473}
474
475static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
476{
477 (*pos)++;
478 return ct_expect_get_next(seq, v);
479}
480
481static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800482 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100483{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800484 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100485}
486
487static int exp_seq_show(struct seq_file *s, void *v)
488{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700489 struct nf_conntrack_expect *expect;
490 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700491 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700492
493 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100494
495 if (expect->timeout.function)
496 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
497 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
498 else
499 seq_printf(s, "- ");
500 seq_printf(s, "l3proto = %u proto=%u ",
501 expect->tuple.src.l3num,
502 expect->tuple.dst.protonum);
503 print_tuple(s, &expect->tuple,
504 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100505 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100506 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700507
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700508 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
509 seq_printf(s, "PERMANENT");
510 delim = ",";
511 }
512 if (expect->flags & NF_CT_EXPECT_INACTIVE)
513 seq_printf(s, "%sINACTIVE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700514
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100515 return seq_putc(s, '\n');
516}
517
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700518static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100519 .start = exp_seq_start,
520 .next = exp_seq_next,
521 .stop = exp_seq_stop,
522 .show = exp_seq_show
523};
524
525static int exp_open(struct inode *inode, struct file *file)
526{
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700527 return seq_open_private(file, &exp_seq_ops,
528 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100529}
530
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700531static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100532 .owner = THIS_MODULE,
533 .open = exp_open,
534 .read = seq_read,
535 .llseek = seq_lseek,
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700536 .release = seq_release_private,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100537};
538#endif /* CONFIG_PROC_FS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700539
540static int __init exp_proc_init(void)
541{
542#ifdef CONFIG_PROC_FS
543 struct proc_dir_entry *proc;
544
Eric W. Biederman457c4cb2007-09-12 12:01:34 +0200545 proc = proc_net_fops_create(&init_net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700546 if (!proc)
547 return -ENOMEM;
548#endif /* CONFIG_PROC_FS */
549 return 0;
550}
551
552static void exp_proc_remove(void)
553{
554#ifdef CONFIG_PROC_FS
Eric W. Biederman457c4cb2007-09-12 12:01:34 +0200555 proc_net_remove(&init_net, "nf_conntrack_expect");
Patrick McHardye9c1b082007-07-07 22:32:53 -0700556#endif /* CONFIG_PROC_FS */
557}
558
Patrick McHardya71c0852007-07-07 22:33:47 -0700559module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0600);
560
Patrick McHardye9c1b082007-07-07 22:32:53 -0700561int __init nf_conntrack_expect_init(void)
562{
Patrick McHardya71c0852007-07-07 22:33:47 -0700563 int err = -ENOMEM;
564
565 if (!nf_ct_expect_hsize) {
566 nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
567 if (!nf_ct_expect_hsize)
568 nf_ct_expect_hsize = 1;
569 }
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700570 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700571
572 nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
573 &nf_ct_expect_vmalloc);
574 if (nf_ct_expect_hash == NULL)
575 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700576
577 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
578 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900579 0, 0, NULL);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700580 if (!nf_ct_expect_cachep)
Patrick McHardya71c0852007-07-07 22:33:47 -0700581 goto err2;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700582
583 err = exp_proc_init();
584 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700585 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700586
587 return 0;
588
Patrick McHardya71c0852007-07-07 22:33:47 -0700589err3:
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700590 kmem_cache_destroy(nf_ct_expect_cachep);
591err2:
Patrick McHardya71c0852007-07-07 22:33:47 -0700592 nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_vmalloc,
593 nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700594err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700595 return err;
596}
597
598void nf_conntrack_expect_fini(void)
599{
600 exp_proc_remove();
601 kmem_cache_destroy(nf_ct_expect_cachep);
Patrick McHardya71c0852007-07-07 22:33:47 -0700602 nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_vmalloc,
603 nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700604}