RT-AC66 3.0.0.4.374.130 core
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / net / ipv4 / netfilter / ipt_account.c
blobba03da55123703ee532b77dbc9a8af1267b7f9c9
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.
6 */
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>
15 #include <linux/ip.h>
16 #include <linux/in.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>
28 #else
29 #include <linux/netfilter_ipv4/ip_tables.h>
30 #endif
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]
40 #endif
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
49 static int debug = 0;
50 #endif
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");
57 #endif
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 {
67 u_int64_t b_all;
68 u_int64_t p_all;
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;
80 struct timespec time;
83 /* defines for "show" table option */
84 #define SHOW_ANY 0
85 #define SHOW_SRC 1
86 #define SHOW_DST 2
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;
110 } stats;
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 */
120 #else
121 static DECLARE_MUTEX(ipt_account_mutex); /* additional checkentry protection */
122 #endif
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);
137 #endif
140 * Allocate memory for table.
142 table = vmalloc(sizeof(struct t_ipt_account_table));
143 if (!table) {
144 printk(KERN_ERR "ipt_account [ipt_account_table_init]: table = vmalloc(sizeof(struct t_ipt_account_table)) failed.\n");
145 goto cleanup_none;
147 memset(table, 0, sizeof(struct t_ipt_account_table));
150 * Table attributes.
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;
160 * Table properties.
162 table->shortlisting = info->shortlisting;
163 table->timesrc = 1;
164 table->timedst = 1;
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");
180 goto cleanup_table;
182 memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
183 } else {
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");
187 goto cleanup_table;
189 memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
193 * Reset locks.
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);
201 if (!table->pde) {
202 goto cleanup_stats;
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);
214 return table;
217 * If something goes wrong we end here.
219 cleanup_stats:
220 if (table->shortlisting)
221 vfree(table->stats.s);
222 else
223 vfree(table->stats.l);
225 cleanup_table:
226 vfree(table);
227 cleanup_none:
228 return NULL;
233 * Function destroys table. Table *must* be already unlinked.
235 static void
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);
240 #endif
241 remove_proc_entry(table->pde->name, table->pde->parent);
242 if (table->shortlisting)
243 vfree(table->stats.s);
244 else
245 vfree(table->stats.l);
246 vfree(table);
250 * Function increments use counter for table.
252 static inline void
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);
257 #endif
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.
265 static inline void
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);
270 #endif
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
281 * specified name.
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))
291 return table;
293 return NULL;
297 * Function, which returns a structure pointer to a table with
298 * specified name. When such table is found its use coutner
299 * is incremented.
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);
308 #endif
309 read_lock_bh(&ipt_account_lock);
310 table = __ipt_account_table_find(name);
311 if (!table) {
312 read_unlock_bh(&ipt_account_lock);
313 return NULL;
315 atomic_inc(&table->use);
316 read_unlock_bh(&ipt_account_lock);
317 return table;
321 * Helper function, with updates statistics for specified IP. It's only
322 * used for tables created without --ashort switch.
324 static inline void
325 __account_long(struct t_ipt_account_stat_long *stat, const struct sk_buff *skb)
327 stat->b_all += skb->len;
328 stat->p_all++;
330 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
331 switch (ip_hdr(skb)->protocol) {
332 #else
333 switch (skb->nh.iph->protocol) {
334 #endif
335 case IPPROTO_TCP:
336 stat->b_tcp += skb->len;
337 stat->p_tcp++;
338 break;
339 case IPPROTO_UDP:
340 stat->b_udp += skb->len;
341 stat->p_udp++;
342 break;
343 case IPPROTO_ICMP:
344 stat->b_icmp += skb->len;
345 stat->p_icmp++;
346 break;
347 default:
348 stat->b_other += skb->len;
349 stat->p_other++;
354 * Same as above, but used for tables created with --ashort switch.
356 static inline void
357 __account_short(struct t_ipt_account_stat_short *stat, const struct sk_buff *skb)
359 stat->b_all += skb->len;
360 stat->p_all++;
364 * Match function. Here we do accounting stuff.
366 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
367 static bool
368 #else
369 static int
370 #endif
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
376 #else
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,
381 #endif
382 const void *matchinfo,
383 int offset,
384 unsigned int protoff,
385 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
386 bool *hotdrop
387 #else
388 int *hotdrop
389 #endif
390 #endif
393 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
394 struct t_ipt_account_info *info = (struct t_ipt_account_info *)(par->matchinfo);
395 #else
396 struct t_ipt_account_info *info = (struct t_ipt_account_info *)matchinfo;
397 #endif
398 struct t_ipt_account_table *table = info->table;
399 u_int32_t address;
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)
404 bool ret = false;
405 #else
406 int ret = 0;
407 #endif
409 #ifdef DEBUG_IPT_ACCOUNT
410 if (debug) printk(KERN_DEBUG "ipt_account [match]: name = %s\n", table->name);
411 #endif
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);
415 #else
416 address = ntohl(skb->nh.iph->saddr);
417 #endif
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);
425 #else
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);
427 #endif
428 #endif
429 /* Update counters this host. */
430 if (!table->shortlisting) {
431 __account_long(&table->stats.l[address - table->network].src, skb);
432 if (table->timesrc)
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;
439 } else {
440 __account_short(&table->stats.s[address - table->network].src, skb);
441 if (table->timedst)
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)
451 ret = true;
452 #else
453 ret = 1;
454 #endif
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);
461 #else
462 address = ntohl(skb->nh.iph->daddr);
463 #endif
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);
469 #else
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);
471 #endif
472 #endif
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;
480 } else {
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)
490 ret = true;
491 #else
492 ret = 1;
493 #endif
496 return ret;
500 * Checkentry function.
502 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
503 static int
504 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
505 static bool
506 #else
507 static int
508 #endif
509 checkentry(
510 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
511 const struct xt_mtchk_param *par
512 #else
513 const char *tablename,
514 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
515 const void *ip,
516 #else
517 const struct ipt_entry *ip,
518 #endif
519 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
520 const struct xt_match *match,
521 #endif
522 void *matchinfo,
523 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
524 unsigned int matchsize,
525 #endif
526 unsigned int hook_mask
527 #endif
530 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
531 struct t_ipt_account_info *info = (struct t_ipt_account_info*)(par->matchinfo);
532 #else
533 struct t_ipt_account_info *info = matchinfo;
534 #endif
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);
539 #endif
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)));
544 #endif
545 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
546 return -EINVAL;
547 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
548 return false;
549 #else
550 return 0;
551 #endif
553 #endif
556 * Sanity checks.
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)
561 return -EINVAL;
562 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
563 return false;
564 #else
565 return 0;
566 #endif
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)
571 return -EINVAL;
572 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
573 return false;
574 #else
575 return 0;
576 #endif
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)
581 return -EINVAL;
582 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
583 return false;
584 #else
585 return 0;
586 #endif
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);
595 #else
596 down(&ipt_account_mutex);
597 #endif
598 table = ipt_account_table_find_get(info->name);
599 if (table) {
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);
605 #else
606 up(&ipt_account_mutex);
607 #endif
608 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
609 return -EINVAL;
610 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
611 return false;
612 #else
613 return 0;
614 #endif
616 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
617 mutex_unlock(&ipt_account_mutex);
618 #else
619 up(&ipt_account_mutex);
620 #endif
621 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
622 return -EINVAL;
623 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
624 return true;
625 #else
626 return 1;
627 #endif
628 } else {
629 #ifdef DEBUG_IPT_ACCOUNT
630 if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, checking.\n");
631 #endif
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);
644 #else
645 up(&ipt_account_mutex);
646 #endif
647 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
648 return -EINVAL;
649 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
650 return false;
651 #else
652 return 0;
653 #endif
655 #ifdef DEBUG_IPT_ACCOUNT
656 if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, reusing.\n");
657 #endif
659 * Link rule with table.
661 info->table = table;
663 } else {
664 #ifdef DEBUG_IPT_ACCOUNT
665 if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table not found, creating new one.\n");
666 #endif
668 * Table not exist, create new one.
670 info->table = table = ipt_account_table_init(info);
671 if (!table) {
672 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
673 mutex_unlock(&ipt_account_mutex);
674 #else
675 up(&ipt_account_mutex);
676 #endif
677 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
678 return -EINVAL;
679 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
680 return false;
681 #else
682 return 0;
683 #endif
686 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
687 mutex_unlock(&ipt_account_mutex);
688 #else
689 up(&ipt_account_mutex);
690 #endif
691 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
692 return 0;
693 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
694 return true;
695 #else
696 return 1;
697 #endif
701 * Destroy function.
703 static void
704 destroy(
705 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
706 const struct xt_mtdtor_param *par
707 #else
708 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
709 const struct xt_match *match,
710 #endif
711 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
712 void *matchinfo
713 #else
714 void *matchinfo,
715 unsigned int matchsize
716 #endif
717 #endif
720 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
721 struct t_ipt_account_info *info = (struct t_ipt_account_info*)(par->matchinfo);
722 #else
723 struct t_ipt_account_info *info = matchinfo;
724 #endif
726 #ifdef DEBUG_IPT_ACCOUNT
727 if (debug) printk(KERN_DEBUG "ipt_account [destroy]: name = %s\n", info->name);
728 #endif
729 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
730 if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
731 #ifdef DEBUG_IPT_ACCOUNT
732 if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
733 #endif
734 return;
736 #endif
739 * Release table, by decreasing its usage counter. When
740 * counter hits zero, memory used by table structure is
741 * released and table is removed from list.
743 ipt_account_table_put(info->table);
744 return;
747 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
748 static struct xt_match account_match = {
749 #else
750 static struct ipt_match account_match = {
751 #endif
752 .name = "account",
753 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
754 .family = NFPROTO_IPV4,
755 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
756 .family = AF_INET,
757 #endif
758 .match = &match,
759 .checkentry = &checkentry,
760 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
761 .matchsize = sizeof(struct t_ipt_account_info),
762 #endif
763 .destroy = &destroy,
764 .me = THIS_MODULE
768 * Below functions (ipt_account_seq_start, ipt_account_seq_next,
769 * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write)
770 * are used to implement proc stuff.
772 static void *ipt_account_seq_start(struct seq_file *sf, loff_t *pos)
774 struct proc_dir_entry *pde = sf->private;
775 struct t_ipt_account_table *table = pde->data;
776 unsigned int *i;
778 if (table->resetonread) {
779 /* When we reset entries after read we must have exclusive lock. */
780 write_lock_bh(&table->stats_lock);
781 } else {
782 read_lock_bh(&table->stats_lock);
784 if (*pos >= table->count)
785 return NULL;
786 i = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
787 if (!i)
788 return ERR_PTR(-ENOMEM);
789 *i = *pos;
790 return i;
793 static void *ipt_account_seq_next(struct seq_file *sf, void *v, loff_t *pos)
795 struct proc_dir_entry *pde = sf->private;
796 struct t_ipt_account_table *table = pde->data;
797 unsigned int *i = (unsigned int *)v;
799 *pos = ++(*i);
800 if (*i >= table->count) {
801 kfree(v);
802 return NULL;
804 return i;
807 static void ipt_account_seq_stop(struct seq_file *sf, void *v)
809 struct proc_dir_entry *pde = sf->private;
810 struct t_ipt_account_table *table = pde->data;
811 kfree(v);
812 if (table->resetonread) {
813 write_unlock_bh(&table->stats_lock);
814 } else {
815 read_unlock_bh(&table->stats_lock);
819 static int ipt_account_seq_show(struct seq_file *sf, void *v)
821 struct proc_dir_entry *pde = sf->private;
822 struct t_ipt_account_table *table = pde->data;
823 unsigned int *i = (unsigned int *)v;
825 struct timespec now = CURRENT_TIME_SEC;
827 u_int32_t address = table->network + *i;
829 if (!table->shortlisting) {
830 struct t_ipt_account_stats_long *l = &table->stats.l[*i];
831 /* Don't list rows not matching show requirements. */
832 if (
833 ((table->show == SHOW_SRC) && (l->src.p_all == 0)) ||
834 ((table->show == SHOW_DST) && (l->dst.p_all == 0)) ||
835 ((table->show == SHOW_SRC_OR_DST) && ((l->src.p_all == 0) && (l->dst.p_all == 0))) ||
836 ((table->show == SHOW_SRC_AND_DST) && ((l->src.p_all == 0) || (l->dst.p_all == 0)))
838 return 0;
840 seq_printf(sf,
841 "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",
842 HIPQUAD(address),
843 l->src.b_all,
844 l->src.b_tcp,
845 l->src.b_udp,
846 l->src.b_icmp,
847 l->src.b_other,
848 l->src.p_all,
849 l->src.p_tcp,
850 l->src.p_udp,
851 l->src.p_icmp,
852 l->src.p_other,
853 l->dst.b_all,
854 l->dst.b_tcp,
855 l->dst.b_udp,
856 l->dst.b_icmp,
857 l->dst.b_other,
858 l->dst.p_all,
859 l->dst.p_tcp,
860 l->dst.p_udp,
861 l->dst.p_icmp,
862 l->dst.p_other,
863 now.tv_sec - l->time.tv_sec
865 if (table->resetonread)
866 memset(l, 0, sizeof(struct t_ipt_account_stats_long));
868 } else {
869 struct t_ipt_account_stats_short *s = &table->stats.s[*i];
870 if (
871 ((table->show == SHOW_SRC) && (s->src.p_all == 0)) ||
872 ((table->show == SHOW_DST) && (s->dst.p_all == 0)) ||
873 ((table->show == SHOW_SRC_OR_DST) && ((s->src.p_all == 0) && (s->dst.p_all == 0))) ||
874 ((table->show == SHOW_SRC_AND_DST) && ((s->src.p_all == 0) || (s->dst.p_all == 0)))
876 return 0;
878 seq_printf(sf,
879 "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
880 HIPQUAD(address),
881 s->src.b_all,
882 s->src.p_all,
883 s->dst.b_all,
884 s->dst.p_all,
885 now.tv_sec - s->time.tv_sec
887 if (table->resetonread)
888 memset(s, 0, sizeof(struct t_ipt_account_stats_short));
891 return 0;
894 static struct seq_operations ipt_account_seq_ops = {
895 .start = ipt_account_seq_start,
896 .next = ipt_account_seq_next,
897 .stop = ipt_account_seq_stop,
898 .show = ipt_account_seq_show
901 static ssize_t ipt_account_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ofs)
903 char buffer[1024];
904 struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
905 struct t_ipt_account_table *table = pde->data;
907 u_int32_t o[4], ip;
908 struct t_ipt_account_stats_long l;
909 struct t_ipt_account_stats_short s;
911 #ifdef DEBUG_IPT_ACCOUNT
912 if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_proc_write]: name = %s.\n", table->name);
913 #endif
914 if (copy_from_user(buffer, input, 1024))
915 return -EFAULT;
916 buffer[1023] = '\0';
918 if (!strncmp(buffer, "reset\n", 6)) {
920 * User requested to clear all table. Ignorant, does
921 * he known how match time it took us to fill it? ;-)
923 write_lock_bh(&table->stats_lock);
924 if (table->shortlisting)
925 memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
926 else
927 memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
928 write_unlock_bh(&table->stats_lock);
929 } else if (!strncmp(buffer, "reset-on-read=yes\n", 18)) {
931 * We must be sure that ipt_account_seq_* is not running now. This option
932 * changes lock type which is taken in ipt_account_seq_{start|stop}. When
933 * we change this option without this lock, and ipt_account_seq_start is
934 * already run (but not ipt_account_seq_stop) there is possibility that
935 * we execute wrong "unlock" function.
937 write_lock_bh(&table->stats_lock);
938 table->resetonread = 1;
939 write_unlock_bh(&table->stats_lock);
940 } else if (!strncmp(buffer, "reset-on-read=no\n", 17)) {
941 write_lock_bh(&table->stats_lock);
942 table->resetonread = 0;
943 write_unlock_bh(&table->stats_lock);
944 } else if (!strncmp(buffer, "reset-on-read\n", 14)) {
945 write_lock_bh(&table->stats_lock);
946 table->resetonread = 1;
947 write_unlock_bh(&table->stats_lock);
948 } else if (!strncmp(buffer, "show=any\n", 9)) {
950 * Here we should lock but we don't have to. So we don't lock. We only get
951 * wrong results on already run ipt_account_seq_show, but we won't crush
952 * the system.
954 table->show = SHOW_ANY;
955 } else if (!strncmp(buffer, "show=src\n", 9)) {
956 table->show = SHOW_SRC;
957 } else if (!strncmp(buffer, "show=dst\n", 9)) {
958 table->show = SHOW_DST;
959 } else if (!strncmp(buffer, "show=src-or-dst\n", 16) || !strncmp(buffer, "show=dst-or-src\n", 16)) {
960 table->show = SHOW_SRC_OR_DST;
961 } else if (!strncmp(buffer, "show=src-and-dst\n", 17) || !strncmp(buffer, "show=dst-and-src\n", 17)) {
962 table->show = SHOW_SRC_AND_DST;
963 } else if (!strncmp(buffer, "time=any\n", 9)) {
964 table->timesrc = table->timedst = 1;
965 } else if (!strncmp(buffer, "time=src\n", 9)) {
966 table->timesrc = 1;
967 table->timedst = 0;
968 } else if (!strncmp(buffer, "time=dst\n", 9)) {
969 table->timesrc = 0;
970 table->timedst = 1;
971 } 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",
972 &o[0], &o[1], &o[2], &o[3],
973 &l.src.b_all, &l.src.b_tcp, &l.src.b_udp, &l.src.b_icmp, &l.src.b_other,
974 &l.src.p_all, &l.src.p_tcp, &l.src.p_udp, &l.src.p_icmp, &l.src.p_other,
975 &l.dst.b_all, &l.dst.b_tcp, &l.dst.b_udp, &l.dst.b_icmp, &l.dst.b_other,
976 &l.dst.p_all, &l.dst.p_tcp, &l.dst.p_udp, &l.dst.p_icmp, &l.dst.p_other,
977 &l.time.tv_sec) == 25 ) {
979 * We got line formated like long listing row. We have to
980 * check, if IP is accounted by table. If so, we
981 * simply replace row with user's one.
983 ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
984 if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
986 * Ignore user input time. Set current time.
988 l.time = CURRENT_TIME_SEC;
989 write_lock_bh(&table->stats_lock);
990 table->stats.l[ip - table->network] = l;
991 write_unlock_bh(&table->stats_lock);
993 } 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",
994 &o[0], &o[1], &o[2], &o[3],
995 &s.src.b_all,
996 &s.src.p_all,
997 &s.dst.b_all,
998 &s.dst.p_all,
999 &s.time.tv_sec) == 9) {
1001 * We got line formated like short listing row. Do the
1002 * same action like above.
1004 ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
1005 if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
1006 s.time = CURRENT_TIME_SEC;
1007 write_lock_bh(&table->stats_lock);
1008 table->stats.s[ip - table->network] = s;
1009 write_unlock_bh(&table->stats_lock);
1011 } else {
1013 * We don't understand what user have just wrote.
1015 return -EIO;
1018 return size;
1021 static int ipt_account_proc_open(struct inode *inode, struct file *file)
1023 int ret = seq_open(file, &ipt_account_seq_ops);
1024 if (!ret) {
1025 struct seq_file *sf = file->private_data;
1026 struct proc_dir_entry *pde = PDE(inode);
1027 struct t_ipt_account_table *table = pde->data;
1029 sf->private = pde;
1031 ipt_account_table_get(table);
1033 return ret;
1036 static int ipt_account_proc_release(struct inode *inode, struct file *file)
1038 struct proc_dir_entry *pde = PDE(inode);
1039 struct t_ipt_account_table *table = pde->data;
1040 int ret;
1042 ret = seq_release(inode, file);
1044 if (!ret)
1045 ipt_account_table_put(table);
1047 return ret;
1050 static struct file_operations ipt_account_proc_fops = {
1051 .owner = THIS_MODULE,
1052 .open = ipt_account_proc_open,
1053 .read = seq_read,
1054 .write = ipt_account_proc_write,
1055 .llseek = seq_lseek,
1056 .release = ipt_account_proc_release
1060 * Module init function.
1062 static int __init init(void)
1064 int ret = 0;
1066 printk(KERN_INFO "ipt_account %s : Piotr Gasidlo <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION);
1068 /* Check module parameters. */
1069 if (netmask > 32 || netmask < 0) {
1070 printk(KERN_ERR "ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask);
1071 ret = -EINVAL;
1072 goto cleanup_none;
1075 /* Register match. */
1076 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
1077 if (xt_register_match(&account_match)) {
1078 #else
1079 if (ipt_register_match(&account_match)) {
1080 #endif
1081 ret = -EINVAL;
1082 goto cleanup_none;
1085 /* Create /proc/net/ipt_account/ entry. */
1086 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
1087 ipt_account_procdir = proc_mkdir("ipt_account", init_net.proc_net);
1088 #else
1089 ipt_account_procdir = proc_mkdir("ipt_account", proc_net);
1090 #endif
1091 if (!ipt_account_procdir) {
1092 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
1093 printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", init_proc.proc_net) failed.\n");
1094 #else
1095 printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", proc_net) failed.\n");
1096 #endif
1097 ret = -ENOMEM;
1098 goto cleanup_match;
1101 return ret;
1103 /* If something goes wrong we end here. */
1104 cleanup_match:
1105 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
1106 xt_unregister_match(&account_match);
1107 #else
1108 ipt_unregister_match(&account_match);
1109 #endif
1110 cleanup_none:
1111 return ret;
1115 * Module exit function.
1117 static void __exit fini(void)
1119 /* Remove /proc/net/ipt_account/ */
1120 remove_proc_entry(ipt_account_procdir->name, ipt_account_procdir->parent);
1121 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
1122 xt_unregister_match(&account_match);
1123 #else
1124 ipt_unregister_match(&account_match);
1125 #endif
1128 module_init(init);
1129 module_exit(fini);