blob: a20fb0bd1efe850543a9dea62fdf4bf53330a335 [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
Patrick McHardyf264a7d2007-07-07 22:36:24 -070035unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070036
Patrick McHardye9c1b082007-07-07 22:32:53 -070037static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010038
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020039static HLIST_HEAD(nf_ct_userspace_expect_list);
40
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010041/* nf_conntrack_expect helper functions */
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020042void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
43 u32 pid, int report)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010044{
45 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020046 struct net *net = nf_ct_exp_net(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010047
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010048 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);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020054 if (!(exp->flags & NF_CT_EXPECT_USERSPACE))
55 master_help->expecting[exp->class]--;
56
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020057 nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report);
Patrick McHardy68236452007-07-07 22:30:49 -070058 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070059
Alexey Dobriyan0d55af82008-10-08 11:35:07 +020060 NF_CT_STAT_INC(net, expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010061}
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020062EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010063
Patrick McHardy68236452007-07-07 22:30:49 -070064static void nf_ct_expectation_timed_out(unsigned long ul_expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010065{
66 struct nf_conntrack_expect *exp = (void *)ul_expect;
67
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080068 spin_lock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010069 nf_ct_unlink_expect(exp);
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080070 spin_unlock_bh(&nf_conntrack_lock);
Patrick McHardy68236452007-07-07 22:30:49 -070071 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010072}
73
Patrick McHardya71c0852007-07-07 22:33:47 -070074static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
75{
Patrick McHardy34498822007-12-17 22:45:52 -080076 unsigned int hash;
77
Changli Gaof682cef2011-01-05 04:23:23 +000078 if (unlikely(!nf_conntrack_hash_rnd)) {
79 init_nf_conntrack_hash_rnd();
Patrick McHardya71c0852007-07-07 22:33:47 -070080 }
81
Patrick McHardy34498822007-12-17 22:45:52 -080082 hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
Patrick McHardya71c0852007-07-07 22:33:47 -070083 (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
Changli Gaof682cef2011-01-05 04:23:23 +000084 (__force __u16)tuple->dst.u.all) ^ nf_conntrack_hash_rnd);
Patrick McHardy34498822007-12-17 22:45:52 -080085 return ((u64)hash * nf_ct_expect_hsize) >> 32;
Patrick McHardya71c0852007-07-07 22:33:47 -070086}
87
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010088struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010089__nf_ct_expect_find(struct net *net, u16 zone,
90 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010091{
92 struct nf_conntrack_expect *i;
Patrick McHardya71c0852007-07-07 22:33:47 -070093 struct hlist_node *n;
94 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010095
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020096 if (!net->ct.expect_count)
Patrick McHardya71c0852007-07-07 22:33:47 -070097 return NULL;
98
99 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200100 hlist_for_each_entry_rcu(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100101 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
102 nf_ct_zone(i->master) == zone)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100103 return i;
104 }
105 return NULL;
106}
Patrick McHardy68236452007-07-07 22:30:49 -0700107EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100108
109/* Just find a expectation corresponding to a tuple. */
110struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100111nf_ct_expect_find_get(struct net *net, u16 zone,
112 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100113{
114 struct nf_conntrack_expect *i;
115
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800116 rcu_read_lock();
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100117 i = __nf_ct_expect_find(net, zone, tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800118 if (i && !atomic_inc_not_zero(&i->use))
119 i = NULL;
120 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100121
122 return i;
123}
Patrick McHardy68236452007-07-07 22:30:49 -0700124EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100125
126/* If an expectation for this connection is found, it gets delete from
127 * global list then returned. */
128struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100129nf_ct_find_expectation(struct net *net, u16 zone,
130 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100131{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700132 struct nf_conntrack_expect *i, *exp = NULL;
133 struct hlist_node *n;
134 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100135
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200136 if (!net->ct.expect_count)
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700137 return NULL;
138
139 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200140 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700141 if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100142 nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
143 nf_ct_zone(i->master) == zone) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700144 exp = i;
145 break;
146 }
147 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800148 if (!exp)
149 return NULL;
150
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100151 /* If master is not in hash table yet (ie. packet hasn't left
152 this machine yet), how can other end know about expected?
153 Hence these are not the droids you are looking for (if
154 master ct never got confirmed, we'd hold a reference to it
155 and weird things would happen to future packets). */
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800156 if (!nf_ct_is_confirmed(exp->master))
157 return NULL;
158
159 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
160 atomic_inc(&exp->use);
161 return exp;
162 } else if (del_timer(&exp->timeout)) {
163 nf_ct_unlink_expect(exp);
164 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100165 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800166
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100167 return NULL;
168}
169
170/* delete all expectations for this conntrack */
171void nf_ct_remove_expectations(struct nf_conn *ct)
172{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100173 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700174 struct nf_conntrack_expect *exp;
175 struct hlist_node *n, *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100176
177 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700178 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100179 return;
180
Patrick McHardyb5605802007-07-07 22:35:56 -0700181 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
182 if (del_timer(&exp->timeout)) {
183 nf_ct_unlink_expect(exp);
184 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800185 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100186 }
187}
Patrick McHardy13b18332006-12-02 22:11:25 -0800188EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100189
190/* Would two expected things clash? */
191static inline int expect_clash(const struct nf_conntrack_expect *a,
192 const struct nf_conntrack_expect *b)
193{
194 /* Part covered by intersection of masks must be unequal,
195 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700196 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100197 int count;
198
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100199 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100200
201 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
202 intersect_mask.src.u3.all[count] =
203 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
204 }
205
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100206 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
207}
208
209static inline int expect_matches(const struct nf_conntrack_expect *a,
210 const struct nf_conntrack_expect *b)
211{
Joe Perchesf64f9e72009-11-29 16:55:45 -0800212 return a->master == b->master && a->class == b->class &&
213 nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100214 nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
215 nf_ct_zone(a->master) == nf_ct_zone(b->master);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100216}
217
218/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700219void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100220{
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800221 spin_lock_bh(&nf_conntrack_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700222 if (del_timer(&exp->timeout)) {
223 nf_ct_unlink_expect(exp);
224 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100225 }
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800226 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100227}
Patrick McHardy68236452007-07-07 22:30:49 -0700228EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100229
230/* We don't increase the master conntrack refcount for non-fulfilled
231 * conntracks. During the conntrack destruction, the expectations are
232 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700233struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100234{
235 struct nf_conntrack_expect *new;
236
Patrick McHardy68236452007-07-07 22:30:49 -0700237 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100238 if (!new)
239 return NULL;
240
241 new->master = me;
242 atomic_set(&new->use, 1);
243 return new;
244}
Patrick McHardy68236452007-07-07 22:30:49 -0700245EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100246
Patrick McHardy6002f2662008-03-25 20:09:15 -0700247void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200248 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700249 const union nf_inet_addr *saddr,
250 const union nf_inet_addr *daddr,
251 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800252{
253 int len;
254
255 if (family == AF_INET)
256 len = 4;
257 else
258 len = 16;
259
260 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700261 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800262 exp->expectfn = NULL;
263 exp->helper = NULL;
264 exp->tuple.src.l3num = family;
265 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800266
267 if (saddr) {
268 memcpy(&exp->tuple.src.u3, saddr, len);
269 if (sizeof(exp->tuple.src.u3) > len)
270 /* address needs to be cleared for nf_ct_tuple_equal */
271 memset((void *)&exp->tuple.src.u3 + len, 0x00,
272 sizeof(exp->tuple.src.u3) - len);
273 memset(&exp->mask.src.u3, 0xFF, len);
274 if (sizeof(exp->mask.src.u3) > len)
275 memset((void *)&exp->mask.src.u3 + len, 0x00,
276 sizeof(exp->mask.src.u3) - len);
277 } else {
278 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
279 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
280 }
281
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800282 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100283 exp->tuple.src.u.all = *src;
284 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800285 } else {
286 exp->tuple.src.u.all = 0;
287 exp->mask.src.u.all = 0;
288 }
289
Patrick McHardyd4156e82007-07-07 22:31:32 -0700290 memcpy(&exp->tuple.dst.u3, daddr, len);
291 if (sizeof(exp->tuple.dst.u3) > len)
292 /* address needs to be cleared for nf_ct_tuple_equal */
293 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
294 sizeof(exp->tuple.dst.u3) - len);
295
Al Viroa34c4582007-07-26 17:33:19 +0100296 exp->tuple.dst.u.all = *dst;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800297}
Patrick McHardy68236452007-07-07 22:30:49 -0700298EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800299
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800300static void nf_ct_expect_free_rcu(struct rcu_head *head)
301{
302 struct nf_conntrack_expect *exp;
303
304 exp = container_of(head, struct nf_conntrack_expect, rcu);
305 kmem_cache_free(nf_ct_expect_cachep, exp);
306}
307
Patrick McHardy68236452007-07-07 22:30:49 -0700308void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100309{
310 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800311 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100312}
Patrick McHardy68236452007-07-07 22:30:49 -0700313EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100314
Patrick McHardy68236452007-07-07 22:30:49 -0700315static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100316{
317 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200318 struct net *net = nf_ct_exp_net(exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700319 const struct nf_conntrack_expect_policy *p;
Patrick McHardya71c0852007-07-07 22:33:47 -0700320 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100321
322 atomic_inc(&exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700323
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200324 if (master_help) {
325 hlist_add_head(&exp->lnode, &master_help->expectations);
326 master_help->expecting[exp->class]++;
327 } else if (exp->flags & NF_CT_EXPECT_USERSPACE)
328 hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
Patrick McHardya71c0852007-07-07 22:33:47 -0700329
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200330 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
331 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100332
Patrick McHardy68236452007-07-07 22:30:49 -0700333 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
334 (unsigned long)exp);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200335 if (master_help) {
336 p = &master_help->helper->expect_policy[exp->class];
337 exp->timeout.expires = jiffies + p->timeout * HZ;
338 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100339 add_timer(&exp->timeout);
340
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100341 atomic_inc(&exp->use);
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200342 NF_CT_STAT_INC(net, expect_create);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100343}
344
345/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700346static void evict_oldest_expect(struct nf_conn *master,
347 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100348{
Patrick McHardyb5605802007-07-07 22:35:56 -0700349 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700350 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700351 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100352
Patrick McHardy6002f2662008-03-25 20:09:15 -0700353 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
354 if (exp->class == new->class)
355 last = exp;
356 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700357
Patrick McHardy6002f2662008-03-25 20:09:15 -0700358 if (last && del_timer(&last->timeout)) {
359 nf_ct_unlink_expect(last);
360 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100361 }
362}
363
364static inline int refresh_timer(struct nf_conntrack_expect *i)
365{
366 struct nf_conn_help *master_help = nfct_help(i->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700367 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100368
369 if (!del_timer(&i->timeout))
370 return 0;
371
Patrick McHardy6002f2662008-03-25 20:09:15 -0700372 p = &master_help->helper->expect_policy[i->class];
373 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100374 add_timer(&i->timeout);
375 return 1;
376}
377
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100378static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100379{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700380 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100381 struct nf_conntrack_expect *i;
382 struct nf_conn *master = expect->master;
383 struct nf_conn_help *master_help = nfct_help(master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200384 struct net *net = nf_ct_exp_net(expect);
Patrick McHardya71c0852007-07-07 22:33:47 -0700385 struct hlist_node *n;
386 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200387 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100388
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200389 /* Don't allow expectations created from kernel-space with no helper */
390 if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
391 (!master_help || (master_help && !master_help->helper))) {
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700392 ret = -ESHUTDOWN;
393 goto out;
394 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700395 h = nf_ct_expect_dst_hash(&expect->tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200396 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100397 if (expect_matches(i, expect)) {
398 /* Refresh timer: if it's dying, ignore.. */
399 if (refresh_timer(i)) {
400 ret = 0;
401 goto out;
402 }
403 } else if (expect_clash(i, expect)) {
404 ret = -EBUSY;
405 goto out;
406 }
407 }
408 /* Will be over limit? */
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200409 if (master_help) {
410 p = &master_help->helper->expect_policy[expect->class];
411 if (p->max_expected &&
412 master_help->expecting[expect->class] >= p->max_expected) {
413 evict_oldest_expect(master, expect);
414 if (master_help->expecting[expect->class]
415 >= p->max_expected) {
416 ret = -EMFILE;
417 goto out;
418 }
Patrick McHardy6002f2662008-03-25 20:09:15 -0700419 }
420 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100421
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200422 if (net->ct.expect_count >= nf_ct_expect_max) {
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700423 if (net_ratelimit())
424 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700425 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700426 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700427 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100428out:
429 return ret;
430}
431
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100432int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
433 u32 pid, int report)
434{
435 int ret;
436
437 spin_lock_bh(&nf_conntrack_lock);
438 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200439 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100440 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200441
442 ret = 0;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100443 nf_ct_expect_insert(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200444 spin_unlock_bh(&nf_conntrack_lock);
445 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
446 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100447out:
448 spin_unlock_bh(&nf_conntrack_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100449 return ret;
450}
451EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
452
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200453void nf_ct_remove_userspace_expectations(void)
454{
455 struct nf_conntrack_expect *exp;
456 struct hlist_node *n, *next;
457
458 hlist_for_each_entry_safe(exp, n, next,
459 &nf_ct_userspace_expect_list, lnode) {
460 if (del_timer(&exp->timeout)) {
461 nf_ct_unlink_expect(exp);
462 nf_ct_expect_put(exp);
463 }
464 }
465}
466EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
467
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100468#ifdef CONFIG_PROC_FS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700469struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200470 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700471 unsigned int bucket;
472};
473
474static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
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;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800478 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100479
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700480 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200481 n = rcu_dereference(net->ct.expect_hash[st->bucket].first);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800482 if (n)
483 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100484 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700485 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100486}
487
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700488static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
489 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100490{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200491 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700492 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100493
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800494 head = rcu_dereference(head->next);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700495 while (head == NULL) {
496 if (++st->bucket >= nf_ct_expect_hsize)
497 return NULL;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200498 head = rcu_dereference(net->ct.expect_hash[st->bucket].first);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700499 }
500 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100501}
502
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700503static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
504{
505 struct hlist_node *head = ct_expect_get_first(seq);
506
507 if (head)
508 while (pos && (head = ct_expect_get_next(seq, head)))
509 pos--;
510 return pos ? NULL : head;
511}
512
513static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800514 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700515{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800516 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700517 return ct_expect_get_idx(seq, *pos);
518}
519
520static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
521{
522 (*pos)++;
523 return ct_expect_get_next(seq, v);
524}
525
526static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800527 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100528{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800529 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100530}
531
532static int exp_seq_show(struct seq_file *s, void *v)
533{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700534 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100535 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700536 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700537 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700538
539 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100540
541 if (expect->timeout.function)
542 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
543 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
544 else
545 seq_printf(s, "- ");
546 seq_printf(s, "l3proto = %u proto=%u ",
547 expect->tuple.src.l3num,
548 expect->tuple.dst.protonum);
549 print_tuple(s, &expect->tuple,
550 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100551 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100552 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700553
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700554 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
555 seq_printf(s, "PERMANENT");
556 delim = ",";
557 }
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200558 if (expect->flags & NF_CT_EXPECT_INACTIVE) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700559 seq_printf(s, "%sINACTIVE", delim);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200560 delim = ",";
561 }
562 if (expect->flags & NF_CT_EXPECT_USERSPACE)
563 seq_printf(s, "%sUSERSPACE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700564
Patrick McHardyb87921b2010-02-11 12:22:48 +0100565 helper = rcu_dereference(nfct_help(expect->master)->helper);
566 if (helper) {
567 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
568 if (helper->expect_policy[expect->class].name)
569 seq_printf(s, "/%s",
570 helper->expect_policy[expect->class].name);
571 }
572
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100573 return seq_putc(s, '\n');
574}
575
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700576static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100577 .start = exp_seq_start,
578 .next = exp_seq_next,
579 .stop = exp_seq_stop,
580 .show = exp_seq_show
581};
582
583static int exp_open(struct inode *inode, struct file *file)
584{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200585 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700586 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100587}
588
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700589static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100590 .owner = THIS_MODULE,
591 .open = exp_open,
592 .read = seq_read,
593 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200594 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100595};
596#endif /* CONFIG_PROC_FS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700597
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200598static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700599{
600#ifdef CONFIG_PROC_FS
601 struct proc_dir_entry *proc;
602
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200603 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700604 if (!proc)
605 return -ENOMEM;
606#endif /* CONFIG_PROC_FS */
607 return 0;
608}
609
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200610static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700611{
612#ifdef CONFIG_PROC_FS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200613 proc_net_remove(net, "nf_conntrack_expect");
Patrick McHardye9c1b082007-07-07 22:32:53 -0700614#endif /* CONFIG_PROC_FS */
615}
616
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800617module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700618
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200619int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700620{
Patrick McHardya71c0852007-07-07 22:33:47 -0700621 int err = -ENOMEM;
622
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200623 if (net_eq(net, &init_net)) {
624 if (!nf_ct_expect_hsize) {
Patrick McHardyd696c7b2010-02-08 11:18:07 -0800625 nf_ct_expect_hsize = net->ct.htable_size / 256;
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200626 if (!nf_ct_expect_hsize)
627 nf_ct_expect_hsize = 1;
628 }
629 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700630 }
631
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200632 net->ct.expect_count = 0;
633 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
Eric Dumazetea781f12009-03-25 21:05:46 +0100634 &net->ct.expect_vmalloc, 0);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200635 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700636 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700637
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200638 if (net_eq(net, &init_net)) {
639 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
Patrick McHardye9c1b082007-07-07 22:32:53 -0700640 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900641 0, 0, NULL);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200642 if (!nf_ct_expect_cachep)
643 goto err2;
644 }
Patrick McHardye9c1b082007-07-07 22:32:53 -0700645
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200646 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700647 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700648 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700649
650 return 0;
651
Patrick McHardya71c0852007-07-07 22:33:47 -0700652err3:
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200653 if (net_eq(net, &init_net))
654 kmem_cache_destroy(nf_ct_expect_cachep);
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700655err2:
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200656 nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
Patrick McHardya71c0852007-07-07 22:33:47 -0700657 nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700658err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700659 return err;
660}
661
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200662void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700663{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200664 exp_proc_remove(net);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200665 if (net_eq(net, &init_net)) {
666 rcu_barrier(); /* Wait for call_rcu() before destroy */
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200667 kmem_cache_destroy(nf_ct_expect_cachep);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200668 }
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200669 nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
Patrick McHardya71c0852007-07-07 22:33:47 -0700670 nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700671}