blob: bbb21402596d995eb27adcef109c63cd85fd4def [file] [log] [blame]
Martin Josefsson77ab9cf2006-11-29 02:34:58 +01001/* Expectation handling for nf_conntrack. */
2
3/* (C) 1999-2001 Paul `Rusty' Russell
4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
5 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/types.h>
13#include <linux/netfilter.h>
14#include <linux/skbuff.h>
15#include <linux/proc_fs.h>
16#include <linux/seq_file.h>
17#include <linux/stddef.h>
18#include <linux/slab.h>
19#include <linux/err.h>
20#include <linux/percpu.h>
21#include <linux/kernel.h>
Patrick McHardya71c0852007-07-07 22:33:47 -070022#include <linux/jhash.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020023#include <net/net_namespace.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010024
25#include <net/netfilter/nf_conntrack.h>
26#include <net/netfilter/nf_conntrack_core.h>
27#include <net/netfilter/nf_conntrack_expect.h>
28#include <net/netfilter/nf_conntrack_helper.h>
29#include <net/netfilter/nf_conntrack_tuple.h>
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010030#include <net/netfilter/nf_conntrack_zones.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010031
Patrick McHardya71c0852007-07-07 22:33:47 -070032unsigned int nf_ct_expect_hsize __read_mostly;
33EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
34
35static unsigned int nf_ct_expect_hash_rnd __read_mostly;
Patrick McHardyf264a7d2007-07-07 22:36:24 -070036unsigned int nf_ct_expect_max __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070037static int nf_ct_expect_hash_rnd_initted __read_mostly;
Patrick McHardya71c0852007-07-07 22:33:47 -070038
Patrick McHardye9c1b082007-07-07 22:32:53 -070039static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010040
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020041static HLIST_HEAD(nf_ct_userspace_expect_list);
42
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010043/* nf_conntrack_expect helper functions */
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020044void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
45 u32 pid, int report)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010046{
47 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020048 struct net *net = nf_ct_exp_net(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010049
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010050 NF_CT_ASSERT(!timer_pending(&exp->timeout));
51
Patrick McHardy7d0742d2008-01-31 04:38:19 -080052 hlist_del_rcu(&exp->hnode);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020053 net->ct.expect_count--;
Patrick McHardya71c0852007-07-07 22:33:47 -070054
Patrick McHardyb5605802007-07-07 22:35:56 -070055 hlist_del(&exp->lnode);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020056 if (!(exp->flags & NF_CT_EXPECT_USERSPACE))
57 master_help->expecting[exp->class]--;
58
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020059 nf_ct_expect_event_report(IPEXP_DESTROY, exp, pid, report);
Patrick McHardy68236452007-07-07 22:30:49 -070060 nf_ct_expect_put(exp);
Patrick McHardyb5605802007-07-07 22:35:56 -070061
Alexey Dobriyan0d55af82008-10-08 11:35:07 +020062 NF_CT_STAT_INC(net, expect_delete);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010063}
Pablo Neira Ayusoebbf41d2010-10-19 10:19:06 +020064EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010065
Patrick McHardy68236452007-07-07 22:30:49 -070066static void nf_ct_expectation_timed_out(unsigned long ul_expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010067{
68 struct nf_conntrack_expect *exp = (void *)ul_expect;
69
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080070 spin_lock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010071 nf_ct_unlink_expect(exp);
Patrick McHardyf8ba1af2008-01-31 04:38:58 -080072 spin_unlock_bh(&nf_conntrack_lock);
Patrick McHardy68236452007-07-07 22:30:49 -070073 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010074}
75
Patrick McHardya71c0852007-07-07 22:33:47 -070076static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple)
77{
Patrick McHardy34498822007-12-17 22:45:52 -080078 unsigned int hash;
79
Patrick McHardya71c0852007-07-07 22:33:47 -070080 if (unlikely(!nf_ct_expect_hash_rnd_initted)) {
Hagen Paul Pfeiferaf07d242009-02-20 10:48:06 +010081 get_random_bytes(&nf_ct_expect_hash_rnd,
82 sizeof(nf_ct_expect_hash_rnd));
Patrick McHardya71c0852007-07-07 22:33:47 -070083 nf_ct_expect_hash_rnd_initted = 1;
84 }
85
Patrick McHardy34498822007-12-17 22:45:52 -080086 hash = jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all),
Patrick McHardya71c0852007-07-07 22:33:47 -070087 (((tuple->dst.protonum ^ tuple->src.l3num) << 16) |
Patrick McHardy34498822007-12-17 22:45:52 -080088 (__force __u16)tuple->dst.u.all) ^ nf_ct_expect_hash_rnd);
89 return ((u64)hash * nf_ct_expect_hsize) >> 32;
Patrick McHardya71c0852007-07-07 22:33:47 -070090}
91
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010092struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010093__nf_ct_expect_find(struct net *net, u16 zone,
94 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010095{
96 struct nf_conntrack_expect *i;
Patrick McHardya71c0852007-07-07 22:33:47 -070097 struct hlist_node *n;
98 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010099
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200100 if (!net->ct.expect_count)
Patrick McHardya71c0852007-07-07 22:33:47 -0700101 return NULL;
102
103 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200104 hlist_for_each_entry_rcu(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100105 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
106 nf_ct_zone(i->master) == zone)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100107 return i;
108 }
109 return NULL;
110}
Patrick McHardy68236452007-07-07 22:30:49 -0700111EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100112
113/* Just find a expectation corresponding to a tuple. */
114struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100115nf_ct_expect_find_get(struct net *net, u16 zone,
116 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100117{
118 struct nf_conntrack_expect *i;
119
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800120 rcu_read_lock();
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100121 i = __nf_ct_expect_find(net, zone, tuple);
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800122 if (i && !atomic_inc_not_zero(&i->use))
123 i = NULL;
124 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100125
126 return i;
127}
Patrick McHardy68236452007-07-07 22:30:49 -0700128EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100129
130/* If an expectation for this connection is found, it gets delete from
131 * global list then returned. */
132struct nf_conntrack_expect *
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100133nf_ct_find_expectation(struct net *net, u16 zone,
134 const struct nf_conntrack_tuple *tuple)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100135{
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700136 struct nf_conntrack_expect *i, *exp = NULL;
137 struct hlist_node *n;
138 unsigned int h;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100139
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200140 if (!net->ct.expect_count)
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700141 return NULL;
142
143 h = nf_ct_expect_dst_hash(tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200144 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700145 if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100146 nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
147 nf_ct_zone(i->master) == zone) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700148 exp = i;
149 break;
150 }
151 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800152 if (!exp)
153 return NULL;
154
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100155 /* If master is not in hash table yet (ie. packet hasn't left
156 this machine yet), how can other end know about expected?
157 Hence these are not the droids you are looking for (if
158 master ct never got confirmed, we'd hold a reference to it
159 and weird things would happen to future packets). */
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800160 if (!nf_ct_is_confirmed(exp->master))
161 return NULL;
162
163 if (exp->flags & NF_CT_EXPECT_PERMANENT) {
164 atomic_inc(&exp->use);
165 return exp;
166 } else if (del_timer(&exp->timeout)) {
167 nf_ct_unlink_expect(exp);
168 return exp;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100169 }
Yasuyuki Kozakaiece00642006-12-05 13:44:57 -0800170
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100171 return NULL;
172}
173
174/* delete all expectations for this conntrack */
175void nf_ct_remove_expectations(struct nf_conn *ct)
176{
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100177 struct nf_conn_help *help = nfct_help(ct);
Patrick McHardyb5605802007-07-07 22:35:56 -0700178 struct nf_conntrack_expect *exp;
179 struct hlist_node *n, *next;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100180
181 /* Optimization: most connection never expect any others. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700182 if (!help)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100183 return;
184
Patrick McHardyb5605802007-07-07 22:35:56 -0700185 hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
186 if (del_timer(&exp->timeout)) {
187 nf_ct_unlink_expect(exp);
188 nf_ct_expect_put(exp);
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800189 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100190 }
191}
Patrick McHardy13b18332006-12-02 22:11:25 -0800192EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100193
194/* Would two expected things clash? */
195static inline int expect_clash(const struct nf_conntrack_expect *a,
196 const struct nf_conntrack_expect *b)
197{
198 /* Part covered by intersection of masks must be unequal,
199 otherwise they clash */
Patrick McHardyd4156e82007-07-07 22:31:32 -0700200 struct nf_conntrack_tuple_mask intersect_mask;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100201 int count;
202
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100203 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100204
205 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
206 intersect_mask.src.u3.all[count] =
207 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
208 }
209
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100210 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
211}
212
213static inline int expect_matches(const struct nf_conntrack_expect *a,
214 const struct nf_conntrack_expect *b)
215{
Joe Perchesf64f9e72009-11-29 16:55:45 -0800216 return a->master == b->master && a->class == b->class &&
217 nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +0100218 nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
219 nf_ct_zone(a->master) == nf_ct_zone(b->master);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100220}
221
222/* Generally a bad idea to call this: could have matched already. */
Patrick McHardy68236452007-07-07 22:30:49 -0700223void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100224{
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800225 spin_lock_bh(&nf_conntrack_lock);
Patrick McHardy4e1d4e62007-07-07 22:32:03 -0700226 if (del_timer(&exp->timeout)) {
227 nf_ct_unlink_expect(exp);
228 nf_ct_expect_put(exp);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100229 }
Patrick McHardyf8ba1af2008-01-31 04:38:58 -0800230 spin_unlock_bh(&nf_conntrack_lock);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100231}
Patrick McHardy68236452007-07-07 22:30:49 -0700232EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100233
234/* We don't increase the master conntrack refcount for non-fulfilled
235 * conntracks. During the conntrack destruction, the expectations are
236 * always killed before the conntrack itself */
Patrick McHardy68236452007-07-07 22:30:49 -0700237struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100238{
239 struct nf_conntrack_expect *new;
240
Patrick McHardy68236452007-07-07 22:30:49 -0700241 new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100242 if (!new)
243 return NULL;
244
245 new->master = me;
246 atomic_set(&new->use, 1);
247 return new;
248}
Patrick McHardy68236452007-07-07 22:30:49 -0700249EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100250
Patrick McHardy6002f2662008-03-25 20:09:15 -0700251void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
Jan Engelhardt76108ce2008-10-08 11:35:00 +0200252 u_int8_t family,
Patrick McHardy1d9d7522008-03-25 20:07:58 -0700253 const union nf_inet_addr *saddr,
254 const union nf_inet_addr *daddr,
255 u_int8_t proto, const __be16 *src, const __be16 *dst)
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800256{
257 int len;
258
259 if (family == AF_INET)
260 len = 4;
261 else
262 len = 16;
263
264 exp->flags = 0;
Patrick McHardy6002f2662008-03-25 20:09:15 -0700265 exp->class = class;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800266 exp->expectfn = NULL;
267 exp->helper = NULL;
268 exp->tuple.src.l3num = family;
269 exp->tuple.dst.protonum = proto;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800270
271 if (saddr) {
272 memcpy(&exp->tuple.src.u3, saddr, len);
273 if (sizeof(exp->tuple.src.u3) > len)
274 /* address needs to be cleared for nf_ct_tuple_equal */
275 memset((void *)&exp->tuple.src.u3 + len, 0x00,
276 sizeof(exp->tuple.src.u3) - len);
277 memset(&exp->mask.src.u3, 0xFF, len);
278 if (sizeof(exp->mask.src.u3) > len)
279 memset((void *)&exp->mask.src.u3 + len, 0x00,
280 sizeof(exp->mask.src.u3) - len);
281 } else {
282 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
283 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
284 }
285
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800286 if (src) {
Al Viroa34c4582007-07-26 17:33:19 +0100287 exp->tuple.src.u.all = *src;
288 exp->mask.src.u.all = htons(0xFFFF);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800289 } else {
290 exp->tuple.src.u.all = 0;
291 exp->mask.src.u.all = 0;
292 }
293
Patrick McHardyd4156e82007-07-07 22:31:32 -0700294 memcpy(&exp->tuple.dst.u3, daddr, len);
295 if (sizeof(exp->tuple.dst.u3) > len)
296 /* address needs to be cleared for nf_ct_tuple_equal */
297 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
298 sizeof(exp->tuple.dst.u3) - len);
299
Al Viroa34c4582007-07-26 17:33:19 +0100300 exp->tuple.dst.u.all = *dst;
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800301}
Patrick McHardy68236452007-07-07 22:30:49 -0700302EXPORT_SYMBOL_GPL(nf_ct_expect_init);
Patrick McHardyd6a9b652006-12-02 22:08:01 -0800303
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800304static void nf_ct_expect_free_rcu(struct rcu_head *head)
305{
306 struct nf_conntrack_expect *exp;
307
308 exp = container_of(head, struct nf_conntrack_expect, rcu);
309 kmem_cache_free(nf_ct_expect_cachep, exp);
310}
311
Patrick McHardy68236452007-07-07 22:30:49 -0700312void nf_ct_expect_put(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100313{
314 if (atomic_dec_and_test(&exp->use))
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800315 call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100316}
Patrick McHardy68236452007-07-07 22:30:49 -0700317EXPORT_SYMBOL_GPL(nf_ct_expect_put);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100318
Patrick McHardy68236452007-07-07 22:30:49 -0700319static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100320{
321 struct nf_conn_help *master_help = nfct_help(exp->master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200322 struct net *net = nf_ct_exp_net(exp);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700323 const struct nf_conntrack_expect_policy *p;
Patrick McHardya71c0852007-07-07 22:33:47 -0700324 unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100325
326 atomic_inc(&exp->use);
Patrick McHardyb5605802007-07-07 22:35:56 -0700327
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200328 if (master_help) {
329 hlist_add_head(&exp->lnode, &master_help->expectations);
330 master_help->expecting[exp->class]++;
331 } else if (exp->flags & NF_CT_EXPECT_USERSPACE)
332 hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
Patrick McHardya71c0852007-07-07 22:33:47 -0700333
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200334 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
335 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100336
Patrick McHardy68236452007-07-07 22:30:49 -0700337 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
338 (unsigned long)exp);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200339 if (master_help) {
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100340 p = &rcu_dereference_protected(
341 master_help->helper,
342 lockdep_is_held(&nf_conntrack_lock)
343 )->expect_policy[exp->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200344 exp->timeout.expires = jiffies + p->timeout * HZ;
345 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100346 add_timer(&exp->timeout);
347
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100348 atomic_inc(&exp->use);
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200349 NF_CT_STAT_INC(net, expect_create);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100350}
351
352/* Race with expectations being used means we could have none to find; OK. */
Patrick McHardy6002f2662008-03-25 20:09:15 -0700353static void evict_oldest_expect(struct nf_conn *master,
354 struct nf_conntrack_expect *new)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100355{
Patrick McHardyb5605802007-07-07 22:35:56 -0700356 struct nf_conn_help *master_help = nfct_help(master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700357 struct nf_conntrack_expect *exp, *last = NULL;
Patrick McHardyb5605802007-07-07 22:35:56 -0700358 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100359
Patrick McHardy6002f2662008-03-25 20:09:15 -0700360 hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
361 if (exp->class == new->class)
362 last = exp;
363 }
Patrick McHardyb5605802007-07-07 22:35:56 -0700364
Patrick McHardy6002f2662008-03-25 20:09:15 -0700365 if (last && del_timer(&last->timeout)) {
366 nf_ct_unlink_expect(last);
367 nf_ct_expect_put(last);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100368 }
369}
370
371static inline int refresh_timer(struct nf_conntrack_expect *i)
372{
373 struct nf_conn_help *master_help = nfct_help(i->master);
Patrick McHardy6002f2662008-03-25 20:09:15 -0700374 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100375
376 if (!del_timer(&i->timeout))
377 return 0;
378
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100379 p = &rcu_dereference_protected(
380 master_help->helper,
381 lockdep_is_held(&nf_conntrack_lock)
382 )->expect_policy[i->class];
Patrick McHardy6002f2662008-03-25 20:09:15 -0700383 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100384 add_timer(&i->timeout);
385 return 1;
386}
387
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100388static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100389{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700390 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100391 struct nf_conntrack_expect *i;
392 struct nf_conn *master = expect->master;
393 struct nf_conn_help *master_help = nfct_help(master);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200394 struct net *net = nf_ct_exp_net(expect);
Patrick McHardya71c0852007-07-07 22:33:47 -0700395 struct hlist_node *n;
396 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200397 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100398
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200399 /* Don't allow expectations created from kernel-space with no helper */
400 if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
401 (!master_help || (master_help && !master_help->helper))) {
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700402 ret = -ESHUTDOWN;
403 goto out;
404 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700405 h = nf_ct_expect_dst_hash(&expect->tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200406 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100407 if (expect_matches(i, expect)) {
408 /* Refresh timer: if it's dying, ignore.. */
409 if (refresh_timer(i)) {
410 ret = 0;
411 goto out;
412 }
413 } else if (expect_clash(i, expect)) {
414 ret = -EBUSY;
415 goto out;
416 }
417 }
418 /* Will be over limit? */
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200419 if (master_help) {
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100420 p = &rcu_dereference_protected(
421 master_help->helper,
422 lockdep_is_held(&nf_conntrack_lock)
423 )->expect_policy[expect->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200424 if (p->max_expected &&
425 master_help->expecting[expect->class] >= p->max_expected) {
426 evict_oldest_expect(master, expect);
427 if (master_help->expecting[expect->class]
428 >= p->max_expected) {
429 ret = -EMFILE;
430 goto out;
431 }
Patrick McHardy6002f2662008-03-25 20:09:15 -0700432 }
433 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100434
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200435 if (net->ct.expect_count >= nf_ct_expect_max) {
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700436 if (net_ratelimit())
437 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700438 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700439 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700440 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100441out:
442 return ret;
443}
444
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100445int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
446 u32 pid, int report)
447{
448 int ret;
449
450 spin_lock_bh(&nf_conntrack_lock);
451 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200452 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100453 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200454
455 ret = 0;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100456 nf_ct_expect_insert(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200457 spin_unlock_bh(&nf_conntrack_lock);
458 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
459 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100460out:
461 spin_unlock_bh(&nf_conntrack_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100462 return ret;
463}
464EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
465
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200466void nf_ct_remove_userspace_expectations(void)
467{
468 struct nf_conntrack_expect *exp;
469 struct hlist_node *n, *next;
470
471 hlist_for_each_entry_safe(exp, n, next,
472 &nf_ct_userspace_expect_list, lnode) {
473 if (del_timer(&exp->timeout)) {
474 nf_ct_unlink_expect(exp);
475 nf_ct_expect_put(exp);
476 }
477 }
478}
479EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
480
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100481#ifdef CONFIG_PROC_FS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700482struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200483 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700484 unsigned int bucket;
485};
486
487static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100488{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200489 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700490 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800491 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100492
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700493 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100494 n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800495 if (n)
496 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100497 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700498 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100499}
500
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700501static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
502 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100503{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200504 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700505 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100506
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100507 head = rcu_dereference(hlist_next_rcu(head));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700508 while (head == NULL) {
509 if (++st->bucket >= nf_ct_expect_hsize)
510 return NULL;
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100511 head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700512 }
513 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100514}
515
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700516static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
517{
518 struct hlist_node *head = ct_expect_get_first(seq);
519
520 if (head)
521 while (pos && (head = ct_expect_get_next(seq, head)))
522 pos--;
523 return pos ? NULL : head;
524}
525
526static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800527 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700528{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800529 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700530 return ct_expect_get_idx(seq, *pos);
531}
532
533static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
534{
535 (*pos)++;
536 return ct_expect_get_next(seq, v);
537}
538
539static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800540 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100541{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800542 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100543}
544
545static int exp_seq_show(struct seq_file *s, void *v)
546{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700547 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100548 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700549 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700550 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700551
552 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100553
554 if (expect->timeout.function)
555 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
556 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
557 else
558 seq_printf(s, "- ");
559 seq_printf(s, "l3proto = %u proto=%u ",
560 expect->tuple.src.l3num,
561 expect->tuple.dst.protonum);
562 print_tuple(s, &expect->tuple,
563 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100564 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100565 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700566
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700567 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
568 seq_printf(s, "PERMANENT");
569 delim = ",";
570 }
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200571 if (expect->flags & NF_CT_EXPECT_INACTIVE) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700572 seq_printf(s, "%sINACTIVE", delim);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200573 delim = ",";
574 }
575 if (expect->flags & NF_CT_EXPECT_USERSPACE)
576 seq_printf(s, "%sUSERSPACE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700577
Patrick McHardyb87921b2010-02-11 12:22:48 +0100578 helper = rcu_dereference(nfct_help(expect->master)->helper);
579 if (helper) {
580 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
581 if (helper->expect_policy[expect->class].name)
582 seq_printf(s, "/%s",
583 helper->expect_policy[expect->class].name);
584 }
585
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100586 return seq_putc(s, '\n');
587}
588
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700589static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100590 .start = exp_seq_start,
591 .next = exp_seq_next,
592 .stop = exp_seq_stop,
593 .show = exp_seq_show
594};
595
596static int exp_open(struct inode *inode, struct file *file)
597{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200598 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700599 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100600}
601
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700602static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100603 .owner = THIS_MODULE,
604 .open = exp_open,
605 .read = seq_read,
606 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200607 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100608};
609#endif /* CONFIG_PROC_FS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700610
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200611static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700612{
613#ifdef CONFIG_PROC_FS
614 struct proc_dir_entry *proc;
615
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200616 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700617 if (!proc)
618 return -ENOMEM;
619#endif /* CONFIG_PROC_FS */
620 return 0;
621}
622
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200623static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700624{
625#ifdef CONFIG_PROC_FS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200626 proc_net_remove(net, "nf_conntrack_expect");
Patrick McHardye9c1b082007-07-07 22:32:53 -0700627#endif /* CONFIG_PROC_FS */
628}
629
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800630module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700631
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200632int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700633{
Patrick McHardya71c0852007-07-07 22:33:47 -0700634 int err = -ENOMEM;
635
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200636 if (net_eq(net, &init_net)) {
637 if (!nf_ct_expect_hsize) {
Patrick McHardyd696c7b2010-02-08 11:18:07 -0800638 nf_ct_expect_hsize = net->ct.htable_size / 256;
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200639 if (!nf_ct_expect_hsize)
640 nf_ct_expect_hsize = 1;
641 }
642 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700643 }
644
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200645 net->ct.expect_count = 0;
646 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
Eric Dumazetea781f12009-03-25 21:05:46 +0100647 &net->ct.expect_vmalloc, 0);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200648 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700649 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700650
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200651 if (net_eq(net, &init_net)) {
652 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
Patrick McHardye9c1b082007-07-07 22:32:53 -0700653 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900654 0, 0, NULL);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200655 if (!nf_ct_expect_cachep)
656 goto err2;
657 }
Patrick McHardye9c1b082007-07-07 22:32:53 -0700658
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200659 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700660 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700661 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700662
663 return 0;
664
Patrick McHardya71c0852007-07-07 22:33:47 -0700665err3:
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200666 if (net_eq(net, &init_net))
667 kmem_cache_destroy(nf_ct_expect_cachep);
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700668err2:
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 McHardya71c0852007-07-07 22:33:47 -0700671err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700672 return err;
673}
674
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200675void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700676{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200677 exp_proc_remove(net);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200678 if (net_eq(net, &init_net)) {
679 rcu_barrier(); /* Wait for call_rcu() before destroy */
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200680 kmem_cache_destroy(nf_ct_expect_cachep);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200681 }
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200682 nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc,
Patrick McHardya71c0852007-07-07 22:33:47 -0700683 nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700684}