]> git.pld-linux.org Git - packages/kernel.git/blame - kernel-ipt_account.patch
- up to 2.6.17.28
[packages/kernel.git] / kernel-ipt_account.patch
CommitLineData
1f0d84f2 1diff -uNrp linux/net/ipv4/netfilter/ipt_account.c linux/net/ipv4/netfilter/ipt_account.c
2--- linux/net/ipv4/netfilter/ipt_account.c 1970-01-01 01:00:00.000000000 +0100
3+++ linux/net/ipv4/netfilter/ipt_account.c 2007-08-04 16:22:15.000000000 +0200
4@@ -0,0 +1,973 @@
5+/* Copyright (c) 2004-2007 Piotr 'QuakeR' Gasidlo <quaker@barbara.eu.org>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License version 2 as
9+ * published by the Free Software Foundation.
10+ */
11+
12+#include <linux/version.h>
13+#include <linux/module.h>
14+#include <linux/skbuff.h>
15+#include <linux/vmalloc.h>
16+#include <linux/proc_fs.h>
17+#include <linux/seq_file.h>
18+#include <linux/time.h>
19+#include <linux/ip.h>
20+#include <linux/in.h>
21+
22+#define IPT_ACCOUNT_VERSION "0.1.21"
23+
24+//#define DEBUG_IPT_ACCOUNT
25+
26+MODULE_AUTHOR("Piotr Gasidlo <quaker@barbara.eu.org>");
27+MODULE_DESCRIPTION("Traffic accounting module");
28+MODULE_LICENSE("GPL");
29+
30+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
31+#include <linux/netfilter/x_tables.h>
32+#else
33+#include <linux/netfilter_ipv4/ip_tables.h>
34+#endif
35+#include <linux/netfilter_ipv4/ipt_account.h>
36+
37+/* defaults, can be overriden */
38+static unsigned int netmask = 16; /* Safe netmask, if you try to create table
39+ for larger netblock you will get error.
40+ Increase by command line only when you
41+ known what are you doing. */
42+
43+#ifdef DEBUG_IPT_ACCOUNT
44+static int debug = 0;
45+#endif
46+module_param(netmask, uint, 0400);
47+
48+MODULE_PARM_DESC(netmask,"maximum *save* netmask");
49+#ifdef DEBUG_IPT_ACCOUNT
50+module_param(debug, bool, 0600);
51+MODULE_PARM_DESC(debug,"enable debugging output");
52+#endif
53+
54+/* structure with statistics counter, used when table is created without --ashort switch */
55+struct t_ipt_account_stat_long {
56+ u_int64_t b_all, b_tcp, b_udp, b_icmp, b_other;
57+ u_int64_t p_all, p_tcp, p_udp, p_icmp, p_other;
58+};
59+
60+/* same as above, for tables created with --ashort switch */
61+struct t_ipt_account_stat_short {
62+ u_int64_t b_all;
63+ u_int64_t p_all;
64+};
65+
66+/* structure holding to/from statistics for single ip when table is created without --ashort switch */
67+struct t_ipt_account_stats_long {
68+ struct t_ipt_account_stat_long src, dst;
69+ struct timespec time; /* time, when statistics was last modified */
70+};
71+
72+/* same as above, for tables created with --ashort switch */
73+struct t_ipt_account_stats_short {
74+ struct t_ipt_account_stat_short src, dst;
75+ struct timespec time;
76+};
77+
78+/* defines for "show" table option */
79+#define SHOW_ANY 0
80+#define SHOW_SRC 1
81+#define SHOW_DST 2
82+#define SHOW_SRC_OR_DST 3
83+#define SHOW_SRC_AND_DST 4
84+
85+/* structure describing single table */
86+struct t_ipt_account_table {
87+ struct list_head list;
88+ atomic_t use; /* use counter, the number of rules which points to this table */
89+
90+ char name[IPT_ACCOUNT_NAME_LEN + 1]; /* table name ( = filename in /proc/net/ipt_account/) */
91+ u_int32_t network, netmask, count; /* network/netmask/hosts count coverted by table */
92+
93+ int shortlisting:1; /* gather only total statistics (set for tables created with --ashort switch) */
94+ int timesrc:1; /* update time when accounting outgoing traffic */
95+ int timedst:1; /* update time when accounting incomming traffic */
96+ int resetonread:1; /* reset statistics after reading it via proc */
97+ int show; /* show with entries */
98+
99+ /* FIXME: why int show:3 results in 'warning: comparison is always 0 due to width of bit-field' in ipt_account_seq_show
100+ * gcc -v: gcc version 3.4.6 */
101+
102+ union { /* statistics for each ip in network/netmask */
103+ struct t_ipt_account_stats_long *l;
104+ struct t_ipt_account_stats_short *s;
105+ } stats;
106+ rwlock_t stats_lock; /* lock, to assure that above union can be safely modified */
107+
108+ struct proc_dir_entry *pde; /* handle to proc entry */
109+};
110+
111+static LIST_HEAD(ipt_account_tables);
112+static rwlock_t ipt_account_lock = RW_LOCK_UNLOCKED; /* lock, to assure that table list can be safely modified */
113+static DECLARE_MUTEX(ipt_account_mutex); /* additional checkentry protection */
114+
115+static struct file_operations ipt_account_proc_fops;
116+static struct proc_dir_entry *ipt_account_procdir;
117+
118+/*
119+ * Function creates new table and inserts it into linked list.
120+ */
121+static struct t_ipt_account_table *
122+ipt_account_table_init(struct t_ipt_account_info *info)
123+{
124+ struct t_ipt_account_table *table;
125+
126+#ifdef DEBUG_IPT_ACCOUNT
127+ if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_init]: name = %s\n", info->name);
128+#endif
129+
130+ /*
131+ * Allocate memory for table.
132+ */
133+ table = vmalloc(sizeof(struct t_ipt_account_table));
134+ if (!table) {
135+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table = vmalloc(sizeof(struct t_ipt_account_table)) failed.\n");
136+ goto cleanup_none;
137+ }
138+ memset(table, 0, sizeof(struct t_ipt_account_table));
139+
140+ /*
141+ * Table attributes.
142+ */
143+ strncpy(table->name, info->name, IPT_ACCOUNT_NAME_LEN);
144+ table->name[IPT_ACCOUNT_NAME_LEN] = '\0';
145+
146+ table->network = info->network;
147+ table->netmask = info->netmask;
148+ table->count = (0xffffffff ^ table->netmask) + 1;
149+
150+ /*
151+ * Table properties.
152+ */
153+ table->shortlisting = info->shortlisting;
154+ table->timesrc = 1;
155+ table->timedst = 1;
156+ table->resetonread = 0;
157+ table->show = SHOW_ANY;
158+
159+ /*
160+ * Initialize use counter.
161+ */
162+ atomic_set(&table->use, 1);
163+
164+ /*
165+ * Allocate memory for statistic counters.
166+ */
167+ if (table->shortlisting) {
168+ table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count);
169+ if (!table->stats.s) {
170+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count) failed.\n");
171+ goto cleanup_table;
172+ }
173+ memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
174+ } else {
175+ table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count);
176+ if (!table->stats.l) {
177+ printk(KERN_ERR "ipt_account [ipt_account_table_init]: table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count) failed.\n");
178+ goto cleanup_table;
179+ }
180+ memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
181+ }
182+
183+ /*
184+ * Reset locks.
185+ */
186+ table->stats_lock = RW_LOCK_UNLOCKED;
187+
188+ /*
189+ * Create /proc/ipt_account/name entry.
190+ */
191+ table->pde = create_proc_entry(table->name, S_IWUSR | S_IRUSR, ipt_account_procdir);
192+ if (!table->pde) {
193+ goto cleanup_stats;
194+ }
195+ table->pde->proc_fops = &ipt_account_proc_fops;
196+ table->pde->data = table;
197+
198+ /*
199+ * Insert table into list.
200+ */
201+ write_lock_bh(&ipt_account_lock);
202+ list_add(&table->list, &ipt_account_tables);
203+ write_unlock_bh(&ipt_account_lock);
204+
205+ return table;
206+
207+ /*
208+ * If something goes wrong we end here.
209+ */
210+cleanup_stats:
211+ if (table->shortlisting)
212+ vfree(table->stats.s);
213+ else
214+ vfree(table->stats.l);
215+
216+cleanup_table:
217+ vfree(table);
218+cleanup_none:
219+ return NULL;
220+
221+}
222+
223+/*
224+ * Function destroys table. Table *must* be already unlinked.
225+ */
226+static void
227+ipt_account_table_destroy(struct t_ipt_account_table *table)
228+{
229+#ifdef DEBUG_IPT_ACCOUNT
230+ if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_destory]: name = %s\n", table->name);
231+#endif
232+ remove_proc_entry(table->pde->name, table->pde->parent);
233+ if (table->shortlisting)
234+ vfree(table->stats.s);
235+ else
236+ vfree(table->stats.l);
237+ vfree(table);
238+}
239+
240+/*
241+ * Function increments use counter for table.
242+ */
243+static inline void
244+ipt_account_table_get(struct t_ipt_account_table *table)
245+{
246+#ifdef DEBUG_IPT_ACCOUNT
247+ if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_get]: name = %s\n", table->name);
248+#endif
249+ atomic_inc(&table->use);
250+}
251+
252+/*
253+ * Function decrements use counter for table. If use counter drops to zero,
254+ * table is removed from linked list and destroyed.
255+ */
256+static inline void
257+ipt_account_table_put(struct t_ipt_account_table *table)
258+{
259+#ifdef DEBUG_IPT_ACCOUNT
260+ if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_put]: name = %s\n", table->name);
261+#endif
262+ if (atomic_dec_and_test(&table->use)) {
263+ write_lock_bh(&ipt_account_lock);
264+ list_del(&table->list);
265+ write_unlock_bh(&ipt_account_lock);
266+ ipt_account_table_destroy(table);
267+ }
268+}
269+
270+/*
271+ * Helper function, which returns a structure pointer to a table with
272+ * specified name.
273+ */
274+static struct t_ipt_account_table *
275+__ipt_account_table_find(char *name)
276+{
277+ struct list_head *pos;
278+ list_for_each(pos, &ipt_account_tables) {
279+ struct t_ipt_account_table *table = list_entry(pos,
280+ struct t_ipt_account_table, list);
281+ if (!strncmp(table->name, name, IPT_ACCOUNT_NAME_LEN))
282+ return table;
283+ }
284+ return NULL;
285+}
286+
287+/*
288+ * Function, which returns a structure pointer to a table with
289+ * specified name. When such table is found its use coutner
290+ * is incremented.
291+ */
292+static inline struct t_ipt_account_table *
293+ipt_account_table_find_get(char *name)
294+{
295+ struct t_ipt_account_table *table;
296+
297+#ifdef DEBUG_IPT_ACCOUNT
298+ if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_table_find_get]: name = %s\n", name);
299+#endif
300+ read_lock_bh(&ipt_account_lock);
301+ table = __ipt_account_table_find(name);
302+ if (!table) {
303+ read_unlock_bh(&ipt_account_lock);
304+ return NULL;
305+ }
306+ atomic_inc(&table->use);
307+ read_unlock_bh(&ipt_account_lock);
308+ return table;
309+}
310+
311+/*
312+ * Helper function, with updates statistics for specified IP. It's only
313+ * used for tables created without --ashort switch.
314+ */
315+static inline void
316+__account_long(struct t_ipt_account_stat_long *stat, const struct sk_buff *skb)
317+{
318+ stat->b_all += skb->len;
319+ stat->p_all++;
320+
321+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
322+ switch (ip_hdr(skb)->protocol) {
323+#else
324+ switch (skb->nh.iph->protocol) {
325+#endif
326+ case IPPROTO_TCP:
327+ stat->b_tcp += skb->len;
328+ stat->p_tcp++;
329+ break;
330+ case IPPROTO_UDP:
331+ stat->b_udp += skb->len;
332+ stat->p_udp++;
333+ break;
334+ case IPPROTO_ICMP:
335+ stat->b_icmp += skb->len;
336+ stat->p_icmp++;
337+ break;
338+ default:
339+ stat->b_other += skb->len;
340+ stat->p_other++;
341+ }
342+}
343+
344+/*
345+ * Same as above, but used for tables created with --ashort switch.
346+ */
347+static inline void
348+__account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb)
349+{
350+ stat->b_all += skb->len;
351+ stat->p_all++;
352+}
353+
354+/*
355+ * Match function. Here we do accounting stuff.
356+ */
43b51ae9 357+static bool
1f0d84f2 358+match(const struct sk_buff *skb,
359+ const struct net_device *in,
360+ const struct net_device *out,
361+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
362+ const struct xt_match *match,
363+#endif
364+ const void *matchinfo,
365+ int offset,
366+ unsigned int protoff,
43b51ae9 367+ bool *hotdrop)
1f0d84f2 368+{
369+ struct t_ipt_account_info *info = (struct t_ipt_account_info *)matchinfo;
370+ struct t_ipt_account_table *table = info->table;
371+ u_int32_t address;
372+ /* Get current time. */
373+ struct timespec now = CURRENT_TIME_SEC;
374+ /* Default we assume no match. */
43b51ae9 375+ bool ret = false;
1f0d84f2 376+
377+#ifdef DEBUG_IPT_ACCOUNT
378+ if (debug) printk(KERN_DEBUG "ipt_account [match]: name = %s\n", table->name);
379+#endif
380+ /* Check whether traffic from source ip address ... */
381+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
382+ address = ntohl(ip_hdr(skb)->saddr);
383+#else
384+ address = ntohl(skb->nh.iph->saddr);
385+#endif
386+ /* ... is being accounted by this table. */
387+ if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
388+ write_lock_bh(&table->stats_lock);
389+ /* Yes, account this packet. */
390+#ifdef DEBUG_IPT_ACCOUNT
391+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
392+ if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), ip_hdr(skb)->protocol);
393+#else
394+ if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol);
395+#endif
396+#endif
397+ /* Update counters this host. */
398+ if (!table->shortlisting) {
399+ __account_long(&table->stats.l[address - table->network].src, skb);
400+ if (table->timesrc)
401+ table->stats.l[address - table->network].time = now;
402+ /* Update also counters for all hosts in this table (network address) */
403+ if (table->count > 1) {
404+ __account_long(&table->stats.l[0].src, skb);
405+ table->stats.l[0].time = now;
406+ }
407+ } else {
408+ __account_short(&table->stats.s[address - table->network].src, skb);
409+ if (table->timedst)
410+ table->stats.s[address - table->network].time = now;
411+ if (table->count > 1) {
412+ __account_short(&table->stats.s[0].src, skb);
413+ table->stats.s[0].time = now;
414+ }
415+ }
416+ write_unlock_bh(&table->stats_lock);
417+ /* Yes, it's a match. */
43b51ae9 418+ ret = true;
1f0d84f2 419+ }
420+
421+ /* Do the same thing with destination ip address. */
422+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
423+ address = ntohl(ip_hdr(skb)->daddr);
424+#else
425+ address = ntohl(skb->nh.iph->daddr);
426+#endif
427+ if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
428+ write_lock_bh(&table->stats_lock);
429+#ifdef DEBUG_IPT_ACCOUNT
430+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
431+ if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), ip_hdr(skb)->protocol);
432+#else
433+ if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address), skb->nh.iph->protocol);
434+#endif
435+#endif
436+ if (!table->shortlisting) {
437+ __account_long(&table->stats.l[address - table->network].dst, skb);
438+ table->stats.l[address - table->network].time = now;
439+ if (table->count > 1) {
440+ __account_long(&table->stats.l[0].dst, skb);
441+ table->stats.l[0].time = now;
442+ }
443+ } else {
444+ __account_short(&table->stats.s[address - table->network].dst, skb);
445+ table->stats.s[address - table->network].time = now;
446+ if (table->count > 1) {
447+ __account_short(&table->stats.s[0].dst, skb);
448+ table->stats.s[0].time = now;
449+ }
450+ }
451+ write_unlock_bh(&table->stats_lock);
43b51ae9 452+ ret = true;
1f0d84f2 453+ }
454+
455+ return ret;
456+}
457+
458+/*
459+ * Checkentry function.
460+ */
43b51ae9 461+static bool
1f0d84f2 462+checkentry(const char *tablename,
463+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
464+ const void *ip,
465+#else
466+ const struct ipt_entry *ip,
467+#endif
468+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
469+ const struct xt_match *match,
470+#endif
471+ void *matchinfo,
472+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
473+ unsigned int matchsize,
474+#endif
475+ unsigned int hook_mask)
476+{
477+ struct t_ipt_account_info *info = matchinfo;
478+ struct t_ipt_account_table *table;
479+
480+#ifdef DEBUG_IPT_ACCOUNT
481+ if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: name = %s\n", info->name);
482+#endif
483+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
484+ if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
485+#ifdef DEBUG_IPT_ACCOUNT
486+ if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
487+#endif
43b51ae9 488+ return false;
1f0d84f2 489+ }
490+#endif
491+
492+ /*
493+ * Sanity checks.
494+ */
495+ if (info->netmask < ((~0L << (32 - netmask)) & 0xffffffff)) {
496+ printk(KERN_ERR "ipt_account[checkentry]: too big netmask (increase module 'netmask' parameter).\n");
43b51ae9 497+ return false;
1f0d84f2 498+ }
499+ if ((info->network & info->netmask) != info->network) {
500+ printk(KERN_ERR "ipt_account[checkentry]: wrong network/netmask.\n");
43b51ae9 501+ return false;
1f0d84f2 502+ }
503+ if (info->name[0] == '\0') {
504+ printk(KERN_ERR "ipt_account[checkentry]: wrong table name.\n");
43b51ae9 505+ return false;
1f0d84f2 506+ }
507+
508+ /*
509+ * We got new rule. Try to find table with the same name as given in info structure.
510+ * Mutex magic based on xt_hashlimit.c.
511+ */
512+ down(&ipt_account_mutex);
513+ table = ipt_account_table_find_get(info->name);
514+ if (table) {
515+ if (info->table != NULL) {
516+ if (info->table != table) {
517+ printk(KERN_ERR "ipt_account[checkentry]: reloaded rule has invalid table pointer.\n");
518+ up(&ipt_account_mutex);
43b51ae9 519+ return false;
1f0d84f2 520+ }
521+ up(&ipt_account_mutex);
43b51ae9 522+ return true;
1f0d84f2 523+ } else {
524+#ifdef DEBUG_IPT_ACCOUNT
525+ if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, checking.\n");
526+#endif
527+ /*
528+ * Table exists, but whether rule network/netmask/shortlisting matches
529+ * table network/netmask/shortlisting. Failure on missmatch.
530+ */
531+ if (table->network != info->network || table->netmask != info->netmask || table->shortlisting != info->shortlisting) {
532+ printk(KERN_ERR "ipt_account [checkentry]: table found, rule network/netmask/shortlisting not match table network/netmask/shortlisting.\n");
533+ /*
534+ * Remember to release table usage counter.
535+ */
536+ ipt_account_table_put(table);
537+ up(&ipt_account_mutex);
43b51ae9 538+ return false;
1f0d84f2 539+ }
540+#ifdef DEBUG_IPT_ACCOUNT
541+ if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, reusing.\n");
542+#endif
543+ /*
544+ * Link rule with table.
545+ */
546+ info->table = table;
547+ }
548+ } else {
549+#ifdef DEBUG_IPT_ACCOUNT
550+ if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table not found, creating new one.\n");
551+#endif
552+ /*
553+ * Table not exist, create new one.
554+ */
555+ info->table = table = ipt_account_table_init(info);
556+ if (!table) {
557+ up(&ipt_account_mutex);
43b51ae9 558+ return false;
1f0d84f2 559+ }
560+ }
561+ up(&ipt_account_mutex);
43b51ae9 562+ return true;
1f0d84f2 563+}
564+
565+/*
566+ * Destroy function.
567+ */
568+static void
569+destroy(
570+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
571+ const struct xt_match *match,
572+#endif
573+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
574+ void *matchinfo
575+#else
576+ void *matchinfo,
577+ unsigned int matchsize
578+#endif
579+)
580+{
581+ struct t_ipt_account_info *info = matchinfo;
582+
583+#ifdef DEBUG_IPT_ACCOUNT
584+ if (debug) printk(KERN_DEBUG "ipt_account [destroy]: name = %s\n", info->name);
585+#endif
586+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
587+ if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
588+#ifdef DEBUG_IPT_ACCOUNT
589+ if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
590+#endif
591+ return;
592+ }
593+#endif
594+
595+ /*
596+ * Release table, by decreasing its usage counter. When
597+ * counter hits zero, memory used by table structure is
598+ * released and table is removed from list.
599+ */
600+ ipt_account_table_put(info->table);
601+ return;
602+}
603+
604+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
605+static struct xt_match account_match = {
606+#else
607+static struct ipt_match account_match = {
608+#endif
609+ .name = "account",
610+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
611+ .family = AF_INET,
612+#endif
613+ .match = &match,
614+ .checkentry = &checkentry,
615+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
616+ .matchsize = sizeof(struct t_ipt_account_info),
617+#endif
618+ .destroy = &destroy,
619+ .me = THIS_MODULE
620+};
621+
622+/*
623+ * Below functions (ipt_account_seq_start, ipt_account_seq_next,
624+ * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write)
625+ * are used to implement proc stuff.
626+ */
627+static void *ipt_account_seq_start(struct seq_file *sf, loff_t *pos)
628+{
629+ struct proc_dir_entry *pde = sf->private;
630+ struct t_ipt_account_table *table = pde->data;
631+ unsigned int *i;
632+
633+ if (table->resetonread) {
634+ /* When we reset entries after read we must have exclusive lock. */
635+ write_lock_bh(&table->stats_lock);
636+ } else {
637+ read_lock_bh(&table->stats_lock);
638+ }
639+ if (*pos >= table->count)
640+ return NULL;
641+ i = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
642+ if (!i)
643+ return ERR_PTR(-ENOMEM);
644+ *i = *pos;
645+ return i;
646+}
647+
648+static void *ipt_account_seq_next(struct seq_file *sf, void *v, loff_t *pos)
649+{
650+ struct proc_dir_entry *pde = sf->private;
651+ struct t_ipt_account_table *table = pde->data;
652+ unsigned int *i = (unsigned int *)v;
653+
654+ *pos = ++(*i);
655+ if (*i >= table->count) {
656+ kfree(v);
657+ return NULL;
658+ }
659+ return i;
660+}
661+
662+static void ipt_account_seq_stop(struct seq_file *sf, void *v)
663+{
664+ struct proc_dir_entry *pde = sf->private;
665+ struct t_ipt_account_table *table = pde->data;
666+ kfree(v);
667+ if (table->resetonread) {
668+ write_unlock_bh(&table->stats_lock);
669+ } else {
670+ read_unlock_bh(&table->stats_lock);
671+ }
672+}
673+
674+static int ipt_account_seq_show(struct seq_file *sf, void *v)
675+{
676+ struct proc_dir_entry *pde = sf->private;
677+ struct t_ipt_account_table *table = pde->data;
678+ unsigned int *i = (unsigned int *)v;
679+
680+ struct timespec now = CURRENT_TIME_SEC;
681+
682+ u_int32_t address = table->network + *i;
683+
684+ if (!table->shortlisting) {
685+ struct t_ipt_account_stats_long *l = &table->stats.l[*i];
686+ /* Don't list rows not matching show requirements. */
687+ if (
688+ ((table->show == SHOW_SRC) && (l->src.p_all == 0)) ||
689+ ((table->show == SHOW_DST) && (l->dst.p_all == 0)) ||
690+ ((table->show == SHOW_SRC_OR_DST) && ((l->src.p_all == 0) && (l->dst.p_all == 0))) ||
691+ ((table->show == SHOW_SRC_AND_DST) && ((l->src.p_all == 0) || (l->dst.p_all == 0)))
692+ ) {
693+ return 0;
694+ }
695+ seq_printf(sf,
696+ "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",
697+ HIPQUAD(address),
698+ l->src.b_all,
699+ l->src.b_tcp,
700+ l->src.b_udp,
701+ l->src.b_icmp,
702+ l->src.b_other,
703+ l->src.p_all,
704+ l->src.p_tcp,
705+ l->src.p_udp,
706+ l->src.p_icmp,
707+ l->src.p_other,
708+ l->dst.b_all,
709+ l->dst.b_tcp,
710+ l->dst.b_udp,
711+ l->dst.b_icmp,
712+ l->dst.b_other,
713+ l->dst.p_all,
714+ l->dst.p_tcp,
715+ l->dst.p_udp,
716+ l->dst.p_icmp,
717+ l->dst.p_other,
718+ now.tv_sec - l->time.tv_sec
719+ );
720+ if (table->resetonread)
721+ memset(l, 0, sizeof(struct t_ipt_account_stats_long));
722+
723+ } else {
724+ struct t_ipt_account_stats_short *s = &table->stats.s[*i];
725+ if (
726+ ((table->show == SHOW_SRC) && (s->src.p_all == 0)) ||
727+ ((table->show == SHOW_DST) && (s->dst.p_all == 0)) ||
728+ ((table->show == SHOW_SRC_OR_DST) && ((s->src.p_all == 0) && (s->dst.p_all == 0))) ||
729+ ((table->show == SHOW_SRC_AND_DST) && ((s->src.p_all == 0) || (s->dst.p_all == 0)))
730+ ) {
731+ return 0;
732+ }
733+ seq_printf(sf,
734+ "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
735+ HIPQUAD(address),
736+ s->src.b_all,
737+ s->src.p_all,
738+ s->dst.b_all,
739+ s->dst.p_all,
740+ now.tv_sec - s->time.tv_sec
741+ );
742+ if (table->resetonread)
743+ memset(s, 0, sizeof(struct t_ipt_account_stats_short));
744+ }
745+
746+ return 0;
747+}
748+
749+static struct seq_operations ipt_account_seq_ops = {
750+ .start = ipt_account_seq_start,
751+ .next = ipt_account_seq_next,
752+ .stop = ipt_account_seq_stop,
753+ .show = ipt_account_seq_show
754+};
755+
756+static ssize_t ipt_account_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ofs)
757+{
758+ char buffer[1024];
759+ struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
760+ struct t_ipt_account_table *table = pde->data;
761+
762+ u_int32_t o[4], ip;
763+ struct t_ipt_account_stats_long l;
764+ struct t_ipt_account_stats_short s;
765+
766+#ifdef DEBUG_IPT_ACCOUNT
767+ if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_proc_write]: name = %s.\n", table->name);
768+#endif
769+ if (copy_from_user(buffer, input, 1024))
770+ return -EFAULT;
771+ buffer[1023] = '\0';
772+
773+ if (!strncmp(buffer, "reset\n", 6)) {
774+ /*
775+ * User requested to clear all table. Ignorant, does
776+ * he known how match time it took us to fill it? ;-)
777+ */
778+ write_lock_bh(&table->stats_lock);
779+ if (table->shortlisting)
780+ memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
781+ else
782+ memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
783+ write_unlock_bh(&table->stats_lock);
784+ } else if (!strncmp(buffer, "reset-on-read=yes\n", 18)) {
785+ /*
786+ * We must be sure that ipt_account_seq_* is not running now. This option
787+ * changes lock type which is taken in ipt_account_seq_{start|stop}. When
788+ * we change this option without this lock, and ipt_account_seq_start is
789+ * already run (but not ipt_account_seq_stop) there is possibility that
790+ * we execute wrong "unlock" function.
791+ */
792+ write_lock_bh(&table->stats_lock);
793+ table->resetonread = 1;
794+ write_unlock_bh(&table->stats_lock);
795+ } else if (!strncmp(buffer, "reset-on-read=no\n", 17)) {
796+ write_lock_bh(&table->stats_lock);
797+ table->resetonread = 0;
798+ write_unlock_bh(&table->stats_lock);
799+ } else if (!strncmp(buffer, "reset-on-read\n", 14)) {
800+ write_lock_bh(&table->stats_lock);
801+ table->resetonread = 1;
802+ write_unlock_bh(&table->stats_lock);
803+ } else if (!strncmp(buffer, "show=any\n", 9)) {
804+ /*
805+ * Here we should lock but we don't have to. So we don't lock. We only get
806+ * wrong results on already run ipt_account_seq_show, but we won't crush
807+ * the system.
808+ */
809+ table->show = SHOW_ANY;
810+ } else if (!strncmp(buffer, "show=src\n", 9)) {
811+ table->show = SHOW_SRC;
812+ } else if (!strncmp(buffer, "show=dst\n", 9)) {
813+ table->show = SHOW_DST;
814+ } else if (!strncmp(buffer, "show=src-or-dst\n", 16) || !strncmp(buffer, "show=dst-or-src\n", 16)) {
815+ table->show = SHOW_SRC_OR_DST;
816+ } else if (!strncmp(buffer, "show=src-and-dst\n", 17) || !strncmp(buffer, "show=dst-and-src\n", 17)) {
817+ table->show = SHOW_SRC_AND_DST;
818+ } else if (!strncmp(buffer, "time=any\n", 9)) {
819+ table->timesrc = table->timedst = 1;
820+ } else if (!strncmp(buffer, "time=src\n", 9)) {
821+ table->timesrc = 1;
822+ table->timedst = 0;
823+ } else if (!strncmp(buffer, "time=dst\n", 9)) {
824+ table->timesrc = 0;
825+ table->timedst = 1;
826+ } 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",
827+ &o[0], &o[1], &o[2], &o[3],
828+ &l.src.b_all, &l.src.b_tcp, &l.src.b_udp, &l.src.b_icmp, &l.src.b_other,
829+ &l.src.p_all, &l.src.p_tcp, &l.src.p_udp, &l.src.p_icmp, &l.src.p_other,
830+ &l.dst.b_all, &l.dst.b_tcp, &l.dst.b_udp, &l.dst.b_icmp, &l.dst.b_other,
831+ &l.dst.p_all, &l.dst.p_tcp, &l.dst.p_udp, &l.dst.p_icmp, &l.dst.p_other,
832+ &l.time.tv_sec) == 25 ) {
833+ /*
834+ * We got line formated like long listing row. We have to
835+ * check, if IP is accounted by table. If so, we
836+ * simply replace row with user's one.
837+ */
838+ ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
839+ if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
840+ /*
841+ * Ignore user input time. Set current time.
842+ */
843+ l.time = CURRENT_TIME_SEC;
844+ write_lock_bh(&table->stats_lock);
845+ table->stats.l[ip - table->network] = l;
846+ write_unlock_bh(&table->stats_lock);
847+ }
848+ } 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",
849+ &o[0], &o[1], &o[2], &o[3],
850+ &s.src.b_all,
851+ &s.src.p_all,
852+ &s.dst.b_all,
853+ &s.dst.p_all,
854+ &s.time.tv_sec) == 9) {
855+ /*
856+ * We got line formated like short listing row. Do the
857+ * same action like above.
858+ */
859+ ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
860+ if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
861+ s.time = CURRENT_TIME_SEC;
862+ write_lock_bh(&table->stats_lock);
863+ table->stats.s[ip - table->network] = s;
864+ write_unlock_bh(&table->stats_lock);
865+ }
866+ } else {
867+ /*
868+ * We don't understand what user have just wrote.
869+ */
870+ return -EIO;
871+ }
872+
873+ return size;
874+}
875+
876+static int ipt_account_proc_open(struct inode *inode, struct file *file)
877+{
878+ int ret = seq_open(file, &ipt_account_seq_ops);
879+ if (!ret) {
880+ struct seq_file *sf = file->private_data;
881+ struct proc_dir_entry *pde = PDE(inode);
882+ struct t_ipt_account_table *table = pde->data;
883+
884+ sf->private = pde;
885+
886+ ipt_account_table_get(table);
887+ }
888+ return ret;
889+}
890+
891+static int ipt_account_proc_release(struct inode *inode, struct file *file)
892+{
893+ struct proc_dir_entry *pde = PDE(inode);
894+ struct t_ipt_account_table *table = pde->data;
895+ int ret;
896+
897+ ret = seq_release(inode, file);
898+
899+ if (!ret)
900+ ipt_account_table_put(table);
901+
902+ return ret;
903+}
904+
905+static struct file_operations ipt_account_proc_fops = {
906+ .owner = THIS_MODULE,
907+ .open = ipt_account_proc_open,
908+ .read = seq_read,
909+ .write = ipt_account_proc_write,
910+ .llseek = seq_lseek,
911+ .release = ipt_account_proc_release
912+};
913+
914+/*
915+ * Module init function.
916+ */
917+static int __init init(void)
918+{
919+ int ret = 0;
920+
921+ printk(KERN_INFO "ipt_account %s : Piotr Gasidlo <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION);
922+
923+ /* Check module parameters. */
924+ if (netmask > 32 || netmask < 0) {
925+ printk(KERN_ERR "ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask);
926+ ret = -EINVAL;
927+ goto cleanup_none;
928+ }
929+
930+ /* Register match. */
931+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
932+ if (xt_register_match(&account_match)) {
933+#else
934+ if (ipt_register_match(&account_match)) {
935+#endif
936+ ret = -EINVAL;
937+ goto cleanup_none;
938+ }
939+
940+ /* Create /proc/net/ipt_account/ entry. */
143a4708 941+ ipt_account_procdir = proc_mkdir("ipt_account", init_net.proc_net);
1f0d84f2 942+ if (!ipt_account_procdir) {
143a4708 943+ printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", init_net.proc_net) failed.\n");
1f0d84f2 944+ ret = -ENOMEM;
945+ goto cleanup_match;
946+ }
947+
948+ return ret;
949+
950+ /* If something goes wrong we end here. */
951+cleanup_match:
952+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
953+ xt_unregister_match(&account_match);
954+#else
955+ ipt_unregister_match(&account_match);
956+#endif
957+cleanup_none:
958+ return ret;
959+}
960+
961+/*
962+ * Module exit function.
963+ */
964+static void __exit fini(void)
965+{
966+ /* Remove /proc/net/ipt_account/ */
967+ remove_proc_entry(ipt_account_procdir->name, ipt_account_procdir->parent);
968+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
969+ xt_unregister_match(&account_match);
970+#else
971+ ipt_unregister_match(&account_match);
972+#endif
973+}
974+
975+module_init(init);
976+module_exit(fini);
977+
978diff -uNrp linux/include/linux/netfilter_ipv4/ipt_account.h linux/include/linux/netfilter_ipv4/ipt_account.h
979--- linux/include/linux/netfilter_ipv4/ipt_account.h 1970-01-01 01:00:00.000000000 +0100
980+++ linux/include/linux/netfilter_ipv4/ipt_account.h 2007-08-04 15:24:49.000000000 +0200
981@@ -0,0 +1,24 @@
982+/* Copyright (c) 2004-2007 Piotr 'QuakeR' Gasidlo <quaker@barbara.eu.org>
983+ *
984+ * This program is free software; you can redistribute it and/or modify
985+ * it under the terms of the GNU General Public License version 2 as
986+ * published by the Free Software Foundation.
987+ */
988+
989+#ifndef _IPT_ACCOUNT_H_
990+#define _IPT_ACCOUNT_H_
991+
992+#define IPT_ACCOUNT_NAME_LEN 64
993+
994+struct t_ipt_account_table;
995+
996+struct t_ipt_account_info {
997+ char name[IPT_ACCOUNT_NAME_LEN + 1];
998+ u_int32_t network, netmask;
999+ int shortlisting:1;
1000+ /* pointer to the table for fast matching */
1001+ struct t_ipt_account_table *table;
1002+};
1003+
1004+#endif /* _IPT_ACCOUNT_H */
1005+
1006diff -uNrp linux/net/ipv4/netfilter/Makefile.a linux/net/ipv4/netfilter/Makefile.b
b1eea20b 1007--- linux/net/ipv4/netfilter/Makefile 1971-01-01 01:00:00.000000000 +0100
1f0d84f2 1008+++ linux/net/ipv4/netfilter/Makefile 2007-08-15 13:23:25.375304000 +0200
a77c2279 1009@@ -0,0 +0,1 @@
1f0d84f2 1010+obj-$(CONFIG_IP_NF_MATCH_ACCOUNT) += ipt_account.o
c973efff 1011diff -Nur linux/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig
1012--- linux/net/ipv4/netfilter/Kconfig 2006-05-02 23:38:44.000000000 +0200
1013+++ linux/net/ipv4/netfilter/Kconfig 2006-05-04 11:23:02.000000000 +0200
1014@@ -606,5 +606,51 @@
1015 Allows altering the ARP packet payload: source and destination
1016 hardware and network addresses.
1017
1018+config IP_NF_MATCH_ACCOUNT
1019+ tristate "account match support"
1020+ depends on IP_NF_IPTABLES && PROC_FS
1021+ help
1022+ This match is used for accounting traffic for all hosts in
1023+ defined network/netmask.
1024+
1025+ Features:
1026+ - long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics
1027+ - one iptables rule for all hosts in network/netmask
1028+ - loading/saving counters (by reading/writting to procfs entries)
1029+
1030+ Example usage:
1031+
1032+ account traffic for/to 192.168.0.0/24 network into table mynetwork:
1033+
1034+ # iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24
1035+
1036+ account traffic for/to WWW serwer for 192.168.0.0/24 network into table
1037+ mywwwserver:
1038+
1039+ # iptables -A INPUT -p tcp --dport 80
1040+ -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
1041+ # iptables -A OUTPUT -p tcp --sport 80
1042+ -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
1043+
1044+ read counters:
1045+
1046+ # cat /proc/net/ipt_account/mynetwork
1047+ # cat /proc/net/ipt_account/mywwwserver
1048+
1049+ set counters:
1050+
1051+ # echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver
1052+
1053+ Webpage:
1054+ http://www.barbara.eu.org/~quaker/ipt_account/
1055+
1056+config IP_NF_MATCH_ACCOUNT_DEBUG
1057+ bool "account debugging output"
1058+ depends on IP_NF_MATCH_ACCOUNT
1059+ help
1060+ Say Y to get lots of debugging output.
1061+
1062+
1063+
1064 endmenu
1065
This page took 0.41054 seconds and 4 git commands to generate.