diff options
author | Patrick McHardy <kaber@trash.net> | 2006-11-28 20:35:22 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-03 00:31:20 -0500 |
commit | e4bd8bce3e8b53e2c0a0d5c9afbc29731e517f8d (patch) | |
tree | 8a7a2e8c5130e002a6324703c31e67770af05993 /net/ipv4 | |
parent | a999e6837603e4b5a164333c93918a1292f074c8 (diff) |
[NETFILTER]: nf_conntrack: /proc compatibility with old connection tracking
This patch adds /proc/net/ip_conntrack, /proc/net/ip_conntrack_expect and
/proc/net/stat/ip_conntrack files to keep old programs using them working.
The /proc/net/ip_conntrack and /proc/net/ip_conntrack_expect files show only
IPv4 entries, the /proc/net/stat/ip_conntrack shows global statistics.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/Makefile | 5 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 13 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | 412 |
3 files changed, 429 insertions, 1 deletions
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 09aaed1a8063..4ce20ebc4d6c 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -89,6 +89,11 @@ obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o | |||
89 | 89 | ||
90 | # objects for l3 independent conntrack | 90 | # objects for l3 independent conntrack |
91 | nf_conntrack_ipv4-objs := nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o | 91 | nf_conntrack_ipv4-objs := nf_conntrack_l3proto_ipv4.o nf_conntrack_proto_icmp.o |
92 | ifeq ($(CONFIG_NF_CONNTRACK_PROC_COMPAT),y) | ||
93 | ifeq ($(CONFIG_PROC_FS),y) | ||
94 | nf_conntrack_ipv4-objs += nf_conntrack_l3proto_ipv4_compat.o | ||
95 | endif | ||
96 | endif | ||
92 | 97 | ||
93 | # l3 independent conntrack | 98 | # l3 independent conntrack |
94 | obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o | 99 | obj-$(CONFIG_NF_CONNTRACK_IPV4) += nf_conntrack_ipv4.o |
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index bcec6822f2ee..f24e872d4b90 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | |||
@@ -492,8 +492,16 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) | |||
492 | printk("nf_conntrack_ipv4: can't register hooks.\n"); | 492 | printk("nf_conntrack_ipv4: can't register hooks.\n"); |
493 | goto cleanup_ipv4; | 493 | goto cleanup_ipv4; |
494 | } | 494 | } |
495 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) | ||
496 | ret = nf_conntrack_ipv4_compat_init(); | ||
497 | if (ret < 0) | ||
498 | goto cleanup_hooks; | ||
499 | #endif | ||
495 | return ret; | 500 | return ret; |
496 | 501 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) | |
502 | cleanup_hooks: | ||
503 | nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); | ||
504 | #endif | ||
497 | cleanup_ipv4: | 505 | cleanup_ipv4: |
498 | nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); | 506 | nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); |
499 | cleanup_icmp: | 507 | cleanup_icmp: |
@@ -510,6 +518,9 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) | |||
510 | static void __exit nf_conntrack_l3proto_ipv4_fini(void) | 518 | static void __exit nf_conntrack_l3proto_ipv4_fini(void) |
511 | { | 519 | { |
512 | synchronize_net(); | 520 | synchronize_net(); |
521 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) | ||
522 | nf_conntrack_ipv4_compat_fini(); | ||
523 | #endif | ||
513 | nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); | 524 | nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); |
514 | nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); | 525 | nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); |
515 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp); | 526 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp); |
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c new file mode 100644 index 000000000000..3b31bc649608 --- /dev/null +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | |||
@@ -0,0 +1,412 @@ | |||
1 | /* ip_conntrack proc compat - based on ip_conntrack_standalone.c | ||
2 | * | ||
3 | * (C) 1999-2001 Paul `Rusty' Russell | ||
4 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/proc_fs.h> | ||
12 | #include <linux/seq_file.h> | ||
13 | #include <linux/percpu.h> | ||
14 | |||
15 | #include <linux/netfilter.h> | ||
16 | #include <net/netfilter/nf_conntrack_core.h> | ||
17 | #include <net/netfilter/nf_conntrack_l3proto.h> | ||
18 | #include <net/netfilter/nf_conntrack_l4proto.h> | ||
19 | #include <net/netfilter/nf_conntrack_expect.h> | ||
20 | |||
21 | #if 0 | ||
22 | #define DEBUGP printk | ||
23 | #else | ||
24 | #define DEBUGP(format, args...) | ||
25 | #endif | ||
26 | |||
27 | #ifdef CONFIG_NF_CT_ACCT | ||
28 | static unsigned int | ||
29 | seq_print_counters(struct seq_file *s, | ||
30 | const struct ip_conntrack_counter *counter) | ||
31 | { | ||
32 | return seq_printf(s, "packets=%llu bytes=%llu ", | ||
33 | (unsigned long long)counter->packets, | ||
34 | (unsigned long long)counter->bytes); | ||
35 | } | ||
36 | #else | ||
37 | #define seq_print_counters(x, y) 0 | ||
38 | #endif | ||
39 | |||
40 | struct ct_iter_state { | ||
41 | unsigned int bucket; | ||
42 | }; | ||
43 | |||
44 | static struct list_head *ct_get_first(struct seq_file *seq) | ||
45 | { | ||
46 | struct ct_iter_state *st = seq->private; | ||
47 | |||
48 | for (st->bucket = 0; | ||
49 | st->bucket < nf_conntrack_htable_size; | ||
50 | st->bucket++) { | ||
51 | if (!list_empty(&nf_conntrack_hash[st->bucket])) | ||
52 | return nf_conntrack_hash[st->bucket].next; | ||
53 | } | ||
54 | return NULL; | ||
55 | } | ||
56 | |||
57 | static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head) | ||
58 | { | ||
59 | struct ct_iter_state *st = seq->private; | ||
60 | |||
61 | head = head->next; | ||
62 | while (head == &nf_conntrack_hash[st->bucket]) { | ||
63 | if (++st->bucket >= nf_conntrack_htable_size) | ||
64 | return NULL; | ||
65 | head = nf_conntrack_hash[st->bucket].next; | ||
66 | } | ||
67 | return head; | ||
68 | } | ||
69 | |||
70 | static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos) | ||
71 | { | ||
72 | struct list_head *head = ct_get_first(seq); | ||
73 | |||
74 | if (head) | ||
75 | while (pos && (head = ct_get_next(seq, head))) | ||
76 | pos--; | ||
77 | return pos ? NULL : head; | ||
78 | } | ||
79 | |||
80 | static void *ct_seq_start(struct seq_file *seq, loff_t *pos) | ||
81 | { | ||
82 | read_lock_bh(&nf_conntrack_lock); | ||
83 | return ct_get_idx(seq, *pos); | ||
84 | } | ||
85 | |||
86 | static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) | ||
87 | { | ||
88 | (*pos)++; | ||
89 | return ct_get_next(s, v); | ||
90 | } | ||
91 | |||
92 | static void ct_seq_stop(struct seq_file *s, void *v) | ||
93 | { | ||
94 | read_unlock_bh(&nf_conntrack_lock); | ||
95 | } | ||
96 | |||
97 | static int ct_seq_show(struct seq_file *s, void *v) | ||
98 | { | ||
99 | const struct nf_conntrack_tuple_hash *hash = v; | ||
100 | const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash); | ||
101 | struct nf_conntrack_l3proto *l3proto; | ||
102 | struct nf_conntrack_l4proto *l4proto; | ||
103 | |||
104 | NF_CT_ASSERT(ct); | ||
105 | |||
106 | /* we only want to print DIR_ORIGINAL */ | ||
107 | if (NF_CT_DIRECTION(hash)) | ||
108 | return 0; | ||
109 | if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num != AF_INET) | ||
110 | return 0; | ||
111 | |||
112 | l3proto = __nf_ct_l3proto_find(ct->tuplehash[IP_CT_DIR_ORIGINAL] | ||
113 | .tuple.src.l3num); | ||
114 | NF_CT_ASSERT(l3proto); | ||
115 | l4proto = __nf_ct_l4proto_find(ct->tuplehash[IP_CT_DIR_ORIGINAL] | ||
116 | .tuple.src.l3num, | ||
117 | ct->tuplehash[IP_CT_DIR_ORIGINAL] | ||
118 | .tuple.dst.protonum); | ||
119 | NF_CT_ASSERT(l4proto); | ||
120 | |||
121 | if (seq_printf(s, "%-8s %u %ld ", | ||
122 | l4proto->name, | ||
123 | ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum, | ||
124 | timer_pending(&ct->timeout) | ||
125 | ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0) | ||
126 | return -ENOSPC; | ||
127 | |||
128 | if (l3proto->print_conntrack(s, ct)) | ||
129 | return -ENOSPC; | ||
130 | |||
131 | if (l4proto->print_conntrack(s, ct)) | ||
132 | return -ENOSPC; | ||
133 | |||
134 | if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, | ||
135 | l3proto, l4proto)) | ||
136 | return -ENOSPC; | ||
137 | |||
138 | if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) | ||
139 | return -ENOSPC; | ||
140 | |||
141 | if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) | ||
142 | if (seq_printf(s, "[UNREPLIED] ")) | ||
143 | return -ENOSPC; | ||
144 | |||
145 | if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, | ||
146 | l3proto, l4proto)) | ||
147 | return -ENOSPC; | ||
148 | |||
149 | if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) | ||
150 | return -ENOSPC; | ||
151 | |||
152 | if (test_bit(IPS_ASSURED_BIT, &ct->status)) | ||
153 | if (seq_printf(s, "[ASSURED] ")) | ||
154 | return -ENOSPC; | ||
155 | |||
156 | #ifdef CONFIG_NF_CONNTRACK_MARK | ||
157 | if (seq_printf(s, "mark=%u ", ct->mark)) | ||
158 | return -ENOSPC; | ||
159 | #endif | ||
160 | |||
161 | #ifdef CONFIG_NF_CONNTRACK_SECMARK | ||
162 | if (seq_printf(s, "secmark=%u ", ct->secmark)) | ||
163 | return -ENOSPC; | ||
164 | #endif | ||
165 | |||
166 | if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) | ||
167 | return -ENOSPC; | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static struct seq_operations ct_seq_ops = { | ||
173 | .start = ct_seq_start, | ||
174 | .next = ct_seq_next, | ||
175 | .stop = ct_seq_stop, | ||
176 | .show = ct_seq_show | ||
177 | }; | ||
178 | |||
179 | static int ct_open(struct inode *inode, struct file *file) | ||
180 | { | ||
181 | struct seq_file *seq; | ||
182 | struct ct_iter_state *st; | ||
183 | int ret; | ||
184 | |||
185 | st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL); | ||
186 | if (st == NULL) | ||
187 | return -ENOMEM; | ||
188 | ret = seq_open(file, &ct_seq_ops); | ||
189 | if (ret) | ||
190 | goto out_free; | ||
191 | seq = file->private_data; | ||
192 | seq->private = st; | ||
193 | memset(st, 0, sizeof(struct ct_iter_state)); | ||
194 | return ret; | ||
195 | out_free: | ||
196 | kfree(st); | ||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | static struct file_operations ct_file_ops = { | ||
201 | .owner = THIS_MODULE, | ||
202 | .open = ct_open, | ||
203 | .read = seq_read, | ||
204 | .llseek = seq_lseek, | ||
205 | .release = seq_release_private, | ||
206 | }; | ||
207 | |||
208 | /* expects */ | ||
209 | static void *exp_seq_start(struct seq_file *s, loff_t *pos) | ||
210 | { | ||
211 | struct list_head *e = &nf_conntrack_expect_list; | ||
212 | loff_t i; | ||
213 | |||
214 | /* strange seq_file api calls stop even if we fail, | ||
215 | * thus we need to grab lock since stop unlocks */ | ||
216 | read_lock_bh(&nf_conntrack_lock); | ||
217 | |||
218 | if (list_empty(e)) | ||
219 | return NULL; | ||
220 | |||
221 | for (i = 0; i <= *pos; i++) { | ||
222 | e = e->next; | ||
223 | if (e == &nf_conntrack_expect_list) | ||
224 | return NULL; | ||
225 | } | ||
226 | return e; | ||
227 | } | ||
228 | |||
229 | static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos) | ||
230 | { | ||
231 | struct list_head *e = v; | ||
232 | |||
233 | ++*pos; | ||
234 | e = e->next; | ||
235 | |||
236 | if (e == &nf_conntrack_expect_list) | ||
237 | return NULL; | ||
238 | |||
239 | return e; | ||
240 | } | ||
241 | |||
242 | static void exp_seq_stop(struct seq_file *s, void *v) | ||
243 | { | ||
244 | read_unlock_bh(&nf_conntrack_lock); | ||
245 | } | ||
246 | |||
247 | static int exp_seq_show(struct seq_file *s, void *v) | ||
248 | { | ||
249 | struct nf_conntrack_expect *exp = v; | ||
250 | |||
251 | if (exp->tuple.src.l3num != AF_INET) | ||
252 | return 0; | ||
253 | |||
254 | if (exp->timeout.function) | ||
255 | seq_printf(s, "%ld ", timer_pending(&exp->timeout) | ||
256 | ? (long)(exp->timeout.expires - jiffies)/HZ : 0); | ||
257 | else | ||
258 | seq_printf(s, "- "); | ||
259 | |||
260 | seq_printf(s, "proto=%u ", exp->tuple.dst.protonum); | ||
261 | |||
262 | print_tuple(s, &exp->tuple, | ||
263 | __nf_ct_l3proto_find(exp->tuple.src.l3num), | ||
264 | __nf_ct_l4proto_find(exp->tuple.src.l3num, | ||
265 | exp->tuple.dst.protonum)); | ||
266 | return seq_putc(s, '\n'); | ||
267 | } | ||
268 | |||
269 | static struct seq_operations exp_seq_ops = { | ||
270 | .start = exp_seq_start, | ||
271 | .next = exp_seq_next, | ||
272 | .stop = exp_seq_stop, | ||
273 | .show = exp_seq_show | ||
274 | }; | ||
275 | |||
276 | static int exp_open(struct inode *inode, struct file *file) | ||
277 | { | ||
278 | return seq_open(file, &exp_seq_ops); | ||
279 | } | ||
280 | |||
281 | static struct file_operations ip_exp_file_ops = { | ||
282 | .owner = THIS_MODULE, | ||
283 | .open = exp_open, | ||
284 | .read = seq_read, | ||
285 | .llseek = seq_lseek, | ||
286 | .release = seq_release | ||
287 | }; | ||
288 | |||
289 | static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) | ||
290 | { | ||
291 | int cpu; | ||
292 | |||
293 | if (*pos == 0) | ||
294 | return SEQ_START_TOKEN; | ||
295 | |||
296 | for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { | ||
297 | if (!cpu_possible(cpu)) | ||
298 | continue; | ||
299 | *pos = cpu+1; | ||
300 | return &per_cpu(nf_conntrack_stat, cpu); | ||
301 | } | ||
302 | |||
303 | return NULL; | ||
304 | } | ||
305 | |||
306 | static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
307 | { | ||
308 | int cpu; | ||
309 | |||
310 | for (cpu = *pos; cpu < NR_CPUS; ++cpu) { | ||
311 | if (!cpu_possible(cpu)) | ||
312 | continue; | ||
313 | *pos = cpu+1; | ||
314 | return &per_cpu(nf_conntrack_stat, cpu); | ||
315 | } | ||
316 | |||
317 | return NULL; | ||
318 | } | ||
319 | |||
320 | static void ct_cpu_seq_stop(struct seq_file *seq, void *v) | ||
321 | { | ||
322 | } | ||
323 | |||
324 | static int ct_cpu_seq_show(struct seq_file *seq, void *v) | ||
325 | { | ||
326 | unsigned int nr_conntracks = atomic_read(&nf_conntrack_count); | ||
327 | struct ip_conntrack_stat *st = v; | ||
328 | |||
329 | if (v == SEQ_START_TOKEN) { | ||
330 | seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n"); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " | ||
335 | "%08x %08x %08x %08x %08x %08x %08x %08x \n", | ||
336 | nr_conntracks, | ||
337 | st->searched, | ||
338 | st->found, | ||
339 | st->new, | ||
340 | st->invalid, | ||
341 | st->ignore, | ||
342 | st->delete, | ||
343 | st->delete_list, | ||
344 | st->insert, | ||
345 | st->insert_failed, | ||
346 | st->drop, | ||
347 | st->early_drop, | ||
348 | st->error, | ||
349 | |||
350 | st->expect_new, | ||
351 | st->expect_create, | ||
352 | st->expect_delete | ||
353 | ); | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static struct seq_operations ct_cpu_seq_ops = { | ||
358 | .start = ct_cpu_seq_start, | ||
359 | .next = ct_cpu_seq_next, | ||
360 | .stop = ct_cpu_seq_stop, | ||
361 | .show = ct_cpu_seq_show, | ||
362 | }; | ||
363 | |||
364 | static int ct_cpu_seq_open(struct inode *inode, struct file *file) | ||
365 | { | ||
366 | return seq_open(file, &ct_cpu_seq_ops); | ||
367 | } | ||
368 | |||
369 | static struct file_operations ct_cpu_seq_fops = { | ||
370 | .owner = THIS_MODULE, | ||
371 | .open = ct_cpu_seq_open, | ||
372 | .read = seq_read, | ||
373 | .llseek = seq_lseek, | ||
374 | .release = seq_release_private, | ||
375 | }; | ||
376 | |||
377 | int __init nf_conntrack_ipv4_compat_init(void) | ||
378 | { | ||
379 | struct proc_dir_entry *proc, *proc_exp, *proc_stat; | ||
380 | |||
381 | proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops); | ||
382 | if (!proc) | ||
383 | goto err1; | ||
384 | |||
385 | proc_exp = proc_net_fops_create("ip_conntrack_expect", 0440, | ||
386 | &ip_exp_file_ops); | ||
387 | if (!proc_exp) | ||
388 | goto err2; | ||
389 | |||
390 | proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, proc_net_stat); | ||
391 | if (!proc_stat) | ||
392 | goto err3; | ||
393 | |||
394 | proc_stat->proc_fops = &ct_cpu_seq_fops; | ||
395 | proc_stat->owner = THIS_MODULE; | ||
396 | |||
397 | return 0; | ||
398 | |||
399 | err3: | ||
400 | proc_net_remove("ip_conntrack_expect"); | ||
401 | err2: | ||
402 | proc_net_remove("ip_conntrack"); | ||
403 | err1: | ||
404 | return -ENOMEM; | ||
405 | } | ||
406 | |||
407 | void __exit nf_conntrack_ipv4_compat_fini(void) | ||
408 | { | ||
409 | remove_proc_entry("ip_conntrack", proc_net_stat); | ||
410 | proc_net_remove("ip_conntrack_expect"); | ||
411 | proc_net_remove("ip_conntrack"); | ||
412 | } | ||