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