blob: 3a8a34a6d37c807bd995eb0a43f3498ddf567662 [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 -070031unsigned int nf_ct_expect_hsize __read_mostly;
32EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
33
34static unsigned int nf_ct_expect_hash_rnd __read_mostly;
Patrick McHardyf264a7d2007-07-07 22:36:24 -070035unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070036static int nf_ct_expect_hash_rnd_initted __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070037
Patrick McHardye9c1b082007-07-07 22:32:53 -070038static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010039
40/* nf_conntrack_expect helper functions */
41void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
42{
43 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020044 struct net *net = nf_ct_exp_net(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010045
46 NF_CT_ASSERT(master_help);
47 NF_CT_ASSERT(!timer_pending(&exp->timeout));
48
Patrick McHardy7d0742d2008-01-31 04:38:19 -080049 hlist_del_rcu(&exp->hnode);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020050 net->ct.expect_count--;
Patrick McHardya71c0852007-07-07 22:33:47 -070051
Patrick McHardyb5605802007-07-07 22:35:56 -070052 hlist_del(&exp->lnode);
Patrick McHardy6002f2662008-03-25 20:09:15 -070053 master_help->expecting[exp->class]--;
Patrick McHardy68236452007-07-07 22:30:49 -070054 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070055
Alexey Dobriyan0d55af82008-10-08 11:35:07 +020056 NF_CT_STAT_INC(net, expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010057}
Patrick McHardy13b18332006-12-02 22:11:25 -080058EXPORT_SYMBOL_GPL(nf_ct_unlink_expect);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010059
Patrick McHardy68236452007-07-07 22:30:49 -070060static void nf_ct_expectation_timed_out(unsigned long ul_expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010061{
62 struct nf_conntrack_expect *exp = (void *)ul_expect;
63
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080064 spin_lock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010065 nf_ct_unlink_expect(exp);
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080066 spin_unlock_bh(&nf_conntrack_lock);
Patrick McHardy68236452007-07-07 22:30:49 -070067 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010068}
69
Patrick McHardya71c0852007-07-07 22:33:47 -070070static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
71{
Patrick McHardy34498822007-12-17 22:45:52 -080072 unsigned int hash;
73
Patrick McHardya71c0852007-07-07 22:33:47 -070074 if (unlikely(!nf_ct_expect_hash_rnd_initted)) {
75 get_random_bytes(&nf_ct_expect_hash_rnd, 4);
76 nf_ct_expect_hash_rnd_initted = 1;
77 }
78
Patrick McHardy34498822007-12-17 22:45:52 -080079 hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
Patrick McHardya71c0852007-07-07 22:33:47 -070080 (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
Patrick McHardy34498822007-12-17 22:45:52 -080081 (__force __u16)tuple->dst.u.all) ^ nf_ct_expect_hash_rnd);
82 return ((u64)hash * nf_ct_expect_hsize) >> 32;
Patrick McHardya71c0852007-07-07 22:33:47 -070083}
84
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010085struct nf_conntrack_expect *
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020086__nf_ct_expect_find(struct net *net, const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010087{
88 struct nf_conntrack_expect *i;
Patrick McHardya71c0852007-07-07 22:33:47 -070089 struct hlist_node *n;
90 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010091
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020092 if (!net->ct.expect_count)
Patrick McHardya71c0852007-07-07 22:33:47 -070093 return NULL;
94
95 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020096 hlist_for_each_entry_rcu(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010097 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask))
98 return i;
99 }
100 return NULL;
101}
Patrick McHardy68236452007-07-07 22:30:49 -0700102EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100103
104/* Just find a expectation corresponding to a tuple. */
105struct nf_conntrack_expect *
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200106nf_ct_expect_find_get(struct net *net, const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100107{
108 struct nf_conntrack_expect *i;
109
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800110 rcu_read_lock();
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200111 i = __nf_ct_expect_find(net, tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800112 if (i && !atomic_inc_not_zero(&i->use))
113 i = NULL;
114 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100115
116 return i;
117}
Patrick McHardy68236452007-07-07 22:30:49 -0700118EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100119
120/* If an expectation for this connection is found, it gets delete from
121 * global list then returned. */
122struct nf_conntrack_expect *
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200123nf_ct_find_expectation(struct net *net, const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100124{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700125 struct nf_conntrack_expect *i, *exp = NULL;
126 struct hlist_node *n;
127 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100128
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200129 if (!net->ct.expect_count)
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700130 return NULL;
131
132 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200133 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700134 if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
135 nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) {
136 exp = i;
137 break;
138 }
139 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800140 if (!exp)
141 return NULL;
142
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100143 /* If master is not in hash table yet (ie. packet hasn't left
144 this machine yet), how can other end know about expected?
145 Hence these are not the droids you are looking for (if
146 master ct never got confirmed, we'd hold a reference to it
147 and weird things would happen to future packets). */
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800148 if (!nf_ct_is_confirmed(exp->master))
149 return NULL;
150
151 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
152 atomic_inc(&exp->use);
153 return exp;
154 } else if (del_timer(&exp->timeout)) {
155 nf_ct_unlink_expect(exp);
156 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100157 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800158
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100159 return NULL;
160}
161
162/* delete all expectations for this conntrack */
163void nf_ct_remove_expectations(struct nf_conn *ct)
164{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100165 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700166 struct nf_conntrack_expect *exp;
167 struct hlist_node *n, *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100168
169 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700170 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100171 return;
172
Patrick McHardyb5605802007-07-07 22:35:56 -0700173 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
174 if (del_timer(&exp->timeout)) {
175 nf_ct_unlink_expect(exp);
176 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800177 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100178 }
179}
Patrick McHardy13b18332006-12-02 22:11:25 -0800180EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100181
182/* Would two expected things clash? */
183static inline int expect_clash(const struct nf_conntrack_expect *a,
184 const struct nf_conntrack_expect *b)
185{
186 /* Part covered by intersection of masks must be unequal,
187 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700188 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100189 int count;
190
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100191 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100192
193 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
194 intersect_mask.src.u3.all[count] =
195 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
196 }
197
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100198 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
199}
200
201static inline int expect_matches(const struct nf_conntrack_expect *a,
202 const struct nf_conntrack_expect *b)
203{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700204 return a->master == b->master && a->class == b->class
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100205 && nf_ct_tuple_equal(&a->tuple, &b->tuple)
Patrick McHardyd4156e82007-07-07 22:31:32 -0700206 && nf_ct_tuple_mask_equal(&a->mask, &b->mask);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100207}
208
209/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700210void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100211{
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800212 spin_lock_bh(&nf_conntrack_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700213 if (del_timer(&exp->timeout)) {
214 nf_ct_unlink_expect(exp);
215 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100216 }
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800217 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100218}
Patrick McHardy68236452007-07-07 22:30:49 -0700219EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100220
221/* We don't increase the master conntrack refcount for non-fulfilled
222 * conntracks. During the conntrack destruction, the expectations are
223 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700224struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100225{
226 struct nf_conntrack_expect *new;
227
Patrick McHardy68236452007-07-07 22:30:49 -0700228 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100229 if (!new)
230 return NULL;
231
232 new->master = me;
233 atomic_set(&new->use, 1);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800234 INIT_RCU_HEAD(&new->rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100235 return new;
236}
Patrick McHardy68236452007-07-07 22:30:49 -0700237EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100238
Patrick McHardy6002f2662008-03-25 20:09:15 -0700239void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200240 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700241 const union nf_inet_addr *saddr,
242 const union nf_inet_addr *daddr,
243 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800244{
245 int len;
246
247 if (family == AF_INET)
248 len = 4;
249 else
250 len = 16;
251
252 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700253 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800254 exp->expectfn = NULL;
255 exp->helper = NULL;
256 exp->tuple.src.l3num = family;
257 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800258
259 if (saddr) {
260 memcpy(&exp->tuple.src.u3, saddr, len);
261 if (sizeof(exp->tuple.src.u3) > len)
262 /* address needs to be cleared for nf_ct_tuple_equal */
263 memset((void *)&exp->tuple.src.u3 + len, 0x00,
264 sizeof(exp->tuple.src.u3) - len);
265 memset(&exp->mask.src.u3, 0xFF, len);
266 if (sizeof(exp->mask.src.u3) > len)
267 memset((void *)&exp->mask.src.u3 + len, 0x00,
268 sizeof(exp->mask.src.u3) - len);
269 } else {
270 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
271 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
272 }
273
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800274 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100275 exp->tuple.src.u.all = *src;
276 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800277 } else {
278 exp->tuple.src.u.all = 0;
279 exp->mask.src.u.all = 0;
280 }
281
Patrick McHardyd4156e82007-07-07 22:31:32 -0700282 memcpy(&exp->tuple.dst.u3, daddr, len);
283 if (sizeof(exp->tuple.dst.u3) > len)
284 /* address needs to be cleared for nf_ct_tuple_equal */
285 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
286 sizeof(exp->tuple.dst.u3) - len);
287
Al Viroa34c4582007-07-26 17:33:19 +0100288 exp->tuple.dst.u.all = *dst;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800289}
Patrick McHardy68236452007-07-07 22:30:49 -0700290EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800291
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800292static void nf_ct_expect_free_rcu(struct rcu_head *head)
293{
294 struct nf_conntrack_expect *exp;
295
296 exp = container_of(head, struct nf_conntrack_expect, rcu);
297 kmem_cache_free(nf_ct_expect_cachep, exp);
298}
299
Patrick McHardy68236452007-07-07 22:30:49 -0700300void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100301{
302 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800303 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100304}
Patrick McHardy68236452007-07-07 22:30:49 -0700305EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100306
Patrick McHardy68236452007-07-07 22:30:49 -0700307static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100308{
309 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200310 struct net *net = nf_ct_exp_net(exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700311 const struct nf_conntrack_expect_policy *p;
Patrick McHardya71c0852007-07-07 22:33:47 -0700312 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100313
314 atomic_inc(&exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700315
316 hlist_add_head(&exp->lnode, &master_help->expectations);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700317 master_help->expecting[exp->class]++;
Patrick McHardya71c0852007-07-07 22:33:47 -0700318
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200319 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
320 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100321
Patrick McHardy68236452007-07-07 22:30:49 -0700322 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
323 (unsigned long)exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700324 p = &master_help->helper->expect_policy[exp->class];
325 exp->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100326 add_timer(&exp->timeout);
327
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100328 atomic_inc(&exp->use);
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200329 NF_CT_STAT_INC(net, expect_create);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100330}
331
332/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700333static void evict_oldest_expect(struct nf_conn *master,
334 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100335{
Patrick McHardyb5605802007-07-07 22:35:56 -0700336 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700337 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700338 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100339
Patrick McHardy6002f2662008-03-25 20:09:15 -0700340 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
341 if (exp->class == new->class)
342 last = exp;
343 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700344
Patrick McHardy6002f2662008-03-25 20:09:15 -0700345 if (last && del_timer(&last->timeout)) {
346 nf_ct_unlink_expect(last);
347 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100348 }
349}
350
351static inline int refresh_timer(struct nf_conntrack_expect *i)
352{
353 struct nf_conn_help *master_help = nfct_help(i->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700354 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100355
356 if (!del_timer(&i->timeout))
357 return 0;
358
Patrick McHardy6002f2662008-03-25 20:09:15 -0700359 p = &master_help->helper->expect_policy[i->class];
360 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100361 add_timer(&i->timeout);
362 return 1;
363}
364
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100365static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100366{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700367 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100368 struct nf_conntrack_expect *i;
369 struct nf_conn *master = expect->master;
370 struct nf_conn_help *master_help = nfct_help(master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200371 struct net *net = nf_ct_exp_net(expect);
Patrick McHardya71c0852007-07-07 22:33:47 -0700372 struct hlist_node *n;
373 unsigned int h;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100374 int ret = 0;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100375
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700376 if (!master_help->helper) {
377 ret = -ESHUTDOWN;
378 goto out;
379 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700380 h = nf_ct_expect_dst_hash(&expect->tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200381 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100382 if (expect_matches(i, expect)) {
383 /* Refresh timer: if it's dying, ignore.. */
384 if (refresh_timer(i)) {
385 ret = 0;
386 goto out;
387 }
388 } else if (expect_clash(i, expect)) {
389 ret = -EBUSY;
390 goto out;
391 }
392 }
393 /* Will be over limit? */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700394 p = &master_help->helper->expect_policy[expect->class];
395 if (p->max_expected &&
396 master_help->expecting[expect->class] >= p->max_expected) {
397 evict_oldest_expect(master, expect);
398 if (master_help->expecting[expect->class] >= p->max_expected) {
399 ret = -EMFILE;
400 goto out;
401 }
402 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100403
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200404 if (net->ct.expect_count >= nf_ct_expect_max) {
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700405 if (net_ratelimit())
406 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700407 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700408 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700409 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100410out:
411 return ret;
412}
413
414int nf_ct_expect_related(struct nf_conntrack_expect *expect)
415{
416 int ret;
417
418 spin_lock_bh(&nf_conntrack_lock);
419 ret = __nf_ct_expect_check(expect);
420 if (ret < 0)
421 goto out;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700422
Patrick McHardy68236452007-07-07 22:30:49 -0700423 nf_ct_expect_insert(expect);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100424 atomic_inc(&expect->use);
425 spin_unlock_bh(&nf_conntrack_lock);
Patrick McHardy68236452007-07-07 22:30:49 -0700426 nf_ct_expect_event(IPEXP_NEW, expect);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100427 nf_ct_expect_put(expect);
428 return ret;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100429out:
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800430 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100431 return ret;
432}
Patrick McHardy68236452007-07-07 22:30:49 -0700433EXPORT_SYMBOL_GPL(nf_ct_expect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100434
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100435int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
436 u32 pid, int report)
437{
438 int ret;
439
440 spin_lock_bh(&nf_conntrack_lock);
441 ret = __nf_ct_expect_check(expect);
442 if (ret < 0)
443 goto out;
444 nf_ct_expect_insert(expect);
445out:
446 spin_unlock_bh(&nf_conntrack_lock);
447 if (ret == 0)
448 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
449 return ret;
450}
451EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
452
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100453#ifdef CONFIG_PROC_FS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700454struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200455 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700456 unsigned int bucket;
457};
458
459static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100460{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200461 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700462 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800463 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100464
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700465 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200466 n = rcu_dereference(net->ct.expect_hash[st->bucket].first);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800467 if (n)
468 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100469 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700470 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100471}
472
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700473static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
474 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100475{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200476 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700477 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100478
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800479 head = rcu_dereference(head->next);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700480 while (head == NULL) {
481 if (++st->bucket >= nf_ct_expect_hsize)
482 return NULL;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200483 head = rcu_dereference(net->ct.expect_hash[st->bucket].first);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700484 }
485 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100486}
487
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700488static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
489{
490 struct hlist_node *head = ct_expect_get_first(seq);
491
492 if (head)
493 while (pos && (head = ct_expect_get_next(seq, head)))
494 pos--;
495 return pos ? NULL : head;
496}
497
498static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800499 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700500{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800501 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700502 return ct_expect_get_idx(seq, *pos);
503}
504
505static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
506{
507 (*pos)++;
508 return ct_expect_get_next(seq, v);
509}
510
511static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800512 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100513{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800514 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100515}
516
517static int exp_seq_show(struct seq_file *s, void *v)
518{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700519 struct nf_conntrack_expect *expect;
520 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700521 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700522
523 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100524
525 if (expect->timeout.function)
526 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
527 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
528 else
529 seq_printf(s, "- ");
530 seq_printf(s, "l3proto = %u proto=%u ",
531 expect->tuple.src.l3num,
532 expect->tuple.dst.protonum);
533 print_tuple(s, &expect->tuple,
534 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100535 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100536 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700537
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700538 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
539 seq_printf(s, "PERMANENT");
540 delim = ",";
541 }
542 if (expect->flags & NF_CT_EXPECT_INACTIVE)
543 seq_printf(s, "%sINACTIVE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700544
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100545 return seq_putc(s, '\n');
546}
547
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700548static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100549 .start = exp_seq_start,
550 .next = exp_seq_next,
551 .stop = exp_seq_stop,
552 .show = exp_seq_show
553};
554
555static int exp_open(struct inode *inode, struct file *file)
556{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200557 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700558 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100559}
560
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700561static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100562 .owner = THIS_MODULE,
563 .open = exp_open,
564 .read = seq_read,
565 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200566 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100567};
568#endif /* CONFIG_PROC_FS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700569
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200570static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700571{
572#ifdef CONFIG_PROC_FS
573 struct proc_dir_entry *proc;
574
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200575 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700576 if (!proc)
577 return -ENOMEM;
578#endif /* CONFIG_PROC_FS */
579 return 0;
580}
581
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200582static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700583{
584#ifdef CONFIG_PROC_FS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200585 proc_net_remove(net, "nf_conntrack_expect");
Patrick McHardye9c1b082007-07-07 22:32:53 -0700586#endif /* CONFIG_PROC_FS */
587}
588
Patrick McHardya71c0852007-07-07 22:33:47 -0700589module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0600);
590
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200591int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700592{
Patrick McHardya71c0852007-07-07 22:33:47 -0700593 int err = -ENOMEM;
594
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200595 if (net_eq(net, &init_net)) {
596 if (!nf_ct_expect_hsize) {
597 nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
598 if (!nf_ct_expect_hsize)
599 nf_ct_expect_hsize = 1;
600 }
601 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700602 }
603
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200604 net->ct.expect_count = 0;
605 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
606 &net->ct.expect_vmalloc);
607 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700608 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700609
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200610 if (net_eq(net, &init_net)) {
611 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
Patrick McHardye9c1b082007-07-07 22:32:53 -0700612 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900613 0, 0, NULL);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200614 if (!nf_ct_expect_cachep)
615 goto err2;
616 }
Patrick McHardye9c1b082007-07-07 22:32:53 -0700617
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200618 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700619 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700620 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700621
622 return 0;
623
Patrick McHardya71c0852007-07-07 22:33:47 -0700624err3:
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200625 if (net_eq(net, &init_net))
626 kmem_cache_destroy(nf_ct_expect_cachep);
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700627err2:
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200628 nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
Patrick McHardya71c0852007-07-07 22:33:47 -0700629 nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700630err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700631 return err;
632}
633
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200634void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700635{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200636 exp_proc_remove(net);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200637 if (net_eq(net, &init_net))
638 kmem_cache_destroy(nf_ct_expect_cachep);
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}