1 include/linux/netfilter_ipv4/ipt_account.h | 26
2 net/ipv4/netfilter/Kconfig | 46 +
3 net/ipv4/netfilter/Makefile | 1
4 net/ipv4/netfilter/ipt_account.c | 937 +++++++++++++++++++++++++++++
5 4 files changed, 1010 insertions(+)
7 diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ipt_account.h linux/include/linux/netfilter_ipv4/ipt_account.h
8 --- linux.org/include/linux/netfilter_ipv4/ipt_account.h 1970-01-01 01:00:00.000000000 +0100
9 +++ linux/include/linux/netfilter_ipv4/ipt_account.h 2006-05-04 11:23:02.000000000 +0200
12 + * accounting match (ipt_account.c)
13 + * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org)
17 + * This software is distributed under the terms of GNU GPL
20 +#ifndef _IPT_ACCOUNT_H_
21 +#define _IPT_ACCOUNT_H_
23 +#define IPT_ACCOUNT_NAME_LEN 64
25 +#define IPT_ACCOUNT_NAME "ipt_account"
26 +#define IPT_ACCOUNT_VERSION "0.1.7"
28 +struct t_ipt_account_info {
29 + char name[IPT_ACCOUNT_NAME_LEN];
37 diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig
38 --- linux.org/net/ipv4/netfilter/Kconfig 2006-05-02 23:38:44.000000000 +0200
39 +++ linux/net/ipv4/netfilter/Kconfig 2006-05-04 11:23:02.000000000 +0200
41 Allows altering the ARP packet payload: source and destination
42 hardware and network addresses.
44 +config IP_NF_MATCH_ACCOUNT
45 + tristate "account match support"
46 + depends on IP_NF_IPTABLES && PROC_FS
48 + This match is used for accounting traffic for all hosts in
49 + defined network/netmask.
52 + - long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics
53 + - one iptables rule for all hosts in network/netmask
54 + - loading/saving counters (by reading/writting to procfs entries)
58 + account traffic for/to 192.168.0.0/24 network into table mynetwork:
60 + # iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24
62 + account traffic for/to WWW serwer for 192.168.0.0/24 network into table
65 + # iptables -A INPUT -p tcp --dport 80
66 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
67 + # iptables -A OUTPUT -p tcp --sport 80
68 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
72 + # cat /proc/net/ipt_account/mynetwork
73 + # cat /proc/net/ipt_account/mywwwserver
77 + # echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver
80 + http://www.barbara.eu.org/~quaker/ipt_account/
82 +config IP_NF_MATCH_ACCOUNT_DEBUG
83 + bool "account debugging output"
84 + depends on IP_NF_MATCH_ACCOUNT
86 + Say Y to get lots of debugging output.
92 diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile
93 --- linux.org/net/ipv4/netfilter/Makefile 2006-05-02 23:38:44.000000000 +0200
94 +++ linux/net/ipv4/netfilter/Makefile 2006-05-04 11:23:02.000000000 +0200
96 +obj-$(CONFIG_IP_NF_MATCH_ACCOUNT) += ipt_account.o
97 diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ipt_account.c linux/net/ipv4/netfilter/ipt_account.c
98 --- linux.org/net/ipv4/netfilter/ipt_account.c 1970-01-01 01:00:00.000000000 +0100
99 +++ linux/net/ipv4/netfilter/ipt_account.c 2006-05-04 11:23:02.000000000 +0200
102 + * accounting match (ipt_account.c)
103 + * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org)
107 + * This software is distributed under the terms of GNU GPL
110 +#include <linux/module.h>
111 +#include <linux/skbuff.h>
112 +#include <linux/proc_fs.h>
113 +#include <linux/spinlock.h>
114 +#include <linux/vmalloc.h>
115 +#include <linux/interrupt.h>
116 +#include <linux/ctype.h>
118 +#include <linux/seq_file.h>
120 +#include <asm/uaccess.h>
122 +#include <linux/ip.h>
123 +#include <linux/tcp.h>
124 +#include <linux/udp.h>
126 +#include <linux/netfilter_ipv4/ip_tables.h>
127 +#include <linux/netfilter_ipv4/ipt_account.h>
129 +#if defined(CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG)
130 + #define dprintk(format,args...) printk(format,##args)
132 + #define dprintk(format,args...)
135 +static char version[] =
136 +KERN_INFO IPT_ACCOUNT_NAME " " IPT_ACCOUNT_VERSION " : Piotr Gasid³o <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n";
138 +/* rights for files created in /proc/net/ipt_account/ */
139 +static int permissions = 0644;
140 +/* maximal netmask for single table */
141 +static int netmask = 16;
143 +/* module information */
144 +MODULE_AUTHOR("Piotr Gasidlo <quaker@barbara.eu.org>");
145 +MODULE_DESCRIPTION("Traffic accounting modules");
146 +MODULE_LICENSE("GPL");
147 +module_param(permissions, int, 0400);
148 +module_param(netmask, int, 0400);
149 +MODULE_PARM_DESC(permissions,"permissions on /proc/net/ipt_account/* files");
150 +MODULE_PARM_DESC(netmask, "maximum *save* size of one list (netmask)");
152 +/* structure with statistics counters */
153 +struct t_ipt_account_stat {
154 + u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other; /* byte counters for all/tcp/udp/icmp/other traffic */
155 + u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other; /* packet counters for all/tcp/udp/icmp/other traffic */
158 +/* stucture with statistics counters, used when table is created with --ashort switch */
159 +struct t_ipt_account_stat_short {
160 + u_int64_t b_all; /* byte counters for all traffic */
161 + u_int64_t p_all; /* packet counters for all traffic */
164 +/* structure holding to/from statistics for single ip */
165 +struct t_ipt_account_ip_list {
166 + struct t_ipt_account_stat src;
167 + struct t_ipt_account_stat dest;
168 + unsigned long time; /* time when this record was last updated */
172 +/* same as above, for tables with --ashort switch */
173 +struct t_ipt_account_ip_list_short {
174 + struct t_ipt_account_stat_short src;
175 + struct t_ipt_account_stat_short dest;
176 + unsigned long time;
179 +/* structure describing single table */
180 +struct t_ipt_account_table {
181 + char name[IPT_ACCOUNT_NAME_LEN]; /* table name ( = filename in /proc/net/ipt_account/) */
182 + union { /* table with statistics for each ip in network/netmask */
183 + struct t_ipt_account_ip_list *l;
184 + struct t_ipt_account_ip_list_short *s;
186 + u_int32_t network; /* network/netmask covered by table*/
189 + int shortlisting:1; /* show only total columns of counters */
190 + int use_count; /* rules counter - counting number of rules using this table */
191 + struct t_ipt_account_table *next;
192 + spinlock_t ip_list_lock;
193 + struct proc_dir_entry *status_file;
196 +/* we must use spinlocks to avoid parallel modifications of table list */
197 +static spinlock_t account_lock = SPIN_LOCK_UNLOCKED;
199 +static struct proc_dir_entry *proc_net_ipt_account = NULL;
201 +/* root pointer holding list of the tables */
202 +static struct t_ipt_account_table *account_tables = NULL;
204 +/* convert ascii to ip */
205 +int atoip(char *buffer, u_int32_t *ip) {
207 + char *bufferptr = buffer;
213 + /* first must be a digit */
214 + if (!isdigit(*bufferptr))
217 + /* parse first 3 octets (III.III.III.iii) */
218 + for (part = 0, shift = 24; *bufferptr && shift; bufferptr++) {
219 + if (isdigit(*bufferptr)) {
220 + part = part * 10 + (*bufferptr - '0');
223 + if (*bufferptr == '.') {
226 + *ip |= part << shift;
234 + /* we expect more digts */
237 + /* parse last octet (iii.iii.iii.III) */
238 + for (; *bufferptr; bufferptr++) {
239 + if (isdigit(*bufferptr)) {
240 + part = part * 10 + (*bufferptr - '0');
249 + return (bufferptr - buffer);
252 +/* convert ascii to 64bit integer */
253 +int atoi64(char *buffer, u_int64_t *i) {
254 + char *bufferptr = buffer;
259 + while (isdigit(*bufferptr)) {
260 + *i = *i * 10 + (*bufferptr - '0');
263 + return (bufferptr - buffer);
266 +static void *account_seq_start(struct seq_file *s, loff_t *pos)
268 + struct proc_dir_entry *pde = s->private;
269 + struct t_ipt_account_table *table = pde->data;
271 + unsigned int *bucket;
273 + spin_lock_bh(&table->ip_list_lock);
274 + if (*pos >= table->count)
277 + bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
279 + return ERR_PTR(-ENOMEM);
284 +static void *account_seq_next(struct seq_file *s, void *v, loff_t *pos)
286 + struct proc_dir_entry *pde = s->private;
287 + struct t_ipt_account_table *table = pde->data;
289 + unsigned int *bucket = (unsigned int *)v;
291 + *pos = ++(*bucket);
292 + if (*pos >= table->count) {
299 +static void account_seq_stop(struct seq_file *s, void *v)
301 + struct proc_dir_entry *pde = s->private;
302 + struct t_ipt_account_table *table = pde->data;
303 + unsigned int *bucket = (unsigned int *)v;
305 + spin_unlock_bh(&table->ip_list_lock);
308 +static int account_seq_write(struct file *file, const char *ubuffer,
309 + size_t ulength, loff_t *pos)
311 + struct proc_dir_entry *pde = ((struct seq_file *)file->private_data)->private;
312 + struct t_ipt_account_table *table = pde->data;
313 + char buffer[1024], *bufferptr;
318 + struct t_ipt_account_ip_list l;
319 + struct t_ipt_account_ip_list_short s;
320 + u_int64_t *p, dummy;
323 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() entered.\n");
324 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() ulength = %zi.\n", ulength);
327 + if (ulength > 1024)
329 + if (copy_from_user(buffer, ubuffer, length))
331 + buffer[length - 1] = 0;
332 + bufferptr = buffer;
334 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() buffer = \'%s\' length = %i.\n", buffer, length);
336 + /* reset table counters */
337 + if (!memcmp(buffer, "reset", 5)) {
338 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got \"reset\".\n");
339 + if (!table->shortlisting) {
340 + spin_lock_bh(&table->ip_list_lock);
341 + memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count);
342 + spin_unlock_bh(&table->ip_list_lock);
344 + spin_lock_bh(&table->ip_list_lock);
345 + memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count);
346 + spin_unlock_bh(&table->ip_list_lock);
351 + if (!memcmp(buffer, "ip", 2)) {
352 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got \"ip\".\n");
354 + if (!isspace(*bufferptr)) {
355 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
356 + return length; /* expected space */
359 + if (*bufferptr != '=') {
360 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
361 + return length; /* expected equal */
364 + if (!isspace(*bufferptr)) {
365 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
366 + return length; /* expected space */
369 + if (!(len = atoip(bufferptr, &ip))) {
370 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected ip (%ti).\n", bufferptr - buffer);
371 + return length; /* expected ip */
374 + if ((ip & table->netmask) != table->network) {
375 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected ip [%u.%u.%u.%u] from table's network/netmask [%u.%u.%u.%u/%u.%u.%u.%u].\n", HIPQUAD(ip), HIPQUAD(table->network), HIPQUAD(table->netmask));
376 + return length; /* expected ip from table's network/netmask */
378 + if (!table->shortlisting) {
379 + memset(&l, 0, sizeof(struct t_ipt_account_ip_list));
380 + while(*bufferptr) {
381 + if (!isspace(*bufferptr)) {
382 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
383 + return length; /* expected space */
386 + if (!memcmp(bufferptr, "bytes_src", 9)) {
387 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_src (%ti).\n", bufferptr - buffer);
390 + } else if (!memcmp(bufferptr, "bytes_dest", 10)) {
391 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_dest (%ti).\n", bufferptr - buffer);
394 + } else if (!memcmp(bufferptr, "packets_src", 11)) {
395 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_src (%ti).\n", bufferptr - buffer);
398 + } else if (!memcmp(bufferptr, "packets_dest", 12)) {
399 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_dest (%ti).\n", bufferptr - buffer);
402 + } else if (!memcmp(bufferptr, "time", 4)) {
403 + /* time hack, ignore time tokens */
404 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got time (%ti).\n", bufferptr - buffer);
406 + if (!isspace(*bufferptr)) {
407 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
408 + return length; /* expected space */
411 + if (*bufferptr != '=') {
412 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
413 + return length; /* expected equal */
416 + if (!isspace(*bufferptr)) {
417 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
418 + return length; /* expected space */
421 + if (!(len = atoi64(bufferptr, &dummy))) {
422 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%ti).\n", bufferptr - buffer);
423 + return length; /* expected int64 */
425 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\n", dummy, bufferptr - buffer);
427 + continue; /* skip time token */
429 + return length; /* expected token */
430 + if (!isspace(*bufferptr)) {
431 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
432 + return length; /* expected space */
435 + if (*bufferptr != '=') {
436 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
437 + return length; /* expected equal */
440 + for (i = 0; i < 5; i++) {
441 + if (!isspace(*bufferptr)) {
442 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
443 + return length; /* expected space */
446 + if (!(len = atoi64(bufferptr, p))) {
447 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%ti).\n", bufferptr - buffer);
448 + return length; /* expected int64 */
450 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\n", *p, bufferptr - buffer);
455 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() updating row.\n");
456 + spin_lock_bh(&table->ip_list_lock);
457 + /* update counters, do not overwrite time field */
458 + memcpy(&table->ip_list.l[ip - table->network], &l, sizeof(struct t_ipt_account_ip_list) - sizeof(unsigned long));
459 + spin_unlock_bh(&table->ip_list_lock);
461 + memset(&s, 0, sizeof(struct t_ipt_account_ip_list_short));
462 + while(*bufferptr) {
463 + if (!isspace(*bufferptr)) {
464 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
465 + return length; /* expected space */
468 + if (!memcmp(bufferptr, "bytes_src", 9)) {
469 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_src (%ti).\n", bufferptr - buffer);
472 + } else if (!memcmp(bufferptr, "bytes_dest", 10)) {
473 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_dest (%ti).\n", bufferptr - buffer);
476 + } else if (!memcmp(bufferptr, "packets_src", 11)) {
477 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_src (%ti).\n", bufferptr - buffer);
480 + } else if (!memcmp(bufferptr, "packets_dest", 12)) {
481 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_dest (%ti).\n", bufferptr - buffer);
484 + } else if (!memcmp(bufferptr, "time", 4)) {
485 + /* time hack, ignore time tokens */
486 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got time (%ti).\n", bufferptr - buffer);
488 + if (!isspace(*bufferptr)) {
489 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
490 + return length; /* expected space */
493 + if (*bufferptr != '=') {
494 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
495 + return length; /* expected equal */
498 + if (!isspace(*bufferptr)) {
499 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
500 + return length; /* expected space */
503 + if (!(len = atoi64(bufferptr, &dummy))) {
504 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%ti).\n", bufferptr - buffer);
505 + return length; /* expected int64 */
507 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\n", dummy, bufferptr - buffer);
509 + continue; /* skip time token */
511 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected token (%ti).\n", bufferptr - buffer);
512 + return length; /* expected token */
514 + if (!isspace(*bufferptr)) {
515 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
516 + return length; /* expected space */
519 + if (*bufferptr != '=') {
520 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
521 + return length; /* expected equal */
524 + if (!isspace(*bufferptr)) {
525 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
526 + return length; /* expected space */
529 + if (!(len = atoi64(bufferptr, p))) {
530 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%ti).\n", bufferptr - buffer);
531 + return length; /* expected int64 */
533 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\n", *p, bufferptr - buffer);
536 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() updating row.\n");
537 + spin_lock_bh(&table->ip_list_lock);
538 + /* update counters, do not overwrite time field */
539 + memcpy(&table->ip_list.s[ip - table->network], &s, sizeof(struct t_ipt_account_ip_list_short) - sizeof(unsigned long));
540 + spin_unlock_bh(&table->ip_list_lock);
544 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() left.\n");
549 +static int account_seq_show(struct seq_file *s, void *v)
551 + struct proc_dir_entry *pde = s->private;
552 + struct t_ipt_account_table *table = pde->data;
553 + unsigned int *bucket = (unsigned int *)v;
555 + u_int32_t address = table->network + *bucket;
556 + struct timespec last;
558 + if (!table->shortlisting) {
559 + jiffies_to_timespec(jiffies - table->ip_list.l[*bucket].time, &last);
561 + "ip = %u.%u.%u.%u bytes_src = %llu %llu %llu %llu %llu packets_src = %llu %llu %llu %llu %llu bytes_dest = %llu %llu %llu %llu %llu packets_dest = %llu %llu %llu %llu %llu time = %lu\n",
563 + table->ip_list.l[*bucket].src.b_all,
564 + table->ip_list.l[*bucket].src.b_tcp,
565 + table->ip_list.l[*bucket].src.b_udp,
566 + table->ip_list.l[*bucket].src.b_icmp,
567 + table->ip_list.l[*bucket].src.b_other,
568 + table->ip_list.l[*bucket].src.p_all,
569 + table->ip_list.l[*bucket].src.p_tcp,
570 + table->ip_list.l[*bucket].src.p_udp,
571 + table->ip_list.l[*bucket].src.p_icmp,
572 + table->ip_list.l[*bucket].src.p_other,
573 + table->ip_list.l[*bucket].dest.b_all,
574 + table->ip_list.l[*bucket].dest.b_tcp,
575 + table->ip_list.l[*bucket].dest.b_udp,
576 + table->ip_list.l[*bucket].dest.b_icmp,
577 + table->ip_list.l[*bucket].dest.b_other,
578 + table->ip_list.l[*bucket].dest.p_all,
579 + table->ip_list.l[*bucket].dest.p_tcp,
580 + table->ip_list.l[*bucket].dest.p_udp,
581 + table->ip_list.l[*bucket].dest.p_icmp,
582 + table->ip_list.l[*bucket].dest.p_other,
586 + jiffies_to_timespec(jiffies - table->ip_list.s[*bucket].time, &last);
588 + "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dest = %llu packets_dest = %llu time = %lu\n",
590 + table->ip_list.s[*bucket].src.b_all,
591 + table->ip_list.s[*bucket].src.p_all,
592 + table->ip_list.s[*bucket].dest.b_all,
593 + table->ip_list.s[*bucket].dest.p_all,
600 +static struct seq_operations account_seq_ops = {
601 + .start = account_seq_start,
602 + .next = account_seq_next,
603 + .stop = account_seq_stop,
604 + .show = account_seq_show
607 +static int account_seq_open(struct inode *inode, struct file *file)
609 + int ret = seq_open(file, &account_seq_ops);
612 + struct seq_file *sf = file->private_data;
613 + sf->private = PDE(inode);
618 +static struct file_operations account_file_ops = {
619 + .owner = THIS_MODULE,
620 + .open = account_seq_open,
622 + .write = account_seq_write,
623 + .llseek = seq_lseek,
624 + .release = seq_release
627 +/* do raw accounting */
628 +static inline void do_account(struct t_ipt_account_stat *stat, const struct sk_buff *skb) {
630 + /* update packet & bytes counters in *stat structure */
631 + stat->b_all += skb->len;
634 + switch (skb->nh.iph->protocol) {
636 + stat->b_tcp += skb->len;
640 + stat->b_udp += skb->len;
644 + stat->b_icmp += skb->len;
648 + stat->b_other += skb->len;
653 +static inline void do_account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb) {
655 + /* update packet & bytes counters in *stat structure */
656 + stat->b_all += skb->len;
660 +static int match(const struct sk_buff *skb,
661 + const struct net_device *in,
662 + const struct net_device *out,
663 + const void *matchinfo,
665 + unsigned int protoff,
669 + const struct t_ipt_account_info *info = (struct t_ipt_account_info*)matchinfo;
670 + struct t_ipt_account_table *table;
676 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() entered.\n");
677 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() match name = %s.\n", info->name);
679 + spin_lock_bh(&account_lock);
680 + /* find the right table */
681 + table = account_tables;
682 + while (table && strncmp(table->name, info->name, IPT_ACCOUNT_NAME_LEN) && (table = table->next));
683 + spin_unlock_bh(&account_lock);
685 + if (table == NULL) {
686 + /* ups, no table with that name */
687 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() table %s not found. Leaving.\n", info->name);
691 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() table found %s\n", table->name);
693 + /* lock table while updating statistics */
694 + spin_lock_bh(&table->ip_list_lock);
696 + /* default: no match */
699 + /* get current time */
702 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() got packet src = %u.%u.%u.%u, dst = %u.%u.%u.%u, proto = %u.\n", NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr), skb->nh.iph->protocol);
704 + /* check whether traffic from source ip address ... */
705 + address = ntohl(skb->nh.iph->saddr);
706 + /* ... is being accounted by this table */
707 + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
708 + /* yes, account this packet */
709 + dprintk(KERN_INFO "ipt_account: match() accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol);
710 + /* update counters this host */
711 + if (!table->shortlisting) {
712 + do_account(&table->ip_list.l[address - table->network].src, skb);
713 + table->ip_list.l[address - table->network].time = now;
714 + /* update also counters for all hosts in this table (network address) */
715 + if (table->netmask != INADDR_BROADCAST) {
716 + do_account(&table->ip_list.l[0].src, skb);
717 + table->ip_list.l[0].time = now;
720 + do_account_short(&table->ip_list.s[address - table->network].src, skb);
721 + table->ip_list.s[address - table->network].time = now;
722 + /* update also counters for all hosts in this table (network address) */
723 + if (table->netmask != INADDR_BROADCAST) {
724 + do_account_short(&table->ip_list.s[0].src, skb);
725 + table->ip_list.s[0].time = now;
728 + /* yes, it's a match */
732 + /* do the same thing with destination ip address */
733 + address = ntohl(skb->nh.iph->daddr);
734 + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
735 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol);
736 + if (!table->shortlisting) {
737 + do_account(&table->ip_list.l[address - table->network].dest, skb);
738 + table->ip_list.l[address - table->network].time = now;
739 + if (table->netmask != INADDR_BROADCAST) {
740 + do_account(&table->ip_list.l[0].dest, skb);
741 + table->ip_list.s[0].time = now;
744 + do_account_short(&table->ip_list.s[address - table->network].dest, skb);
745 + table->ip_list.s[address - table->network].time = now;
746 + if (table->netmask != INADDR_BROADCAST) {
747 + do_account_short(&table->ip_list.s[0].dest, skb);
748 + table->ip_list.s[0].time = now;
753 + spin_unlock_bh(&table->ip_list_lock);
755 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() left.\n");
760 +static int checkentry(const char *tablename,
761 + const struct ipt_ip *ip,
763 + unsigned int matchinfosize,
764 + unsigned int hook_mask)
766 + const struct t_ipt_account_info *info = matchinfo;
767 + struct t_ipt_account_table *table, *find_table, *last_table;
770 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() entered.\n");
772 + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return 0;
773 + if (!info->name || !info->name[0]) return 0;
775 + /* find whether table with this name already exists */
776 + spin_lock_bh(&account_lock);
777 + find_table = account_tables;
778 + while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_ACCOUNT_NAME_LEN) && (find_table = find_table->next) );
779 + if (find_table != NULL) {
780 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table %s found.\n", info->name);
781 + /* if table exists, check whether table network/netmask equals rule network/netmask */
782 + if (find_table->network != info->network || find_table->netmask != info->netmask || find_table->shortlisting != info->shortlisting) {
783 + spin_unlock_bh(&account_lock);
784 + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() wrong parameters (not equals existing table parameters).\n");
788 + /* increment table use count */
789 + find_table->use_count++;
790 + spin_unlock_bh(&account_lock);
791 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() incrementing use count.\n");
795 + spin_unlock_bh(&account_lock);
797 + /* check netmask first, before allocating memory */
798 + if (info->netmask < ((1 << netmask) - 1)) {
799 + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() too big netmask.\n");
804 + /* table doesn't exist - create new */
805 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu for new table %s.\n", sizeof(struct t_ipt_account_table), info->name);
806 + table = vmalloc(sizeof(struct t_ipt_account_table));
807 + if (table == NULL) {
808 + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %zu for new table %s.\n", sizeof(struct t_ipt_account_table), info->name);
813 + /* setup table parameters */
814 + table->ip_list_lock = SPIN_LOCK_UNLOCKED;
815 + table->next = NULL;
816 + table->use_count = 1;
817 + table->network = info->network;
818 + table->netmask = info->netmask;
819 + table->shortlisting = info->shortlisting;
820 + table->count = (~table->netmask) + 1;
821 + strncpy(table->name,info->name,IPT_ACCOUNT_NAME_LEN);
822 + table->name[IPT_ACCOUNT_NAME_LEN - 1] = '\0';
824 + /* allocate memory for table->ip_list */
825 + if (!table->shortlisting) {
826 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count);
827 + table->ip_list.l = vmalloc(sizeof(struct t_ipt_account_ip_list) * table->count);
828 + if (table->ip_list.l == NULL) {
829 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count);
831 + goto failure_table;
833 + memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count);
835 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count);
836 + table->ip_list.s = vmalloc(sizeof(struct t_ipt_account_ip_list_short) * table->count);
837 + if (table->ip_list.s == NULL) {
838 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count);
840 + goto failure_table;
842 + memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count);
845 + /* put table into chain */
846 + spin_lock_bh(&account_lock);
847 + find_table = account_tables;
848 + while( (last_table = find_table) && strncmp(info->name, find_table->name, IPT_ACCOUNT_NAME_LEN) && (find_table = find_table->next) );
849 + if (find_table != NULL) {
850 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table %s found.\n", info->name);
851 + if (find_table->network != info->network || find_table->netmask != info->netmask) {
852 + spin_unlock_bh(&account_lock);
853 + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() wrong network/netmask.\n");
855 + goto failure_ip_list;
857 + find_table->use_count++;
858 + spin_unlock_bh(&account_lock);
859 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() incrementing use count.\n");
861 + goto failure_ip_list;
864 + account_tables = table;
866 + last_table->next = table;
867 + spin_unlock_bh(&account_lock);
869 + /* create procfs status file */
870 + table->status_file = create_proc_entry(table->name, permissions, proc_net_ipt_account);
871 + if (table->status_file == NULL) {
873 + goto failure_unlink;
875 + table->status_file->owner = THIS_MODULE;
876 + table->status_file->data = table;
878 + table->status_file->proc_fops = &account_file_ops;
880 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left.\n");
881 + /* everything went just okey */
884 + /* do cleanup in case of failure */
886 + /* remove table from list */
887 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() removing table.\n");
888 + spin_lock_bh(&account_lock);
890 + table = account_tables;
891 + if (table == NULL) {
892 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() no table found. Leaving.\n");
893 + spin_unlock_bh(&account_lock);
896 + while (strncmp(info->name, table->name, IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next));
897 + if (table == NULL) {
898 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table already destroyed. Leaving.\n");
899 + spin_unlock_bh(&account_lock);
903 + last_table->next = table->next;
905 + account_tables = table->next;
906 + spin_unlock_bh(&account_lock);
908 + /* free memory allocated for statistics table */
909 + if (!table->shortlisting)
910 + vfree(table->ip_list.l);
912 + vfree(table->ip_list.s);
917 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left. Table not created.\n");
918 + /* failure return */
922 +static void destroy(void *matchinfo,
923 + unsigned int matchinfosize)
925 + const struct t_ipt_account_info *info = matchinfo;
926 + struct t_ipt_account_table *table, *last_table;
928 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() entered.\n");
930 + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return;
932 + /* search for table */
933 + spin_lock_bh(&account_lock);
935 + table = account_tables;
936 + if(table == NULL) {
937 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() no tables found. Leaving.\n");
938 + spin_unlock_bh(&account_lock);
941 + while( strncmp(info->name,table->name,IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next) );
942 + if (table == NULL) {
943 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() no table %s not found. Leaving.\n", info->name);
944 + spin_unlock_bh(&account_lock);
948 + /* decrement table use-count */
949 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() decrementing use count.\n");
950 + table->use_count--;
951 + if (table->use_count) {
952 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table still in use. Leaving.\n");
953 + spin_unlock_bh(&account_lock);
957 + /* remove table if use-count is zero */
958 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table %s not used. Removing.\n", table->name);
962 + last_table->next = table->next;
964 + account_tables = table->next;
965 + spin_unlock_bh(&account_lock);
967 + /* wait while table is still in use */
968 + spin_lock_bh(&table->ip_list_lock);
969 + spin_unlock_bh(&table->ip_list_lock);
971 + /* remove proc entries */
972 + remove_proc_entry(table->name, proc_net_ipt_account);
975 + if (!table->shortlisting)
976 + vfree(table->ip_list.l);
978 + vfree(table->ip_list.s);
981 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() left.\n");
985 +static struct ipt_match account_match = {
988 + .checkentry = &checkentry,
989 + .destroy = &destroy,
993 +static int __init init(void)
997 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() entered.\n");
1000 + if (netmask > 32 || netmask < 0) {
1001 + printk(KERN_INFO "account: Wrong netmask given by netmask parameter (%i). Valid is 32 to 0.\n", netmask);
1006 + /* create /proc/net/ipt_account directory */
1007 + proc_net_ipt_account = proc_mkdir("ipt_account", proc_net);
1008 + if (!proc_net_ipt_account) {
1009 + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to create procfs entry.\n");
1013 + proc_net_ipt_account->owner = THIS_MODULE;
1015 + err = ipt_register_match(&account_match);
1017 + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to register match.\n");
1018 + remove_proc_entry("ipt_account", proc_net);
1021 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() left.\n");
1025 +static void __exit fini(void)
1027 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() entered.\n");
1029 + ipt_unregister_match(&account_match);
1030 + /* remove /proc/net/ipt_account/ directory */
1031 + remove_proc_entry("ipt_account", proc_net);
1033 + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() left.\n");