1 /* Copyright (c) 2004-2011 Piotr 'QuakeR' Gasidlo <quaker@barbara.eu.org>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
8 #include <linux/version.h>
9 #include <linux/module.h>
10 #include <linux/skbuff.h>
11 #include <linux/vmalloc.h>
12 #include <linux/proc_fs.h>
13 #include <linux/seq_file.h>
14 #include <linux/time.h>
18 #define IPT_ACCOUNT_VERSION "0.1.21"
20 //#define DEBUG_IPT_ACCOUNT
22 MODULE_AUTHOR("Piotr Gasidlo <quaker@barbara.eu.org>");
23 MODULE_DESCRIPTION("Traffic accounting module");
24 MODULE_LICENSE("GPL");
26 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
27 #include <linux/netfilter/x_tables.h>
29 #include <linux/netfilter_ipv4/ip_tables.h>
31 #include <linux/netfilter_ipv4/ipt_account.h>
33 /* Compatibility, should replace all HIPQUAD with %pI4 and use network byte order not host byte order */
34 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
35 #define HIPQUAD(addr) \
36 ((unsigned char *)&addr)[3], \
37 ((unsigned char *)&addr)[2], \
38 ((unsigned char *)&addr)[1], \
39 ((unsigned char *)&addr)[0]
42 /* defaults, can be overriden */
43 static unsigned int netmask
= 16; /* Safe netmask, if you try to create table
44 for larger netblock you will get error.
45 Increase by command line only when you
46 known what are you doing. */
48 #ifdef DEBUG_IPT_ACCOUNT
51 module_param(netmask
, uint
, 0400);
53 MODULE_PARM_DESC(netmask
,"maximum *save* netmask");
54 #ifdef DEBUG_IPT_ACCOUNT
55 module_param(debug
, bool, 0600);
56 MODULE_PARM_DESC(debug
,"enable debugging output");
59 /* structure with statistics counter, used when table is created without --ashort switch */
60 struct t_ipt_account_stat_long
{
61 u_int64_t b_all
, b_tcp
, b_udp
, b_icmp
, b_other
;
62 u_int64_t p_all
, p_tcp
, p_udp
, p_icmp
, p_other
;
65 /* same as above, for tables created with --ashort switch */
66 struct t_ipt_account_stat_short
{
71 /* structure holding to/from statistics for single ip when table is created without --ashort switch */
72 struct t_ipt_account_stats_long
{
73 struct t_ipt_account_stat_long src
, dst
;
74 struct timespec time
; /* time, when statistics was last modified */
77 /* same as above, for tables created with --ashort switch */
78 struct t_ipt_account_stats_short
{
79 struct t_ipt_account_stat_short src
, dst
;
83 /* defines for "show" table option */
87 #define SHOW_SRC_OR_DST 3
88 #define SHOW_SRC_AND_DST 4
90 /* structure describing single table */
91 struct t_ipt_account_table
{
92 struct list_head list
;
93 atomic_t use
; /* use counter, the number of rules which points to this table */
95 char name
[IPT_ACCOUNT_NAME_LEN
+ 1]; /* table name ( = filename in /proc/net/ipt_account/) */
96 u_int32_t network
, netmask
, count
; /* network/netmask/hosts count coverted by table */
98 int shortlisting
:1; /* gather only total statistics (set for tables created with --ashort switch) */
99 int timesrc
:1; /* update time when accounting outgoing traffic */
100 int timedst
:1; /* update time when accounting incomming traffic */
101 int resetonread
:1; /* reset statistics after reading it via proc */
102 int show
; /* show with entries */
104 /* FIXME: why int show:3 results in 'warning: comparison is always 0 due to width of bit-field' in ipt_account_seq_show
105 * gcc -v: gcc version 3.4.6 */
107 union { /* statistics for each ip in network/netmask */
108 struct t_ipt_account_stats_long
*l
;
109 struct t_ipt_account_stats_short
*s
;
111 rwlock_t stats_lock
; /* lock, to assure that above union can be safely modified */
113 struct proc_dir_entry
*pde
; /* handle to proc entry */
116 static LIST_HEAD(ipt_account_tables
);
117 static rwlock_t ipt_account_lock
= RW_LOCK_UNLOCKED
; /* lock, to assure that table list can be safely modified */
118 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
119 static DEFINE_MUTEX(ipt_account_mutex
); /* additional checkentry protection */
121 static DECLARE_MUTEX(ipt_account_mutex
); /* additional checkentry protection */
124 static struct file_operations ipt_account_proc_fops
;
125 static struct proc_dir_entry
*ipt_account_procdir
;
128 * Function creates new table and inserts it into linked list.
130 static struct t_ipt_account_table
*
131 ipt_account_table_init(struct t_ipt_account_info
*info
)
133 struct t_ipt_account_table
*table
;
135 #ifdef DEBUG_IPT_ACCOUNT
136 if (debug
) printk(KERN_DEBUG
"ipt_account [ipt_account_table_init]: name = %s\n", info
->name
);
140 * Allocate memory for table.
142 table
= vmalloc(sizeof(struct t_ipt_account_table
));
144 printk(KERN_ERR
"ipt_account [ipt_account_table_init]: table = vmalloc(sizeof(struct t_ipt_account_table)) failed.\n");
147 memset(table
, 0, sizeof(struct t_ipt_account_table
));
152 strncpy(table
->name
, info
->name
, IPT_ACCOUNT_NAME_LEN
);
153 table
->name
[IPT_ACCOUNT_NAME_LEN
] = '\0';
155 table
->network
= info
->network
;
156 table
->netmask
= info
->netmask
;
157 table
->count
= (0xffffffff ^ table
->netmask
) + 1;
162 table
->shortlisting
= info
->shortlisting
;
165 table
->resetonread
= 0;
166 table
->show
= SHOW_ANY
;
169 * Initialize use counter.
171 atomic_set(&table
->use
, 1);
174 * Allocate memory for statistic counters.
176 if (table
->shortlisting
) {
177 table
->stats
.s
= vmalloc(sizeof(struct t_ipt_account_stats_short
) * table
->count
);
178 if (!table
->stats
.s
) {
179 printk(KERN_ERR
"ipt_account [ipt_account_table_init]: table->stats.s = vmalloc(sizeof(struct t_ipt_account_stats_short) * table->count) failed.\n");
182 memset(table
->stats
.s
, 0, sizeof(struct t_ipt_account_stats_short
) * table
->count
);
184 table
->stats
.l
= vmalloc(sizeof(struct t_ipt_account_stats_long
) * table
->count
);
185 if (!table
->stats
.l
) {
186 printk(KERN_ERR
"ipt_account [ipt_account_table_init]: table->stats.l = vmalloc(sizeof(struct t_ipt_account_stats_long) * table->count) failed.\n");
189 memset(table
->stats
.l
, 0, sizeof(struct t_ipt_account_stats_long
) * table
->count
);
195 table
->stats_lock
= RW_LOCK_UNLOCKED
;
198 * Create /proc/ipt_account/name entry.
200 table
->pde
= create_proc_entry(table
->name
, S_IWUSR
| S_IRUSR
, ipt_account_procdir
);
204 table
->pde
->proc_fops
= &ipt_account_proc_fops
;
205 table
->pde
->data
= table
;
208 * Insert table into list.
210 write_lock_bh(&ipt_account_lock
);
211 list_add(&table
->list
, &ipt_account_tables
);
212 write_unlock_bh(&ipt_account_lock
);
217 * If something goes wrong we end here.
220 if (table
->shortlisting
)
221 vfree(table
->stats
.s
);
223 vfree(table
->stats
.l
);
233 * Function destroys table. Table *must* be already unlinked.
236 ipt_account_table_destroy(struct t_ipt_account_table
*table
)
238 #ifdef DEBUG_IPT_ACCOUNT
239 if (debug
) printk(KERN_DEBUG
"ipt_account [ipt_account_table_destory]: name = %s\n", table
->name
);
241 remove_proc_entry(table
->pde
->name
, table
->pde
->parent
);
242 if (table
->shortlisting
)
243 vfree(table
->stats
.s
);
245 vfree(table
->stats
.l
);
250 * Function increments use counter for table.
253 ipt_account_table_get(struct t_ipt_account_table
*table
)
255 #ifdef DEBUG_IPT_ACCOUNT
256 if (debug
) printk(KERN_DEBUG
"ipt_account [ipt_account_table_get]: name = %s\n", table
->name
);
258 atomic_inc(&table
->use
);
262 * Function decrements use counter for table. If use counter drops to zero,
263 * table is removed from linked list and destroyed.
266 ipt_account_table_put(struct t_ipt_account_table
*table
)
268 #ifdef DEBUG_IPT_ACCOUNT
269 if (debug
) printk(KERN_DEBUG
"ipt_account [ipt_account_table_put]: name = %s\n", table
->name
);
271 if (atomic_dec_and_test(&table
->use
)) {
272 write_lock_bh(&ipt_account_lock
);
273 list_del(&table
->list
);
274 write_unlock_bh(&ipt_account_lock
);
275 ipt_account_table_destroy(table
);
280 * Helper function, which returns a structure pointer to a table with
283 static struct t_ipt_account_table
*
284 __ipt_account_table_find(char *name
)
286 struct list_head
*pos
;
287 list_for_each(pos
, &ipt_account_tables
) {
288 struct t_ipt_account_table
*table
= list_entry(pos
,
289 struct t_ipt_account_table
, list
);
290 if (!strncmp(table
->name
, name
, IPT_ACCOUNT_NAME_LEN
))
297 * Function, which returns a structure pointer to a table with
298 * specified name. When such table is found its use coutner
301 static inline struct t_ipt_account_table
*
302 ipt_account_table_find_get(char *name
)
304 struct t_ipt_account_table
*table
;
306 #ifdef DEBUG_IPT_ACCOUNT
307 if (debug
) printk(KERN_DEBUG
"ipt_account [ipt_account_table_find_get]: name = %s\n", name
);
309 read_lock_bh(&ipt_account_lock
);
310 table
= __ipt_account_table_find(name
);
312 read_unlock_bh(&ipt_account_lock
);
315 atomic_inc(&table
->use
);
316 read_unlock_bh(&ipt_account_lock
);
321 * Helper function, with updates statistics for specified IP. It's only
322 * used for tables created without --ashort switch.
325 __account_long(struct t_ipt_account_stat_long
*stat
, const struct sk_buff
*skb
)
327 stat
->b_all
+= skb
->len
;
330 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
331 switch (ip_hdr(skb
)->protocol
) {
333 switch (skb
->nh
.iph
->protocol
) {
336 stat
->b_tcp
+= skb
->len
;
340 stat
->b_udp
+= skb
->len
;
344 stat
->b_icmp
+= skb
->len
;
348 stat
->b_other
+= skb
->len
;
354 * Same as above, but used for tables created with --ashort switch.
357 __account_short(struct t_ipt_account_stat_short
*stat
, const struct sk_buff
*skb
)
359 stat
->b_all
+= skb
->len
;
364 * Match function. Here we do accounting stuff.
366 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
371 match(const struct sk_buff
*skb
,
372 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
373 struct xt_action_param
*par
374 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
375 const struct xt_match_param
*par
377 const struct net_device
*in
,
378 const struct net_device
*out
,
379 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
380 const struct xt_match
*match
,
382 const void *matchinfo
,
384 unsigned int protoff
,
385 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
393 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
394 struct t_ipt_account_info
*info
= (struct t_ipt_account_info
*)(par
->matchinfo
);
396 struct t_ipt_account_info
*info
= (struct t_ipt_account_info
*)matchinfo
;
398 struct t_ipt_account_table
*table
= info
->table
;
400 /* Get current time. */
401 struct timespec now
= CURRENT_TIME_SEC
;
402 /* Default we assume no match. */
403 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
409 #ifdef DEBUG_IPT_ACCOUNT
410 if (debug
) printk(KERN_DEBUG
"ipt_account [match]: name = %s\n", table
->name
);
412 /* Check whether traffic from source ip address ... */
413 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
414 address
= ntohl(ip_hdr(skb
)->saddr
);
416 address
= ntohl(skb
->nh
.iph
->saddr
);
418 /* ... is being accounted by this table. */
419 if (address
&& ((u_int32_t
)(address
& table
->netmask
) == (u_int32_t
)table
->network
)) {
420 write_lock_bh(&table
->stats_lock
);
421 /* Yes, account this packet. */
422 #ifdef DEBUG_IPT_ACCOUNT
423 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
424 if (debug
) printk(KERN_DEBUG
"ipt_account: [match]: accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address
), ip_hdr(skb
)->protocol
);
426 if (debug
) printk(KERN_DEBUG
"ipt_account: [match]: accounting packet src = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address
), skb
->nh
.iph
->protocol
);
429 /* Update counters this host. */
430 if (!table
->shortlisting
) {
431 __account_long(&table
->stats
.l
[address
- table
->network
].src
, skb
);
433 table
->stats
.l
[address
- table
->network
].time
= now
;
434 /* Update also counters for all hosts in this table (network address) */
435 if (table
->count
> 1) {
436 __account_long(&table
->stats
.l
[0].src
, skb
);
437 table
->stats
.l
[0].time
= now
;
440 __account_short(&table
->stats
.s
[address
- table
->network
].src
, skb
);
442 table
->stats
.s
[address
- table
->network
].time
= now
;
443 if (table
->count
> 1) {
444 __account_short(&table
->stats
.s
[0].src
, skb
);
445 table
->stats
.s
[0].time
= now
;
448 write_unlock_bh(&table
->stats_lock
);
449 /* Yes, it's a match. */
450 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
458 /* Do the same thing with destination ip address. */
459 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
460 address
= ntohl(ip_hdr(skb
)->daddr
);
462 address
= ntohl(skb
->nh
.iph
->daddr
);
464 if (address
&& ((u_int32_t
)(address
& table
->netmask
) == (u_int32_t
)table
->network
)) {
465 write_lock_bh(&table
->stats_lock
);
466 #ifdef DEBUG_IPT_ACCOUNT
467 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
468 if (debug
) printk(KERN_DEBUG
"ipt_account: [match]: accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address
), ip_hdr(skb
)->protocol
);
470 if (debug
) printk(KERN_DEBUG
"ipt_account: [match]: accounting packet dst = %u.%u.%u.%u, proto = %u.\n", HIPQUAD(address
), skb
->nh
.iph
->protocol
);
473 if (!table
->shortlisting
) {
474 __account_long(&table
->stats
.l
[address
- table
->network
].dst
, skb
);
475 table
->stats
.l
[address
- table
->network
].time
= now
;
476 if (table
->count
> 1) {
477 __account_long(&table
->stats
.l
[0].dst
, skb
);
478 table
->stats
.l
[0].time
= now
;
481 __account_short(&table
->stats
.s
[address
- table
->network
].dst
, skb
);
482 table
->stats
.s
[address
- table
->network
].time
= now
;
483 if (table
->count
> 1) {
484 __account_short(&table
->stats
.s
[0].dst
, skb
);
485 table
->stats
.s
[0].time
= now
;
488 write_unlock_bh(&table
->stats_lock
);
489 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
500 * Checkentry function.
502 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
504 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
510 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
511 const struct xt_mtchk_param
*par
513 const char *tablename
,
514 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
517 const struct ipt_entry
*ip
,
519 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
520 const struct xt_match
*match
,
523 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
524 unsigned int matchsize
,
526 unsigned int hook_mask
530 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
531 struct t_ipt_account_info
*info
= (struct t_ipt_account_info
*)(par
->matchinfo
);
533 struct t_ipt_account_info
*info
= matchinfo
;
535 struct t_ipt_account_table
*table
;
537 #ifdef DEBUG_IPT_ACCOUNT
538 if (debug
) printk(KERN_DEBUG
"ipt_account [checkentry]: name = %s\n", info
->name
);
540 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
541 if (matchsize
!= IPT_ALIGN(sizeof(struct t_ipt_account_info
))) {
542 #ifdef DEBUG_IPT_ACCOUNT
543 if (debug
) printk(KERN_DEBUG
"ipt_account [checkentry]: matchsize %u != %u\n", matchsize
, IPT_ALIGN(sizeof(struct t_ipt_account_info
)));
545 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
547 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
558 if (info
->netmask
< ((~0L << (32 - netmask
)) & 0xffffffff)) {
559 printk(KERN_ERR
"ipt_account[checkentry]: too big netmask (increase module 'netmask' parameter).\n");
560 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
562 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
568 if ((info
->network
& info
->netmask
) != info
->network
) {
569 printk(KERN_ERR
"ipt_account[checkentry]: wrong network/netmask.\n");
570 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
572 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
578 if (info
->name
[0] == '\0') {
579 printk(KERN_ERR
"ipt_account[checkentry]: wrong table name.\n");
580 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
582 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
590 * We got new rule. Try to find table with the same name as given in info structure.
591 * Mutex magic based on xt_hashlimit.c.
593 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
594 mutex_lock(&ipt_account_mutex
);
596 down(&ipt_account_mutex
);
598 table
= ipt_account_table_find_get(info
->name
);
600 if (info
->table
!= NULL
) {
601 if (info
->table
!= table
) {
602 printk(KERN_ERR
"ipt_account[checkentry]: reloaded rule has invalid table pointer.\n");
603 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
604 mutex_unlock(&ipt_account_mutex
);
606 up(&ipt_account_mutex
);
608 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
610 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
616 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
617 mutex_unlock(&ipt_account_mutex
);
619 up(&ipt_account_mutex
);
621 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
623 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
629 #ifdef DEBUG_IPT_ACCOUNT
630 if (debug
) printk(KERN_DEBUG
"ipt_account [checkentry]: table found, checking.\n");
633 * Table exists, but whether rule network/netmask/shortlisting matches
634 * table network/netmask/shortlisting. Failure on missmatch.
636 if (table
->network
!= info
->network
|| table
->netmask
!= info
->netmask
|| table
->shortlisting
!= info
->shortlisting
) {
637 printk(KERN_ERR
"ipt_account [checkentry]: table found, rule network/netmask/shortlisting not match table network/netmask/shortlisting.\n");
639 * Remember to release table usage counter.
641 ipt_account_table_put(table
);
642 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
643 mutex_unlock(&ipt_account_mutex
);
645 up(&ipt_account_mutex
);
647 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
649 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
655 #ifdef DEBUG_IPT_ACCOUNT
656 if (debug
) printk(KERN_DEBUG
"ipt_account [checkentry]: table found, reusing.\n");
659 * Link rule with table.
664 #ifdef DEBUG_IPT_ACCOUNT
665 if (debug
) printk(KERN_DEBUG
"ipt_account [checkentry]: table not found, creating new one.\n");
668 * Table not exist, create new one.
670 info
->table
= table
= ipt_account_table_init(info
);
672 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
673 mutex_unlock(&ipt_account_mutex
);
675 up(&ipt_account_mutex
);
677 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
679 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
686 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
687 mutex_unlock(&ipt_account_mutex
);
689 up(&ipt_account_mutex
);
691 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
703 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
704 const struct xt_mtdtor_param
*par
706 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
707 const struct xt_match
*match
,
709 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
713 unsigned int matchsize
718 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
719 struct t_ipt_account_info
*info
= (struct t_ipt_account_info
*)(par
->matchinfo
);
721 struct t_ipt_account_info
*info
= matchinfo
;
724 #ifdef DEBUG_IPT_ACCOUNT
725 if (debug
) printk(KERN_DEBUG
"ipt_account [destroy]: name = %s\n", info
->name
);
727 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
728 if (matchsize
!= IPT_ALIGN(sizeof(struct t_ipt_account_info
))) {
729 #ifdef DEBUG_IPT_ACCOUNT
730 if (debug
) printk(KERN_DEBUG
"ipt_account [checkentry]: matchsize %u != %u\n", matchsize
, IPT_ALIGN(sizeof(struct t_ipt_account_info
)));
737 * Release table, by decreasing its usage counter. When
738 * counter hits zero, memory used by table structure is
739 * released and table is removed from list.
741 ipt_account_table_put(info
->table
);
745 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
746 static struct xt_match account_match
= {
748 static struct ipt_match account_match
= {
751 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
752 .family
= NFPROTO_IPV4
,
753 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
757 .checkentry
= &checkentry
,
758 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
759 .matchsize
= sizeof(struct t_ipt_account_info
),
766 * Below functions (ipt_account_seq_start, ipt_account_seq_next,
767 * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write)
768 * are used to implement proc stuff.
770 static void *ipt_account_seq_start(struct seq_file
*sf
, loff_t
*pos
)
772 struct proc_dir_entry
*pde
= sf
->private;
773 struct t_ipt_account_table
*table
= pde
->data
;
776 if (table
->resetonread
) {
777 /* When we reset entries after read we must have exclusive lock. */
778 write_lock_bh(&table
->stats_lock
);
780 read_lock_bh(&table
->stats_lock
);
782 if (*pos
>= table
->count
)
784 i
= kmalloc(sizeof(unsigned int), GFP_ATOMIC
);
786 return ERR_PTR(-ENOMEM
);
791 static void *ipt_account_seq_next(struct seq_file
*sf
, void *v
, loff_t
*pos
)
793 struct proc_dir_entry
*pde
= sf
->private;
794 struct t_ipt_account_table
*table
= pde
->data
;
795 unsigned int *i
= (unsigned int *)v
;
798 if (*i
>= table
->count
) {
805 static void ipt_account_seq_stop(struct seq_file
*sf
, void *v
)
807 struct proc_dir_entry
*pde
= sf
->private;
808 struct t_ipt_account_table
*table
= pde
->data
;
810 if (table
->resetonread
) {
811 write_unlock_bh(&table
->stats_lock
);
813 read_unlock_bh(&table
->stats_lock
);
817 static int ipt_account_seq_show(struct seq_file
*sf
, void *v
)
819 struct proc_dir_entry
*pde
= sf
->private;
820 struct t_ipt_account_table
*table
= pde
->data
;
821 unsigned int *i
= (unsigned int *)v
;
823 struct timespec now
= CURRENT_TIME_SEC
;
825 u_int32_t address
= table
->network
+ *i
;
827 if (!table
->shortlisting
) {
828 struct t_ipt_account_stats_long
*l
= &table
->stats
.l
[*i
];
829 /* Don't list rows not matching show requirements. */
831 ((table
->show
== SHOW_SRC
) && (l
->src
.p_all
== 0)) ||
832 ((table
->show
== SHOW_DST
) && (l
->dst
.p_all
== 0)) ||
833 ((table
->show
== SHOW_SRC_OR_DST
) && ((l
->src
.p_all
== 0) && (l
->dst
.p_all
== 0))) ||
834 ((table
->show
== SHOW_SRC_AND_DST
) && ((l
->src
.p_all
== 0) || (l
->dst
.p_all
== 0)))
839 "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",
861 now
.tv_sec
- l
->time
.tv_sec
863 if (table
->resetonread
)
864 memset(l
, 0, sizeof(struct t_ipt_account_stats_long
));
867 struct t_ipt_account_stats_short
*s
= &table
->stats
.s
[*i
];
869 ((table
->show
== SHOW_SRC
) && (s
->src
.p_all
== 0)) ||
870 ((table
->show
== SHOW_DST
) && (s
->dst
.p_all
== 0)) ||
871 ((table
->show
== SHOW_SRC_OR_DST
) && ((s
->src
.p_all
== 0) && (s
->dst
.p_all
== 0))) ||
872 ((table
->show
== SHOW_SRC_AND_DST
) && ((s
->src
.p_all
== 0) || (s
->dst
.p_all
== 0)))
877 "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
883 now
.tv_sec
- s
->time
.tv_sec
885 if (table
->resetonread
)
886 memset(s
, 0, sizeof(struct t_ipt_account_stats_short
));
892 static struct seq_operations ipt_account_seq_ops
= {
893 .start
= ipt_account_seq_start
,
894 .next
= ipt_account_seq_next
,
895 .stop
= ipt_account_seq_stop
,
896 .show
= ipt_account_seq_show
899 static ssize_t
ipt_account_proc_write(struct file
*file
, const char __user
*input
, size_t size
, loff_t
*ofs
)
902 struct proc_dir_entry
*pde
= PDE(file
->f_dentry
->d_inode
);
903 struct t_ipt_account_table
*table
= pde
->data
;
906 struct t_ipt_account_stats_long l
;
907 struct t_ipt_account_stats_short s
;
909 #ifdef DEBUG_IPT_ACCOUNT
910 if (debug
) printk(KERN_DEBUG
"ipt_account [ipt_account_proc_write]: name = %s.\n", table
->name
);
912 if (copy_from_user(buffer
, input
, 1024))
916 if (!strncmp(buffer
, "reset\n", 6)) {
918 * User requested to clear all table. Ignorant, does
919 * he known how match time it took us to fill it? ;-)
921 write_lock_bh(&table
->stats_lock
);
922 if (table
->shortlisting
)
923 memset(table
->stats
.s
, 0, sizeof(struct t_ipt_account_stats_short
) * table
->count
);
925 memset(table
->stats
.l
, 0, sizeof(struct t_ipt_account_stats_long
) * table
->count
);
926 write_unlock_bh(&table
->stats_lock
);
927 } else if (!strncmp(buffer
, "reset-on-read=yes\n", 18)) {
929 * We must be sure that ipt_account_seq_* is not running now. This option
930 * changes lock type which is taken in ipt_account_seq_{start|stop}. When
931 * we change this option without this lock, and ipt_account_seq_start is
932 * already run (but not ipt_account_seq_stop) there is possibility that
933 * we execute wrong "unlock" function.
935 write_lock_bh(&table
->stats_lock
);
936 table
->resetonread
= 1;
937 write_unlock_bh(&table
->stats_lock
);
938 } else if (!strncmp(buffer
, "reset-on-read=no\n", 17)) {
939 write_lock_bh(&table
->stats_lock
);
940 table
->resetonread
= 0;
941 write_unlock_bh(&table
->stats_lock
);
942 } else if (!strncmp(buffer
, "reset-on-read\n", 14)) {
943 write_lock_bh(&table
->stats_lock
);
944 table
->resetonread
= 1;
945 write_unlock_bh(&table
->stats_lock
);
946 } else if (!strncmp(buffer
, "show=any\n", 9)) {
948 * Here we should lock but we don't have to. So we don't lock. We only get
949 * wrong results on already run ipt_account_seq_show, but we won't crush
952 table
->show
= SHOW_ANY
;
953 } else if (!strncmp(buffer
, "show=src\n", 9)) {
954 table
->show
= SHOW_SRC
;
955 } else if (!strncmp(buffer
, "show=dst\n", 9)) {
956 table
->show
= SHOW_DST
;
957 } else if (!strncmp(buffer
, "show=src-or-dst\n", 16) || !strncmp(buffer
, "show=dst-or-src\n", 16)) {
958 table
->show
= SHOW_SRC_OR_DST
;
959 } else if (!strncmp(buffer
, "show=src-and-dst\n", 17) || !strncmp(buffer
, "show=dst-and-src\n", 17)) {
960 table
->show
= SHOW_SRC_AND_DST
;
961 } else if (!strncmp(buffer
, "time=any\n", 9)) {
962 table
->timesrc
= table
->timedst
= 1;
963 } else if (!strncmp(buffer
, "time=src\n", 9)) {
966 } else if (!strncmp(buffer
, "time=dst\n", 9)) {
969 } 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",
970 &o
[0], &o
[1], &o
[2], &o
[3],
971 &l
.src
.b_all
, &l
.src
.b_tcp
, &l
.src
.b_udp
, &l
.src
.b_icmp
, &l
.src
.b_other
,
972 &l
.src
.p_all
, &l
.src
.p_tcp
, &l
.src
.p_udp
, &l
.src
.p_icmp
, &l
.src
.p_other
,
973 &l
.dst
.b_all
, &l
.dst
.b_tcp
, &l
.dst
.b_udp
, &l
.dst
.b_icmp
, &l
.dst
.b_other
,
974 &l
.dst
.p_all
, &l
.dst
.p_tcp
, &l
.dst
.p_udp
, &l
.dst
.p_icmp
, &l
.dst
.p_other
,
975 &l
.time
.tv_sec
) == 25 ) {
977 * We got line formated like long listing row. We have to
978 * check, if IP is accounted by table. If so, we
979 * simply replace row with user's one.
981 ip
= o
[0] << 24 | o
[1] << 16 | o
[2] << 8 | o
[3];
982 if ((u_int32_t
)(ip
& table
->netmask
) == (u_int32_t
)table
->network
) {
984 * Ignore user input time. Set current time.
986 l
.time
= CURRENT_TIME_SEC
;
987 write_lock_bh(&table
->stats_lock
);
988 table
->stats
.l
[ip
- table
->network
] = l
;
989 write_unlock_bh(&table
->stats_lock
);
991 } 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",
992 &o
[0], &o
[1], &o
[2], &o
[3],
997 &s
.time
.tv_sec
) == 9) {
999 * We got line formated like short listing row. Do the
1000 * same action like above.
1002 ip
= o
[0] << 24 | o
[1] << 16 | o
[2] << 8 | o
[3];
1003 if ((u_int32_t
)(ip
& table
->netmask
) == (u_int32_t
)table
->network
) {
1004 s
.time
= CURRENT_TIME_SEC
;
1005 write_lock_bh(&table
->stats_lock
);
1006 table
->stats
.s
[ip
- table
->network
] = s
;
1007 write_unlock_bh(&table
->stats_lock
);
1011 * We don't understand what user have just wrote.
1019 static int ipt_account_proc_open(struct inode
*inode
, struct file
*file
)
1021 int ret
= seq_open(file
, &ipt_account_seq_ops
);
1023 struct seq_file
*sf
= file
->private_data
;
1024 struct proc_dir_entry
*pde
= PDE(inode
);
1025 struct t_ipt_account_table
*table
= pde
->data
;
1029 ipt_account_table_get(table
);
1034 static int ipt_account_proc_release(struct inode
*inode
, struct file
*file
)
1036 struct proc_dir_entry
*pde
= PDE(inode
);
1037 struct t_ipt_account_table
*table
= pde
->data
;
1040 ret
= seq_release(inode
, file
);
1043 ipt_account_table_put(table
);
1048 static struct file_operations ipt_account_proc_fops
= {
1049 .owner
= THIS_MODULE
,
1050 .open
= ipt_account_proc_open
,
1052 .write
= ipt_account_proc_write
,
1053 .llseek
= seq_lseek
,
1054 .release
= ipt_account_proc_release
1058 * Module init function.
1060 static int __init
init(void)
1064 printk(KERN_INFO
"ipt_account %s : Piotr Gasidlo <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION
);
1066 /* Check module parameters. */
1067 if (netmask
> 32 || netmask
< 0) {
1068 printk(KERN_ERR
"ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask
);
1073 /* Register match. */
1074 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
1075 if (xt_register_match(&account_match
)) {
1077 if (ipt_register_match(&account_match
)) {
1083 /* Create /proc/net/ipt_account/ entry. */
1084 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
1085 ipt_account_procdir
= proc_mkdir("ipt_account", init_net
.proc_net
);
1087 ipt_account_procdir
= proc_mkdir("ipt_account", proc_net
);
1089 if (!ipt_account_procdir
) {
1090 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
1091 printk(KERN_ERR
"ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", init_proc.proc_net) failed.\n");
1093 printk(KERN_ERR
"ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", proc_net) failed.\n");
1101 /* If something goes wrong we end here. */
1103 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
1104 xt_unregister_match(&account_match
);
1106 ipt_unregister_match(&account_match
);
1113 * Module exit function.
1115 static void __exit
fini(void)
1117 /* Remove /proc/net/ipt_account/ */
1118 remove_proc_entry(ipt_account_procdir
->name
, ipt_account_procdir
->parent
);
1119 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
1120 xt_unregister_match(&account_match
);
1122 ipt_unregister_match(&account_match
);