1 diff -uNrp linux/net/ipv4/netfilter/ipt_account.c linux/net/ipv4/netfilter/ipt_account.c
2 --- linux/net/ipv4/netfilter/ipt_account.c 1970-01-01 01:00:00.000000000 +0100
3 +++ linux/net/ipv4/netfilter/ipt_account.c 2007-08-04 16:22:15.000000000 +0200
5 +/* Copyright (c) 2004-2007 Piotr 'QuakeR' Gasidlo <quaker@barbara.eu.org>
7 + * This program is free software; you can redistribute it and/or modify
8 + * it under the terms of the GNU General Public License version 2 as
9 + * published by the Free Software Foundation.
12 +#include <linux/version.h>
13 +#include <linux/module.h>
14 +#include <linux/skbuff.h>
15 +#include <linux/vmalloc.h>
16 +#include <linux/proc_fs.h>
17 +#include <linux/seq_file.h>
18 +#include <linux/time.h>
19 +#include <linux/ip.h>
20 +#include <linux/in.h>
22 +#define IPT_ACCOUNT_VERSION "0.1.21"
24 +#define DEBUG_IPT_ACCOUNT
26 +MODULE_AUTHOR("Piotr Gasidlo <quaker@barbara.eu.org>");
27 +MODULE_DESCRIPTION("Traffic accounting module");
28 +MODULE_LICENSE("GPL");
30 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
31 +#include <linux/netfilter/x_tables.h>
33 +#include <linux/netfilter_ipv4/ip_tables.h>
35 +#include <linux/netfilter_ipv4/ipt_account.h>
37 +/* defaults, can be overriden */
38 +static unsigned int netmask = 16; /* Safe netmask, if you try to create table
39 + for larger netblock you will get error.
40 + Increase by command line only when you
41 + known what are you doing. */
43 +#ifdef DEBUG_IPT_ACCOUNT
44 +static int debug = 0;
46 +module_param(netmask, uint, 0400);
48 +MODULE_PARM_DESC(netmask,"maximum *save* netmask");
49 +#ifdef DEBUG_IPT_ACCOUNT
50 +module_param(debug, bool, 0600);
51 +MODULE_PARM_DESC(debug,"enable debugging output");
54 +/* structure with statistics counter, used when table is created without --ashort switch */
55 +struct t_ipt_account_stat_long {
56 + u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other;
57 + u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other;
60 +/* same as above, for tables created with --ashort switch */
61 +struct t_ipt_account_stat_short {
66 +/* structure holding to/from statistics for single ip when table is created without --ashort switch */
67 +struct t_ipt_account_stats_long {
68 + struct t_ipt_account_stat_long src, dst;
69 + struct timespec time; /* time, when statistics was last modified */
72 +/* same as above, for tables created with --ashort switch */
73 +struct t_ipt_account_stats_short {
74 + struct t_ipt_account_stat_short src, dst;
75 + struct timespec time;
78 +/* defines for "show" table option */
82 +#define SHOW_SRC_OR_DST 3
83 +#define SHOW_SRC_AND_DST 4
85 +/* structure describing single table */
86 +struct t_ipt_account_table {
87 + struct list_head list;
88 + atomic_t use; /* use counter, the number of rules which points to this table */
90 + char name[IPT_ACCOUNT_NAME_LEN + 1]; /* table name ( = filename in /proc/net/ipt_account/) */
91 + u_int32_t network, netmask, count; /* network/netmask/hosts count coverted by table */
93 + int shortlisting:1; /* gather only total statistics (set for tables created with --ashort switch) */
94 + int timesrc:1; /* update time when accounting outgoing traffic */
95 + int timedst:1; /* update time when accounting incomming traffic */
96 + int resetonread:1; /* reset statistics after reading it via proc */
97 + int show; /* show with entries */
99 + /* FIXME: why int show:3 results in 'warning: comparison is always 0 due to width of bit-field' in ipt_account_seq_show
100 + * gcc -v: gcc version 3.4.6 */
102 + union { /* statistics for each ip in network/netmask */
103 + struct t_ipt_account_stats_long *l;
104 + struct t_ipt_account_stats_short *s;
106 + rwlock_t stats_lock; /* lock, to assure that above union can be safely modified */
108 + struct proc_dir_entry *pde; /* handle to proc entry */
111 +static LIST_HEAD(ipt_account_tables);
112 +static rwlock_t ipt_account_lock = RW_LOCK_UNLOCKED; /* lock, to assure that table list can be safely modified */
113 +static DECLARE_MUTEX(ipt_account_mutex); /* additional checkentry protection */
115 +static struct file_operations ipt_account_proc_fops;
116 +static struct proc_dir_entry *ipt_account_procdir;
119 + * Function creates new table and inserts it into linked list.
121 +static struct t_ipt_account_table *
122 +ipt_account_table_init(struct t_ipt_account_info *info)
124 + struct t_ipt_account_table *table;
126 +#ifdef DEBUG_IPT_ACCOUNT
127 + if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_init]: name = %s\n", info->name);
131 + * Allocate memory for table.
133 + table = vmalloc(sizeof(struct t_ipt_account_table));
135 + printk(KERN_ERR "ipt_account [ipt_account_table_init]: table = vmalloc(sizeof(struct t_ipt_account_table)) failed.\n");
138 + memset(table, 0, sizeof(struct t_ipt_account_table));
141 + * Table attributes.
143 + strncpy(table->name, info->name, IPT_ACCOUNT_NAME_LEN);
144 + table->name[IPT_ACCOUNT_NAME_LEN] = '\0';
146 + table->network = info->network;
147 + table->netmask = info->netmask;
148 + table->count = (0xffffffff ^ table->netmask) + 1;
151 + * Table properties.
153 + table->shortlisting = info->shortlisting;
154 + table->timesrc = 1;
155 + table->timedst = 1;
156 + table->resetonread = 0;
157 + table->show = SHOW_ANY;
160 + * Initialize use counter.
162 + atomic_set(&table->use, 1);
165 + * Allocate memory for statistic counters.
167 + if (table->shortlisting) {
168 + table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count);
169 + if (!table->stats.s) {
170 + printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count) failed.\n");
171 + goto cleanup_table;
173 + memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
175 + table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count);
176 + if (!table->stats.l) {
177 + printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count) failed.\n");
178 + goto cleanup_table;
180 + memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
186 + table->stats_lock = RW_LOCK_UNLOCKED;
189 + * Create /proc/ipt_account/name entry.
191 + table->pde = create_proc_entry(table->name, S_IWUSR | S_IRUSR, ipt_account_procdir);
193 + goto cleanup_stats;
195 + table->pde->proc_fops = &ipt_account_proc_fops;
196 + table->pde->data = table;
199 + * Insert table into list.
201 + write_lock_bh(&ipt_account_lock);
202 + list_add(&table->list, &ipt_account_tables);
203 + write_unlock_bh(&ipt_account_lock);
208 + * If something goes wrong we end here.
211 + if (table->shortlisting)
212 + vfree(table->stats.s);
214 + vfree(table->stats.l);
224 + * Function destroys table. Table *must* be already unlinked.
227 +ipt_account_table_destroy(struct t_ipt_account_table *table)
229 +#ifdef DEBUG_IPT_ACCOUNT
230 + if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_destory]: name = %s\n", table->name);
232 + remove_proc_entry(table->pde->name, table->pde->parent);
233 + if (table->shortlisting)
234 + vfree(table->stats.s);
236 + vfree(table->stats.l);
241 + * Function increments use counter for table.
244 +ipt_account_table_get(struct t_ipt_account_table *table)
246 +#ifdef DEBUG_IPT_ACCOUNT
247 + if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_get]: name = %s\n", table->name);
249 + atomic_inc(&table->use);
253 + * Function decrements use counter for table. If use counter drops to zero,
254 + * table is removed from linked list and destroyed.
257 +ipt_account_table_put(struct t_ipt_account_table *table)
259 +#ifdef DEBUG_IPT_ACCOUNT
260 + if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_put]: name = %s\n", table->name);
262 + if (atomic_dec_and_test(&table->use)) {
263 + write_lock_bh(&ipt_account_lock);
264 + list_del(&table->list);
265 + write_unlock_bh(&ipt_account_lock);
266 + ipt_account_table_destroy(table);
271 + * Helper function, which returns a structure pointer to a table with
274 +static struct t_ipt_account_table *
275 +__ipt_account_table_find(char *name)
277 + struct list_head *pos;
278 + list_for_each(pos, &ipt_account_tables) {
279 + struct t_ipt_account_table *table = list_entry(pos,
280 + struct t_ipt_account_table, list);
281 + if (!strncmp(table->name, name, IPT_ACCOUNT_NAME_LEN))
288 + * Function, which returns a structure pointer to a table with
289 + * specified name. When such table is found its use coutner
292 +static inline struct t_ipt_account_table *
293 +ipt_account_table_find_get(char *name)
295 + struct t_ipt_account_table *table;
297 +#ifdef DEBUG_IPT_ACCOUNT
298 + if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_find_get]: name = %s\n", name);
300 + read_lock_bh(&ipt_account_lock);
301 + table = __ipt_account_table_find(name);
303 + read_unlock_bh(&ipt_account_lock);
306 + atomic_inc(&table->use);
307 + read_unlock_bh(&ipt_account_lock);
312 + * Helper function, with updates statistics for specified IP. It's only
313 + * used for tables created without --ashort switch.
316 +__account_long(struct t_ipt_account_stat_long *stat, const struct sk_buff *skb)
318 + stat->b_all += skb->len;
321 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
322 + switch (ip_hdr(skb)->protocol) {
324 + switch (skb->nh.iph->protocol) {
327 + stat->b_tcp += skb->len;
331 + stat->b_udp += skb->len;
335 + stat->b_icmp += skb->len;
339 + stat->b_other += skb->len;
345 + * Same as above, but used for tables created with --ashort switch.
348 +__account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb)
350 + stat->b_all += skb->len;
355 + * Match function. Here we do accounting stuff.
358 +match(const struct sk_buff *skb,
359 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
360 + const struct net_device *in,
361 + const struct net_device *out,
362 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
363 + const struct xt_match *match,
365 + const void *matchinfo,
367 + unsigned int protoff,
370 + const struct xt_match_param *par)
373 + struct t_ipt_account_info *info = (struct t_ipt_account_info *)par->matchinfo;
374 + struct t_ipt_account_table *table = info->table;
376 + /* Get current time. */
377 + struct timespec now = CURRENT_TIME_SEC;
378 + /* Default we assume no match. */
381 +#ifdef DEBUG_IPT_ACCOUNT
382 + if (debug) printk(KERN_DEBUG "ipt_account [match]: name = %s\n", table->name);
384 + /* Check whether traffic from source ip address ... */
385 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
386 + address = ntohl(ip_hdr(skb)->saddr);
388 + address = ntohl(skb->nh.iph->saddr);
390 + /* ... is being accounted by this table. */
391 + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
392 + write_lock_bh(&table->stats_lock);
393 + /* Yes, account this packet. */
394 +#ifdef DEBUG_IPT_ACCOUNT
395 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
396 + if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet src = %pI4, proto = %u.\n", &address, ip_hdr(skb)->protocol);
398 + if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet src = %pI4, proto = %u.\n", &address, skb->nh.iph->protocol);
401 + /* Update counters this host. */
402 + if (!table->shortlisting) {
403 + __account_long(&table->stats.l[address - table->network].src, skb);
404 + if (table->timesrc)
405 + table->stats.l[address - table->network].time = now;
406 + /* Update also counters for all hosts in this table (network address) */
407 + if (table->count > 1) {
408 + __account_long(&table->stats.l[0].src, skb);
409 + table->stats.l[0].time = now;
412 + __account_short(&table->stats.s[address - table->network].src, skb);
413 + if (table->timedst)
414 + table->stats.s[address - table->network].time = now;
415 + if (table->count > 1) {
416 + __account_short(&table->stats.s[0].src, skb);
417 + table->stats.s[0].time = now;
420 + write_unlock_bh(&table->stats_lock);
421 + /* Yes, it's a match. */
425 + /* Do the same thing with destination ip address. */
426 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
427 + address = ntohl(ip_hdr(skb)->daddr);
429 + address = ntohl(skb->nh.iph->daddr);
431 + if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
432 + write_lock_bh(&table->stats_lock);
433 +#ifdef DEBUG_IPT_ACCOUNT
434 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
435 + if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet dst = %pI4, proto = %u.\n", &address, ip_hdr(skb)->protocol);
437 + if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet dst = %pI4, proto = %u.\n", &address, skb->nh.iph->protocol);
440 + if (!table->shortlisting) {
441 + __account_long(&table->stats.l[address - table->network].dst, skb);
442 + table->stats.l[address - table->network].time = now;
443 + if (table->count > 1) {
444 + __account_long(&table->stats.l[0].dst, skb);
445 + table->stats.l[0].time = now;
448 + __account_short(&table->stats.s[address - table->network].dst, skb);
449 + table->stats.s[address - table->network].time = now;
450 + if (table->count > 1) {
451 + __account_short(&table->stats.s[0].dst, skb);
452 + table->stats.s[0].time = now;
455 + write_unlock_bh(&table->stats_lock);
463 + * Checkentry function.
466 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
467 +checkentry(const struct xt_mtchk_param *par)
469 +checkentry(const char *tablename,
470 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
473 + const struct ipt_entry *ip,
475 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
476 + const struct xt_match *match,
479 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
480 + unsigned int matchsize,
482 + unsigned int hook_mask)
485 + struct t_ipt_account_info *info = par->matchinfo;
486 + struct t_ipt_account_table *table;
488 +#ifdef DEBUG_IPT_ACCOUNT
489 + if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: name = %s\n", info->name);
491 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
492 + if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
493 +#ifdef DEBUG_IPT_ACCOUNT
494 + if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
503 + if (info->netmask < ((~0L << (32 - netmask)) & 0xffffffff)) {
504 + printk(KERN_ERR "ipt_account[checkentry]: too big netmask (increase module 'netmask' parameter).\n");
507 + if ((info->network & info->netmask) != info->network) {
508 + printk(KERN_ERR "ipt_account[checkentry]: wrong network/netmask.\n");
511 + if (info->name[0] == '\0') {
512 + printk(KERN_ERR "ipt_account[checkentry]: wrong table name.\n");
517 + * We got new rule. Try to find table with the same name as given in info structure.
518 + * Mutex magic based on xt_hashlimit.c.
520 + down(&ipt_account_mutex);
521 + table = ipt_account_table_find_get(info->name);
523 + if (info->table != NULL) {
524 + if (info->table != table) {
525 + printk(KERN_ERR "ipt_account[checkentry]: reloaded rule has invalid table pointer.\n");
526 + up(&ipt_account_mutex);
529 + up(&ipt_account_mutex);
532 +#ifdef DEBUG_IPT_ACCOUNT
533 + if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, checking.\n");
536 + * Table exists, but whether rule network/netmask/shortlisting matches
537 + * table network/netmask/shortlisting. Failure on missmatch.
539 + if (table->network != info->network || table->netmask != info->netmask || table->shortlisting != info->shortlisting) {
540 + printk(KERN_ERR "ipt_account [checkentry]: table found, rule network/netmask/shortlisting not match table network/netmask/shortlisting.\n");
542 + * Remember to release table usage counter.
544 + ipt_account_table_put(table);
545 + up(&ipt_account_mutex);
548 +#ifdef DEBUG_IPT_ACCOUNT
549 + if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, reusing.\n");
552 + * Link rule with table.
554 + info->table = table;
557 +#ifdef DEBUG_IPT_ACCOUNT
558 + if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table not found, creating new one.\n");
561 + * Table not exist, create new one.
563 + info->table = table = ipt_account_table_init(info);
565 + up(&ipt_account_mutex);
569 + up(&ipt_account_mutex);
574 + * Destroy function.
578 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
579 + const struct xt_mtdtor_param *par
581 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
582 + const struct xt_match *match,
584 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
588 + unsigned int matchsize
593 + struct t_ipt_account_info *info = par->matchinfo;
595 +#ifdef DEBUG_IPT_ACCOUNT
596 + if (debug) printk(KERN_DEBUG "ipt_account [destroy]: name = %s\n", info->name);
598 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
599 + if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
600 +#ifdef DEBUG_IPT_ACCOUNT
601 + if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
608 + * Release table, by decreasing its usage counter. When
609 + * counter hits zero, memory used by table structure is
610 + * released and table is removed from list.
612 + ipt_account_table_put(info->table);
616 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
617 +static struct xt_match account_match = {
619 +static struct ipt_match account_match = {
622 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
626 + .checkentry = checkentry,
627 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
628 + .matchsize = sizeof(struct t_ipt_account_info),
630 + .destroy = &destroy,
635 + * Below functions (ipt_account_seq_start, ipt_account_seq_next,
636 + * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write)
637 + * are used to implement proc stuff.
639 +static void *ipt_account_seq_start(struct seq_file *sf, loff_t *pos)
641 + struct proc_dir_entry *pde = sf->private;
642 + struct t_ipt_account_table *table = pde->data;
645 + if (table->resetonread) {
646 + /* When we reset entries after read we must have exclusive lock. */
647 + write_lock_bh(&table->stats_lock);
649 + read_lock_bh(&table->stats_lock);
651 + if (*pos >= table->count)
653 + i = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
655 + return ERR_PTR(-ENOMEM);
660 +static void *ipt_account_seq_next(struct seq_file *sf, void *v, loff_t *pos)
662 + struct proc_dir_entry *pde = sf->private;
663 + struct t_ipt_account_table *table = pde->data;
664 + unsigned int *i = (unsigned int *)v;
667 + if (*i >= table->count) {
674 +static void ipt_account_seq_stop(struct seq_file *sf, void *v)
676 + struct proc_dir_entry *pde = sf->private;
677 + struct t_ipt_account_table *table = pde->data;
679 + if (table->resetonread) {
680 + write_unlock_bh(&table->stats_lock);
682 + read_unlock_bh(&table->stats_lock);
686 +static int ipt_account_seq_show(struct seq_file *sf, void *v)
688 + struct proc_dir_entry *pde = sf->private;
689 + struct t_ipt_account_table *table = pde->data;
690 + unsigned int *i = (unsigned int *)v;
692 + struct timespec now = CURRENT_TIME_SEC;
694 + u_int32_t address = table->network + *i;
696 + if (!table->shortlisting) {
697 + struct t_ipt_account_stats_long *l = &table->stats.l[*i];
698 + /* Don't list rows not matching show requirements. */
700 + ((table->show == SHOW_SRC) && (l->src.p_all == 0)) ||
701 + ((table->show == SHOW_DST) && (l->dst.p_all == 0)) ||
702 + ((table->show == SHOW_SRC_OR_DST) && ((l->src.p_all == 0) && (l->dst.p_all == 0))) ||
703 + ((table->show == SHOW_SRC_AND_DST) && ((l->src.p_all == 0) || (l->dst.p_all == 0)))
708 + "ip = %pI4 bytes_src = %llu %llu %llu %llu %llu packets_src = %llu %llu %llu %llu %llu bytes_dst = %llu %llu %llu %llu %llu packets_dst = %llu %llu %llu %llu %llu time = %lu\n",
730 + now.tv_sec - l->time.tv_sec
732 + if (table->resetonread)
733 + memset(l, 0, sizeof(struct t_ipt_account_stats_long));
736 + struct t_ipt_account_stats_short *s = &table->stats.s[*i];
738 + ((table->show == SHOW_SRC) && (s->src.p_all == 0)) ||
739 + ((table->show == SHOW_DST) && (s->dst.p_all == 0)) ||
740 + ((table->show == SHOW_SRC_OR_DST) && ((s->src.p_all == 0) && (s->dst.p_all == 0))) ||
741 + ((table->show == SHOW_SRC_AND_DST) && ((s->src.p_all == 0) || (s->dst.p_all == 0)))
746 + "ip = %pI4 bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
752 + now.tv_sec - s->time.tv_sec
754 + if (table->resetonread)
755 + memset(s, 0, sizeof(struct t_ipt_account_stats_short));
761 +static struct seq_operations ipt_account_seq_ops = {
762 + .start = ipt_account_seq_start,
763 + .next = ipt_account_seq_next,
764 + .stop = ipt_account_seq_stop,
765 + .show = ipt_account_seq_show
768 +static ssize_t ipt_account_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ofs)
771 + struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
772 + struct t_ipt_account_table *table = pde->data;
774 + u_int32_t o[4], ip;
775 + struct t_ipt_account_stats_long l;
776 + struct t_ipt_account_stats_short s;
778 +#ifdef DEBUG_IPT_ACCOUNT
779 + if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_proc_write]: name = %s.\n", table->name);
781 + if (copy_from_user(buffer, input, 1024))
783 + buffer[1023] = '\0';
785 + if (!strncmp(buffer, "reset\n", 6)) {
787 + * User requested to clear all table. Ignorant, does
788 + * he known how match time it took us to fill it? ;-)
790 + write_lock_bh(&table->stats_lock);
791 + if (table->shortlisting)
792 + memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
794 + memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
795 + write_unlock_bh(&table->stats_lock);
796 + } else if (!strncmp(buffer, "reset-on-read=yes\n", 18)) {
798 + * We must be sure that ipt_account_seq_* is not running now. This option
799 + * changes lock type which is taken in ipt_account_seq_{start|stop}. When
800 + * we change this option without this lock, and ipt_account_seq_start is
801 + * already run (but not ipt_account_seq_stop) there is possibility that
802 + * we execute wrong "unlock" function.
804 + write_lock_bh(&table->stats_lock);
805 + table->resetonread = 1;
806 + write_unlock_bh(&table->stats_lock);
807 + } else if (!strncmp(buffer, "reset-on-read=no\n", 17)) {
808 + write_lock_bh(&table->stats_lock);
809 + table->resetonread = 0;
810 + write_unlock_bh(&table->stats_lock);
811 + } else if (!strncmp(buffer, "reset-on-read\n", 14)) {
812 + write_lock_bh(&table->stats_lock);
813 + table->resetonread = 1;
814 + write_unlock_bh(&table->stats_lock);
815 + } else if (!strncmp(buffer, "show=any\n", 9)) {
817 + * Here we should lock but we don't have to. So we don't lock. We only get
818 + * wrong results on already run ipt_account_seq_show, but we won't crush
821 + table->show = SHOW_ANY;
822 + } else if (!strncmp(buffer, "show=src\n", 9)) {
823 + table->show = SHOW_SRC;
824 + } else if (!strncmp(buffer, "show=dst\n", 9)) {
825 + table->show = SHOW_DST;
826 + } else if (!strncmp(buffer, "show=src-or-dst\n", 16) || !strncmp(buffer, "show=dst-or-src\n", 16)) {
827 + table->show = SHOW_SRC_OR_DST;
828 + } else if (!strncmp(buffer, "show=src-and-dst\n", 17) || !strncmp(buffer, "show=dst-and-src\n", 17)) {
829 + table->show = SHOW_SRC_AND_DST;
830 + } else if (!strncmp(buffer, "time=any\n", 9)) {
831 + table->timesrc = table->timedst = 1;
832 + } else if (!strncmp(buffer, "time=src\n", 9)) {
833 + table->timesrc = 1;
834 + table->timedst = 0;
835 + } else if (!strncmp(buffer, "time=dst\n", 9)) {
836 + table->timesrc = 0;
837 + table->timedst = 1;
838 + } else if (!table->shortlisting && sscanf(buffer, "ip = %u.%u.%u.%u bytes_src = %llu %llu %llu %llu %llu packets_src = %llu %llu %llu %llu %llu bytes_dst = %llu %llu %llu %llu %llu packets_dst = %llu %llu %llu %llu %llu time = %lu",
839 + &o[0], &o[1], &o[2], &o[3],
840 + &l.src.b_all, &l.src.b_tcp, &l.src.b_udp, &l.src.b_icmp, &l.src.b_other,
841 + &l.src.p_all, &l.src.p_tcp, &l.src.p_udp, &l.src.p_icmp, &l.src.p_other,
842 + &l.dst.b_all, &l.dst.b_tcp, &l.dst.b_udp, &l.dst.b_icmp, &l.dst.b_other,
843 + &l.dst.p_all, &l.dst.p_tcp, &l.dst.p_udp, &l.dst.p_icmp, &l.dst.p_other,
844 + &l.time.tv_sec) == 25 ) {
846 + * We got line formated like long listing row. We have to
847 + * check, if IP is accounted by table. If so, we
848 + * simply replace row with user's one.
850 + ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
851 + if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
853 + * Ignore user input time. Set current time.
855 + l.time = CURRENT_TIME_SEC;
856 + write_lock_bh(&table->stats_lock);
857 + table->stats.l[ip - table->network] = l;
858 + write_unlock_bh(&table->stats_lock);
860 + } else if (table->shortlisting && sscanf(buffer, "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
861 + &o[0], &o[1], &o[2], &o[3],
866 + &s.time.tv_sec) == 9) {
868 + * We got line formated like short listing row. Do the
869 + * same action like above.
871 + ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
872 + if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
873 + s.time = CURRENT_TIME_SEC;
874 + write_lock_bh(&table->stats_lock);
875 + table->stats.s[ip - table->network] = s;
876 + write_unlock_bh(&table->stats_lock);
880 + * We don't understand what user have just wrote.
888 +static int ipt_account_proc_open(struct inode *inode, struct file *file)
890 + int ret = seq_open(file, &ipt_account_seq_ops);
892 + struct seq_file *sf = file->private_data;
893 + struct proc_dir_entry *pde = PDE(inode);
894 + struct t_ipt_account_table *table = pde->data;
898 + ipt_account_table_get(table);
903 +static int ipt_account_proc_release(struct inode *inode, struct file *file)
905 + struct proc_dir_entry *pde = PDE(inode);
906 + struct t_ipt_account_table *table = pde->data;
909 + ret = seq_release(inode, file);
912 + ipt_account_table_put(table);
917 +static struct file_operations ipt_account_proc_fops = {
918 + .owner = THIS_MODULE,
919 + .open = ipt_account_proc_open,
921 + .write = ipt_account_proc_write,
922 + .llseek = seq_lseek,
923 + .release = ipt_account_proc_release
927 + * Module init function.
929 +static int __init init(void)
933 + printk(KERN_INFO "ipt_account %s : Piotr Gasidlo <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION);
935 + /* Check module parameters. */
936 + if (netmask > 32 || netmask < 0) {
937 + printk(KERN_ERR "ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask);
942 + /* Register match. */
943 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
944 + if (xt_register_match(&account_match)) {
946 + if (ipt_register_match(&account_match)) {
952 + /* Create /proc/net/ipt_account/ entry. */
953 + ipt_account_procdir = proc_mkdir("ipt_account", init_net.proc_net);
954 + if (!ipt_account_procdir) {
955 + printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", init_net.proc_net) failed.\n");
957 + goto cleanup_match;
962 + /* If something goes wrong we end here. */
964 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
965 + xt_unregister_match(&account_match);
967 + ipt_unregister_match(&account_match);
974 + * Module exit function.
976 +static void __exit fini(void)
978 + /* Remove /proc/net/ipt_account/ */
979 + remove_proc_entry(ipt_account_procdir->name, ipt_account_procdir->parent);
980 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
981 + xt_unregister_match(&account_match);
983 + ipt_unregister_match(&account_match);
990 diff -uNrp linux/include/linux/netfilter_ipv4/ipt_account.h linux/include/linux/netfilter_ipv4/ipt_account.h
991 --- linux/include/linux/netfilter_ipv4/ipt_account.h 1970-01-01 01:00:00.000000000 +0100
992 +++ linux/include/linux/netfilter_ipv4/ipt_account.h 2007-08-04 15:24:49.000000000 +0200
994 +/* Copyright (c) 2004-2007 Piotr 'QuakeR' Gasidlo <quaker@barbara.eu.org>
996 + * This program is free software; you can redistribute it and/or modify
997 + * it under the terms of the GNU General Public License version 2 as
998 + * published by the Free Software Foundation.
1001 +#ifndef _IPT_ACCOUNT_H_
1002 +#define _IPT_ACCOUNT_H_
1004 +#define IPT_ACCOUNT_NAME_LEN 64
1006 +struct t_ipt_account_table;
1008 +struct t_ipt_account_info {
1009 + char name[IPT_ACCOUNT_NAME_LEN + 1];
1010 + u_int32_t network, netmask;
1011 + int shortlisting:1;
1012 + /* pointer to the table for fast matching */
1013 + struct t_ipt_account_table *table;
1016 +#endif /* _IPT_ACCOUNT_H */
1018 diff -uNrp linux/net/ipv4/netfilter/Makefile.a linux/net/ipv4/netfilter/Makefile.b
1019 --- linux/net/ipv4/netfilter/Makefile 1971-01-01 01:00:00.000000000 +0100
1020 +++ linux/net/ipv4/netfilter/Makefile 2007-08-15 13:23:25.375304000 +0200
1022 +obj-$(CONFIG_IP_NF_MATCH_ACCOUNT) += ipt_account.o
1023 diff -Nur linux/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig
1024 --- linux/net/ipv4/netfilter/Kconfig 2006-05-02 23:38:44.000000000 +0200
1025 +++ linux/net/ipv4/netfilter/Kconfig 2006-05-04 11:23:02.000000000 +0200
1026 @@ -606,5 +606,51 @@
1027 Allows altering the ARP packet payload: source and destination
1028 hardware and network addresses.
1030 +config IP_NF_MATCH_ACCOUNT
1031 + tristate "account match support"
1032 + depends on IP_NF_IPTABLES && PROC_FS
1034 + This match is used for accounting traffic for all hosts in
1035 + defined network/netmask.
1038 + - long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics
1039 + - one iptables rule for all hosts in network/netmask
1040 + - loading/saving counters (by reading/writting to procfs entries)
1044 + account traffic for/to 192.168.0.0/24 network into table mynetwork:
1046 + # iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24
1048 + account traffic for/to WWW serwer for 192.168.0.0/24 network into table
1051 + # iptables -A INPUT -p tcp --dport 80
1052 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
1053 + # iptables -A OUTPUT -p tcp --sport 80
1054 + -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
1058 + # cat /proc/net/ipt_account/mynetwork
1059 + # cat /proc/net/ipt_account/mywwwserver
1063 + # echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver
1066 + http://www.barbara.eu.org/~quaker/ipt_account/
1068 +config IP_NF_MATCH_ACCOUNT_DEBUG
1069 + bool "account debugging output"
1070 + depends on IP_NF_MATCH_ACCOUNT
1072 + Say Y to get lots of debugging output.