blob: 4147ba3f653c085d678e5d7c67b3206553dace20 [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>
Paul Gortmakerd9b93842011-09-18 13:21:27 -040023#include <linux/moduleparam.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040024#include <linux/export.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020025#include <net/net_namespace.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010026
27#include <net/netfilter/nf_conntrack.h>
28#include <net/netfilter/nf_conntrack_core.h>
29#include <net/netfilter/nf_conntrack_expect.h>
30#include <net/netfilter/nf_conntrack_helper.h>
31#include <net/netfilter/nf_conntrack_tuple.h>
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010032#include <net/netfilter/nf_conntrack_zones.h>
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010033
Patrick McHardya71c0852007-07-07 22:33:47 -070034unsigned int nf_ct_expect_hsize __read_mostly;
35EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
36
Patrick McHardyf264a7d2007-07-07 22:36:24 -070037unsigned int nf_ct_expect_max __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
41/* 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
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +010048 NF_CT_ASSERT(master_help);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +010049 NF_CT_ASSERT(!timer_pending(&exp->timeout));
50
Patrick McHardy7d0742d2008-01-31 04:38:19 -080051 hlist_del_rcu(&exp->hnode);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +020052 net->ct.expect_count--;
Patrick McHardya71c0852007-07-07 22:33:47 -070053
Patrick McHardyb5605802007-07-07 22:35:56 -070054 hlist_del(&exp->lnode);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +010055 master_help->expecting[exp->class]--;
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +020056
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
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100315static int 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);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100318 struct nf_conntrack_helper *helper;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200319 struct net *net = nf_ct_exp_net(exp);
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 Ayuso3d058d72011-12-18 01:55:54 +0100325 hlist_add_head(&exp->lnode, &master_help->expectations);
326 master_help->expecting[exp->class]++;
Patrick McHardya71c0852007-07-07 22:33:47 -0700327
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200328 hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
329 net->ct.expect_count++;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100330
Patrick McHardy68236452007-07-07 22:30:49 -0700331 setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
332 (unsigned long)exp);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100333 helper = rcu_dereference_protected(master_help->helper,
334 lockdep_is_held(&nf_conntrack_lock));
335 if (helper) {
336 exp->timeout.expires = jiffies +
337 helper->expect_policy[exp->class].timeout * HZ;
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200338 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100339 add_timer(&exp->timeout);
340
Alexey Dobriyan0d55af82008-10-08 11:35:07 +0200341 NF_CT_STAT_INC(net, expect_create);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100342 return 0;
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
Eric Dumazetc5d277d2010-11-15 19:45:13 +0100372 p = &rcu_dereference_protected(
373 master_help->helper,
374 lockdep_is_held(&nf_conntrack_lock)
375 )->expect_policy[i->class];
Patrick McHardy6002f2662008-03-25 20:09:15 -0700376 i->timeout.expires = jiffies + p->timeout * HZ;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100377 add_timer(&i->timeout);
378 return 1;
379}
380
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100381static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100382{
Patrick McHardy6002f2662008-03-25 20:09:15 -0700383 const struct nf_conntrack_expect_policy *p;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100384 struct nf_conntrack_expect *i;
385 struct nf_conn *master = expect->master;
386 struct nf_conn_help *master_help = nfct_help(master);
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100387 struct nf_conntrack_helper *helper;
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200388 struct net *net = nf_ct_exp_net(expect);
Patrick McHardya71c0852007-07-07 22:33:47 -0700389 struct hlist_node *n;
390 unsigned int h;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200391 int ret = 1;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100392
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100393 if (!master_help) {
Patrick McHarrdy3c158f72007-06-05 12:55:27 -0700394 ret = -ESHUTDOWN;
395 goto out;
396 }
Patrick McHardya71c0852007-07-07 22:33:47 -0700397 h = nf_ct_expect_dst_hash(&expect->tuple);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200398 hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100399 if (expect_matches(i, expect)) {
400 /* Refresh timer: if it's dying, ignore.. */
401 if (refresh_timer(i)) {
402 ret = 0;
403 goto out;
404 }
405 } else if (expect_clash(i, expect)) {
406 ret = -EBUSY;
407 goto out;
408 }
409 }
410 /* Will be over limit? */
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100411 helper = rcu_dereference_protected(master_help->helper,
412 lockdep_is_held(&nf_conntrack_lock));
413 if (helper) {
414 p = &helper->expect_policy[expect->class];
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200415 if (p->max_expected &&
416 master_help->expecting[expect->class] >= p->max_expected) {
417 evict_oldest_expect(master, expect);
418 if (master_help->expecting[expect->class]
419 >= p->max_expected) {
420 ret = -EMFILE;
421 goto out;
422 }
Patrick McHardy6002f2662008-03-25 20:09:15 -0700423 }
424 }
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100425
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200426 if (net->ct.expect_count >= nf_ct_expect_max) {
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700427 if (net_ratelimit())
428 printk(KERN_WARNING
Alexey Dobriyan3d89e9c2008-03-10 16:43:10 -0700429 "nf_conntrack: expectation table full\n");
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700430 ret = -EMFILE;
Patrick McHardyf264a7d2007-07-07 22:36:24 -0700431 }
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100432out:
433 return ret;
434}
435
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100436int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
437 u32 pid, int report)
438{
439 int ret;
440
441 spin_lock_bh(&nf_conntrack_lock);
442 ret = __nf_ct_expect_check(expect);
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200443 if (ret <= 0)
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100444 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200445
Pablo Neira Ayuso3d058d72011-12-18 01:55:54 +0100446 ret = nf_ct_expect_insert(expect);
447 if (ret < 0)
448 goto out;
Pablo Neira Ayuso83731672009-04-06 17:47:20 +0200449 spin_unlock_bh(&nf_conntrack_lock);
450 nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
451 return ret;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100452out:
453 spin_unlock_bh(&nf_conntrack_lock);
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +0100454 return ret;
455}
456EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
457
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200458#ifdef CONFIG_NF_CONNTRACK_PROCFS
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700459struct ct_expect_iter_state {
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200460 struct seq_net_private p;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700461 unsigned int bucket;
462};
463
464static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100465{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200466 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700467 struct ct_expect_iter_state *st = seq->private;
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800468 struct hlist_node *n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100469
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700470 for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100471 n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800472 if (n)
473 return n;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100474 }
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700475 return NULL;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100476}
477
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700478static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
479 struct hlist_node *head)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100480{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200481 struct net *net = seq_file_net(seq);
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700482 struct ct_expect_iter_state *st = seq->private;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100483
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100484 head = rcu_dereference(hlist_next_rcu(head));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700485 while (head == NULL) {
486 if (++st->bucket >= nf_ct_expect_hsize)
487 return NULL;
Eric Dumazet0e60ebe2010-11-15 18:17:21 +0100488 head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket]));
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700489 }
490 return head;
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100491}
492
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700493static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
494{
495 struct hlist_node *head = ct_expect_get_first(seq);
496
497 if (head)
498 while (pos && (head = ct_expect_get_next(seq, head)))
499 pos--;
500 return pos ? NULL : head;
501}
502
503static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800504 __acquires(RCU)
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700505{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800506 rcu_read_lock();
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700507 return ct_expect_get_idx(seq, *pos);
508}
509
510static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
511{
512 (*pos)++;
513 return ct_expect_get_next(seq, v);
514}
515
516static void exp_seq_stop(struct seq_file *seq, void *v)
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800517 __releases(RCU)
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100518{
Patrick McHardy7d0742d2008-01-31 04:38:19 -0800519 rcu_read_unlock();
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100520}
521
522static int exp_seq_show(struct seq_file *s, void *v)
523{
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700524 struct nf_conntrack_expect *expect;
Patrick McHardyb87921b2010-02-11 12:22:48 +0100525 struct nf_conntrack_helper *helper;
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700526 struct hlist_node *n = v;
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700527 char *delim = "";
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700528
529 expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100530
531 if (expect->timeout.function)
532 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
533 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
534 else
535 seq_printf(s, "- ");
536 seq_printf(s, "l3proto = %u proto=%u ",
537 expect->tuple.src.l3num,
538 expect->tuple.dst.protonum);
539 print_tuple(s, &expect->tuple,
540 __nf_ct_l3proto_find(expect->tuple.src.l3num),
Martin Josefsson605dcad2006-11-29 02:35:06 +0100541 __nf_ct_l4proto_find(expect->tuple.src.l3num,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100542 expect->tuple.dst.protonum));
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700543
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700544 if (expect->flags & NF_CT_EXPECT_PERMANENT) {
545 seq_printf(s, "PERMANENT");
546 delim = ",";
547 }
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200548 if (expect->flags & NF_CT_EXPECT_INACTIVE) {
Patrick McHardy359b9ab2008-03-25 20:08:37 -0700549 seq_printf(s, "%sINACTIVE", delim);
Pablo Neira Ayusobc01befd2010-09-28 21:06:34 +0200550 delim = ",";
551 }
552 if (expect->flags & NF_CT_EXPECT_USERSPACE)
553 seq_printf(s, "%sUSERSPACE", delim);
Patrick McHardy4bb119e2008-03-25 20:08:17 -0700554
Patrick McHardyb87921b2010-02-11 12:22:48 +0100555 helper = rcu_dereference(nfct_help(expect->master)->helper);
556 if (helper) {
557 seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
558 if (helper->expect_policy[expect->class].name)
559 seq_printf(s, "/%s",
560 helper->expect_policy[expect->class].name);
561 }
562
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100563 return seq_putc(s, '\n');
564}
565
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700566static const struct seq_operations exp_seq_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100567 .start = exp_seq_start,
568 .next = exp_seq_next,
569 .stop = exp_seq_stop,
570 .show = exp_seq_show
571};
572
573static int exp_open(struct inode *inode, struct file *file)
574{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200575 return seq_open_net(inode, file, &exp_seq_ops,
Pavel Emelyanove2da5912007-10-10 02:29:58 -0700576 sizeof(struct ct_expect_iter_state));
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100577}
578
Patrick McHardy5d08ad42007-07-07 22:34:07 -0700579static const struct file_operations exp_file_ops = {
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100580 .owner = THIS_MODULE,
581 .open = exp_open,
582 .read = seq_read,
583 .llseek = seq_lseek,
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200584 .release = seq_release_net,
Martin Josefsson77ab9cf2006-11-29 02:34:58 +0100585};
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200586#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700587
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200588static int exp_proc_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700589{
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200590#ifdef CONFIG_NF_CONNTRACK_PROCFS
Patrick McHardye9c1b082007-07-07 22:32:53 -0700591 struct proc_dir_entry *proc;
592
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200593 proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700594 if (!proc)
595 return -ENOMEM;
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200596#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700597 return 0;
598}
599
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200600static void exp_proc_remove(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700601{
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200602#ifdef CONFIG_NF_CONNTRACK_PROCFS
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200603 proc_net_remove(net, "nf_conntrack_expect");
Jan Engelhardt54b07dc2011-04-21 09:32:45 +0200604#endif /* CONFIG_NF_CONNTRACK_PROCFS */
Patrick McHardye9c1b082007-07-07 22:32:53 -0700605}
606
Alexey Dobriyan13ccdfc2010-02-08 11:17:22 -0800607module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
Patrick McHardya71c0852007-07-07 22:33:47 -0700608
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200609int nf_conntrack_expect_init(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700610{
Patrick McHardya71c0852007-07-07 22:33:47 -0700611 int err = -ENOMEM;
612
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200613 if (net_eq(net, &init_net)) {
614 if (!nf_ct_expect_hsize) {
Patrick McHardyd696c7b2010-02-08 11:18:07 -0800615 nf_ct_expect_hsize = net->ct.htable_size / 256;
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200616 if (!nf_ct_expect_hsize)
617 nf_ct_expect_hsize = 1;
618 }
619 nf_ct_expect_max = nf_ct_expect_hsize * 4;
Patrick McHardya71c0852007-07-07 22:33:47 -0700620 }
621
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200622 net->ct.expect_count = 0;
Patrick McHardyd862a662011-01-14 15:45:56 +0100623 net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200624 if (net->ct.expect_hash == NULL)
Patrick McHardya71c0852007-07-07 22:33:47 -0700625 goto err1;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700626
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200627 if (net_eq(net, &init_net)) {
628 nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
Patrick McHardye9c1b082007-07-07 22:32:53 -0700629 sizeof(struct nf_conntrack_expect),
Paul Mundt20c2df82007-07-20 10:11:58 +0900630 0, 0, NULL);
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200631 if (!nf_ct_expect_cachep)
632 goto err2;
633 }
Patrick McHardye9c1b082007-07-07 22:32:53 -0700634
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200635 err = exp_proc_init(net);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700636 if (err < 0)
Patrick McHardya71c0852007-07-07 22:33:47 -0700637 goto err3;
Patrick McHardye9c1b082007-07-07 22:32:53 -0700638
639 return 0;
640
Patrick McHardya71c0852007-07-07 22:33:47 -0700641err3:
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200642 if (net_eq(net, &init_net))
643 kmem_cache_destroy(nf_ct_expect_cachep);
Alexey Dobriyan12293bf2008-05-29 03:19:37 -0700644err2:
Patrick McHardyd862a662011-01-14 15:45:56 +0100645 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardya71c0852007-07-07 22:33:47 -0700646err1:
Patrick McHardye9c1b082007-07-07 22:32:53 -0700647 return err;
648}
649
Alexey Dobriyan9b03f382008-10-08 11:35:03 +0200650void nf_conntrack_expect_fini(struct net *net)
Patrick McHardye9c1b082007-07-07 22:32:53 -0700651{
Alexey Dobriyandc5129f2008-10-08 11:35:06 +0200652 exp_proc_remove(net);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200653 if (net_eq(net, &init_net)) {
654 rcu_barrier(); /* Wait for call_rcu() before destroy */
Alexey Dobriyan08f65472008-10-08 11:35:09 +0200655 kmem_cache_destroy(nf_ct_expect_cachep);
Jesper Dangaard Brouer308ff822009-06-25 16:32:52 +0200656 }
Patrick McHardyd862a662011-01-14 15:45:56 +0100657 nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
Patrick McHardye9c1b082007-07-07 22:32:53 -0700658}