blob: cd1e8e0970f226b5a2dd5e82aa2d121680ff24a3 [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
Eric Dumazet3bfd45f2010-11-16 10:19:18 +0100322 /* two references : one for hash insert, one for the timer */
323 atomic_add(2, &exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700324
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200325 if (master_help) {
326 hlist_add_head(&exp->lnode, &master_help->expectations);
327 master_help->expecting[exp->class]++;
328 } else if (exp->flags & NF_CT_EXPECT_USERSPACE)
329 hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
Patrick McHardya71c0852007-07-07 22:33:47 -0700330
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200331 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
332 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100333
Patrick McHardy68236452007-07-07 22:30:49 -0700334 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
335 (unsigned long)exp);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200336 if (master_help) {
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100337 p = &rcu_dereference_protected(
338 master_help->helper,
339 lockdep_is_held(&nf_conntrack_lock)
340 )->expect_policy[exp->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200341 exp->timeout.expires = jiffies + p->timeout * HZ;
342 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100343 add_timer(&exp->timeout);
344
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200345 NF_CT_STAT_INC(net, expect_create);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100346}
347
348/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700349static void evict_oldest_expect(struct nf_conn *master,
350 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100351{
Patrick McHardyb5605802007-07-07 22:35:56 -0700352 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700353 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700354 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100355
Patrick McHardy6002f2662008-03-25 20:09:15 -0700356 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
357 if (exp->class == new->class)
358 last = exp;
359 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700360
Patrick McHardy6002f2662008-03-25 20:09:15 -0700361 if (last && del_timer(&last->timeout)) {
362 nf_ct_unlink_expect(last);
363 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100364 }
365}
366
367static inline int refresh_timer(struct nf_conntrack_expect *i)
368{
369 struct nf_conn_help *master_help = nfct_help(i->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700370 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100371
372 if (!del_timer(&i->timeout))
373 return 0;
374
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100375 p = &rcu_dereference_protected(
376 master_help->helper,
377 lockdep_is_held(&nf_conntrack_lock)
378 )->expect_policy[i->class];
Patrick McHardy6002f2662008-03-25 20:09:15 -0700379 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100380 add_timer(&i->timeout);
381 return 1;
382}
383
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100384static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100385{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700386 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100387 struct nf_conntrack_expect *i;
388 struct nf_conn *master = expect->master;
389 struct nf_conn_help *master_help = nfct_help(master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200390 struct net *net = nf_ct_exp_net(expect);
Patrick McHardya71c0852007-07-07 22:33:47 -0700391 struct hlist_node *n;
392 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200393 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100394
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200395 /* Don't allow expectations created from kernel-space with no helper */
396 if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
397 (!master_help || (master_help && !master_help->helper))) {
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700398 ret = -ESHUTDOWN;
399 goto out;
400 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700401 h = nf_ct_expect_dst_hash(&expect->tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200402 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100403 if (expect_matches(i, expect)) {
404 /* Refresh timer: if it's dying, ignore.. */
405 if (refresh_timer(i)) {
406 ret = 0;
407 goto out;
408 }
409 } else if (expect_clash(i, expect)) {
410 ret = -EBUSY;
411 goto out;
412 }
413 }
414 /* Will be over limit? */
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200415 if (master_help) {
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100416 p = &rcu_dereference_protected(
417 master_help->helper,
418 lockdep_is_held(&nf_conntrack_lock)
419 )->expect_policy[expect->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200420 if (p->max_expected &&
421 master_help->expecting[expect->class] >= p->max_expected) {
422 evict_oldest_expect(master, expect);
423 if (master_help->expecting[expect->class]
424 >= p->max_expected) {
425 ret = -EMFILE;
426 goto out;
427 }
Patrick McHardy6002f2662008-03-25 20:09:15 -0700428 }
429 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100430
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200431 if (net->ct.expect_count >= nf_ct_expect_max) {
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700432 if (net_ratelimit())
433 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700434 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700435 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700436 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100437out:
438 return ret;
439}
440
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100441int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
442 u32 pid, int report)
443{
444 int ret;
445
446 spin_lock_bh(&nf_conntrack_lock);
447 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200448 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100449 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200450
451 ret = 0;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100452 nf_ct_expect_insert(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200453 spin_unlock_bh(&nf_conntrack_lock);
454 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
455 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100456out:
457 spin_unlock_bh(&nf_conntrack_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100458 return ret;
459}
460EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
461
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200462void nf_ct_remove_userspace_expectations(void)
463{
464 struct nf_conntrack_expect *exp;
465 struct hlist_node *n, *next;
466
467 hlist_for_each_entry_safe(exp, n, next,
468 &nf_ct_userspace_expect_list, lnode) {
469 if (del_timer(&exp->timeout)) {
470 nf_ct_unlink_expect(exp);
471 nf_ct_expect_put(exp);
472 }
473 }
474}
475EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
476
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100477#ifdef CONFIG_PROC_FS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700478struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200479 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700480 unsigned int bucket;
481};
482
483static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100484{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200485 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700486 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800487 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100488
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700489 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100490 n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800491 if (n)
492 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100493 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700494 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100495}
496
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700497static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
498 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100499{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200500 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700501 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100502
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100503 head = rcu_dereference(hlist_next_rcu(head));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700504 while (head == NULL) {
505 if (++st->bucket >= nf_ct_expect_hsize)
506 return NULL;
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100507 head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700508 }
509 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100510}
511
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700512static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
513{
514 struct hlist_node *head = ct_expect_get_first(seq);
515
516 if (head)
517 while (pos && (head = ct_expect_get_next(seq, head)))
518 pos--;
519 return pos ? NULL : head;
520}
521
522static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800523 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700524{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800525 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700526 return ct_expect_get_idx(seq, *pos);
527}
528
529static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
530{
531 (*pos)++;
532 return ct_expect_get_next(seq, v);
533}
534
535static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800536 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100537{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800538 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100539}
540
541static int exp_seq_show(struct seq_file *s, void *v)
542{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700543 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100544 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700545 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700546 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700547
548 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100549
550 if (expect->timeout.function)
551 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
552 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
553 else
554 seq_printf(s, "- ");
555 seq_printf(s, "l3proto = %u proto=%u ",
556 expect->tuple.src.l3num,
557 expect->tuple.dst.protonum);
558 print_tuple(s, &expect->tuple,
559 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100560 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100561 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700562
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700563 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
564 seq_printf(s, "PERMANENT");
565 delim = ",";
566 }
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200567 if (expect->flags & NF_CT_EXPECT_INACTIVE) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700568 seq_printf(s, "%sINACTIVE", delim);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200569 delim = ",";
570 }
571 if (expect->flags & NF_CT_EXPECT_USERSPACE)
572 seq_printf(s, "%sUSERSPACE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700573
Patrick McHardyb87921b2010-02-11 12:22:48 +0100574 helper = rcu_dereference(nfct_help(expect->master)->helper);
575 if (helper) {
576 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
577 if (helper->expect_policy[expect->class].name)
578 seq_printf(s, "/%s",
579 helper->expect_policy[expect->class].name);
580 }
581
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100582 return seq_putc(s, '\n');
583}
584
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700585static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100586 .start = exp_seq_start,
587 .next = exp_seq_next,
588 .stop = exp_seq_stop,
589 .show = exp_seq_show
590};
591
592static int exp_open(struct inode *inode, struct file *file)
593{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200594 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700595 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100596}
597
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700598static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100599 .owner = THIS_MODULE,
600 .open = exp_open,
601 .read = seq_read,
602 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200603 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100604};
605#endif /* CONFIG_PROC_FS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700606
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200607static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700608{
609#ifdef CONFIG_PROC_FS
610 struct proc_dir_entry *proc;
611
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200612 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700613 if (!proc)
614 return -ENOMEM;
615#endif /* CONFIG_PROC_FS */
616 return 0;
617}
618
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200619static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700620{
621#ifdef CONFIG_PROC_FS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200622 proc_net_remove(net, "nf_conntrack_expect");
Patrick McHardye9c1b082007-07-07 22:32:53 -0700623#endif /* CONFIG_PROC_FS */
624}
625
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800626module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700627
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200628int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700629{
Patrick McHardya71c0852007-07-07 22:33:47 -0700630 int err = -ENOMEM;
631
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200632 if (net_eq(net, &init_net)) {
633 if (!nf_ct_expect_hsize) {
Patrick McHardyd696c7b2010-02-08 11:18:07 -0800634 nf_ct_expect_hsize = net->ct.htable_size / 256;
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200635 if (!nf_ct_expect_hsize)
636 nf_ct_expect_hsize = 1;
637 }
638 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700639 }
640
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200641 net->ct.expect_count = 0;
Patrick McHardyd862a662011-01-14 15:45:56 +0100642 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200643 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700644 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700645
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200646 if (net_eq(net, &init_net)) {
647 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
Patrick McHardye9c1b082007-07-07 22:32:53 -0700648 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900649 0, 0, NULL);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200650 if (!nf_ct_expect_cachep)
651 goto err2;
652 }
Patrick McHardye9c1b082007-07-07 22:32:53 -0700653
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200654 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700655 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700656 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700657
658 return 0;
659
Patrick McHardya71c0852007-07-07 22:33:47 -0700660err3:
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200661 if (net_eq(net, &init_net))
662 kmem_cache_destroy(nf_ct_expect_cachep);
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700663err2:
Patrick McHardyd862a662011-01-14 15:45:56 +0100664 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700665err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700666 return err;
667}
668
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200669void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700670{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200671 exp_proc_remove(net);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200672 if (net_eq(net, &init_net)) {
673 rcu_barrier(); /* Wait for call_rcu() before destroy */
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200674 kmem_cache_destroy(nf_ct_expect_cachep);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200675 }
Patrick McHardyd862a662011-01-14 15:45:56 +0100676 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700677}