[NETFILTER]: Mark old IPv4-only connection tracking scheduled for removal
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / netfilter / nf_conntrack_expect.c
CommitLineData
77ab9cff
MJ
1/* 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>
22
23#include <net/netfilter/nf_conntrack.h>
24#include <net/netfilter/nf_conntrack_core.h>
25#include <net/netfilter/nf_conntrack_expect.h>
26#include <net/netfilter/nf_conntrack_helper.h>
27#include <net/netfilter/nf_conntrack_tuple.h>
28
29LIST_HEAD(nf_conntrack_expect_list);
30kmem_cache_t *nf_conntrack_expect_cachep __read_mostly;
77ab9cff
MJ
31static unsigned int nf_conntrack_expect_next_id;
32
33/* nf_conntrack_expect helper functions */
34void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
35{
36 struct nf_conn_help *master_help = nfct_help(exp->master);
37
38 NF_CT_ASSERT(master_help);
39 NF_CT_ASSERT(!timer_pending(&exp->timeout));
40
41 list_del(&exp->list);
42 NF_CT_STAT_INC(expect_delete);
43 master_help->expecting--;
44 nf_conntrack_expect_put(exp);
45}
46
47static void expectation_timed_out(unsigned long ul_expect)
48{
49 struct nf_conntrack_expect *exp = (void *)ul_expect;
50
51 write_lock_bh(&nf_conntrack_lock);
52 nf_ct_unlink_expect(exp);
53 write_unlock_bh(&nf_conntrack_lock);
54 nf_conntrack_expect_put(exp);
55}
56
57struct nf_conntrack_expect *
58__nf_conntrack_expect_find(const struct nf_conntrack_tuple *tuple)
59{
60 struct nf_conntrack_expect *i;
61
62 list_for_each_entry(i, &nf_conntrack_expect_list, list) {
63 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask))
64 return i;
65 }
66 return NULL;
67}
68
69/* Just find a expectation corresponding to a tuple. */
70struct nf_conntrack_expect *
468ec44b 71nf_conntrack_expect_find_get(const struct nf_conntrack_tuple *tuple)
77ab9cff
MJ
72{
73 struct nf_conntrack_expect *i;
74
75 read_lock_bh(&nf_conntrack_lock);
76 i = __nf_conntrack_expect_find(tuple);
77 if (i)
78 atomic_inc(&i->use);
79 read_unlock_bh(&nf_conntrack_lock);
80
81 return i;
82}
83
84/* If an expectation for this connection is found, it gets delete from
85 * global list then returned. */
86struct nf_conntrack_expect *
87find_expectation(const struct nf_conntrack_tuple *tuple)
88{
89 struct nf_conntrack_expect *i;
90
91 list_for_each_entry(i, &nf_conntrack_expect_list, list) {
92 /* If master is not in hash table yet (ie. packet hasn't left
93 this machine yet), how can other end know about expected?
94 Hence these are not the droids you are looking for (if
95 master ct never got confirmed, we'd hold a reference to it
96 and weird things would happen to future packets). */
97 if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)
98 && nf_ct_is_confirmed(i->master)) {
99 if (i->flags & NF_CT_EXPECT_PERMANENT) {
100 atomic_inc(&i->use);
101 return i;
102 } else if (del_timer(&i->timeout)) {
103 nf_ct_unlink_expect(i);
104 return i;
105 }
106 }
107 }
108 return NULL;
109}
110
111/* delete all expectations for this conntrack */
112void nf_ct_remove_expectations(struct nf_conn *ct)
113{
114 struct nf_conntrack_expect *i, *tmp;
115 struct nf_conn_help *help = nfct_help(ct);
116
117 /* Optimization: most connection never expect any others. */
118 if (!help || help->expecting == 0)
119 return;
120
121 list_for_each_entry_safe(i, tmp, &nf_conntrack_expect_list, list) {
122 if (i->master == ct && del_timer(&i->timeout)) {
123 nf_ct_unlink_expect(i);
124 nf_conntrack_expect_put(i);
125 }
126 }
127}
128
129/* Would two expected things clash? */
130static inline int expect_clash(const struct nf_conntrack_expect *a,
131 const struct nf_conntrack_expect *b)
132{
133 /* Part covered by intersection of masks must be unequal,
134 otherwise they clash */
135 struct nf_conntrack_tuple intersect_mask;
136 int count;
137
138 intersect_mask.src.l3num = a->mask.src.l3num & b->mask.src.l3num;
139 intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
140 intersect_mask.dst.u.all = a->mask.dst.u.all & b->mask.dst.u.all;
141 intersect_mask.dst.protonum = a->mask.dst.protonum
142 & b->mask.dst.protonum;
143
144 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
145 intersect_mask.src.u3.all[count] =
146 a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
147 }
148
149 for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
150 intersect_mask.dst.u3.all[count] =
151 a->mask.dst.u3.all[count] & b->mask.dst.u3.all[count];
152 }
153
154 return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
155}
156
157static inline int expect_matches(const struct nf_conntrack_expect *a,
158 const struct nf_conntrack_expect *b)
159{
160 return a->master == b->master
161 && nf_ct_tuple_equal(&a->tuple, &b->tuple)
162 && nf_ct_tuple_equal(&a->mask, &b->mask);
163}
164
165/* Generally a bad idea to call this: could have matched already. */
166void nf_conntrack_unexpect_related(struct nf_conntrack_expect *exp)
167{
168 struct nf_conntrack_expect *i;
169
170 write_lock_bh(&nf_conntrack_lock);
171 /* choose the the oldest expectation to evict */
172 list_for_each_entry_reverse(i, &nf_conntrack_expect_list, list) {
173 if (expect_matches(i, exp) && del_timer(&i->timeout)) {
174 nf_ct_unlink_expect(i);
175 write_unlock_bh(&nf_conntrack_lock);
176 nf_conntrack_expect_put(i);
177 return;
178 }
179 }
180 write_unlock_bh(&nf_conntrack_lock);
181}
182
183/* We don't increase the master conntrack refcount for non-fulfilled
184 * conntracks. During the conntrack destruction, the expectations are
185 * always killed before the conntrack itself */
186struct nf_conntrack_expect *nf_conntrack_expect_alloc(struct nf_conn *me)
187{
188 struct nf_conntrack_expect *new;
189
190 new = kmem_cache_alloc(nf_conntrack_expect_cachep, GFP_ATOMIC);
191 if (!new)
192 return NULL;
193
194 new->master = me;
195 atomic_set(&new->use, 1);
196 return new;
197}
198
d6a9b650
PM
199void nf_conntrack_expect_init(struct nf_conntrack_expect *exp, int family,
200 union nf_conntrack_address *saddr,
201 union nf_conntrack_address *daddr,
202 u_int8_t proto, __be16 *src, __be16 *dst)
203{
204 int len;
205
206 if (family == AF_INET)
207 len = 4;
208 else
209 len = 16;
210
211 exp->flags = 0;
212 exp->expectfn = NULL;
213 exp->helper = NULL;
214 exp->tuple.src.l3num = family;
215 exp->tuple.dst.protonum = proto;
216 exp->mask.src.l3num = 0xFFFF;
217 exp->mask.dst.protonum = 0xFF;
218
219 if (saddr) {
220 memcpy(&exp->tuple.src.u3, saddr, len);
221 if (sizeof(exp->tuple.src.u3) > len)
222 /* address needs to be cleared for nf_ct_tuple_equal */
223 memset((void *)&exp->tuple.src.u3 + len, 0x00,
224 sizeof(exp->tuple.src.u3) - len);
225 memset(&exp->mask.src.u3, 0xFF, len);
226 if (sizeof(exp->mask.src.u3) > len)
227 memset((void *)&exp->mask.src.u3 + len, 0x00,
228 sizeof(exp->mask.src.u3) - len);
229 } else {
230 memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
231 memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
232 }
233
234 if (daddr) {
235 memcpy(&exp->tuple.dst.u3, daddr, len);
236 if (sizeof(exp->tuple.dst.u3) > len)
237 /* address needs to be cleared for nf_ct_tuple_equal */
238 memset((void *)&exp->tuple.dst.u3 + len, 0x00,
239 sizeof(exp->tuple.dst.u3) - len);
240 memset(&exp->mask.dst.u3, 0xFF, len);
241 if (sizeof(exp->mask.dst.u3) > len)
242 memset((void *)&exp->mask.dst.u3 + len, 0x00,
243 sizeof(exp->mask.dst.u3) - len);
244 } else {
245 memset(&exp->tuple.dst.u3, 0x00, sizeof(exp->tuple.dst.u3));
246 memset(&exp->mask.dst.u3, 0x00, sizeof(exp->mask.dst.u3));
247 }
248
249 if (src) {
250 exp->tuple.src.u.all = (__force u16)*src;
251 exp->mask.src.u.all = 0xFFFF;
252 } else {
253 exp->tuple.src.u.all = 0;
254 exp->mask.src.u.all = 0;
255 }
256
257 if (dst) {
258 exp->tuple.dst.u.all = (__force u16)*dst;
259 exp->mask.dst.u.all = 0xFFFF;
260 } else {
261 exp->tuple.dst.u.all = 0;
262 exp->mask.dst.u.all = 0;
263 }
264}
265EXPORT_SYMBOL_GPL(nf_conntrack_expect_init);
266
77ab9cff
MJ
267void nf_conntrack_expect_put(struct nf_conntrack_expect *exp)
268{
269 if (atomic_dec_and_test(&exp->use))
270 kmem_cache_free(nf_conntrack_expect_cachep, exp);
271}
272
273static void nf_conntrack_expect_insert(struct nf_conntrack_expect *exp)
274{
275 struct nf_conn_help *master_help = nfct_help(exp->master);
276
277 atomic_inc(&exp->use);
278 master_help->expecting++;
279 list_add(&exp->list, &nf_conntrack_expect_list);
280
281 init_timer(&exp->timeout);
282 exp->timeout.data = (unsigned long)exp;
283 exp->timeout.function = expectation_timed_out;
284 exp->timeout.expires = jiffies + master_help->helper->timeout * HZ;
285 add_timer(&exp->timeout);
286
287 exp->id = ++nf_conntrack_expect_next_id;
288 atomic_inc(&exp->use);
289 NF_CT_STAT_INC(expect_create);
290}
291
292/* Race with expectations being used means we could have none to find; OK. */
293static void evict_oldest_expect(struct nf_conn *master)
294{
295 struct nf_conntrack_expect *i;
296
297 list_for_each_entry_reverse(i, &nf_conntrack_expect_list, list) {
298 if (i->master == master) {
299 if (del_timer(&i->timeout)) {
300 nf_ct_unlink_expect(i);
301 nf_conntrack_expect_put(i);
302 }
303 break;
304 }
305 }
306}
307
308static inline int refresh_timer(struct nf_conntrack_expect *i)
309{
310 struct nf_conn_help *master_help = nfct_help(i->master);
311
312 if (!del_timer(&i->timeout))
313 return 0;
314
315 i->timeout.expires = jiffies + master_help->helper->timeout*HZ;
316 add_timer(&i->timeout);
317 return 1;
318}
319
320int nf_conntrack_expect_related(struct nf_conntrack_expect *expect)
321{
322 struct nf_conntrack_expect *i;
323 struct nf_conn *master = expect->master;
324 struct nf_conn_help *master_help = nfct_help(master);
325 int ret;
326
327 NF_CT_ASSERT(master_help);
328
329 write_lock_bh(&nf_conntrack_lock);
330 list_for_each_entry(i, &nf_conntrack_expect_list, list) {
331 if (expect_matches(i, expect)) {
332 /* Refresh timer: if it's dying, ignore.. */
333 if (refresh_timer(i)) {
334 ret = 0;
335 goto out;
336 }
337 } else if (expect_clash(i, expect)) {
338 ret = -EBUSY;
339 goto out;
340 }
341 }
342 /* Will be over limit? */
343 if (master_help->helper->max_expected &&
344 master_help->expecting >= master_help->helper->max_expected)
345 evict_oldest_expect(master);
346
347 nf_conntrack_expect_insert(expect);
348 nf_conntrack_expect_event(IPEXP_NEW, expect);
349 ret = 0;
350out:
351 write_unlock_bh(&nf_conntrack_lock);
352 return ret;
353}
354
355#ifdef CONFIG_PROC_FS
356static void *exp_seq_start(struct seq_file *s, loff_t *pos)
357{
358 struct list_head *e = &nf_conntrack_expect_list;
359 loff_t i;
360
361 /* strange seq_file api calls stop even if we fail,
362 * thus we need to grab lock since stop unlocks */
363 read_lock_bh(&nf_conntrack_lock);
364
365 if (list_empty(e))
366 return NULL;
367
368 for (i = 0; i <= *pos; i++) {
369 e = e->next;
370 if (e == &nf_conntrack_expect_list)
371 return NULL;
372 }
373 return e;
374}
375
376static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
377{
378 struct list_head *e = v;
379
380 ++*pos;
381 e = e->next;
382
383 if (e == &nf_conntrack_expect_list)
384 return NULL;
385
386 return e;
387}
388
389static void exp_seq_stop(struct seq_file *s, void *v)
390{
391 read_unlock_bh(&nf_conntrack_lock);
392}
393
394static int exp_seq_show(struct seq_file *s, void *v)
395{
396 struct nf_conntrack_expect *expect = v;
397
398 if (expect->timeout.function)
399 seq_printf(s, "%ld ", timer_pending(&expect->timeout)
400 ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
401 else
402 seq_printf(s, "- ");
403 seq_printf(s, "l3proto = %u proto=%u ",
404 expect->tuple.src.l3num,
405 expect->tuple.dst.protonum);
406 print_tuple(s, &expect->tuple,
407 __nf_ct_l3proto_find(expect->tuple.src.l3num),
605dcad6 408 __nf_ct_l4proto_find(expect->tuple.src.l3num,
77ab9cff
MJ
409 expect->tuple.dst.protonum));
410 return seq_putc(s, '\n');
411}
412
413static struct seq_operations exp_seq_ops = {
414 .start = exp_seq_start,
415 .next = exp_seq_next,
416 .stop = exp_seq_stop,
417 .show = exp_seq_show
418};
419
420static int exp_open(struct inode *inode, struct file *file)
421{
422 return seq_open(file, &exp_seq_ops);
423}
424
425struct file_operations exp_file_ops = {
426 .owner = THIS_MODULE,
427 .open = exp_open,
428 .read = seq_read,
429 .llseek = seq_lseek,
430 .release = seq_release
431};
432#endif /* CONFIG_PROC_FS */