diff -uNr patch-o-matic-ng-20040621/account/help patch-o-matic-ng-new/account/help --- patch-o-matic-ng-20040621/account/help 2004-03-17 15:50:12.000000000 +0100 +++ patch-o-matic-ng-new/account/help 2004-07-23 02:00:21.000000000 +0200 @@ -1,5 +1,33 @@ -This match is used for accounting traffic. +This match is used for accounting traffic for all hosts in +defined network/netmask. -Short options are available by using 'iptables -m account -h' -Official Website: +Features: +- long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics +- one iptables rule for all hosts in network/netmask +- loading/saving counters (by reading/writting to procfs entries) +Example usage: + +account traffic for/to 192.168.0.0/24 network into table mynetwork: + +# iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24 + +account traffic for/to WWW serwer for 192.168.0.0/24 network into table + mywwwserver: + +# iptables -A INPUT -p tcp --dport 80 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort +# iptables -A OUTPUT -p tcp --sport 80 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort + +read counters: + +# cat /proc/net/ipt_account/mynetwork +# cat /proc/net/ipt_account/mywwwserver + +set counters: + +# echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver + +Webpage: + http://www.barbara.eu.org/~quaker/ipt_account/ diff -uNr patch-o-matic-ng-20040621/account/info patch-o-matic-ng-new/account/info --- patch-o-matic-ng-20040621/account/info 2004-03-17 15:50:12.000000000 +0100 +++ patch-o-matic-ng-new/account/info 2004-07-23 02:00:21.000000000 +0200 @@ -1,4 +1,3 @@ Author: Piotr Gasido Status: working Repository: extra -Requires: linux < 2.6.0 diff -uNr patch-o-matic-ng-20040621/account/linux/Documentation/Configure.help.ladd patch-o-matic-ng-new/account/linux/Documentation/Configure.help.ladd --- patch-o-matic-ng-20040621/account/linux/Documentation/Configure.help.ladd 1970-01-01 01:00:00.000000000 +0100 +++ patch-o-matic-ng-new/account/linux/Documentation/Configure.help.ladd 2004-08-27 16:59:25.000000000 +0200 @@ -0,0 +1,44 @@ +CONFIG_IP_NF_MATCH_TOS +account match support +CONFIG_IP_NF_MATCH_ACCOUNT + This match is used for accounting traffic for all hosts in + defined network/netmask. + + Features: + - long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics + - one iptables rule for all hosts in network/netmask + - loading/saving counters (by reading/writting to procfs entries) + + Example usage: + + account traffic for/to 192.168.0.0/24 network into table mynetwork: + + # iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24 + + account traffic for/to WWW serwer for 192.168.0.0/24 network into table + mywwwserver: + + # iptables -A INPUT -p tcp --dport 80 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort + # iptables -A OUTPUT -p tcp --sport 80 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort + + read counters: + + # cat /proc/net/ipt_account/mynetwork + # cat /proc/net/ipt_account/mywwwserver + + set counters: + + # echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver + + Webpage: + http://www.barbara.eu.org/~quaker/ipt_account/ + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +account debugging output +CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG + Say Y to get lots of debugging output. + diff -uNr patch-o-matic-ng-20040621/account/linux/include/linux/netfilter_ipv4/ipt_account.h patch-o-matic-ng-new/account/linux/include/linux/netfilter_ipv4/ipt_account.h --- patch-o-matic-ng-20040621/account/linux/include/linux/netfilter_ipv4/ipt_account.h 2004-03-17 15:50:12.000000000 +0100 +++ patch-o-matic-ng-new/account/linux/include/linux/netfilter_ipv4/ipt_account.h 2004-08-27 16:34:19.000000000 +0200 @@ -1,8 +1,8 @@ /* * accounting match (ipt_account.c) - * (C) 2003,2004 by Piotr Gasid³o (quaker@barbara.eu.org) + * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org) * - * Version: 0.1.5 + * Version: 0.1.7 * * This software is distributed under the terms of GNU GPL */ @@ -12,10 +12,15 @@ #define IPT_ACCOUNT_NAME_LEN 64 +#define IPT_ACCOUNT_NAME "ipt_account" +#define IPT_ACCOUNT_VERSION "0.1.7" + struct t_ipt_account_info { char name[IPT_ACCOUNT_NAME_LEN]; u_int32_t network; u_int32_t netmask; + int shortlisting:1; }; #endif + diff -uNr patch-o-matic-ng-20040621/account/linux/net/ipv4/netfilter/Config.in.ladd patch-o-matic-ng-new/account/linux/net/ipv4/netfilter/Config.in.ladd --- patch-o-matic-ng-20040621/account/linux/net/ipv4/netfilter/Config.in.ladd 2004-03-17 15:50:12.000000000 +0100 +++ patch-o-matic-ng-new/account/linux/net/ipv4/netfilter/Config.in.ladd 2004-07-23 02:00:21.000000000 +0200 @@ -1,2 +1,5 @@ dep_tristate ' TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES - dep_tristate ' account match support' CONFIG_IP_NF_MATCH_ACCOUNT $CONFIG_IP_NF_IPTABLES + dep_tristate ' account match support' CONFIG_IP_NF_MATCH_ACCOUNT $CONFIG_IP_NF_IPTABLES $CONFIG_PROC_FS + if [ "$CONFIG_IP_NF_MATCH_ACCOUNT" != "n" ]; then + bool ' account debugging output' CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG + fi diff -uNr patch-o-matic-ng-20040621/account/linux/net/ipv4/netfilter/ipt_account.c patch-o-matic-ng-new/account/linux/net/ipv4/netfilter/ipt_account.c --- patch-o-matic-ng-20040621/account/linux/net/ipv4/netfilter/ipt_account.c 2004-03-17 15:50:12.000000000 +0100 +++ patch-o-matic-ng-new/account/linux/net/ipv4/netfilter/ipt_account.c 2004-08-27 16:35:28.000000000 +0200 @@ -1,8 +1,8 @@ /* * accounting match (ipt_account.c) - * (C) 2003,2004 by Piotr Gasid³o (quaker@barbara.eu.org) + * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org) * - * Version: 0.1.5 + * Version: 0.1.7 * * This software is distributed under the terms of GNU GPL */ @@ -13,6 +13,9 @@ #include #include #include +#include + +#include #include @@ -23,238 +26,544 @@ #include #include +#if defined(CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG) + #define dprintk(format,args...) printk(format,##args) +#else + #define dprintk(format,args...) +#endif + static char version[] = -KERN_INFO "ipt_account 0.1.5 : Piotr Gasid³o , http://www.barbara.eu.org/~quaker/ipt_account/\n"; +KERN_INFO IPT_ACCOUNT_NAME " " IPT_ACCOUNT_VERSION " : Piotr Gasid³o , http://www.barbara.eu.org/~quaker/ipt_account/\n"; -/* default rights for files created in /proc/net/ipt_account/ */ -static int ip_list_perms = 0644; - -/* - * safe netmask, if you want account traffic for networks - * bigger that /17 you must specify ip_list_max_hosts parameter - * during load - */ -static int ip_list_max_mask = 17; -static int ip_list_max_hosts_count; -static int debug = 0; +/* rights for files created in /proc/net/ipt_account/ */ +static int permissions = 0644; +/* maximal netmask for single table */ +static int netmask = 16; /* module information */ -MODULE_AUTHOR("Piotr Gasid³o "); +MODULE_AUTHOR("Piotr Gasidlo "); MODULE_DESCRIPTION("Traffic accounting modules"); MODULE_LICENSE("GPL"); -MODULE_PARM(ip_list_perms,"i"); -MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_account/* files"); -MODULE_PARM(ip_list_max_mask, "i"); -MODULE_PARM_DESC(ip_list_max_mask, "maximum *save* size of one list (netmask)"); -#ifdef DEBUG -MODULE_PARM(debug,"i"); -MODULE_PARM_DESC(debug,"debugging level, defaults to 0"); -#endif +MODULE_PARM(permissions,"i"); +MODULE_PARM_DESC(permissions,"permissions on /proc/net/ipt_account/* files"); +MODULE_PARM(netmask, "i"); +MODULE_PARM_DESC(netmask, "maximum *save* size of one list (netmask)"); /* structure with statistics counters */ struct t_ipt_account_stat { u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other; /* byte counters for all/tcp/udp/icmp/other traffic */ u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other; /* packet counters for all/tcp/udp/icmp/other traffic */ }; + +/* stucture with statistics counters, used when table is created with --ashort switch */ +struct t_ipt_account_stat_short { + u_int64_t b_all; /* byte counters for all traffic */ + u_int64_t p_all; /* packet counters for all traffic */ +}; /* structure holding to/from statistics for single ip */ struct t_ipt_account_ip_list { struct t_ipt_account_stat src; struct t_ipt_account_stat dest; + unsigned long time; /* time when this record was last updated */ + +}; + +/* same as above, for tables with --ashort switch */ +struct t_ipt_account_ip_list_short { + struct t_ipt_account_stat_short src; + struct t_ipt_account_stat_short dest; + unsigned long time; }; /* structure describing single table */ struct t_ipt_account_table { char name[IPT_ACCOUNT_NAME_LEN]; /* table name ( = filename in /proc/net/ipt_account/) */ - struct t_ipt_account_ip_list *ip_list; /* table with statistics for each ip in network/netmask */ - struct t_ipt_account_table *next; + union { /* table with statistics for each ip in network/netmask */ + struct t_ipt_account_ip_list *l; + struct t_ipt_account_ip_list_short *s; + } ip_list; u_int32_t network; /* network/netmask covered by table*/ u_int32_t netmask; + u_int32_t count; + int shortlisting:1; /* show only total columns of counters */ int use_count; /* rules counter - counting number of rules using this table */ + struct t_ipt_account_table *next; spinlock_t ip_list_lock; - struct proc_dir_entry *status_proc_64, *status_proc_32; + struct proc_dir_entry *status_file; }; /* we must use spinlocks to avoid parallel modifications of table list */ -static spinlock_t ipt_account_tables_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t account_lock = SPIN_LOCK_UNLOCKED; static struct proc_dir_entry *proc_net_ipt_account = NULL; /* root pointer holding list of the tables */ -static struct t_ipt_account_table *ipt_account_tables = NULL; +static struct t_ipt_account_table *account_tables = NULL; -static int ip_list_read_proc_64(char *buffer, char **start, off_t offset, - int length, int *eof, void *data) { - - int len = 0, last_len = 0; - off_t pos = 0, begin = 0; +/* convert ascii to ip */ +int atoip(char *buffer, u_int32_t *ip) { - u_int32_t address, index; + char *bufferptr = buffer; + int part, shift; + + /* zero ip */ + *ip = 0; - struct t_ipt_account_table *table = (struct t_ipt_account_table*)data; + /* first must be a digit */ + if (!isdigit(*bufferptr)) + return 0; - spin_lock(&table->ip_list_lock); - for (address = table->network; (u_int32_t)(address & table->netmask) == (u_int32_t)(table->network); address++) { - last_len = len; - index = address - table->network; - len += sprintf(buffer + len, - "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\n", - HIPQUAD(address), - table->ip_list[index].src.b_all, - table->ip_list[index].src.b_tcp, - table->ip_list[index].src.b_udp, - table->ip_list[index].src.b_icmp, - table->ip_list[index].src.b_other, - - table->ip_list[index].src.p_all, - table->ip_list[index].src.p_tcp, - table->ip_list[index].src.p_udp, - table->ip_list[index].src.p_icmp, - table->ip_list[index].src.p_other, - - table->ip_list[index].dest.b_all, - table->ip_list[index].dest.b_tcp, - table->ip_list[index].dest.b_udp, - table->ip_list[index].dest.b_icmp, - table->ip_list[index].dest.b_other, - - table->ip_list[index].dest.p_all, - table->ip_list[index].dest.p_tcp, - table->ip_list[index].dest.p_udp, - table->ip_list[index].dest.p_icmp, - table->ip_list[index].dest.p_other - ); - pos = begin + len; - if (pos < offset) { - len = 0; - begin = pos; + /* parse first 3 octets (III.III.III.iii) */ + for (part = 0, shift = 24; *bufferptr && shift; bufferptr++) { + if (isdigit(*bufferptr)) { + part = part * 10 + (*bufferptr - '0'); + continue; } - if (pos > offset + length) { - len = last_len; - break; + if (*bufferptr == '.') { + if (part > 255) + return 0; + *ip |= part << shift; + shift -= 8; + part = 0; + continue; } + return 0; } - spin_unlock(&table->ip_list_lock); - *start = buffer + (offset - begin); - len -= (offset - begin); - if (len > length) - len = length; - return len; -} - -static int ip_list_read_proc_32(char *buffer, char **start, off_t offset, - int length, int *eof, void *data) { - - int len = 0, last_len = 0; - off_t pos = 0, begin = 0; - - u_int32_t address, index; - - struct t_ipt_account_table *table = (struct t_ipt_account_table*)data; - - spin_lock(&table->ip_list_lock); - for (address = table->network; (u_int32_t)(address & table->netmask) == (u_int32_t)(table->network); address++) { - last_len = len; - index = address - table->network; - len += sprintf(buffer + len, - "ip = %u.%u.%u.%u bytes_src = %u %u %u %u %u packets_src = %u %u %u %u %u bytes_dest = %u %u %u %u %u packets_dest = %u %u %u %u %u\n", - HIPQUAD(address), - (u_int32_t)table->ip_list[index].src.b_all, - (u_int32_t)table->ip_list[index].src.b_tcp, - (u_int32_t)table->ip_list[index].src.b_udp, - (u_int32_t)table->ip_list[index].src.b_icmp, - (u_int32_t)table->ip_list[index].src.b_other, - - (u_int32_t)table->ip_list[index].src.p_all, - (u_int32_t)table->ip_list[index].src.p_tcp, - (u_int32_t)table->ip_list[index].src.p_udp, - (u_int32_t)table->ip_list[index].src.p_icmp, - (u_int32_t)table->ip_list[index].src.p_other, - - (u_int32_t)table->ip_list[index].dest.b_all, - (u_int32_t)table->ip_list[index].dest.b_tcp, - (u_int32_t)table->ip_list[index].dest.b_udp, - (u_int32_t)table->ip_list[index].dest.b_icmp, - (u_int32_t)table->ip_list[index].dest.b_other, - - (u_int32_t)table->ip_list[index].dest.p_all, - (u_int32_t)table->ip_list[index].dest.p_tcp, - (u_int32_t)table->ip_list[index].dest.p_udp, - (u_int32_t)table->ip_list[index].dest.p_icmp, - (u_int32_t)table->ip_list[index].dest.p_other - - ); - pos = begin + len; - if (pos < offset) { - len = 0; - begin = pos; - } - if (pos > offset + length) { - len = last_len; + + /* we expect more digts */ + if (!*bufferptr) + return 0; + /* parse last octet (iii.iii.iii.III) */ + for (; *bufferptr; bufferptr++) { + if (isdigit(*bufferptr)) { + part = part * 10 + (*bufferptr - '0'); + continue; + } else { + if (part > 255) + return 0; break; - } + } + } + *ip |= part; + return (bufferptr - buffer); +} + +/* convert ascii to 64bit integer */ +int atoi64(char *buffer, u_int64_t *i) { + char *bufferptr = buffer; + + /* zero integer */ + *i = 0; + + while (isdigit(*bufferptr)) { + *i = *i * 10 + (*bufferptr - '0'); + bufferptr++; } - spin_unlock(&table->ip_list_lock); - *start = buffer + (offset - begin); - len -= (offset - begin); - if (len > length) - len = length; - return len; + return (bufferptr - buffer); } -static int ip_list_write_proc(struct file *file, const char *buffer, - unsigned long length, void *data) { +static void *account_seq_start(struct seq_file *s, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; - int len = (length > 1024) ? length : 1024; - struct t_ipt_account_table *table = (struct t_ipt_account_table*)data; - char kernel_buffer[1024]; - u_int32_t hosts_count = INADDR_BROADCAST - table->netmask + 1; + unsigned int *bucket; - copy_from_user(kernel_buffer, buffer, len); - kernel_buffer[len - 1] = 0; + spin_lock_bh(&table->ip_list_lock); + if (*pos >= table->count) + return NULL; + + bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (!bucket) + return ERR_PTR(-ENOMEM); + *bucket = *pos; + return bucket; +} + +static void *account_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + + unsigned int *bucket = (unsigned int *)v; - /* echo "reset" > /proc/net/ipt_recent/table clears the table */ - if (!strncmp(kernel_buffer, "reset", len)) { - spin_lock(&table->ip_list_lock); - memset(table->ip_list, 0, sizeof(struct t_ipt_account_ip_list) * hosts_count); - spin_unlock(&table->ip_list_lock); + *pos = ++(*bucket); + if (*pos >= table->count) { + kfree(v); + return NULL; + } + return bucket; +} + +static void account_seq_stop(struct seq_file *s, void *v) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + unsigned int *bucket = (unsigned int *)v; + kfree(bucket); + spin_unlock_bh(&table->ip_list_lock); +} + +static int account_seq_write(struct file *file, const char *ubuffer, + size_t ulength, loff_t *pos) +{ + struct proc_dir_entry *pde = ((struct seq_file *)file->private_data)->private; + struct t_ipt_account_table *table = pde->data; + char buffer[1024], *bufferptr; + int length; + + u_int32_t ip; + int len, i; + struct t_ipt_account_ip_list l; + struct t_ipt_account_ip_list_short s; + u_int64_t *p, dummy; + + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() entered.\n"); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() ulength = %i.\n", ulength); + + length = ulength; + if (ulength > 1024) + length = 1024; + if (copy_from_user(buffer, ubuffer, length)) + return -EFAULT; + buffer[length - 1] = 0; + bufferptr = buffer; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() buffer = \'%s\' length = %i.\n", buffer, length); + + /* reset table counters */ + if (!memcmp(buffer, "reset", 5)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got \"reset\".\n"); + if (!table->shortlisting) { + spin_lock_bh(&table->ip_list_lock); + memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count); + spin_unlock_bh(&table->ip_list_lock); + } else { + spin_lock_bh(&table->ip_list_lock); + memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count); + spin_unlock_bh(&table->ip_list_lock); + } + return length; + } + + if (!memcmp(buffer, "ip", 2)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got \"ip\".\n"); + bufferptr += 2; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoip(bufferptr, &ip))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected ip (%i).\n", bufferptr - buffer); + return length; /* expected ip */ + } + bufferptr += len; + if ((ip & table->netmask) != table->network) { + 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)); + return length; /* expected ip from table's network/netmask */ + } + if (!table->shortlisting) { + memset(&l, 0, sizeof(struct t_ipt_account_ip_list)); + while(*bufferptr) { + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!memcmp(bufferptr, "bytes_src", 9)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_src (%i).\n", bufferptr - buffer); + p = &l.src.b_all; + bufferptr += 9; + } else if (!memcmp(bufferptr, "bytes_dest", 10)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_dest (%i).\n", bufferptr - buffer); + p = &l.dest.b_all; + bufferptr += 10; + } else if (!memcmp(bufferptr, "packets_src", 11)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_src (%i).\n", bufferptr - buffer); + p = &l.src.p_all; + bufferptr += 11; + } else if (!memcmp(bufferptr, "packets_dest", 12)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_dest (%i).\n", bufferptr - buffer); + p = &l.dest.p_all; + bufferptr += 12; + } else if (!memcmp(bufferptr, "time", 4)) { + /* time hack, ignore time tokens */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got time (%i).\n", bufferptr - buffer); + bufferptr += 4; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, &dummy))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", dummy, bufferptr - buffer); + bufferptr += len; + continue; /* skip time token */ + } else + return length; /* expected token */ + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + for (i = 0; i < 5; i++) { + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, p))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", *p, bufferptr - buffer); + bufferptr += len; + p++; + } + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() updating row.\n"); + spin_lock_bh(&table->ip_list_lock); + /* update counters, do not overwrite time field */ + memcpy(&table->ip_list.l[ip - table->network], &l, sizeof(struct t_ipt_account_ip_list) - sizeof(unsigned long)); + spin_unlock_bh(&table->ip_list_lock); + } else { + memset(&s, 0, sizeof(struct t_ipt_account_ip_list_short)); + while(*bufferptr) { + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!memcmp(bufferptr, "bytes_src", 9)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_src (%i).\n", bufferptr - buffer); + p = &s.src.b_all; + bufferptr += 9; + } else if (!memcmp(bufferptr, "bytes_dest", 10)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_dest (%i).\n", bufferptr - buffer); + p = &s.dest.b_all; + bufferptr += 10; + } else if (!memcmp(bufferptr, "packets_src", 11)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_src (%i).\n", bufferptr - buffer); + p = &s.src.p_all; + bufferptr += 11; + } else if (!memcmp(bufferptr, "packets_dest", 12)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_dest (%i).\n", bufferptr - buffer); + p = &s.dest.p_all; + bufferptr += 12; + } else if (!memcmp(bufferptr, "time", 4)) { + /* time hack, ignore time tokens */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got time (%i).\n", bufferptr - buffer); + bufferptr += 4; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, &dummy))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", dummy, bufferptr - buffer); + bufferptr += len; + continue; /* skip time token */ + } else { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected token (%i).\n", bufferptr - buffer); + return length; /* expected token */ + } + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, p))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", *p, bufferptr - buffer); + bufferptr += len; + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() updating row.\n"); + spin_lock_bh(&table->ip_list_lock); + /* update counters, do not overwrite time field */ + memcpy(&table->ip_list.s[ip - table->network], &s, sizeof(struct t_ipt_account_ip_list_short) - sizeof(unsigned long)); + spin_unlock_bh(&table->ip_list_lock); + } } - return len; + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() left.\n"); + return length; } + +static int account_seq_show(struct seq_file *s, void *v) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + unsigned int *bucket = (unsigned int *)v; + + u_int32_t address = table->network + *bucket; + + struct timespec last; + + if (!table->shortlisting) { + jiffies_to_timespec(jiffies - table->ip_list.l[*bucket].time, &last); + seq_printf(s, + "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", + HIPQUAD(address), + table->ip_list.l[*bucket].src.b_all, + table->ip_list.l[*bucket].src.b_tcp, + table->ip_list.l[*bucket].src.b_udp, + table->ip_list.l[*bucket].src.b_icmp, + table->ip_list.l[*bucket].src.b_other, + table->ip_list.l[*bucket].src.p_all, + table->ip_list.l[*bucket].src.p_tcp, + table->ip_list.l[*bucket].src.p_udp, + table->ip_list.l[*bucket].src.p_icmp, + table->ip_list.l[*bucket].src.p_other, + table->ip_list.l[*bucket].dest.b_all, + table->ip_list.l[*bucket].dest.b_tcp, + table->ip_list.l[*bucket].dest.b_udp, + table->ip_list.l[*bucket].dest.b_icmp, + table->ip_list.l[*bucket].dest.b_other, + table->ip_list.l[*bucket].dest.p_all, + table->ip_list.l[*bucket].dest.p_tcp, + table->ip_list.l[*bucket].dest.p_udp, + table->ip_list.l[*bucket].dest.p_icmp, + table->ip_list.l[*bucket].dest.p_other, + last.tv_sec + ); + } else { + jiffies_to_timespec(jiffies - table->ip_list.s[*bucket].time, &last); + seq_printf(s, + "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dest = %llu packets_dest = %llu time = %lu\n", + HIPQUAD(address), + table->ip_list.s[*bucket].src.b_all, + table->ip_list.s[*bucket].src.p_all, + table->ip_list.s[*bucket].dest.b_all, + table->ip_list.s[*bucket].dest.p_all, + last.tv_sec + ); + } + return 0; +} + +static struct seq_operations account_seq_ops = { + .start = account_seq_start, + .next = account_seq_next, + .stop = account_seq_stop, + .show = account_seq_show +}; + +static int account_seq_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &account_seq_ops); + + if (!ret) { + struct seq_file *sf = file->private_data; + sf->private = PDE(inode); + } + return ret; +} + +static struct file_operations account_file_ops = { + .owner = THIS_MODULE, + .open = account_seq_open, + .read = seq_read, + .write = account_seq_write, + .llseek = seq_lseek, + .release = seq_release +}; + /* do raw accounting */ -static void do_account(struct t_ipt_account_stat *stat, u_int8_t proto, u_int16_t pktlen) { +static inline void do_account(struct t_ipt_account_stat *stat, const struct sk_buff *skb) { /* update packet & bytes counters in *stat structure */ - stat->b_all += pktlen; + stat->b_all += skb->len; stat->p_all++; - switch (proto) { + switch (skb->nh.iph->protocol) { case IPPROTO_TCP: - stat->b_tcp += pktlen; + stat->b_tcp += skb->len; stat->p_tcp++; break; case IPPROTO_UDP: - stat->b_udp += pktlen; + stat->b_udp += skb->len; stat->p_udp++; break; case IPPROTO_ICMP: - stat->b_icmp += pktlen; + stat->b_icmp += skb->len; stat->p_icmp++; break; default: - stat->b_other += pktlen; + stat->b_other += skb->len; stat->p_other++; } } +static inline void do_account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb) { + + /* update packet & bytes counters in *stat structure */ + stat->b_all += skb->len; + stat->p_all++; +} + static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, - const void *hdr, + const void *hdr, u_int16_t datalen, int *hotdrop) { @@ -262,90 +571,92 @@ const struct t_ipt_account_info *info = (struct t_ipt_account_info*)matchinfo; struct t_ipt_account_table *table; int ret; + unsigned long now; u_int32_t address; - u_int16_t pktlen; - u_int8_t proto; - if (debug) { - printk(KERN_INFO "ipt_account: match() entering.\n"); - printk(KERN_INFO "ipt_account: match() match name = %s.\n", info->name); - } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() entered.\n"); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() match name = %s.\n", info->name); - spin_lock(&ipt_account_tables_lock); + spin_lock_bh(&account_lock); /* find the right table */ - table = ipt_account_tables; + table = account_tables; while (table && strncmp(table->name, info->name, IPT_ACCOUNT_NAME_LEN) && (table = table->next)); - spin_unlock(&ipt_account_tables_lock); + spin_unlock_bh(&account_lock); if (table == NULL) { /* ups, no table with that name */ - if (debug) - printk(KERN_INFO "ipt_account: match() table %s not found. Leaving.\n", info->name); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() table %s not found. Leaving.\n", info->name); return 0; } - if (debug) - printk(KERN_INFO "ipt_account: match() table found %s\n", table->name); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() table found %s\n", table->name); + + /* lock table while updating statistics */ + spin_lock_bh(&table->ip_list_lock); /* default: no match */ ret = 0; - /* get packet protocol/length */ - pktlen = skb->len; - proto = skb->nh.iph->protocol; - - if (debug) - printk(KERN_INFO "ipt_account: 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), - proto - ); + /* get current time */ + now = jiffies; + 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); + /* check whether traffic from source ip address ... */ address = ntohl(skb->nh.iph->saddr); - /* ... is being accounted by this table */ if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { - if (debug) - printk(KERN_INFO "ipt_account: match() accounting packet src = %u.%u.%u.%u, proto = %u.\n", - HIPQUAD(address), - proto - ); /* yes, account this packet */ - spin_lock(&table->ip_list_lock); + dprintk(KERN_INFO "ipt_account: match() accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol); /* update counters this host */ - do_account(&table->ip_list[(u_int32_t)(address - table->network)].src, proto, pktlen); - /* update counters for all hosts in this table (network address) */ - if (table->netmask != INADDR_BROADCAST) - do_account(&table->ip_list[0].src, proto, pktlen); - spin_unlock(&table->ip_list_lock); + if (!table->shortlisting) { + do_account(&table->ip_list.l[address - table->network].src, skb); + table->ip_list.l[address - table->network].time = now; + /* update also counters for all hosts in this table (network address) */ + if (table->netmask != INADDR_BROADCAST) { + do_account(&table->ip_list.l[0].src, skb); + table->ip_list.l[0].time = now; + } + } else { + do_account_short(&table->ip_list.s[address - table->network].src, skb); + table->ip_list.s[address - table->network].time = now; + /* update also counters for all hosts in this table (network address) */ + if (table->netmask != INADDR_BROADCAST) { + do_account_short(&table->ip_list.s[0].src, skb); + table->ip_list.s[0].time = now; + } + } /* yes, it's a match */ ret = 1; } /* do the same thing with destination ip address */ address = ntohl(skb->nh.iph->daddr); - if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { - if (debug) - printk(KERN_INFO "ipt_account: match() accounting packet dst = %u.%u.%u.%u, proto = %u.\n", - HIPQUAD(address), - proto - ); - spin_lock(&table->ip_list_lock); - do_account(&table->ip_list[(u_int32_t)(address - table->network)].dest, proto, pktlen); - if (table->netmask != INADDR_BROADCAST) - do_account(&table->ip_list[0].dest, proto, pktlen); - spin_unlock(&table->ip_list_lock); + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol); + if (!table->shortlisting) { + do_account(&table->ip_list.l[address - table->network].dest, skb); + table->ip_list.l[address - table->network].time = now; + if (table->netmask != INADDR_BROADCAST) { + do_account(&table->ip_list.l[0].dest, skb); + table->ip_list.s[0].time = now; + } + } else { + do_account_short(&table->ip_list.s[address - table->network].dest, skb); + table->ip_list.s[address - table->network].time = now; + if (table->netmask != INADDR_BROADCAST) { + do_account_short(&table->ip_list.s[0].dest, skb); + table->ip_list.s[0].time = now; + } + } ret = 1; - } - - if (debug) - printk(KERN_INFO "ipt_account: match() leaving.\n"); + spin_unlock_bh(&table->ip_list_lock); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() left.\n"); + return ret; - } static int checkentry(const char *tablename, @@ -355,149 +666,159 @@ unsigned int hook_mask) { const struct t_ipt_account_info *info = matchinfo; - struct t_ipt_account_table *table; + struct t_ipt_account_table *table, *find_table, *last_table; + int ret = 0; - char proc_entry_name[IPT_ACCOUNT_NAME_LEN + 3]; + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() entered.\n"); - u_int32_t hosts_count; + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return 0; + if (!info->name || !info->name[0]) return 0; - if (debug) - printk(KERN_INFO "ipt_account: checkentry() entering.\n"); + /* find whether table with this name already exists */ + spin_lock_bh(&account_lock); + find_table = account_tables; + while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_ACCOUNT_NAME_LEN) && (find_table = find_table->next) ); + if (find_table != NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table %s found.\n", info->name); + /* if table exists, check whether table network/netmask equals rule network/netmask */ + if (find_table->network != info->network || find_table->netmask != info->netmask || find_table->shortlisting != info->shortlisting) { + spin_unlock_bh(&account_lock); + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() wrong parameters (not equals existing table parameters).\n"); + ret = 0; + goto failure; + } + /* increment table use count */ + find_table->use_count++; + spin_unlock_bh(&account_lock); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() incrementing use count.\n"); + ret = 1; + goto failure; + } + spin_unlock_bh(&account_lock); - if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) - return 0; - - if (!info->name || !info->name[0]) - return 0; + /* check netmask first, before allocating memory */ + if (info->netmask < ((1 << netmask) - 1)) { + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() too big netmask.\n"); + ret = 0; + goto failure; + } - /* find whether table with this name already exists */ - spin_lock(&ipt_account_tables_lock); - table = ipt_account_tables; - while (table && strncmp(info->name, table->name, IPT_ACCOUNT_NAME_LEN) && (table = table->next)); - - if (table) { - /* yes, table exists */ - if (info->network != table->network || info->netmask != table->netmask) { - /* - * tried to do accounting in existing table, but network/netmask in iptable rule - * doesn't match network/netmask in table structure - deny adding the rule - */ - printk(KERN_ERR "ipt_account: checkentry() table %s found. But table netmask/network %u.%u.%u.%u/%u.%u.%u.%u differs from rule netmask/network %u.%u.%u.%u/%u.%u.%u.%u. Leaving without creating entry.\n", - table->name, - HIPQUAD(table->network), - HIPQUAD(table->netmask), - HIPQUAD(info->network), - HIPQUAD(info->netmask) - ); - spin_unlock(&ipt_account_tables_lock); - return 0; - } - if (debug) - printk(KERN_INFO "ipt_account: checkentry() table %s found. Incrementing use count (use_count = %i). Leaving.\n", table->name, table->use_count); - /* increase table use count */ - table->use_count++; - spin_unlock(&ipt_account_tables_lock); - /* everything went okey */ - return 1; - }; - - if (debug) - printk(KERN_INFO "ipt_account: checkentry() table %s not found. Creating.\n", info->name); - - /* table doesn't exist - create one */ - table = vmalloc(sizeof(struct t_ipt_account_table)); + /* table doesn't exist - create new */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %u for new table %s.\n", sizeof(struct t_ipt_account_table), info->name); + table = vmalloc(sizeof(struct t_ipt_account_table)); if (table == NULL) { - if (debug) - printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (t_account_table) for table %s. Leaving.\n", info->name); - spin_unlock(&ipt_account_tables_lock); - return -ENOMEM; + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %u for new table %s.\n", sizeof(struct t_ipt_account_table), info->name); + ret = 0; /* was -ENOMEM */ + goto failure; } - /* set table parameters */ - strncpy(table->name, info->name, IPT_ACCOUNT_NAME_LEN); - table->use_count = 1; + /* setup table parameters */ + table->ip_list_lock = SPIN_LOCK_UNLOCKED; + table->next = NULL; + table->use_count = 1; table->network = info->network; table->netmask = info->netmask; - table->ip_list_lock = SPIN_LOCK_UNLOCKED; - - hosts_count = INADDR_BROADCAST - table->netmask + 1; + table->shortlisting = info->shortlisting; + table->count = (~table->netmask) + 1; + strncpy(table->name,info->name,IPT_ACCOUNT_NAME_LEN); + table->name[IPT_ACCOUNT_NAME_LEN - 1] = '\0'; + + /* allocate memory for table->ip_list */ + if (!table->shortlisting) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count); + table->ip_list.l = vmalloc(sizeof(struct t_ipt_account_ip_list) * table->count); + if (table->ip_list.l == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count); + ret = 0; /* was -ENOMEM */ + goto failure_table; + } + memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count); + } else { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count); + table->ip_list.s = vmalloc(sizeof(struct t_ipt_account_ip_list_short) * table->count); + if (table->ip_list.s == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count); + ret = 0; /* was -ENOMEM */ + goto failure_table; + } + memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count); + } - if (debug) - printk(KERN_INFO "ipt_account: checkentry() allocating memory for %u hosts (%u netmask).\n", hosts_count, info->netmask); - - /* check whether table is not too big */ - if (hosts_count > ip_list_max_hosts_count) { - printk(KERN_ERR "ipt_account: checkentry() unable allocate memory for %u hosts (%u netmask). Increase value of ip_list_max_mask parameter.\n", hosts_count, info->netmask); - vfree(table); - spin_unlock(&ipt_account_tables_lock); - return -ENOMEM; - } - - table->ip_list = vmalloc(sizeof(struct t_ipt_account_ip_list) * hosts_count); - if (table->ip_list == NULL) { - if (debug) - printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (t_account_ip_list) for table %s. Leaving.\n", table->name); - vfree(table); - spin_unlock(&ipt_account_tables_lock); - return -ENOMEM; - } - - memset(table->ip_list, 0, sizeof(struct t_ipt_account_ip_list) * hosts_count); - - /* - * create entries in /proc/net/ipt_account: one with full 64-bit counters and - * second with 32-bit ones. The second can be used in programs supporting only 32-bit numbers - * (mrtg, rrdtool). - */ - - strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); - strncat(proc_entry_name, "_64", 4); - - table->status_proc_64 = create_proc_entry(proc_entry_name, ip_list_perms, proc_net_ipt_account); - if (table->status_proc_64 == NULL) { - if (debug) - printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (status_proc_64) for table %s. Leaving.\n", table->name); - vfree(table->ip_list); - vfree(table); - spin_unlock(&ipt_account_tables_lock); - return -ENOMEM; - } - - table->status_proc_64->owner = THIS_MODULE; - table->status_proc_64->read_proc = ip_list_read_proc_64; - table->status_proc_64->write_proc = ip_list_write_proc; - table->status_proc_64->data = table; - - strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); - strncat(proc_entry_name, "_32", 4); - - table->status_proc_32 = create_proc_entry(proc_entry_name, ip_list_perms, proc_net_ipt_account); - if (table->status_proc_32 == NULL) { - if (debug) - printk(KERN_INFO "ipt_account: checkentry() unable to allocate memory (status_proc_32) for table %s. Leaving.\n", table->name); - vfree(table->ip_list); - vfree(table); - spin_unlock(&ipt_account_tables_lock); - return -ENOMEM; - } - - table->status_proc_32->owner = THIS_MODULE; - table->status_proc_32->read_proc = ip_list_read_proc_32; - table->status_proc_32->write_proc = ip_list_write_proc; - table->status_proc_32->data = table; - - /* finaly, insert table into list */ - table->next = ipt_account_tables; - ipt_account_tables = table; - - if (debug) - printk(KERN_INFO "ipt_account: checkentry() successfully created table %s (use_count = %i).\n", table->name, table->use_count); + /* put table into chain */ + spin_lock_bh(&account_lock); + find_table = account_tables; + while( (last_table = find_table) && strncmp(info->name, find_table->name, IPT_ACCOUNT_NAME_LEN) && (find_table = find_table->next) ); + if (find_table != NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table %s found.\n", info->name); + if (find_table->network != info->network || find_table->netmask != info->netmask) { + spin_unlock_bh(&account_lock); + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() wrong network/netmask.\n"); + ret = 0; + goto failure_ip_list; + } + find_table->use_count++; + spin_unlock_bh(&account_lock); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() incrementing use count.\n"); + ret = 1; + goto failure_ip_list; + } + if (!last_table) + account_tables = table; + else + last_table->next = table; + spin_unlock_bh(&account_lock); + + /* create procfs status file */ + table->status_file = create_proc_entry(table->name, permissions, proc_net_ipt_account); + if (table->status_file == NULL) { + ret = 0; /* was -ENOMEM */ + goto failure_unlink; + } + table->status_file->owner = THIS_MODULE; + table->status_file->data = table; + wmb(); + table->status_file->proc_fops = &account_file_ops; - spin_unlock(&ipt_account_tables_lock); - - if (debug) - printk(KERN_INFO "ipt_account: checkentry() leaving.\n"); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left.\n"); + /* everything went just okey */ return 1; + + /* do cleanup in case of failure */ +failure_unlink: + /* remove table from list */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() removing table.\n"); + spin_lock_bh(&account_lock); + last_table = NULL; + table = account_tables; + if (table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() no table found. Leaving.\n"); + spin_unlock_bh(&account_lock); + return 0; /* was -ENOMEM */ + } + while (strncmp(info->name, table->name, IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next)); + if (table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table already destroyed. Leaving.\n"); + spin_unlock_bh(&account_lock); + return 0; /* was -ENOMEM */ + } + if (last_table) + last_table->next = table->next; + else + account_tables = table->next; + spin_unlock_bh(&account_lock); +failure_ip_list: + /* free memory allocated for statistics table */ + if (!table->shortlisting) + vfree(table->ip_list.l); + else + vfree(table->ip_list.s); +failure_table: + /* free table */ + vfree(table); +failure: + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left. Table not created.\n"); + /* failure return */ + return ret; } static void destroy(void *matchinfo, @@ -505,111 +826,104 @@ { const struct t_ipt_account_info *info = matchinfo; struct t_ipt_account_table *table, *last_table; - char proc_entry_name[IPT_ACCOUNT_NAME_LEN + 3]; - - if (debug) - printk(KERN_INFO "ipt_account: destroy() entered.\n"); - - if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) - return; - spin_lock(&ipt_account_tables_lock); - table = ipt_account_tables; + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() entered.\n"); + + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return; - if (table == NULL) { - /* list is empty, sometheing is realy wrong! */ - if (debug) - printk(KERN_INFO "ipt_account: destroy() unable to found any tables (asked for %s). Leaving.\n", info->name); - spin_unlock(&ipt_account_tables_lock); + /* search for table */ + spin_lock_bh(&account_lock); + last_table = NULL; + table = account_tables; + if(table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() no tables found. Leaving.\n"); + spin_unlock_bh(&account_lock); return; } - - /* find table combined with this rule - this code is taken for ipt_recent ;) */ - last_table = NULL; - while (strncmp(table->name, info->name, IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next)); - + while( strncmp(info->name,table->name,IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next) ); if (table == NULL) { - printk(KERN_ERR "ipt_account: destroy() unable to found table %s. Leaving.\n", info->name); - spin_unlock(&ipt_account_tables_lock); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() no table %s not found. Leaving.\n", info->name); + spin_unlock_bh(&account_lock); return; } - /* decrease table use counter */ + /* decrement table use-count */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() decrementing use count.\n"); table->use_count--; - if (table->use_count != 0) { - /* table is used by other rule, can't remove it */ - if (debug) - printk(KERN_INFO "ipt_account: destroy() table %s is still used (use_count = %i). Leaving.\n", table->name, table->use_count); - spin_unlock(&ipt_account_tables_lock); + if (table->use_count) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table still in use. Leaving.\n"); + spin_unlock_bh(&account_lock); return; } - /* table is not used by any other tule - remove it */ - if (debug) - printk(KERN_INFO "ipt_account: destroy() removing table %s (use_count = %i).\n", table->name, table->use_count); - - if (last_table) - last_table->next = table->next; + /* remove table if use-count is zero */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table %s not used. Removing.\n", table->name); + + /* unlink table */ + if(last_table) + last_table->next = table->next; + else + account_tables = table->next; + spin_unlock_bh(&account_lock); + + /* wait while table is still in use */ + spin_lock_bh(&table->ip_list_lock); + spin_unlock_bh(&table->ip_list_lock); + + /* remove proc entries */ + remove_proc_entry(table->name, proc_net_ipt_account); + + /* remove table */ + if (!table->shortlisting) + vfree(table->ip_list.l); else - ipt_account_tables = table->next; - - spin_lock(&table->ip_list_lock); - spin_unlock(&table->ip_list_lock); - - /* remove procfs entries */ - strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); - strncat(proc_entry_name, "_64", 4); - remove_proc_entry(proc_entry_name, proc_net_ipt_account); - strncpy(proc_entry_name, table->name, IPT_ACCOUNT_NAME_LEN); - strncat(proc_entry_name, "_32", 4); - remove_proc_entry(proc_entry_name, proc_net_ipt_account); - vfree(table->ip_list); + vfree(table->ip_list.s); vfree(table); - spin_unlock(&ipt_account_tables_lock); - - if (debug) - printk(KERN_INFO "account: destroy() leaving.\n"); - + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() left.\n"); return; } static struct ipt_match account_match = { - { NULL, NULL }, - "account", - &match, - &checkentry, - &destroy, - THIS_MODULE + .name = "account", + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE }; static int __init init(void) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() entered.\n"); printk(version); - if (debug) - printk(KERN_INFO "account: __init(): ip_list_perms = %i, ip_list_max_mask = %i\n", ip_list_perms, ip_list_max_mask); /* check params */ - if (ip_list_max_mask > 32 || ip_list_max_mask < 0) { - printk(KERN_ERR "account: Wrong netmask given by ip_list_max_mask parameter (%u). Valid is 32 to 0.\n", ip_list_max_mask); - return 0; + if (netmask > 32 || netmask < 0) { + printk(KERN_INFO "account: Wrong netmask given by netmask parameter (%i). Valid is 32 to 0.\n", netmask); + return -EINVAL; } - ip_list_max_hosts_count = (1 << (32 - ip_list_max_mask)) + 1; - /* create /proc/net/ipt_account directory */ proc_net_ipt_account = proc_mkdir("ipt_account", proc_net); - if (!proc_net_ipt_account) - return -ENOMEM; + if (!proc_net_ipt_account) { + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to create procfs entry.\n"); + return -EINVAL; /* was -ENOMEM */ + } + proc_net_ipt_account->owner = THIS_MODULE; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() left.\n"); return ipt_register_match(&account_match); } -/* procedura usuwaj±ca modu³ */ static void __exit fini(void) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() entered.\n"); + ipt_unregister_match(&account_match); /* remove /proc/net/ipt_account/ directory */ remove_proc_entry("ipt_account", proc_net); + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() left.\n"); } module_init(init); diff -uNr patch-o-matic-ng-20040621/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h patch-o-matic-ng-new/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h --- patch-o-matic-ng-20040621/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h 1970-01-01 01:00:00.000000000 +0100 +++ patch-o-matic-ng-new/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h 2004-08-27 16:34:19.000000000 +0200 @@ -0,0 +1,26 @@ +/* + * accounting match (ipt_account.c) + * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org) + * + * Version: 0.1.7 + * + * This software is distributed under the terms of GNU GPL + */ + +#ifndef _IPT_ACCOUNT_H_ +#define _IPT_ACCOUNT_H_ + +#define IPT_ACCOUNT_NAME_LEN 64 + +#define IPT_ACCOUNT_NAME "ipt_account" +#define IPT_ACCOUNT_VERSION "0.1.7" + +struct t_ipt_account_info { + char name[IPT_ACCOUNT_NAME_LEN]; + u_int32_t network; + u_int32_t netmask; + int shortlisting:1; +}; + +#endif + diff -uNr patch-o-matic-ng-20040621/account/linux-2.6/net/ipv4/netfilter/Kconfig.ladd patch-o-matic-ng-new/account/linux-2.6/net/ipv4/netfilter/Kconfig.ladd --- patch-o-matic-ng-20040621/account/linux-2.6/net/ipv4/netfilter/Kconfig.ladd 1970-01-01 01:00:00.000000000 +0100 +++ patch-o-matic-ng-new/account/linux-2.6/net/ipv4/netfilter/Kconfig.ladd 2004-08-27 16:40:42.000000000 +0200 @@ -0,0 +1,45 @@ +config IP_NF_MATCH_ACCOUNT + tristate "account match support" + depends on IP_NF_IPTABLES && PROC_FS + help + This match is used for accounting traffic for all hosts in + defined network/netmask. + + Features: + - long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics + - one iptables rule for all hosts in network/netmask + - loading/saving counters (by reading/writting to procfs entries) + + Example usage: + + account traffic for/to 192.168.0.0/24 network into table mynetwork: + + # iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24 + + account traffic for/to WWW serwer for 192.168.0.0/24 network into table + mywwwserver: + + # iptables -A INPUT -p tcp --dport 80 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort + # iptables -A OUTPUT -p tcp --sport 80 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort + + read counters: + + # cat /proc/net/ipt_account/mynetwork + # cat /proc/net/ipt_account/mywwwserver + + set counters: + + # echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver + + Webpage: + http://www.barbara.eu.org/~quaker/ipt_account/ + +config IP_NF_MATCH_ACCOUNT_DEBUG + bool "account debugging output" + depends on IP_NF_MATCH_ACCOUNT + help + Say Y to get lots of debugging output. + + diff -uNr patch-o-matic-ng-20040621/account/linux-2.6/net/ipv4/netfilter/Makefile.ladd patch-o-matic-ng-new/account/linux-2.6/net/ipv4/netfilter/Makefile.ladd --- patch-o-matic-ng-20040621/account/linux-2.6/net/ipv4/netfilter/Makefile.ladd 1970-01-01 01:00:00.000000000 +0100 +++ patch-o-matic-ng-new/account/linux-2.6/net/ipv4/netfilter/Makefile.ladd 2004-03-17 15:50:12.000000000 +0100 @@ -0,0 +1,2 @@ +obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o +obj-$(CONFIG_IP_NF_MATCH_ACCOUNT) += ipt_account.o diff -uNr patch-o-matic-ng-20040621/account/linux-2.6/net/ipv4/netfilter/ipt_account.c patch-o-matic-ng-new/account/linux-2.6/net/ipv4/netfilter/ipt_account.c --- patch-o-matic-ng-20040621/account/linux-2.6/net/ipv4/netfilter/ipt_account.c 1970-01-01 01:00:00.000000000 +0100 +++ patch-o-matic-ng-new/account/linux-2.6/net/ipv4/netfilter/ipt_account.c 2004-08-27 16:26:07.000000000 +0200 @@ -0,0 +1,929 @@ +/* + * accounting match (ipt_account.c) + * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org) + * + * Version: 0.1.7 + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#if defined(CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG) + #define dprintk(format,args...) printk(format,##args) +#else + #define dprintk(format,args...) +#endif + +static char version[] = +KERN_INFO IPT_ACCOUNT_NAME " " IPT_ACCOUNT_VERSION " : Piotr Gasid³o , http://www.barbara.eu.org/~quaker/ipt_account/\n"; + +/* rights for files created in /proc/net/ipt_account/ */ +static int permissions = 0644; +/* maximal netmask for single table */ +static int netmask = 16; + +/* module information */ +MODULE_AUTHOR("Piotr Gasidlo "); +MODULE_DESCRIPTION("Traffic accounting modules"); +MODULE_LICENSE("GPL"); +MODULE_PARM(permissions,"i"); +MODULE_PARM_DESC(permissions,"permissions on /proc/net/ipt_account/* files"); +MODULE_PARM(netmask, "i"); +MODULE_PARM_DESC(netmask, "maximum *save* size of one list (netmask)"); + +/* structure with statistics counters */ +struct t_ipt_account_stat { + u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other; /* byte counters for all/tcp/udp/icmp/other traffic */ + u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other; /* packet counters for all/tcp/udp/icmp/other traffic */ +}; + +/* stucture with statistics counters, used when table is created with --ashort switch */ +struct t_ipt_account_stat_short { + u_int64_t b_all; /* byte counters for all traffic */ + u_int64_t p_all; /* packet counters for all traffic */ +}; + +/* structure holding to/from statistics for single ip */ +struct t_ipt_account_ip_list { + struct t_ipt_account_stat src; + struct t_ipt_account_stat dest; + unsigned long time; /* time when this record was last updated */ + +}; + +/* same as above, for tables with --ashort switch */ +struct t_ipt_account_ip_list_short { + struct t_ipt_account_stat_short src; + struct t_ipt_account_stat_short dest; + unsigned long time; +}; + +/* structure describing single table */ +struct t_ipt_account_table { + char name[IPT_ACCOUNT_NAME_LEN]; /* table name ( = filename in /proc/net/ipt_account/) */ + union { /* table with statistics for each ip in network/netmask */ + struct t_ipt_account_ip_list *l; + struct t_ipt_account_ip_list_short *s; + } ip_list; + u_int32_t network; /* network/netmask covered by table*/ + u_int32_t netmask; + u_int32_t count; + int shortlisting:1; /* show only total columns of counters */ + int use_count; /* rules counter - counting number of rules using this table */ + struct t_ipt_account_table *next; + spinlock_t ip_list_lock; + struct proc_dir_entry *status_file; +}; + +/* we must use spinlocks to avoid parallel modifications of table list */ +static spinlock_t account_lock = SPIN_LOCK_UNLOCKED; + +static struct proc_dir_entry *proc_net_ipt_account = NULL; + +/* root pointer holding list of the tables */ +static struct t_ipt_account_table *account_tables = NULL; + +/* convert ascii to ip */ +int atoip(char *buffer, u_int32_t *ip) { + + char *bufferptr = buffer; + int part, shift; + + /* zero ip */ + *ip = 0; + + /* first must be a digit */ + if (!isdigit(*bufferptr)) + return 0; + + /* parse first 3 octets (III.III.III.iii) */ + for (part = 0, shift = 24; *bufferptr && shift; bufferptr++) { + if (isdigit(*bufferptr)) { + part = part * 10 + (*bufferptr - '0'); + continue; + } + if (*bufferptr == '.') { + if (part > 255) + return 0; + *ip |= part << shift; + shift -= 8; + part = 0; + continue; + } + return 0; + } + + /* we expect more digts */ + if (!*bufferptr) + return 0; + /* parse last octet (iii.iii.iii.III) */ + for (; *bufferptr; bufferptr++) { + if (isdigit(*bufferptr)) { + part = part * 10 + (*bufferptr - '0'); + continue; + } else { + if (part > 255) + return 0; + break; + } + } + *ip |= part; + return (bufferptr - buffer); +} + +/* convert ascii to 64bit integer */ +int atoi64(char *buffer, u_int64_t *i) { + char *bufferptr = buffer; + + /* zero integer */ + *i = 0; + + while (isdigit(*bufferptr)) { + *i = *i * 10 + (*bufferptr - '0'); + bufferptr++; + } + return (bufferptr - buffer); +} + +static void *account_seq_start(struct seq_file *s, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + + unsigned int *bucket; + + spin_lock_bh(&table->ip_list_lock); + if (*pos >= table->count) + return NULL; + + bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (!bucket) + return ERR_PTR(-ENOMEM); + *bucket = *pos; + return bucket; +} + +static void *account_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + + unsigned int *bucket = (unsigned int *)v; + + *pos = ++(*bucket); + if (*pos >= table->count) { + kfree(v); + return NULL; + } + return bucket; +} + +static void account_seq_stop(struct seq_file *s, void *v) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + unsigned int *bucket = (unsigned int *)v; + kfree(bucket); + spin_unlock_bh(&table->ip_list_lock); +} + +static int account_seq_write(struct file *file, const char *ubuffer, + size_t ulength, loff_t *pos) +{ + struct proc_dir_entry *pde = ((struct seq_file *)file->private_data)->private; + struct t_ipt_account_table *table = pde->data; + char buffer[1024], *bufferptr; + int length; + + u_int32_t ip; + int len, i; + struct t_ipt_account_ip_list l; + struct t_ipt_account_ip_list_short s; + u_int64_t *p, dummy; + + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() entered.\n"); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() ulength = %i.\n", ulength); + + length = ulength; + if (ulength > 1024) + length = 1024; + if (copy_from_user(buffer, ubuffer, length)) + return -EFAULT; + buffer[length - 1] = 0; + bufferptr = buffer; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() buffer = \'%s\' length = %i.\n", buffer, length); + + /* reset table counters */ + if (!memcmp(buffer, "reset", 5)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got \"reset\".\n"); + if (!table->shortlisting) { + spin_lock_bh(&table->ip_list_lock); + memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count); + spin_unlock_bh(&table->ip_list_lock); + } else { + spin_lock_bh(&table->ip_list_lock); + memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count); + spin_unlock_bh(&table->ip_list_lock); + } + return length; + } + + if (!memcmp(buffer, "ip", 2)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got \"ip\".\n"); + bufferptr += 2; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoip(bufferptr, &ip))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected ip (%i).\n", bufferptr - buffer); + return length; /* expected ip */ + } + bufferptr += len; + if ((ip & table->netmask) != table->network) { + 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)); + return length; /* expected ip from table's network/netmask */ + } + if (!table->shortlisting) { + memset(&l, 0, sizeof(struct t_ipt_account_ip_list)); + while(*bufferptr) { + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!memcmp(bufferptr, "bytes_src", 9)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_src (%i).\n", bufferptr - buffer); + p = &l.src.b_all; + bufferptr += 9; + } else if (!memcmp(bufferptr, "bytes_dest", 10)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_dest (%i).\n", bufferptr - buffer); + p = &l.dest.b_all; + bufferptr += 10; + } else if (!memcmp(bufferptr, "packets_src", 11)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_src (%i).\n", bufferptr - buffer); + p = &l.src.p_all; + bufferptr += 11; + } else if (!memcmp(bufferptr, "packets_dest", 12)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_dest (%i).\n", bufferptr - buffer); + p = &l.dest.p_all; + bufferptr += 12; + } else if (!memcmp(bufferptr, "time", 4)) { + /* time hack, ignore time tokens */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got time (%i).\n", bufferptr - buffer); + bufferptr += 4; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, &dummy))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", dummy, bufferptr - buffer); + bufferptr += len; + continue; /* skip time token */ + } else + return length; /* expected token */ + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + for (i = 0; i < 5; i++) { + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, p))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", *p, bufferptr - buffer); + bufferptr += len; + p++; + } + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() updating row.\n"); + spin_lock_bh(&table->ip_list_lock); + /* update counters, do not overwrite time field */ + memcpy(&table->ip_list.l[ip - table->network], &l, sizeof(struct t_ipt_account_ip_list) - sizeof(unsigned long)); + spin_unlock_bh(&table->ip_list_lock); + } else { + memset(&s, 0, sizeof(struct t_ipt_account_ip_list_short)); + while(*bufferptr) { + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!memcmp(bufferptr, "bytes_src", 9)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_src (%i).\n", bufferptr - buffer); + p = &s.src.b_all; + bufferptr += 9; + } else if (!memcmp(bufferptr, "bytes_dest", 10)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got bytes_dest (%i).\n", bufferptr - buffer); + p = &s.dest.b_all; + bufferptr += 10; + } else if (!memcmp(bufferptr, "packets_src", 11)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_src (%i).\n", bufferptr - buffer); + p = &s.src.p_all; + bufferptr += 11; + } else if (!memcmp(bufferptr, "packets_dest", 12)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got packets_dest (%i).\n", bufferptr - buffer); + p = &s.dest.p_all; + bufferptr += 12; + } else if (!memcmp(bufferptr, "time", 4)) { + /* time hack, ignore time tokens */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got time (%i).\n", bufferptr - buffer); + bufferptr += 4; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, &dummy))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", dummy, bufferptr - buffer); + bufferptr += len; + continue; /* skip time token */ + } else { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected token (%i).\n", bufferptr - buffer); + return length; /* expected token */ + } + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (*bufferptr != '=') { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%i).\n", bufferptr - buffer); + return length; /* expected equal */ + } + bufferptr += 1; + if (!isspace(*bufferptr)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%i).\n", bufferptr - buffer); + return length; /* expected space */ + } + bufferptr += 1; + if (!(len = atoi64(bufferptr, p))) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected int64 (%i).\n", bufferptr - buffer); + return length; /* expected int64 */ + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%i).\n", *p, bufferptr - buffer); + bufferptr += len; + } + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() updating row.\n"); + spin_lock_bh(&table->ip_list_lock); + /* update counters, do not overwrite time field */ + memcpy(&table->ip_list.s[ip - table->network], &s, sizeof(struct t_ipt_account_ip_list_short) - sizeof(unsigned long)); + spin_unlock_bh(&table->ip_list_lock); + } + } + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() left.\n"); + return length; +} + + +static int account_seq_show(struct seq_file *s, void *v) +{ + struct proc_dir_entry *pde = s->private; + struct t_ipt_account_table *table = pde->data; + unsigned int *bucket = (unsigned int *)v; + + u_int32_t address = table->network + *bucket; + + struct timespec last; + + if (!table->shortlisting) { + jiffies_to_timespec(jiffies - table->ip_list.l[*bucket].time, &last); + seq_printf(s, + "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", + HIPQUAD(address), + table->ip_list.l[*bucket].src.b_all, + table->ip_list.l[*bucket].src.b_tcp, + table->ip_list.l[*bucket].src.b_udp, + table->ip_list.l[*bucket].src.b_icmp, + table->ip_list.l[*bucket].src.b_other, + table->ip_list.l[*bucket].src.p_all, + table->ip_list.l[*bucket].src.p_tcp, + table->ip_list.l[*bucket].src.p_udp, + table->ip_list.l[*bucket].src.p_icmp, + table->ip_list.l[*bucket].src.p_other, + table->ip_list.l[*bucket].dest.b_all, + table->ip_list.l[*bucket].dest.b_tcp, + table->ip_list.l[*bucket].dest.b_udp, + table->ip_list.l[*bucket].dest.b_icmp, + table->ip_list.l[*bucket].dest.b_other, + table->ip_list.l[*bucket].dest.p_all, + table->ip_list.l[*bucket].dest.p_tcp, + table->ip_list.l[*bucket].dest.p_udp, + table->ip_list.l[*bucket].dest.p_icmp, + table->ip_list.l[*bucket].dest.p_other, + last.tv_sec + ); + } else { + jiffies_to_timespec(jiffies - table->ip_list.s[*bucket].time, &last); + seq_printf(s, + "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dest = %llu packets_dest = %llu time = %lu\n", + HIPQUAD(address), + table->ip_list.s[*bucket].src.b_all, + table->ip_list.s[*bucket].src.p_all, + table->ip_list.s[*bucket].dest.b_all, + table->ip_list.s[*bucket].dest.p_all, + last.tv_sec + ); + } + return 0; +} + +static struct seq_operations account_seq_ops = { + .start = account_seq_start, + .next = account_seq_next, + .stop = account_seq_stop, + .show = account_seq_show +}; + +static int account_seq_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &account_seq_ops); + + if (!ret) { + struct seq_file *sf = file->private_data; + sf->private = PDE(inode); + } + return ret; +} + +static struct file_operations account_file_ops = { + .owner = THIS_MODULE, + .open = account_seq_open, + .read = seq_read, + .write = account_seq_write, + .llseek = seq_lseek, + .release = seq_release +}; + +/* do raw accounting */ +static inline void do_account(struct t_ipt_account_stat *stat, const struct sk_buff *skb) { + + /* update packet & bytes counters in *stat structure */ + stat->b_all += skb->len; + stat->p_all++; + + switch (skb->nh.iph->protocol) { + case IPPROTO_TCP: + stat->b_tcp += skb->len; + stat->p_tcp++; + break; + case IPPROTO_UDP: + stat->b_udp += skb->len; + stat->p_udp++; + break; + case IPPROTO_ICMP: + stat->b_icmp += skb->len; + stat->p_icmp++; + break; + default: + stat->b_other += skb->len; + stat->p_other++; + } +} + +static inline void do_account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb) { + + /* update packet & bytes counters in *stat structure */ + stat->b_all += skb->len; + stat->p_all++; +} + +static int match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + int *hotdrop) +{ + + const struct t_ipt_account_info *info = (struct t_ipt_account_info*)matchinfo; + struct t_ipt_account_table *table; + int ret; + unsigned long now; + + u_int32_t address; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() entered.\n"); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() match name = %s.\n", info->name); + + spin_lock_bh(&account_lock); + /* find the right table */ + table = account_tables; + while (table && strncmp(table->name, info->name, IPT_ACCOUNT_NAME_LEN) && (table = table->next)); + spin_unlock_bh(&account_lock); + + if (table == NULL) { + /* ups, no table with that name */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() table %s not found. Leaving.\n", info->name); + return 0; + } + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() table found %s\n", table->name); + + /* lock table while updating statistics */ + spin_lock_bh(&table->ip_list_lock); + + /* default: no match */ + ret = 0; + + /* get current time */ + now = jiffies; + + 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); + + /* check whether traffic from source ip address ... */ + address = ntohl(skb->nh.iph->saddr); + /* ... is being accounted by this table */ + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { + /* yes, account this packet */ + dprintk(KERN_INFO "ipt_account: match() accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol); + /* update counters this host */ + if (!table->shortlisting) { + do_account(&table->ip_list.l[address - table->network].src, skb); + table->ip_list.l[address - table->network].time = now; + /* update also counters for all hosts in this table (network address) */ + if (table->netmask != INADDR_BROADCAST) { + do_account(&table->ip_list.l[0].src, skb); + table->ip_list.l[0].time = now; + } + } else { + do_account_short(&table->ip_list.s[address - table->network].src, skb); + table->ip_list.s[address - table->network].time = now; + /* update also counters for all hosts in this table (network address) */ + if (table->netmask != INADDR_BROADCAST) { + do_account_short(&table->ip_list.s[0].src, skb); + table->ip_list.s[0].time = now; + } + } + /* yes, it's a match */ + ret = 1; + } + + /* do the same thing with destination ip address */ + address = ntohl(skb->nh.iph->daddr); + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol); + if (!table->shortlisting) { + do_account(&table->ip_list.l[address - table->network].dest, skb); + table->ip_list.l[address - table->network].time = now; + if (table->netmask != INADDR_BROADCAST) { + do_account(&table->ip_list.l[0].dest, skb); + table->ip_list.s[0].time = now; + } + } else { + do_account_short(&table->ip_list.s[address - table->network].dest, skb); + table->ip_list.s[address - table->network].time = now; + if (table->netmask != INADDR_BROADCAST) { + do_account_short(&table->ip_list.s[0].dest, skb); + table->ip_list.s[0].time = now; + } + } + ret = 1; + } + spin_unlock_bh(&table->ip_list_lock); + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": match() left.\n"); + + return ret; +} + +static int checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchinfosize, + unsigned int hook_mask) +{ + const struct t_ipt_account_info *info = matchinfo; + struct t_ipt_account_table *table, *find_table, *last_table; + int ret = 0; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() entered.\n"); + + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return 0; + if (!info->name || !info->name[0]) return 0; + + /* find whether table with this name already exists */ + spin_lock_bh(&account_lock); + find_table = account_tables; + while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_ACCOUNT_NAME_LEN) && (find_table = find_table->next) ); + if (find_table != NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table %s found.\n", info->name); + /* if table exists, check whether table network/netmask equals rule network/netmask */ + if (find_table->network != info->network || find_table->netmask != info->netmask || find_table->shortlisting != info->shortlisting) { + spin_unlock_bh(&account_lock); + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() wrong parameters (not equals existing table parameters).\n"); + ret = 0; + goto failure; + } + /* increment table use count */ + find_table->use_count++; + spin_unlock_bh(&account_lock); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() incrementing use count.\n"); + ret = 1; + goto failure; + } + spin_unlock_bh(&account_lock); + + /* check netmask first, before allocating memory */ + if (info->netmask < ((1 << netmask) - 1)) { + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() too big netmask.\n"); + ret = 0; + goto failure; + } + + /* table doesn't exist - create new */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %u for new table %s.\n", sizeof(struct t_ipt_account_table), info->name); + table = vmalloc(sizeof(struct t_ipt_account_table)); + if (table == NULL) { + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %u for new table %s.\n", sizeof(struct t_ipt_account_table), info->name); + ret = 0; /* was -ENOMEM */ + goto failure; + } + + /* setup table parameters */ + table->ip_list_lock = SPIN_LOCK_UNLOCKED; + table->next = NULL; + table->use_count = 1; + table->network = info->network; + table->netmask = info->netmask; + table->shortlisting = info->shortlisting; + table->count = (~table->netmask) + 1; + strncpy(table->name,info->name,IPT_ACCOUNT_NAME_LEN); + table->name[IPT_ACCOUNT_NAME_LEN - 1] = '\0'; + + /* allocate memory for table->ip_list */ + if (!table->shortlisting) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count); + table->ip_list.l = vmalloc(sizeof(struct t_ipt_account_ip_list) * table->count); + if (table->ip_list.l == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count); + ret = 0; /* was -ENOMEM */ + goto failure_table; + } + memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count); + } else { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count); + table->ip_list.s = vmalloc(sizeof(struct t_ipt_account_ip_list_short) * table->count); + if (table->ip_list.s == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to allocate %u for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count); + ret = 0; /* was -ENOMEM */ + goto failure_table; + } + memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count); + } + + /* put table into chain */ + spin_lock_bh(&account_lock); + find_table = account_tables; + while( (last_table = find_table) && strncmp(info->name, find_table->name, IPT_ACCOUNT_NAME_LEN) && (find_table = find_table->next) ); + if (find_table != NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table %s found.\n", info->name); + if (find_table->network != info->network || find_table->netmask != info->netmask) { + spin_unlock_bh(&account_lock); + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() wrong network/netmask.\n"); + ret = 0; + goto failure_ip_list; + } + find_table->use_count++; + spin_unlock_bh(&account_lock); + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() incrementing use count.\n"); + ret = 1; + goto failure_ip_list; + } + if (!last_table) + account_tables = table; + else + last_table->next = table; + spin_unlock_bh(&account_lock); + + /* create procfs status file */ + table->status_file = create_proc_entry(table->name, permissions, proc_net_ipt_account); + if (table->status_file == NULL) { + ret = 0; /* was -ENOMEM */ + goto failure_unlink; + } + table->status_file->owner = THIS_MODULE; + table->status_file->data = table; + wmb(); + table->status_file->proc_fops = &account_file_ops; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left.\n"); + /* everything went just okey */ + return 1; + + /* do cleanup in case of failure */ +failure_unlink: + /* remove table from list */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() removing table.\n"); + spin_lock_bh(&account_lock); + last_table = NULL; + table = account_tables; + if (table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() no table found. Leaving.\n"); + spin_unlock_bh(&account_lock); + return 0; /* was -ENOMEM */ + } + while (strncmp(info->name, table->name, IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next)); + if (table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() table already destroyed. Leaving.\n"); + spin_unlock_bh(&account_lock); + return 0; /* was -ENOMEM */ + } + if (last_table) + last_table->next = table->next; + else + account_tables = table->next; + spin_unlock_bh(&account_lock); +failure_ip_list: + /* free memory allocated for statistics table */ + if (!table->shortlisting) + vfree(table->ip_list.l); + else + vfree(table->ip_list.s); +failure_table: + /* free table */ + vfree(table); +failure: + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left. Table not created.\n"); + /* failure return */ + return ret; +} + +static void destroy(void *matchinfo, + unsigned int matchinfosize) +{ + const struct t_ipt_account_info *info = matchinfo; + struct t_ipt_account_table *table, *last_table; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() entered.\n"); + + if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return; + + /* search for table */ + spin_lock_bh(&account_lock); + last_table = NULL; + table = account_tables; + if(table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() no tables found. Leaving.\n"); + spin_unlock_bh(&account_lock); + return; + } + while( strncmp(info->name,table->name,IPT_ACCOUNT_NAME_LEN) && (last_table = table) && (table = table->next) ); + if (table == NULL) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() no table %s not found. Leaving.\n", info->name); + spin_unlock_bh(&account_lock); + return; + } + + /* decrement table use-count */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() decrementing use count.\n"); + table->use_count--; + if (table->use_count) { + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table still in use. Leaving.\n"); + spin_unlock_bh(&account_lock); + return; + } + + /* remove table if use-count is zero */ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table %s not used. Removing.\n", table->name); + + /* unlink table */ + if(last_table) + last_table->next = table->next; + else + account_tables = table->next; + spin_unlock_bh(&account_lock); + + /* wait while table is still in use */ + spin_lock_bh(&table->ip_list_lock); + spin_unlock_bh(&table->ip_list_lock); + + /* remove proc entries */ + remove_proc_entry(table->name, proc_net_ipt_account); + + /* remove table */ + if (!table->shortlisting) + vfree(table->ip_list.l); + else + vfree(table->ip_list.s); + vfree(table); + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() left.\n"); + return; +} + +static struct ipt_match account_match = { + .name = "account", + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() entered.\n"); + printk(version); + /* check params */ + if (netmask > 32 || netmask < 0) { + printk(KERN_INFO "account: Wrong netmask given by netmask parameter (%i). Valid is 32 to 0.\n", netmask); + return -EINVAL; + } + + /* create /proc/net/ipt_account directory */ + proc_net_ipt_account = proc_mkdir("ipt_account", proc_net); + if (!proc_net_ipt_account) { + printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to create procfs entry.\n"); + return -EINVAL; /* was -ENOMEM */ + } + proc_net_ipt_account->owner = THIS_MODULE; + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() left.\n"); + + return ipt_register_match(&account_match); +} + +static void __exit fini(void) +{ + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() entered.\n"); + + ipt_unregister_match(&account_match); + /* remove /proc/net/ipt_account/ directory */ + remove_proc_entry("ipt_account", proc_net); + + dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() left.\n"); +} + +module_init(init); +module_exit(fini); +