* ipt_account match update
@ 2006-04-01 16:53 Piotr Gasidlo
0 siblings, 0 replies; only message in thread
From: Piotr Gasidlo @ 2006-04-01 16:53 UTC (permalink / raw)
To: netfilter-devel; +Cc: kaber
[-- Attachment #1.1: Type: text/plain, Size: 1135 bytes --]
Hello,
I've rewritten my ipt_account match. Main changes:
- big code cleanup,
- removed code that parsed input from user while writing to proc entry -
I've used sscanf (in kernel) instead,
- match()ing is faster (speeded up code finding the right table to
update)
- code is fully compatible with latest 2.6.16/2.4.32 kernels.
Including diff against patch-o-matic-ng SVN. No iptables (userspace)
patches are needed.
There is one problem. While rewriting I broke binary compatiblity (I've
added new variable to t_ipt_account_info structure, which is used by
iptables userspace program).
And I've question. Do I have to write this "update" like ipt_MARK was
rewriten in kernel? Or should I leave it like it is (ipt_account
is not in stock kernel, it will never be, and after applying patches
from patch-o-matic-ng user *have* to recompile iptables, so problem
I think disapears).
--
Piotr 'QuakeR' Gasidło, BOFH @ pandora.barbara.eu.org
############## sending lusers to /dev/null since 1998
##### Waiting for tomorrow, for a little ray of light
### Waiting for tomorrow just to see your smile again
[-- Attachment #1.2: ipt_account_0.1.20.diff --]
[-- Type: text/plain, Size: 113342 bytes --]
Index: patchlets/account/linux/include/linux/netfilter_ipv4/ipt_account.h
===================================================================
--- patch-o-matic-ng.orig/patchlets/account/linux/include/linux/netfilter_ipv4/ipt_account.h (wersja 6575)
+++ patch-o-matic-ng/patchlets/account/linux/include/linux/netfilter_ipv4/ipt_account.h (kopia robocza)
@@ -1,10 +1,8 @@
-/*
- * accounting match (ipt_account.c)
- * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org)
+/* Copyright (c) 2004-2006 Piotr 'QuakeR' Gasidło <quaker@barbara.eu.org>
*
- * Version: 0.1.7
- *
- * This software is distributed under the terms of GNU GPL
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#ifndef _IPT_ACCOUNT_H_
@@ -12,15 +10,15 @@
#define IPT_ACCOUNT_NAME_LEN 64
-#define IPT_ACCOUNT_NAME "ipt_account"
-#define IPT_ACCOUNT_VERSION "0.1.7"
+struct t_ipt_account_table;
struct t_ipt_account_info {
- char name[IPT_ACCOUNT_NAME_LEN];
- u_int32_t network;
- u_int32_t netmask;
+ char name[IPT_ACCOUNT_NAME_LEN + 1];
+ u_int32_t network, netmask;
int shortlisting:1;
+ /* pointer to the table for fast matching */
+ struct t_ipt_account_table *table;
};
-#endif
+#endif /* _IPT_ACCOUNT_H */
Index: patchlets/account/linux/net/ipv4/netfilter/ipt_account.c
===================================================================
--- patch-o-matic-ng.orig/patchlets/account/linux/net/ipv4/netfilter/ipt_account.c (wersja 6575)
+++ patch-o-matic-ng/patchlets/account/linux/net/ipv4/netfilter/ipt_account.c (kopia robocza)
@@ -1,533 +1,285 @@
-/*
- * accounting match (ipt_account.c)
- * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org)
+/* Copyright (c) 2004-2006 Piotr 'QuakeR' Gasidło <quaker@barbara.eu.org>
*
- * Version: 0.1.7
- *
- * This software is distributed under the terms of GNU GPL
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/time.h>
+#include <asm/uaccess.h>
#include <linux/spinlock.h>
-#include <linux/vmalloc.h>
#include <linux/interrupt.h>
-#include <linux/ctype.h>
-#include <linux/seq_file.h>
+#define IPT_ACCOUNT_VERSION "0.1.20"
-#include <asm/uaccess.h>
+// #define DEBUG_IPT_ACCOUNT
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ipt_account.h>
-
-#if defined(CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG)
- #define dprintk(format,args...) printk(format,##args)
+#ifdef DEBUG_IPT_ACCOUNT
+ #define dprintk(format, args...) printk(format, ##args)
#else
- #define dprintk(format,args...)
+ #define dprintk(format, args...)
#endif
-static char version[] =
-KERN_INFO IPT_ACCOUNT_NAME " " IPT_ACCOUNT_VERSION " : Piotr Gasidło <quaker@barbara.eu.org>, 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 <quaker@barbara.eu.org>");
-MODULE_DESCRIPTION("Traffic accounting modules");
+MODULE_DESCRIPTION("Traffic accounting module");
MODULE_LICENSE("GPL");
-MODULE_PARM(permissions,"i");
-MODULE_PARM_DESC(permissions,"permissions on /proc/net/ipt_account/* files");
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_account.h>
+
+/* defaults, can be overriden */
+static unsigned int netmask = 16; /* Safe netmask, if you try to create table
+ for larger netblock you will get error.
+ Increase by command line only when you
+ known what are you doing. */
+
MODULE_PARM(netmask, "i");
-MODULE_PARM_DESC(netmask, "maximum *save* size of one list (netmask)");
+MODULE_PARM_DESC(netmask,"maximum *save* 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 */
+/* structure with statistics counter, used when table is created without --ashort switch */
+struct t_ipt_account_stat_long {
+ u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other;
+ u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other;
};
-/* stucture with statistics counters, used when table is created with --ashort switch */
+/* same as above, for tables 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 */
+ u_int64_t b_all;
+ u_int64_t p_all;
};
-
-/* 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 */
-
+
+/* structure holding to/from statistics for single ip when table is created without --ashort switch */
+struct t_ipt_account_stats_long {
+ struct t_ipt_account_stat_long src, dst;
+ struct timespec time; /* time, when statistics was last modified */
};
-/* 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;
+/* same as above, for tables created with --ashort switch */
+struct t_ipt_account_stats_short {
+ struct t_ipt_account_stat_short src, dst;
+ struct timespec 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;
-};
+ struct list_head list;
+ atomic_t use; /* use counter, the number of rules which points to this table */
-/* we must use spinlocks to avoid parallel modifications of table list */
-static spinlock_t account_lock = SPIN_LOCK_UNLOCKED;
+ char name[IPT_ACCOUNT_NAME_LEN + 1]; /* table name ( = filename in /proc/net/ipt_account/) */
+ u_int32_t network, netmask, count; /* network/netmask/hosts count coverted by table */
-static struct proc_dir_entry *proc_net_ipt_account = NULL;
+ int shortlisting:1; /* gather only total statistics (set for tables created with --ashort switch) */
+
+ union { /* statistics for each ip in network/netmask */
+ struct t_ipt_account_stats_long *l;
+ struct t_ipt_account_stats_short *s;
+ } stats;
+ rwlock_t stats_lock; /* lock, to assure that above union can be safely modified */
-/* root pointer holding list of the tables */
-static struct t_ipt_account_table *account_tables = NULL;
+ struct proc_dir_entry *pde; /* handle to proc entry */
+};
-/* convert ascii to ip */
-int atoip(char *buffer, u_int32_t *ip) {
+static LIST_HEAD(ipt_account_tables);
+static rwlock_t ipt_account_lock = RW_LOCK_UNLOCKED; /* lock, to assure that table list can be safely modified */
- char *bufferptr = buffer;
- int part, shift;
+static struct file_operations ipt_account_proc_fops;
+static struct proc_dir_entry *ipt_account_procdir;
+
+/*
+ * Function creates new table and inserts it into linked list.
+ */
+static struct t_ipt_account_table *
+ipt_account_table_init(struct t_ipt_account_info *info)
+{
+ struct t_ipt_account_table *table;
- /* zero ip */
- *ip = 0;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_init]: name = %s\n", info->name);
- /* first must be a digit */
- if (!isdigit(*bufferptr))
- return 0;
+ /*
+ * Allocate memory for table.
+ */
+ table = vmalloc(sizeof(struct t_ipt_account_table));
+ if (!table) {
+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table = vmalloc(sizeof(struct t_ipt_account_table)) failed.\n");
+ goto cleanup_none;
+ }
+ memset(table, 0, sizeof(struct t_ipt_account_table));
- /* 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;
+ /*
+ * Table attributes.
+ */
+ strncpy(table->name, info->name, IPT_ACCOUNT_NAME_LEN);
+ table->name[IPT_ACCOUNT_NAME_LEN - 1] = '\0';
+
+ table->network = info->network;
+ table->netmask = info->netmask;
+ table->count = (0xffffffff ^ table->netmask) + 1;
+
+ /*
+ * Table properties.
+ */
+ table->shortlisting = info->shortlisting;
+
+ /*
+ * Initialize use counter.
+ */
+ atomic_set(&table->use, 1);
+
+ /*
+ * Allocate memory for statistic counters.
+ */
+ if (table->shortlisting) {
+ table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count);
+ if (!table->stats.s) {
+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count) failed.\n");
+ goto cleanup_table;
}
- if (*bufferptr == '.') {
- if (part > 255)
- return 0;
- *ip |= part << shift;
- shift -= 8;
- part = 0;
- continue;
+ memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
+ } else {
+ table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count);
+ if (!table->stats.l) {
+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count) failed.\n");
+ goto cleanup_table;
}
- return 0;
+ memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
}
- /* 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;
- *ip |= part;
- break;
- }
+ /*
+ * Reset locks.
+ */
+ table->stats_lock = RW_LOCK_UNLOCKED;
+
+ /*
+ * Create /proc/ipt_account/name entry.
+ */
+ table->pde = create_proc_entry(table->name, S_IWUSR | S_IRUSR, ipt_account_procdir);
+ if (!table->pde) {
+ goto cleanup_stats;
}
- return (bufferptr - buffer);
-}
+ table->pde->proc_fops = &ipt_account_proc_fops;
+ table->pde->data = table;
+
+ /*
+ * Insert table into list.
+ */
+ write_lock_bh(&ipt_account_lock);
+ list_add(&table->list, &ipt_account_tables);
+ write_unlock_bh(&ipt_account_lock);
+
+ return table;
-/* convert ascii to 64bit integer */
-int atoi64(char *buffer, u_int64_t *i) {
- char *bufferptr = buffer;
-
- /* zero integer */
- *i = 0;
+ /*
+ * If something goes wrong we end here.
+ */
+cleanup_stats:
+ if (table->shortlisting)
+ vfree(table->stats.s);
+ else
+ vfree(table->stats.l);
- while (isdigit(*bufferptr)) {
- *i = *i * 10 + (*bufferptr - '0');
- bufferptr++;
- }
- return (bufferptr - buffer);
+cleanup_table:
+ vfree(table);
+cleanup_none:
+ return NULL;
+
}
-static void *account_seq_start(struct seq_file *s, loff_t *pos)
+/*
+ * Function destroys table. Table *must* be already unlinked.
+ */
+static void
+ipt_account_table_destroy(struct t_ipt_account_table *table)
{
- struct proc_dir_entry *pde = s->private;
- struct t_ipt_account_table *table = pde->data;
-
- unsigned int *bucket;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_destory]: name = %s\n", table->name);
- 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;
+ remove_proc_entry(table->pde->name, table->pde->parent);
+ if (table->shortlisting)
+ vfree(table->stats.s);
+ else
+ vfree(table->stats.l);
+ vfree(table);
}
-static void *account_seq_next(struct seq_file *s, void *v, loff_t *pos)
+/*
+ * Function increments use counter for table.
+ */
+static inline void
+ipt_account_table_get(struct t_ipt_account_table *table)
{
- struct proc_dir_entry *pde = s->private;
- struct t_ipt_account_table *table = pde->data;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_get]: name = %s\n", table->name);
- unsigned int *bucket = (unsigned int *)v;
-
- *pos = ++(*bucket);
- if (*pos >= table->count) {
- kfree(v);
- return NULL;
- }
- return bucket;
+ atomic_inc(&table->use);
}
-static void account_seq_stop(struct seq_file *s, void *v)
+/*
+ * Function decrements use counter for table. If use counter drops to zero,
+ * table is removed from linked list and destroyed.
+ */
+static inline void
+ipt_account_table_put(struct t_ipt_account_table *table)
{
- 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);
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_put]: name = %s\n", table->name);
+
+ if (atomic_dec_and_test(&table->use)) {
+ write_lock_bh(&ipt_account_lock);
+ list_del(&table->list);
+ write_unlock_bh(&ipt_account_lock);
+ ipt_account_table_destroy(table);
+ }
}
-static int account_seq_write(struct file *file, const char *ubuffer,
- size_t ulength, loff_t *pos)
+/*
+ * Helper function, which returns a structure pointer to a table with
+ * specified name.
+ */
+static struct t_ipt_account_table *
+__ipt_account_table_find(char *name)
{
- 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 = %zi.\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;
+ struct list_head *pos;
+ list_for_each(pos, &ipt_account_tables) {
+ struct t_ipt_account_table *table = list_entry(pos,
+ struct t_ipt_account_table, list);
+ if (!strncmp(table->name, name, IPT_ACCOUNT_NAME_LEN))
+ return table;
}
-
- 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 (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\n", bufferptr - buffer);
- bufferptr += 4;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\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 (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\n", bufferptr - buffer);
- bufferptr += 4;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\n", dummy, bufferptr - buffer);
- bufferptr += len;
- continue; /* skip time token */
- } else {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected token (%ti).\n", bufferptr - buffer);
- return length; /* expected token */
- }
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\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;
+ return NULL;
}
-
-static int account_seq_show(struct seq_file *s, void *v)
+/*
+ * Function, which returns a structure pointer to a table with
+ * specified name. When such table is found its use coutner
+ * is incremented.
+ */
+static inline struct t_ipt_account_table *
+ipt_account_table_find_get(char *name)
{
- struct proc_dir_entry *pde = s->private;
- struct t_ipt_account_table *table = pde->data;
- unsigned int *bucket = (unsigned int *)v;
+ struct t_ipt_account_table *table;
- u_int32_t address = table->network + *bucket;
- struct timespec last;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_find_get]: name = %s\n", name);
- 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
- );
+ read_lock_bh(&ipt_account_lock);
+ table = __ipt_account_table_find(name);
+ if (!table) {
+ read_unlock_bh(&ipt_account_lock);
+ return NULL;
}
- return 0;
-}
+ atomic_inc(&table->use);
+ read_unlock_bh(&ipt_account_lock);
+ return table;
+}
-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)
+/*
+ * Helper function, with updates statistics for specified IP. It's only
+ * used for tables created without --ashort switch.
+ */
+static inline void
+__account_long(struct t_ipt_account_stat_long *stat, const struct sk_buff *skb)
{
- 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++;
@@ -550,391 +302,467 @@
}
}
-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 */
+/*
+ * Same as above, but used for tables created with --ashort switch.
+ */
+static inline void
+__account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb)
+{
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,
- u_int16_t datalen,
- int *hotdrop)
+/*
+ * Match function. Here we do accounting stuff.
+ */
+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,
+ u_int16_t datalen,
+ 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;
+ struct t_ipt_account_info *info = (struct t_ipt_account_info *)matchinfo;
+ struct t_ipt_account_table *table = info->table;
+ u_int32_t address;
+ /* Get current time. */
+ struct timespec now;
+ jiffies_to_timespec(jiffies, &now);
+ /* Default we assume no match. */
+ int ret = 0;
+
+ dprintk(KERN_DEBUG "ipt_account [match]: name = %s\n", table->name);
- 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 ... */
+ /* Check whether traffic from source ip address ... */
address = ntohl(skb->nh.iph->saddr);
- /* ... is being accounted by this table */
+ /* ... 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 */
+ write_lock_bh(&table->stats_lock);
+ /* Yes, account this packet. */
+ dprintk(KERN_DEBUG "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;
+ __account_long(&table->stats.l[address - table->network].src, skb);
+ table->stats.l[address - table->network].time = now;
+ /* Update also counters for all hosts in this table (network address) */
+ if (table->count > 1) {
+ __account_long(&table->stats.l[0].src, skb);
+ table->stats.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;
+ __account_short(&table->stats.s[address - table->network].src, skb);
+ table->stats.s[address - table->network].time = now;
+ if (table->count > 1) {
+ __account_short(&table->stats.s[0].src, skb);
+ table->stats.s[0].time = now;
}
}
- /* yes, it's a match */
+ write_unlock_bh(&table->stats_lock);
+ /* Yes, it's a match. */
ret = 1;
}
-
- /* do the same thing with destination ip address */
+
+ /* 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);
+ write_lock_bh(&table->stats_lock);
+ dprintk(KERN_DEBUG "ipt_account: [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;
+ __account_long(&table->stats.l[address - table->network].dst, skb);
+ table->stats.l[address - table->network].time = now;
+ if (table->count > 1) {
+ __account_long(&table->stats.l[0].dst, skb);
+ table->stats.l[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;
+ __account_short(&table->stats.s[address - table->network].dst, skb);
+ table->stats.s[address - table->network].time = now;
+ if (table->count > 1) {
+ __account_short(&table->stats.s[0].dst, skb);
+ table->stats.s[0].time = now;
}
}
+ write_unlock_bh(&table->stats_lock);
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)
+/*
+ * Checkentry function.
+ */
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ipt_ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ 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;
+ struct t_ipt_account_info *info = matchinfo;
+ struct t_ipt_account_table *table;
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() entered.\n");
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: name = %s\n", info->name);
+
+ if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info)))
+ return 0;
- 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;
+ /*
+ * Sanity checks.
+ */
+ if (info->netmask < ((~0L << (32 - netmask)) & 0xffffffff)) {
+ printk(KERN_ERR "ipt_account[checkentry]: too big netmask (increase module 'netmask' parameter).\n");
+ return 0;
}
- 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;
+ if ((info->network & info->netmask) != info->network) {
+ printk(KERN_ERR "ipt_account[checkentry]: wrong network/netmask.\n");
+ return 0;
}
-
- /* table doesn't exist - create new */
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu 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 %zu for new table %s.\n", sizeof(struct t_ipt_account_table), info->name);
- ret = 0; /* was -ENOMEM */
- goto failure;
+ if (info->name[0] == '\0') {
+ printk(KERN_ERR "ipt_account[checkentry]: wrong table name.\n");
+ return 0;
}
+
+ /*
+ * Avoid searching list for already seen and added rules.
+ * Simply increase use count and exit.
+ */
+ if (info->table) {
+ ipt_account_table_get(info->table);
+ return 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->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 %zu 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 %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count);
- ret = 0; /* was -ENOMEM */
- goto failure_table;
+ /*
+ * We got new rule. Try to find table with the same name as given in info structure.
+ */
+ table = ipt_account_table_find_get(info->name);
+ if (!table) {
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: table not found, creating new one.\n");
+ /*
+ * Table not exist, create new one.
+ */
+ table = ipt_account_table_init(info);
+ if (!table) {
+ return 0;
}
- memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count);
} else {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu 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 %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count);
- ret = 0; /* was -ENOMEM */
- goto failure_table;
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: table found, checking.\n");
+ /*
+ * Table exists, but whether rule network/netmask/shortlisting matches
+ * table network/netmask/shortlisting. Failure on missmatch.
+ */
+ if (table->network != info->network || table->netmask != info->netmask || table->shortlisting != info->shortlisting) {
+ printk(KERN_ERR "ipt_account [checkentry]: table found, rule network/netmask/shortlisting not match table network/netmask/shortlisting.\n");
+ /*
+ * Remember to release table usage counter.
+ */
+ ipt_account_table_put(table);
+ return 0;
}
- memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count);
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: table found, reusing.\n");
}
+ /*
+ * Link rule with table.
+ */
+ info->table = table;
+ return 1;
+}
+
+/*
+ * Destroy function.
+ */
+static void
+destroy(void *matchinfo, unsigned int matchsize)
+{
+ struct t_ipt_account_info *info = matchinfo;
- /* 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);
+ dprintk(KERN_DEBUG "ipt_account [destroy]: name = %s\n", info->name);
- /* 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();
-// if (!table->shortlisting)
- table->status_file->proc_fops = &account_file_ops;
-// else
-// table->status_file->proc_fops = &account_file_ops_short;
+ /*
+ * Release table, by decreasing its usage counter. When
+ * counter hits zero, memory used by table structure is
+ * released and table is removed from list.
+ */
+ ipt_account_table_put(info->table);
+ return;
+}
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left.\n");
- /* everything went just okey */
- return 1;
+static struct ipt_match account_match = {
+ .name = "account",
+ .match = &match,
+ .checkentry = &checkentry,
+ .destroy = &destroy,
+ .me = THIS_MODULE
+};
- /* 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 */
+/*
+ * Below functions (ipt_account_seq_start, ipt_account_seq_next,
+ * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write)
+ * are used to implement proc stuff.
+ */
+static void *ipt_account_seq_start(struct seq_file *sf, loff_t *pos)
+{
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ unsigned int *i;
+
+ read_lock_bh(&table->stats_lock);
+ if (*pos >= table->count)
+ return NULL;
+ i = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
+ if (!i)
+ return ERR_PTR(-ENOMEM);
+ *i = *pos;
+ return i;
+}
+
+static void *ipt_account_seq_next(struct seq_file *sf, void *v, loff_t *pos)
+{
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ unsigned int *i = (unsigned int *)v;
+
+ *pos = ++(*i);
+ if (*i >= table->count) {
+ kfree(v);
+ return NULL;
}
- 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;
+ return i;
}
-static void destroy(void *matchinfo,
- unsigned int matchinfosize)
+static void ipt_account_seq_stop(struct seq_file *sf, void *v)
{
- const struct t_ipt_account_info *info = matchinfo;
- struct t_ipt_account_table *table, *last_table;
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ kfree(v);
+ read_unlock_bh(&table->stats_lock);
+}
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() entered.\n");
+static int ipt_account_seq_show(struct seq_file *sf, void *v)
+{
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ unsigned int *i = (unsigned int *)v;
- if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return;
+ struct timespec now;
+ jiffies_to_timespec(jiffies, &now);
+
+ u_int32_t address = table->network + *i;
- /* 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;
+ if (!table->shortlisting) {
+ struct t_ipt_account_stats_long *l = &table->stats.l[*i];
+ seq_printf(sf,
+ "ip = %u.%u.%u.%u bytes_src = %Lu %Lu %Lu %Lu %Lu packets_src = %Lu %Lu %Lu %Lu %Lu bytes_dst = %Lu %Lu %Lu %Lu %Lu packets_dst = %Lu %Lu %Lu %Lu %Lu time = %lu\n",
+ HIPQUAD(address),
+ l->src.b_all,
+ l->src.b_tcp,
+ l->src.b_udp,
+ l->src.b_icmp,
+ l->src.b_other,
+ l->src.p_all,
+ l->src.p_tcp,
+ l->src.p_udp,
+ l->src.p_icmp,
+ l->src.p_other,
+ l->dst.b_all,
+ l->dst.b_tcp,
+ l->dst.b_udp,
+ l->dst.b_icmp,
+ l->dst.b_other,
+ l->dst.p_all,
+ l->dst.p_tcp,
+ l->dst.p_udp,
+ l->dst.p_icmp,
+ l->dst.p_other,
+ now.tv_sec - l->time.tv_sec
+ );
+ } else {
+ struct t_ipt_account_stats_short *s = &table->stats.s[*i];
+ seq_printf(sf,
+ "ip = %u.%u.%u.%u bytes_src = %Lu packets_src = %Lu bytes_dst = %Lu packets_dst = %Lu time = %lu\n",
+ HIPQUAD(address),
+ s->src.b_all,
+ s->src.p_all,
+ s->dst.b_all,
+ s->dst.p_all,
+ now.tv_sec - s->time.tv_sec
+ );
}
- 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;
+ return 0;
+}
+
+static struct seq_operations ipt_account_seq_ops = {
+ .start = ipt_account_seq_start,
+ .next = ipt_account_seq_next,
+ .stop = ipt_account_seq_stop,
+ .show = ipt_account_seq_show
+};
+
+static ssize_t ipt_account_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ofs)
+{
+ char buffer[1024];
+ struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+ struct t_ipt_account_table *table = pde->data;
+
+ u_int32_t o[4], ip;
+ struct t_ipt_account_stats_long l;
+ struct t_ipt_account_stats_short s;
+
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_proc_write]: name = %s.\n", table->name);
+
+ if (copy_from_user(buffer, input, 1024))
+ return -EFAULT;
+
+ if (!strncmp(buffer, "reset", 5)) {
+ /*
+ * User requested to clear all table. Ignorant, does
+ * he known how match time it took us to fill it? ;-)
+ */
+ write_lock_bh(&table->stats_lock);
+ if (table->shortlisting)
+ memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
+ else
+ memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
+ write_unlock_bh(&table->stats_lock);
+ } else if (!table->shortlisting && sscanf(buffer, "ip = %u.%u.%u.%u bytes_src = %Lu %Lu %Lu %Lu %Lu packets_src = %Lu %Lu %Lu %Lu %Lu bytes_dst = %Lu %Lu %Lu %Lu %Lu packets_dst = %Lu %Lu %Lu %Lu %Lu time = %lu",
+ &o[0], &o[1], &o[2], &o[3],
+ &l.src.b_all, &l.src.b_tcp, &l.src.b_udp, &l.src.b_icmp, &l.src.b_other,
+ &l.src.p_all, &l.src.p_tcp, &l.src.p_udp, &l.src.p_icmp, &l.src.p_other,
+ &l.dst.b_all, &l.dst.b_tcp, &l.dst.b_udp, &l.dst.b_icmp, &l.dst.b_other,
+ &l.dst.p_all, &l.dst.p_tcp, &l.dst.p_udp, &l.dst.p_icmp, &l.dst.p_other,
+ &l.time.tv_sec) == 25) {
+ /*
+ * We got line formated like long listing row. We have to
+ * check, if IP is accounted by table. If so, we
+ * simply replace row with user's one.
+ */
+ ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
+ if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
+ /*
+ * Ignore user input time. Set current time.
+ */
+ jiffies_to_timespec(jiffies, &l.time);
+ write_lock_bh(&table->stats_lock);
+ table->stats.l[ip - table->network] = l;
+ write_unlock_bh(&table->stats_lock);
+ }
+ } else if (table->shortlisting && sscanf(buffer, "ip = %u.%u.%u.%u bytes_src = %Lu packets_src = %Lu bytes_dst = %Lu packets_dst = %Lu time = %lu\n",
+ &o[0], &o[1], &o[2], &o[3],
+ &s.src.b_all,
+ &s.src.p_all,
+ &s.dst.b_all,
+ &s.dst.p_all,
+ &s.time.tv_sec) == 9) {
+ /*
+ * We got line formated like short listing row. Do the
+ * same action like above.
+ */
+ ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
+ if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
+ jiffies_to_timespec(jiffies, &s.time);
+ write_lock_bh(&table->stats_lock);
+ table->stats.s[ip - table->network] = s;
+ write_unlock_bh(&table->stats_lock);
+ }
+ } else {
+ /*
+ * We don't understand what user have just wrote.
+ */
+ return -EIO;
}
- /* remove table if use-count is zero */
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table %s not used. Removing.\n", table->name);
+ return size;
+}
- /* unlink table */
- if(last_table)
- last_table->next = table->next;
- else
- account_tables = table->next;
- spin_unlock_bh(&account_lock);
+static int ipt_account_proc_open(struct inode *inode, struct file *file)
+{
+ int ret = seq_open(file, &ipt_account_seq_ops);
+ if (!ret) {
+ struct seq_file *sf = file->private_data;
+ struct proc_dir_entry *pde = PDE(inode);
+ struct t_ipt_account_table *table = pde->data;
+
+ sf->private = pde;
- /* wait while table is still in use */
- spin_lock_bh(&table->ip_list_lock);
- spin_unlock_bh(&table->ip_list_lock);
+ ipt_account_table_get(table);
+ }
+ return ret;
+}
- /* remove proc entries */
- remove_proc_entry(table->name, proc_net_ipt_account);
+static int ipt_account_proc_release(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *pde = PDE(inode);
+ struct t_ipt_account_table *table = pde->data;
+ int ret;
- /* remove table */
- if (!table->shortlisting)
- vfree(table->ip_list.l);
- else
- vfree(table->ip_list.s);
- vfree(table);
+ ret = seq_release(inode, file);
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() left.\n");
- return;
+ if (!ret)
+ ipt_account_table_put(table);
+
+ return ret;
}
-static struct ipt_match account_match = {
- .name = "account",
- .match = &match,
- .checkentry = &checkentry,
- .destroy = &destroy,
- .me = THIS_MODULE
+static struct file_operations ipt_account_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = ipt_account_proc_open,
+ .read = seq_read,
+ .write = ipt_account_proc_write,
+ .llseek = seq_lseek,
+ .release = ipt_account_proc_release
};
-static int __init init(void)
+/*
+ * Module init function.
+ */
+static int __init init(void)
{
- int err;
-
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() entered.\n");
- printk(version);
- /* check params */
+ int ret = 0;
+
+ printk(KERN_INFO "ipt_account %s : Piotr Gasidło <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION);
+
+ /* Check module parameters. */
if (netmask > 32 || netmask < 0) {
- printk(KERN_INFO "account: Wrong netmask given by netmask parameter (%i). Valid is 32 to 0.\n", netmask);
- err = -EINVAL;
- goto doexit;
+ printk(KERN_ERR "ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask);
+ ret = -EINVAL;
+ goto cleanup_none;
}
+
+ /* Register match. */
+ if (ipt_register_match(&account_match)) {
+ ret = -EINVAL;
+ goto cleanup_none;
+ }
- /* 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");
- err = -ENOMEM;
- goto doexit;
+ /* Create /proc/net/ipt_account/ entry. */
+ ipt_account_procdir = proc_mkdir("ipt_account", proc_net);
+ if (!ipt_account_procdir) {
+ printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", proc_net) failed.\n");
+ ret = -ENOMEM;
+ goto cleanup_match;
}
- proc_net_ipt_account->owner = THIS_MODULE;
- err = ipt_register_match(&account_match);
- if (err) {
- printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to register match.\n");
- remove_proc_entry("ipt_account", proc_net);
- }
-doexit:
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() left.\n");
- return err;
+ return ret;
+
+ /* If something goes wrong we end here. */
+cleanup_match:
+ ipt_unregister_match(&account_match);
+cleanup_none:
+ return ret;
}
-static void __exit fini(void)
+/*
+ * Module exit function.
+ */
+static void __exit fini(void)
{
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() entered.\n");
-
+ /* Remove /proc/net/ipt_account/ */
+ remove_proc_entry(ipt_account_procdir->name, ipt_account_procdir->parent);
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);
Index: patchlets/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h
===================================================================
--- patch-o-matic-ng.orig/patchlets/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h (wersja 6575)
+++ patch-o-matic-ng/patchlets/account/linux-2.6/include/linux/netfilter_ipv4/ipt_account.h (kopia robocza)
@@ -1,10 +1,8 @@
-/*
- * accounting match (ipt_account.c)
- * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org)
+/* Copyright (c) 2004-2006 Piotr 'QuakeR' Gasidło <quaker@barbara.eu.org>
*
- * Version: 0.1.7
- *
- * This software is distributed under the terms of GNU GPL
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#ifndef _IPT_ACCOUNT_H_
@@ -12,15 +10,15 @@
#define IPT_ACCOUNT_NAME_LEN 64
-#define IPT_ACCOUNT_NAME "ipt_account"
-#define IPT_ACCOUNT_VERSION "0.1.7"
+struct t_ipt_account_table;
struct t_ipt_account_info {
- char name[IPT_ACCOUNT_NAME_LEN];
- u_int32_t network;
- u_int32_t netmask;
+ char name[IPT_ACCOUNT_NAME_LEN + 1];
+ u_int32_t network, netmask;
int shortlisting:1;
+ /* pointer to the table for fast matching */
+ struct t_ipt_account_table *table;
};
-#endif
+#endif /* _IPT_ACCOUNT_H */
Index: patchlets/account/linux-2.6/net/ipv4/netfilter/ipt_account.c
===================================================================
--- patch-o-matic-ng.orig/patchlets/account/linux-2.6/net/ipv4/netfilter/ipt_account.c (wersja 6575)
+++ patch-o-matic-ng/patchlets/account/linux-2.6/net/ipv4/netfilter/ipt_account.c (kopia robocza)
@@ -1,533 +1,282 @@
-/*
- * accounting match (ipt_account.c)
- * (C) 2003,2004 by Piotr Gasidlo (quaker@barbara.eu.org)
+/* Copyright (c) 2004-2006 Piotr 'QuakeR' Gasidło <quaker@barbara.eu.org>
*
- * Version: 0.1.7
- *
- * This software is distributed under the terms of GNU GPL
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <linux/ctype.h>
-
+#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <linux/time.h>
-#include <asm/uaccess.h>
+#define IPT_ACCOUNT_VERSION "0.1.20"
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
+// #define DEBUG_IPT_ACCOUNT
-#include <linux/netfilter_ipv4/ip_tables.h>
-#include <linux/netfilter_ipv4/ipt_account.h>
-
-#if defined(CONFIG_IP_NF_MATCH_ACCOUNT_DEBUG)
- #define dprintk(format,args...) printk(format,##args)
+#ifdef DEBUG_IPT_ACCOUNT
+ #define dprintk(format, args...) printk(format, ##args)
#else
- #define dprintk(format,args...)
+ #define dprintk(format, args...)
#endif
-static char version[] =
-KERN_INFO IPT_ACCOUNT_NAME " " IPT_ACCOUNT_VERSION " : Piotr Gasidło <quaker@barbara.eu.org>, 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 <quaker@barbara.eu.org>");
-MODULE_DESCRIPTION("Traffic accounting modules");
+MODULE_DESCRIPTION("Traffic accounting module");
MODULE_LICENSE("GPL");
-module_param(permissions, int, 0400);
-module_param(netmask, int, 0400);
-MODULE_PARM_DESC(permissions,"permissions on /proc/net/ipt_account/* files");
-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 */
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_account.h>
+
+/* defaults, can be overriden */
+static unsigned int netmask = 16; /* Safe netmask, if you try to create table
+ for larger netblock you will get error.
+ Increase by command line only when you
+ known what are you doing. */
+
+module_param(netmask, uint, 0400);
+MODULE_PARM_DESC(netmask,"maximum *save* netmask");
+
+/* structure with statistics counter, used when table is created without --ashort switch */
+struct t_ipt_account_stat_long {
+ u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other;
+ u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other;
};
-/* stucture with statistics counters, used when table is created with --ashort switch */
+/* same as above, for tables 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 */
+ u_int64_t b_all;
+ u_int64_t p_all;
};
-
-/* 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 */
-
+
+/* structure holding to/from statistics for single ip when table is created without --ashort switch */
+struct t_ipt_account_stats_long {
+ struct t_ipt_account_stat_long src, dst;
+ struct timespec time; /* time, when statistics was last modified */
};
-/* 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;
+/* same as above, for tables created with --ashort switch */
+struct t_ipt_account_stats_short {
+ struct t_ipt_account_stat_short src, dst;
+ struct timespec 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;
-};
+ struct list_head list;
+ atomic_t use; /* use counter, the number of rules which points to this table */
-/* we must use spinlocks to avoid parallel modifications of table list */
-static spinlock_t account_lock = SPIN_LOCK_UNLOCKED;
+ char name[IPT_ACCOUNT_NAME_LEN + 1]; /* table name ( = filename in /proc/net/ipt_account/) */
+ u_int32_t network, netmask, count; /* network/netmask/hosts count coverted by table */
-static struct proc_dir_entry *proc_net_ipt_account = NULL;
+ int shortlisting:1; /* gather only total statistics (set for tables created with --ashort switch) */
+
+ union { /* statistics for each ip in network/netmask */
+ struct t_ipt_account_stats_long *l;
+ struct t_ipt_account_stats_short *s;
+ } stats;
+ rwlock_t stats_lock; /* lock, to assure that above union can be safely modified */
-/* root pointer holding list of the tables */
-static struct t_ipt_account_table *account_tables = NULL;
+ struct proc_dir_entry *pde; /* handle to proc entry */
+};
-/* convert ascii to ip */
-int atoip(char *buffer, u_int32_t *ip) {
+static LIST_HEAD(ipt_account_tables);
+static rwlock_t ipt_account_lock = RW_LOCK_UNLOCKED; /* lock, to assure that table list can be safely modified */
- char *bufferptr = buffer;
- int part, shift;
+static struct file_operations ipt_account_proc_fops;
+static struct proc_dir_entry *ipt_account_procdir;
+
+/*
+ * Function creates new table and inserts it into linked list.
+ */
+static struct t_ipt_account_table *
+ipt_account_table_init(struct t_ipt_account_info *info)
+{
+ struct t_ipt_account_table *table;
- /* zero ip */
- *ip = 0;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_init]: name = %s\n", info->name);
- /* first must be a digit */
- if (!isdigit(*bufferptr))
- return 0;
+ /*
+ * Allocate memory for table.
+ */
+ table = vmalloc(sizeof(struct t_ipt_account_table));
+ if (!table) {
+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table = vmalloc(sizeof(struct t_ipt_account_table)) failed.\n");
+ goto cleanup_none;
+ }
+ memset(table, 0, sizeof(struct t_ipt_account_table));
- /* 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;
+ /*
+ * Table attributes.
+ */
+ strncpy(table->name, info->name, IPT_ACCOUNT_NAME_LEN);
+ table->name[IPT_ACCOUNT_NAME_LEN - 1] = '\0';
+
+ table->network = info->network;
+ table->netmask = info->netmask;
+ table->count = (0xffffffff ^ table->netmask) + 1;
+
+ /*
+ * Table properties.
+ */
+ table->shortlisting = info->shortlisting;
+
+ /*
+ * Initialize use counter.
+ */
+ atomic_set(&table->use, 1);
+
+ /*
+ * Allocate memory for statistic counters.
+ */
+ if (table->shortlisting) {
+ table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count);
+ if (!table->stats.s) {
+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count) failed.\n");
+ goto cleanup_table;
}
- if (*bufferptr == '.') {
- if (part > 255)
- return 0;
- *ip |= part << shift;
- shift -= 8;
- part = 0;
- continue;
+ memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
+ } else {
+ table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count);
+ if (!table->stats.l) {
+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count) failed.\n");
+ goto cleanup_table;
}
- return 0;
+ memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
}
- /* 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;
- *ip |= part;
- break;
- }
+ /*
+ * Reset locks.
+ */
+ table->stats_lock = RW_LOCK_UNLOCKED;
+
+ /*
+ * Create /proc/ipt_account/name entry.
+ */
+ table->pde = create_proc_entry(table->name, S_IWUSR | S_IRUSR, ipt_account_procdir);
+ if (!table->pde) {
+ goto cleanup_stats;
}
- return (bufferptr - buffer);
-}
+ table->pde->proc_fops = &ipt_account_proc_fops;
+ table->pde->data = table;
+
+ /*
+ * Insert table into list.
+ */
+ write_lock_bh(&ipt_account_lock);
+ list_add(&table->list, &ipt_account_tables);
+ write_unlock_bh(&ipt_account_lock);
+
+ return table;
-/* convert ascii to 64bit integer */
-int atoi64(char *buffer, u_int64_t *i) {
- char *bufferptr = buffer;
-
- /* zero integer */
- *i = 0;
+ /*
+ * If something goes wrong we end here.
+ */
+cleanup_stats:
+ if (table->shortlisting)
+ vfree(table->stats.s);
+ else
+ vfree(table->stats.l);
- while (isdigit(*bufferptr)) {
- *i = *i * 10 + (*bufferptr - '0');
- bufferptr++;
- }
- return (bufferptr - buffer);
+cleanup_table:
+ vfree(table);
+cleanup_none:
+ return NULL;
+
}
-static void *account_seq_start(struct seq_file *s, loff_t *pos)
+/*
+ * Function destroys table. Table *must* be already unlinked.
+ */
+static void
+ipt_account_table_destroy(struct t_ipt_account_table *table)
{
- struct proc_dir_entry *pde = s->private;
- struct t_ipt_account_table *table = pde->data;
-
- unsigned int *bucket;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_destory]: name = %s\n", table->name);
- 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;
+ remove_proc_entry(table->pde->name, table->pde->parent);
+ if (table->shortlisting)
+ vfree(table->stats.s);
+ else
+ vfree(table->stats.l);
+ vfree(table);
}
-static void *account_seq_next(struct seq_file *s, void *v, loff_t *pos)
+/*
+ * Function increments use counter for table.
+ */
+static inline void
+ipt_account_table_get(struct t_ipt_account_table *table)
{
- struct proc_dir_entry *pde = s->private;
- struct t_ipt_account_table *table = pde->data;
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_get]: name = %s\n", table->name);
- unsigned int *bucket = (unsigned int *)v;
-
- *pos = ++(*bucket);
- if (*pos >= table->count) {
- kfree(v);
- return NULL;
- }
- return bucket;
+ atomic_inc(&table->use);
}
-static void account_seq_stop(struct seq_file *s, void *v)
+/*
+ * Function decrements use counter for table. If use counter drops to zero,
+ * table is removed from linked list and destroyed.
+ */
+static inline void
+ipt_account_table_put(struct t_ipt_account_table *table)
{
- 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_DEBUG "ipt_account [ipt_account_table_put]: name = %s\n", table->name);
-
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() entered.\n");
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() ulength = %zi.\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 (atomic_dec_and_test(&table->use)) {
+ write_lock_bh(&ipt_account_lock);
+ list_del(&table->list);
+ write_unlock_bh(&ipt_account_lock);
+ ipt_account_table_destroy(table);
}
-
- 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 (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\n", bufferptr - buffer);
- bufferptr += 4;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\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 (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\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 (%ti).\n", bufferptr - buffer);
- bufferptr += 4;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\n", dummy, bufferptr - buffer);
- bufferptr += len;
- continue; /* skip time token */
- } else {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected token (%ti).\n", bufferptr - buffer);
- return length; /* expected token */
- }
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\n", bufferptr - buffer);
- return length; /* expected space */
- }
- bufferptr += 1;
- if (*bufferptr != '=') {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected equal (%ti).\n", bufferptr - buffer);
- return length; /* expected equal */
- }
- bufferptr += 1;
- if (!isspace(*bufferptr)) {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() expected space (%ti).\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 (%ti).\n", bufferptr - buffer);
- return length; /* expected int64 */
- }
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": account_seq_write() got %llu (%ti).\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)
+/*
+ * Helper function, which returns a structure pointer to a table with
+ * specified name.
+ */
+static struct t_ipt_account_table *
+__ipt_account_table_find(char *name)
{
- 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
- );
+ struct list_head *pos;
+ list_for_each(pos, &ipt_account_tables) {
+ struct t_ipt_account_table *table = list_entry(pos,
+ struct t_ipt_account_table, list);
+ if (!strncmp(table->name, name, IPT_ACCOUNT_NAME_LEN))
+ return table;
}
- return 0;
+ return NULL;
}
-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)
+/*
+ * Function, which returns a structure pointer to a table with
+ * specified name. When such table is found its use coutner
+ * is incremented.
+ */
+static inline struct t_ipt_account_table *
+ipt_account_table_find_get(char *name)
{
- int ret = seq_open(file, &account_seq_ops);
+ struct t_ipt_account_table *table;
- if (!ret) {
- struct seq_file *sf = file->private_data;
- sf->private = PDE(inode);
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_table_find_get]: name = %s\n", name);
+
+ read_lock_bh(&ipt_account_lock);
+ table = __ipt_account_table_find(name);
+ if (!table) {
+ read_unlock_bh(&ipt_account_lock);
+ return NULL;
}
- return ret;
-}
+ atomic_inc(&table->use);
+ read_unlock_bh(&ipt_account_lock);
+ return table;
+}
-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 */
+/*
+ * Helper function, with updates statistics for specified IP. It's only
+ * used for tables created without --ashort switch.
+ */
+static inline void
+__account_long(struct t_ipt_account_stat_long *stat, const struct sk_buff *skb)
+{
stat->b_all += skb->len;
stat->p_all++;
@@ -550,386 +299,464 @@
}
}
-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 */
+/*
+ * Same as above, but used for tables created with --ashort switch.
+ */
+static inline void
+__account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb)
+{
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)
+/*
+ * Match function. Here we do accounting stuff.
+ */
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ unsigned int protoff,
+ 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;
+ struct t_ipt_account_info *info = (struct t_ipt_account_info *)matchinfo;
+ struct t_ipt_account_table *table = info->table;
+ u_int32_t address;
+ /* Get current time. */
+ struct timespec now = CURRENT_TIME_SEC;
+ /* Default we assume no match. */
+ int ret = 0;
+
+ dprintk(KERN_DEBUG "ipt_account [match]: name = %s\n", table->name);
- 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 ... */
+ /* Check whether traffic from source ip address ... */
address = ntohl(skb->nh.iph->saddr);
- /* ... is being accounted by this table */
+ /* ... 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 */
+ write_lock_bh(&table->stats_lock);
+ /* Yes, account this packet. */
+ dprintk(KERN_DEBUG "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;
+ __account_long(&table->stats.l[address - table->network].src, skb);
+ table->stats.l[address - table->network].time = now;
+ /* Update also counters for all hosts in this table (network address) */
+ if (table->count > 1) {
+ __account_long(&table->stats.l[0].src, skb);
+ table->stats.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;
+ __account_short(&table->stats.s[address - table->network].src, skb);
+ table->stats.s[address - table->network].time = now;
+ if (table->count > 1) {
+ __account_short(&table->stats.s[0].src, skb);
+ table->stats.s[0].time = now;
}
}
- /* yes, it's a match */
+ write_unlock_bh(&table->stats_lock);
+ /* Yes, it's a match. */
ret = 1;
}
-
- /* do the same thing with destination ip address */
+
+ /* 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);
+ write_lock_bh(&table->stats_lock);
+ dprintk(KERN_DEBUG "ipt_account: [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;
+ __account_long(&table->stats.l[address - table->network].dst, skb);
+ table->stats.l[address - table->network].time = now;
+ if (table->count > 1) {
+ __account_long(&table->stats.l[0].dst, skb);
+ table->stats.l[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;
+ __account_short(&table->stats.s[address - table->network].dst, skb);
+ table->stats.s[address - table->network].time = now;
+ if (table->count > 1) {
+ __account_short(&table->stats.s[0].dst, skb);
+ table->stats.s[0].time = now;
}
}
+ write_unlock_bh(&table->stats_lock);
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)
+/*
+ * Checkentry function.
+ */
+static int
+checkentry(const char *tablename,
+ const void *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ 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;
+ struct t_ipt_account_info *info = matchinfo;
+ struct t_ipt_account_table *table;
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() entered.\n");
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: name = %s\n", info->name);
+
+ if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info)))
+ return 0;
- 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;
+ /*
+ * Sanity checks.
+ */
+ if (info->netmask < ((~0L << (32 - netmask)) & 0xffffffff)) {
+ printk(KERN_ERR "ipt_account[checkentry]: too big netmask (increase module 'netmask' parameter).\n");
+ return 0;
}
- 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;
+ if ((info->network & info->netmask) != info->network) {
+ printk(KERN_ERR "ipt_account[checkentry]: wrong network/netmask.\n");
+ return 0;
}
+ if (info->name[0] == '\0') {
+ printk(KERN_ERR "ipt_account[checkentry]: wrong table name.\n");
+ return 0;
+ }
- /* table doesn't exist - create new */
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu 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 %zu for new table %s.\n", sizeof(struct t_ipt_account_table), info->name);
- ret = 0;
- goto failure;
+ /*
+ * Avoid searching list for already seen and added rules.
+ * Simply increase use count and exit.
+ */
+ if (info->table) {
+ ipt_account_table_get(info->table);
+ return 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->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 %zu 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 %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list) * table->count);
- ret = 0;
- goto failure_table;
+ /*
+ * We got new rule. Try to find table with the same name as given in info structure.
+ */
+ table = ipt_account_table_find_get(info->name);
+ if (!table) {
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: table not found, creating new one.\n");
+ /*
+ * Table not exist, create new one.
+ */
+ table = ipt_account_table_init(info);
+ if (!table) {
+ return 0;
}
- memset(table->ip_list.l, 0, sizeof(struct t_ipt_account_ip_list) * table->count);
} else {
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() allocating %zu 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 %zu for ip_list.\n", sizeof(struct t_ipt_account_ip_list_short) * table->count);
- ret = 0;
- goto failure_table;
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: table found, checking.\n");
+ /*
+ * Table exists, but whether rule network/netmask/shortlisting matches
+ * table network/netmask/shortlisting. Failure on missmatch.
+ */
+ if (table->network != info->network || table->netmask != info->netmask || table->shortlisting != info->shortlisting) {
+ printk(KERN_ERR "ipt_account [checkentry]: table found, rule network/netmask/shortlisting not match table network/netmask/shortlisting.\n");
+ /*
+ * Remember to release table usage counter.
+ */
+ ipt_account_table_put(table);
+ return 0;
}
- memset(table->ip_list.s, 0, sizeof(struct t_ipt_account_ip_list_short) * table->count);
+ dprintk(KERN_DEBUG "ipt_account [checkentry]: table found, reusing.\n");
}
+ /*
+ * Link rule with table.
+ */
+ info->table = table;
+ return 1;
+}
+
+/*
+ * Destroy function.
+ */
+static void
+destroy(void *matchinfo, unsigned int matchsize)
+{
+ struct t_ipt_account_info *info = matchinfo;
- /* 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);
+ dprintk(KERN_DEBUG "ipt_account [destroy]: name = %s\n", info->name);
- /* create procfs status file */
- table->status_file = create_proc_entry(table->name, permissions, proc_net_ipt_account);
- if (table->status_file == NULL) {
- ret = 0;
- goto failure_unlink;
- }
- table->status_file->owner = THIS_MODULE;
- table->status_file->data = table;
- wmb();
- table->status_file->proc_fops = &account_file_ops;
+ /*
+ * Release table, by decreasing its usage counter. When
+ * counter hits zero, memory used by table structure is
+ * released and table is removed from list.
+ */
+ ipt_account_table_put(info->table);
+ return;
+}
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() left.\n");
- /* everything went just okey */
- return 1;
+static struct ipt_match account_match = {
+ .name = "account",
+ .match = &match,
+ .checkentry = &checkentry,
+ .destroy = &destroy,
+ .me = THIS_MODULE
+};
- /* 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;
+/*
+ * Below functions (ipt_account_seq_start, ipt_account_seq_next,
+ * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write)
+ * are used to implement proc stuff.
+ */
+static void *ipt_account_seq_start(struct seq_file *sf, loff_t *pos)
+{
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ unsigned int *i;
+
+ read_lock_bh(&table->stats_lock);
+ if (*pos >= table->count)
+ return NULL;
+ i = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
+ if (!i)
+ return ERR_PTR(-ENOMEM);
+ *i = *pos;
+ return i;
+}
+
+static void *ipt_account_seq_next(struct seq_file *sf, void *v, loff_t *pos)
+{
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ unsigned int *i = (unsigned int *)v;
+
+ *pos = ++(*i);
+ if (*i >= table->count) {
+ kfree(v);
+ return NULL;
}
- 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;
- }
- 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;
+ return i;
}
-static void destroy(void *matchinfo,
- unsigned int matchinfosize)
+static void ipt_account_seq_stop(struct seq_file *sf, void *v)
{
- const struct t_ipt_account_info *info = matchinfo;
- struct t_ipt_account_table *table, *last_table;
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ kfree(v);
+ read_unlock_bh(&table->stats_lock);
+}
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() entered.\n");
+static int ipt_account_seq_show(struct seq_file *sf, void *v)
+{
+ struct proc_dir_entry *pde = sf->private;
+ struct t_ipt_account_table *table = pde->data;
+ unsigned int *i = (unsigned int *)v;
- if (matchinfosize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) return;
+ struct timespec now = CURRENT_TIME_SEC;
+
+ u_int32_t address = table->network + *i;
- /* 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;
+ if (!table->shortlisting) {
+ struct t_ipt_account_stats_long *l = &table->stats.l[*i];
+ seq_printf(sf,
+ "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\n",
+ HIPQUAD(address),
+ l->src.b_all,
+ l->src.b_tcp,
+ l->src.b_udp,
+ l->src.b_icmp,
+ l->src.b_other,
+ l->src.p_all,
+ l->src.p_tcp,
+ l->src.p_udp,
+ l->src.p_icmp,
+ l->src.p_other,
+ l->dst.b_all,
+ l->dst.b_tcp,
+ l->dst.b_udp,
+ l->dst.b_icmp,
+ l->dst.b_other,
+ l->dst.p_all,
+ l->dst.p_tcp,
+ l->dst.p_udp,
+ l->dst.p_icmp,
+ l->dst.p_other,
+ now.tv_sec - l->time.tv_sec
+ );
+ } else {
+ struct t_ipt_account_stats_short *s = &table->stats.s[*i];
+ seq_printf(sf,
+ "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
+ HIPQUAD(address),
+ s->src.b_all,
+ s->src.p_all,
+ s->dst.b_all,
+ s->dst.p_all,
+ now.tv_sec - s->time.tv_sec
+ );
}
- 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;
+ return 0;
+}
+
+static struct seq_operations ipt_account_seq_ops = {
+ .start = ipt_account_seq_start,
+ .next = ipt_account_seq_next,
+ .stop = ipt_account_seq_stop,
+ .show = ipt_account_seq_show
+};
+
+static ssize_t ipt_account_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ofs)
+{
+ char buffer[1024];
+ struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+ struct t_ipt_account_table *table = pde->data;
+
+ u_int32_t o[4], ip;
+ struct t_ipt_account_stats_long l;
+ struct t_ipt_account_stats_short s;
+
+ dprintk(KERN_DEBUG "ipt_account [ipt_account_proc_write]: name = %s.\n", table->name);
+
+ if (copy_from_user(buffer, input, 1024))
+ return -EFAULT;
+
+ if (!strncmp(buffer, "reset", 5)) {
+ /*
+ * User requested to clear all table. Ignorant, does
+ * he known how match time it took us to fill it? ;-)
+ */
+ write_lock_bh(&table->stats_lock);
+ if (table->shortlisting)
+ memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
+ else
+ memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
+ write_unlock_bh(&table->stats_lock);
+ } 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",
+ &o[0], &o[1], &o[2], &o[3],
+ &l.src.b_all, &l.src.b_tcp, &l.src.b_udp, &l.src.b_icmp, &l.src.b_other,
+ &l.src.p_all, &l.src.p_tcp, &l.src.p_udp, &l.src.p_icmp, &l.src.p_other,
+ &l.dst.b_all, &l.dst.b_tcp, &l.dst.b_udp, &l.dst.b_icmp, &l.dst.b_other,
+ &l.dst.p_all, &l.dst.p_tcp, &l.dst.p_udp, &l.dst.p_icmp, &l.dst.p_other,
+ &l.time.tv_sec) == 25 ) {
+ /*
+ * We got line formated like long listing row. We have to
+ * check, if IP is accounted by table. If so, we
+ * simply replace row with user's one.
+ */
+ ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
+ if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
+ /*
+ * Ignore user input time. Set current time.
+ */
+ l.time = CURRENT_TIME_SEC;
+ write_lock_bh(&table->stats_lock);
+ table->stats.l[ip - table->network] = l;
+ write_unlock_bh(&table->stats_lock);
+ }
+ } 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",
+ &o[0], &o[1], &o[2], &o[3],
+ &s.src.b_all,
+ &s.src.p_all,
+ &s.dst.b_all,
+ &s.dst.p_all,
+ &s.time.tv_sec) == 9) {
+ /*
+ * We got line formated like short listing row. Do the
+ * same action like above.
+ */
+ ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
+ if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
+ s.time = CURRENT_TIME_SEC;
+ write_lock_bh(&table->stats_lock);
+ table->stats.s[ip - table->network] = s;
+ write_unlock_bh(&table->stats_lock);
+ }
+ } else {
+ /*
+ * We don't understand what user have just wrote.
+ */
+ return -EIO;
}
- /* remove table if use-count is zero */
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() table %s not used. Removing.\n", table->name);
+ return size;
+}
- /* unlink table */
- if(last_table)
- last_table->next = table->next;
- else
- account_tables = table->next;
- spin_unlock_bh(&account_lock);
+static int ipt_account_proc_open(struct inode *inode, struct file *file)
+{
+ int ret = seq_open(file, &ipt_account_seq_ops);
+ if (!ret) {
+ struct seq_file *sf = file->private_data;
+ struct proc_dir_entry *pde = PDE(inode);
+ struct t_ipt_account_table *table = pde->data;
+
+ sf->private = pde;
- /* wait while table is still in use */
- spin_lock_bh(&table->ip_list_lock);
- spin_unlock_bh(&table->ip_list_lock);
+ ipt_account_table_get(table);
+ }
+ return ret;
+}
- /* remove proc entries */
- remove_proc_entry(table->name, proc_net_ipt_account);
+static int ipt_account_proc_release(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *pde = PDE(inode);
+ struct t_ipt_account_table *table = pde->data;
+ int ret;
- /* remove table */
- if (!table->shortlisting)
- vfree(table->ip_list.l);
- else
- vfree(table->ip_list.s);
- vfree(table);
+ ret = seq_release(inode, file);
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": destory() left.\n");
- return;
+ if (!ret)
+ ipt_account_table_put(table);
+
+ return ret;
}
-static struct ipt_match account_match = {
- .name = "account",
- .match = &match,
- .checkentry = &checkentry,
- .destroy = &destroy,
- .me = THIS_MODULE
+static struct file_operations ipt_account_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = ipt_account_proc_open,
+ .read = seq_read,
+ .write = ipt_account_proc_write,
+ .llseek = seq_lseek,
+ .release = ipt_account_proc_release
};
-static int __init init(void)
+/*
+ * Module init function.
+ */
+static int __init init(void)
{
- int err;
-
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() entered.\n");
- printk(version);
- /* check params */
+ int ret = 0;
+
+ printk(KERN_INFO "ipt_account %s : Piotr Gasidło <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION);
+
+ /* Check module parameters. */
if (netmask > 32 || netmask < 0) {
- printk(KERN_INFO "account: Wrong netmask given by netmask parameter (%i). Valid is 32 to 0.\n", netmask);
- err = -EINVAL;
- goto doexit;
+ printk(KERN_ERR "ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask);
+ ret = -EINVAL;
+ goto cleanup_none;
}
+
+ /* Register match. */
+ if (ipt_register_match(&account_match)) {
+ ret = -EINVAL;
+ goto cleanup_none;
+ }
- /* 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");
- err = -ENOMEM;
- goto doexit;
+ /* Create /proc/net/ipt_account/ entry. */
+ ipt_account_procdir = proc_mkdir("ipt_account", proc_net);
+ if (!ipt_account_procdir) {
+ printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", proc_net) failed.\n");
+ ret = -ENOMEM;
+ goto cleanup_match;
}
- proc_net_ipt_account->owner = THIS_MODULE;
- err = ipt_register_match(&account_match);
- if (err) {
- printk(KERN_INFO IPT_ACCOUNT_NAME ": checkentry() failed to register match.\n");
- remove_proc_entry("ipt_account", proc_net);
- }
-doexit:
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __init() left.\n");
- return err;
+ return ret;
+
+ /* If something goes wrong we end here. */
+cleanup_match:
+ ipt_unregister_match(&account_match);
+cleanup_none:
+ return ret;
}
-static void __exit fini(void)
+/*
+ * Module exit function.
+ */
+static void __exit fini(void)
{
- dprintk(KERN_INFO IPT_ACCOUNT_NAME ": __exit() entered.\n");
-
+ /* Remove /proc/net/ipt_account/ */
+ remove_proc_entry(ipt_account_procdir->name, ipt_account_procdir->parent);
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);
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2006-04-01 16:53 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-01 16:53 ipt_account match update Piotr Gasidlo
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.