]> git.pld-linux.org Git - packages/kernel.git/blob - kernel-ipt_account.patch
- new
[packages/kernel.git] / kernel-ipt_account.patch
1 diff -uNrp linux/net/ipv4/netfilter/ipt_account.c linux/net/ipv4/netfilter/ipt_account.c
2 --- linux/net/ipv4/netfilter/ipt_account.c      1970-01-01 01:00:00.000000000 +0100
3 +++ linux/net/ipv4/netfilter/ipt_account.c      2007-08-04 16:22:15.000000000 +0200
4 @@ -0,0 +1,985 @@
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 + */
357 +static bool
358 +match(const struct sk_buff *skb,
359 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
360 +      const struct net_device *in,
361 +      const struct net_device *out,
362 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
363 +      const struct xt_match *match,
364 +#endif            
365 +      const void *matchinfo,
366 +      int offset,
367 +      unsigned int protoff,
368 +      bool *hotdrop)
369 +#else
370 +       const struct xt_match_param *par)
371 +#endif
372 +{
373 +  struct t_ipt_account_info *info = (struct t_ipt_account_info *)par->matchinfo;
374 +  struct t_ipt_account_table *table = info->table;
375 +  u_int32_t address;  
376 +  /* Get current time. */
377 +  struct timespec now = CURRENT_TIME_SEC;
378 +  /* Default we assume no match. */
379 +  bool ret = false;
380 +    
381 +#ifdef DEBUG_IPT_ACCOUNT  
382 +  if (debug) printk(KERN_DEBUG "ipt_account [match]: name = %s\n", table->name);
383 +#endif  
384 +  /* Check whether traffic from source ip address ... */
385 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)  
386 +  address = ntohl(ip_hdr(skb)->saddr);
387 +#else
388 +  address = ntohl(skb->nh.iph->saddr);
389 +#endif  
390 +  /* ... is being accounted by this table. */ 
391 +  if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {    
392 +    write_lock_bh(&table->stats_lock);
393 +    /* Yes, account this packet. */
394 +#ifdef DEBUG_IPT_ACCOUNT  
395 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)  
396 +    if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet src = %pI4, proto = %u.\n", &address, ip_hdr(skb)->protocol);
397 +#else
398 +    if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet src = %pI4, proto = %u.\n", &address, skb->nh.iph->protocol);
399 +#endif  
400 +#endif  
401 +    /* Update counters this host. */
402 +    if (!table->shortlisting) {
403 +      __account_long(&table->stats.l[address - table->network].src, skb);
404 +      if (table->timesrc)
405 +        table->stats.l[address - table->network].time = now;
406 +      /* Update also counters for all hosts in this table (network address) */
407 +      if (table->count > 1) {
408 +        __account_long(&table->stats.l[0].src, skb);
409 +        table->stats.l[0].time = now;
410 +      }
411 +    } else {
412 +      __account_short(&table->stats.s[address - table->network].src, skb);
413 +      if (table->timedst)
414 +        table->stats.s[address - table->network].time = now;
415 +      if (table->count > 1) {
416 +        __account_short(&table->stats.s[0].src, skb);
417 +        table->stats.s[0].time = now;
418 +      }
419 +    }
420 +    write_unlock_bh(&table->stats_lock);
421 +    /* Yes, it's a match. */
422 +    ret = true;
423 +  }
424 +  
425 +  /* Do the same thing with destination ip address. */
426 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)  
427 +  address = ntohl(ip_hdr(skb)->daddr);
428 +#else
429 +  address = ntohl(skb->nh.iph->daddr);
430 +#endif  
431 +  if (address && ((u_int32_t)(address & table->netmask) == (u_int32_t)table->network)) {
432 +    write_lock_bh(&table->stats_lock);
433 +#ifdef DEBUG_IPT_ACCOUNT 
434 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)  
435 +    if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet dst = %pI4, proto = %u.\n", &address, ip_hdr(skb)->protocol);
436 +#else    
437 +    if (debug) printk(KERN_DEBUG "ipt_account: [match]: accounting packet dst = %pI4, proto = %u.\n", &address, skb->nh.iph->protocol);
438 +#endif  
439 +#endif    
440 +    if (!table->shortlisting) {
441 +      __account_long(&table->stats.l[address - table->network].dst, skb);
442 +      table->stats.l[address - table->network].time = now;
443 +      if (table->count > 1) {
444 +        __account_long(&table->stats.l[0].dst, skb);
445 +        table->stats.l[0].time = now;
446 +      }
447 +    } else {
448 +      __account_short(&table->stats.s[address - table->network].dst, skb);
449 +      table->stats.s[address - table->network].time = now;
450 +      if (table->count > 1) {
451 +        __account_short(&table->stats.s[0].dst, skb);
452 +        table->stats.s[0].time = now;
453 +      }
454 +    }
455 +    write_unlock_bh(&table->stats_lock);
456 +    ret = true;
457 +  }
458 +  
459 +  return ret;
460 +}
461 +
462 +/*
463 + * Checkentry function.
464 + */
465 +static bool
466 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
467 +checkentry(const struct xt_mtchk_param *par)
468 +#else
469 +checkentry(const char *tablename,
470 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)    
471 +     const void *ip,
472 +#else
473 +     const struct ipt_entry *ip,
474 +#endif
475 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)     
476 +     const struct xt_match *match,
477 +#endif     
478 +     void *matchinfo,
479 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
480 +     unsigned int matchsize,
481 +#endif     
482 +     unsigned int hook_mask)
483 +#endif
484 +{
485 +  struct t_ipt_account_info *info = par->matchinfo;
486 +  struct t_ipt_account_table *table;
487 +
488 +#ifdef DEBUG_IPT_ACCOUNT  
489 +  if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: name = %s\n", info->name);
490 +#endif  
491 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
492 +  if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
493 +#ifdef DEBUG_IPT_ACCOUNT  
494 +    if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
495 +#endif    
496 +    return false;
497 +  }
498 +#endif
499 +
500 +  /* 
501 +   * Sanity checks. 
502 +   */
503 +  if (info->netmask < ((~0L << (32 - netmask)) & 0xffffffff)) {
504 +    printk(KERN_ERR "ipt_account[checkentry]: too big netmask (increase module 'netmask' parameter).\n");
505 +    return false;
506 +  }
507 +  if ((info->network & info->netmask) != info->network) {
508 +    printk(KERN_ERR "ipt_account[checkentry]: wrong network/netmask.\n");
509 +    return false;
510 +  }
511 +  if (info->name[0] == '\0') {
512 +    printk(KERN_ERR "ipt_account[checkentry]: wrong table name.\n");
513 +    return false;
514 +  }
515 +
516 +  /*
517 +   * We got new rule. Try to find table with the same name as given in info structure.
518 +   * Mutex magic based on xt_hashlimit.c.
519 +   */
520 +  down(&ipt_account_mutex);
521 +  table = ipt_account_table_find_get(info->name);
522 +  if (table) {
523 +    if (info->table != NULL) {
524 +      if (info->table != table) {
525 +        printk(KERN_ERR "ipt_account[checkentry]: reloaded rule has invalid table pointer.\n");
526 +        up(&ipt_account_mutex);
527 +        return false;
528 +      }
529 +      up(&ipt_account_mutex);
530 +      return true;
531 +    } else {
532 +#ifdef DEBUG_IPT_ACCOUNT  
533 +      if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, checking.\n");
534 +#endif  
535 +      /* 
536 +       * Table exists, but whether rule network/netmask/shortlisting matches 
537 +       * table network/netmask/shortlisting. Failure on missmatch. 
538 +       */   
539 +      if (table->network != info->network || table->netmask != info->netmask || table->shortlisting != info->shortlisting) {
540 +        printk(KERN_ERR "ipt_account [checkentry]: table found, rule network/netmask/shortlisting not match table network/netmask/shortlisting.\n");
541 +        /*
542 +         * Remember to release table usage counter.
543 +         */
544 +        ipt_account_table_put(table);
545 +        up(&ipt_account_mutex);
546 +        return false;
547 +      }
548 +#ifdef DEBUG_IPT_ACCOUNT  
549 +      if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table found, reusing.\n");
550 +#endif  
551 +      /*
552 +       * Link rule with table.
553 +       */ 
554 +      info->table = table;
555 +    }
556 +  } else {
557 +#ifdef DEBUG_IPT_ACCOUNT  
558 +    if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: table not found, creating new one.\n");
559 +#endif  
560 +    /*
561 +     * Table not exist, create new one.
562 +     */
563 +    info->table = table = ipt_account_table_init(info);
564 +    if (!table) {
565 +      up(&ipt_account_mutex);
566 +      return false;
567 +    }
568 +  }
569 +  up(&ipt_account_mutex);
570 +  return true;
571 +}
572 +
573 +/*
574 + * Destroy function.
575 + */
576 +static void
577 +destroy(
578 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
579 +    const struct xt_mtdtor_param *par
580 +#else
581 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)    
582 +    const struct xt_match *match,
583 +#endif    
584 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
585 +    void *matchinfo
586 +#else
587 +    void *matchinfo,
588 +    unsigned int matchsize
589 +#endif    
590 +#endif
591 +)
592 +{
593 +  struct t_ipt_account_info *info = par->matchinfo;
594 +  
595 +#ifdef DEBUG_IPT_ACCOUNT  
596 +  if (debug) printk(KERN_DEBUG "ipt_account [destroy]: name = %s\n", info->name);
597 +#endif  
598 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
599 +  if (matchsize != IPT_ALIGN(sizeof(struct t_ipt_account_info))) {
600 +#ifdef DEBUG_IPT_ACCOUNT  
601 +    if (debug) printk(KERN_DEBUG "ipt_account [checkentry]: matchsize %u != %u\n", matchsize, IPT_ALIGN(sizeof(struct t_ipt_account_info)));
602 +#endif    
603 +    return;
604 +  }
605 +#endif
606 +
607 +  /*
608 +   * Release table, by decreasing its usage counter. When
609 +   * counter hits zero, memory used by table structure is
610 +   * released and table is removed from list.
611 +   */
612 +  ipt_account_table_put(info->table);
613 +  return;
614 +}
615 +
616 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
617 +static struct xt_match account_match = { 
618 +#else
619 +static struct ipt_match account_match = { 
620 +#endif  
621 +  .name = "account", 
622 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
623 +  .family = AF_INET,
624 +#endif  
625 +  .match = match, 
626 +  .checkentry = checkentry, 
627 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
628 +  .matchsize = sizeof(struct t_ipt_account_info),
629 +#endif  
630 +  .destroy = &destroy, 
631 +  .me = THIS_MODULE
632 +};
633 +
634 +/*
635 + * Below functions (ipt_account_seq_start, ipt_account_seq_next, 
636 + * ipt_account_seq_stop, ipt_account_seq_show, ipt_account_proc_write) 
637 + * are used to implement proc stuff.
638 + */
639 +static void *ipt_account_seq_start(struct seq_file *sf, loff_t *pos)
640 +{
641 +  struct proc_dir_entry *pde = sf->private;
642 +  struct t_ipt_account_table *table = pde->data;
643 +  unsigned int *i;
644 +
645 +  if (table->resetonread) {
646 +    /* When we reset entries after read we must have exclusive lock. */
647 +    write_lock_bh(&table->stats_lock);
648 +  } else {
649 +    read_lock_bh(&table->stats_lock);
650 +  }
651 +  if (*pos >= table->count)
652 +    return NULL;
653 +  i = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
654 +  if (!i)
655 +    return ERR_PTR(-ENOMEM);
656 +  *i = *pos;
657 +  return i;
658 +}
659 +
660 +static void *ipt_account_seq_next(struct seq_file *sf, void *v, loff_t *pos)
661 +{
662 +  struct proc_dir_entry *pde = sf->private;
663 +  struct t_ipt_account_table *table = pde->data;
664 +  unsigned int *i = (unsigned int *)v;
665 +
666 +  *pos = ++(*i);
667 +  if (*i >= table->count) {
668 +    kfree(v);
669 +    return NULL;
670 +  }
671 +  return i;
672 +}
673 +
674 +static void ipt_account_seq_stop(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 +  kfree(v);
679 +  if (table->resetonread) {   
680 +    write_unlock_bh(&table->stats_lock);
681 +  } else {
682 +    read_unlock_bh(&table->stats_lock);
683 +  }
684 +}
685 +
686 +static int ipt_account_seq_show(struct seq_file *sf, void *v)
687 +{
688 +  struct proc_dir_entry *pde = sf->private;
689 +  struct t_ipt_account_table *table = pde->data;
690 +  unsigned int *i = (unsigned int *)v;
691 +  
692 +  struct timespec now = CURRENT_TIME_SEC;
693 +  
694 +  u_int32_t address = table->network + *i;
695 +
696 +  if (!table->shortlisting) {
697 +    struct t_ipt_account_stats_long *l = &table->stats.l[*i];
698 +    /* Don't list rows not matching show requirements. */
699 +    if (
700 +        ((table->show == SHOW_SRC) && (l->src.p_all == 0)) ||
701 +        ((table->show == SHOW_DST) && (l->dst.p_all == 0)) ||
702 +        ((table->show == SHOW_SRC_OR_DST) && ((l->src.p_all == 0) && (l->dst.p_all == 0))) ||
703 +        ((table->show == SHOW_SRC_AND_DST) && ((l->src.p_all == 0) || (l->dst.p_all == 0)))
704 +       ) {
705 +      return 0;
706 +    }
707 +    seq_printf(sf,
708 +        "ip = %pI4 bytes_src = %llu %llu %llu %llu %llu packets_src = %llu %llu %llu %llu %llu bytes_dst = %llu %llu %llu %llu %llu packets_dst = %llu %llu %llu %llu %llu time = %lu\n",
709 +        &address,
710 +        l->src.b_all,
711 +        l->src.b_tcp,
712 +        l->src.b_udp,
713 +        l->src.b_icmp,
714 +        l->src.b_other,
715 +        l->src.p_all,
716 +        l->src.p_tcp,
717 +        l->src.p_udp,
718 +        l->src.p_icmp,
719 +        l->src.p_other,
720 +        l->dst.b_all,
721 +        l->dst.b_tcp,
722 +        l->dst.b_udp,
723 +        l->dst.b_icmp,
724 +        l->dst.b_other,       
725 +        l->dst.p_all,
726 +        l->dst.p_tcp,
727 +        l->dst.p_udp,
728 +        l->dst.p_icmp,
729 +        l->dst.p_other,
730 +        now.tv_sec - l->time.tv_sec
731 +      );
732 +    if (table->resetonread)
733 +      memset(l, 0, sizeof(struct t_ipt_account_stats_long));
734 +    
735 +  } else {
736 +    struct t_ipt_account_stats_short *s = &table->stats.s[*i];
737 +    if (
738 +        ((table->show == SHOW_SRC) && (s->src.p_all == 0)) ||
739 +        ((table->show == SHOW_DST) && (s->dst.p_all == 0)) ||
740 +        ((table->show == SHOW_SRC_OR_DST) && ((s->src.p_all == 0) && (s->dst.p_all == 0))) ||
741 +        ((table->show == SHOW_SRC_AND_DST) && ((s->src.p_all == 0) || (s->dst.p_all == 0)))
742 +       ) {
743 +      return 0;
744 +    }
745 +    seq_printf(sf,
746 +        "ip = %pI4 bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n",
747 +        &address,
748 +        s->src.b_all,
749 +        s->src.p_all,
750 +        s->dst.b_all,
751 +        s->dst.p_all,
752 +        now.tv_sec - s->time.tv_sec
753 +      );
754 +    if (table->resetonread)
755 +      memset(s, 0, sizeof(struct t_ipt_account_stats_short));
756 +  }
757 +
758 +  return 0;
759 +}
760 +
761 +static struct seq_operations ipt_account_seq_ops = {
762 +  .start = ipt_account_seq_start,
763 +  .next = ipt_account_seq_next,
764 +  .stop = ipt_account_seq_stop,
765 +  .show = ipt_account_seq_show
766 +};
767 +
768 +static ssize_t ipt_account_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ofs)
769 +{
770 +  char buffer[1024];
771 +  struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
772 +  struct t_ipt_account_table *table = pde->data;
773 +
774 +  u_int32_t o[4], ip;
775 +  struct t_ipt_account_stats_long l;
776 +  struct t_ipt_account_stats_short s;
777 +
778 +#ifdef DEBUG_IPT_ACCOUNT  
779 +  if (debug) printk(KERN_DEBUG "ipt_account [ipt_account_proc_write]: name = %s.\n", table->name);
780 +#endif  
781 +  if (copy_from_user(buffer, input, 1024))
782 +    return -EFAULT;
783 +  buffer[1023] = '\0';
784 +
785 +  if (!strncmp(buffer, "reset\n", 6)) {
786 +    /*
787 +     * User requested to clear all table. Ignorant, does
788 +     * he known how match time it took us to fill it? ;-)
789 +     */
790 +    write_lock_bh(&table->stats_lock);
791 +    if (table->shortlisting)
792 +      memset(table->stats.s, 0, sizeof(struct t_ipt_account_stats_short) * table->count);
793 +    else
794 +      memset(table->stats.l, 0, sizeof(struct t_ipt_account_stats_long) * table->count);
795 +    write_unlock_bh(&table->stats_lock);    
796 +  } else if (!strncmp(buffer, "reset-on-read=yes\n", 18)) {
797 +    /*
798 +     * We must be sure that ipt_account_seq_* is not running now. This option
799 +     * changes lock type which is taken in ipt_account_seq_{start|stop}. When
800 +     * we change this option without this lock, and ipt_account_seq_start is
801 +     * already run (but not ipt_account_seq_stop) there is possibility that
802 +     * we execute wrong "unlock" function.
803 +     */
804 +    write_lock_bh(&table->stats_lock);
805 +    table->resetonread = 1;
806 +    write_unlock_bh(&table->stats_lock);
807 +  } else if (!strncmp(buffer, "reset-on-read=no\n", 17)) {
808 +    write_lock_bh(&table->stats_lock);
809 +    table->resetonread = 0; 
810 +    write_unlock_bh(&table->stats_lock);
811 +  } else if (!strncmp(buffer, "reset-on-read\n", 14)) {
812 +    write_lock_bh(&table->stats_lock);
813 +    table->resetonread = 1;
814 +    write_unlock_bh(&table->stats_lock);
815 +  } else if (!strncmp(buffer, "show=any\n", 9)) {
816 +    /*
817 +     * Here we should lock but we don't have to. So we don't lock. We only get
818 +     * wrong results on already run ipt_account_seq_show, but we won't crush
819 +     * the system.
820 +     */
821 +    table->show = SHOW_ANY;
822 +  } else if (!strncmp(buffer, "show=src\n", 9)) {
823 +    table->show = SHOW_SRC;
824 +  } else if (!strncmp(buffer, "show=dst\n", 9)) {
825 +    table->show = SHOW_DST;
826 +  } else if (!strncmp(buffer, "show=src-or-dst\n", 16) || !strncmp(buffer, "show=dst-or-src\n", 16)) {
827 +    table->show = SHOW_SRC_OR_DST;
828 +  } else if (!strncmp(buffer, "show=src-and-dst\n", 17) || !strncmp(buffer, "show=dst-and-src\n", 17)) {
829 +    table->show = SHOW_SRC_AND_DST;
830 +  } else if (!strncmp(buffer, "time=any\n", 9)) {
831 +    table->timesrc = table->timedst = 1;
832 +  } else if (!strncmp(buffer, "time=src\n", 9)) {
833 +    table->timesrc = 1;
834 +    table->timedst = 0;
835 +  } else if (!strncmp(buffer, "time=dst\n", 9)) {
836 +    table->timesrc = 0;
837 +    table->timedst = 1;
838 +  } else if (!table->shortlisting && sscanf(buffer, "ip = %u.%u.%u.%u bytes_src = %llu %llu %llu %llu %llu packets_src = %llu %llu %llu %llu %llu bytes_dst = %llu %llu %llu %llu %llu packets_dst = %llu %llu %llu %llu %llu time = %lu",        
839 +        &o[0], &o[1], &o[2], &o[3],
840 +        &l.src.b_all, &l.src.b_tcp, &l.src.b_udp, &l.src.b_icmp, &l.src.b_other,
841 +        &l.src.p_all, &l.src.p_tcp, &l.src.p_udp, &l.src.p_icmp, &l.src.p_other,
842 +        &l.dst.b_all, &l.dst.b_tcp, &l.dst.b_udp, &l.dst.b_icmp, &l.dst.b_other,
843 +        &l.dst.p_all, &l.dst.p_tcp, &l.dst.p_udp, &l.dst.p_icmp, &l.dst.p_other,
844 +        &l.time.tv_sec) == 25 ) {
845 +    /*
846 +     * We got line formated like long listing row. We have to
847 +     * check, if IP is accounted by table. If so, we
848 +     * simply replace row with user's one.
849 +     */
850 +    ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];
851 +    if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
852 +      /*
853 +       * Ignore user input time. Set current time.
854 +       */
855 +      l.time = CURRENT_TIME_SEC;
856 +      write_lock_bh(&table->stats_lock);
857 +      table->stats.l[ip - table->network] = l;
858 +      write_unlock_bh(&table->stats_lock);
859 +    }
860 +  } else if (table->shortlisting && sscanf(buffer, "ip = %u.%u.%u.%u bytes_src = %llu packets_src = %llu bytes_dst = %llu packets_dst = %llu time = %lu\n", 
861 +        &o[0], &o[1], &o[2], &o[3], 
862 +        &s.src.b_all, 
863 +        &s.src.p_all, 
864 +        &s.dst.b_all, 
865 +        &s.dst.p_all, 
866 +        &s.time.tv_sec) == 9) {   
867 +    /*
868 +     * We got line formated like short listing row. Do the
869 +     * same action like above.
870 +     */
871 +    ip = o[0] << 24 | o[1] << 16 | o[2] << 8 | o[3];    
872 +    if ((u_int32_t)(ip & table->netmask) == (u_int32_t)table->network) {
873 +      s.time = CURRENT_TIME_SEC;
874 +      write_lock_bh(&table->stats_lock);
875 +      table->stats.s[ip - table->network] = s;
876 +      write_unlock_bh(&table->stats_lock);
877 +    }
878 +  } else {
879 +    /*
880 +     * We don't understand what user have just wrote.
881 +     */
882 +    return -EIO;
883 +  }
884 +
885 +  return size;
886 +}
887 +
888 +static int ipt_account_proc_open(struct inode *inode, struct file *file)
889 +{
890 +  int ret = seq_open(file, &ipt_account_seq_ops);
891 +  if (!ret) {
892 +    struct seq_file *sf = file->private_data;
893 +    struct proc_dir_entry *pde = PDE(inode);
894 +    struct t_ipt_account_table *table = pde->data;
895 +    
896 +    sf->private = pde;
897 +
898 +    ipt_account_table_get(table);
899 +  }
900 +  return ret;
901 +}
902 +
903 +static int ipt_account_proc_release(struct inode *inode, struct file *file)
904 +{
905 +  struct proc_dir_entry *pde = PDE(inode);
906 +  struct t_ipt_account_table *table = pde->data;
907 +  int ret;
908 +
909 +  ret = seq_release(inode, file);
910 +
911 +  if (!ret)
912 +    ipt_account_table_put(table);
913 +  
914 +  return ret;
915 +}
916 +
917 +static struct file_operations ipt_account_proc_fops = {
918 +  .owner = THIS_MODULE,
919 +  .open = ipt_account_proc_open,
920 +  .read = seq_read,
921 +  .write = ipt_account_proc_write,
922 +  .llseek = seq_lseek,
923 +  .release = ipt_account_proc_release
924 +};
925 +
926 +/*
927 + * Module init function.
928 + */
929 +static int __init init(void)
930 +{
931 +  int ret = 0;
932 +
933 +  printk(KERN_INFO "ipt_account %s : Piotr Gasidlo <quaker@barbara.eu.org>, http://www.barbara.eu.org/~quaker/ipt_account/\n", IPT_ACCOUNT_VERSION);
934 +
935 +  /* Check module parameters. */
936 +  if (netmask > 32 || netmask < 0) {
937 +    printk(KERN_ERR "ipt_account[__init]: Wrong netmask given as parameter (%i). Valid is 32 to 0.\n", netmask);
938 +    ret = -EINVAL;
939 +    goto cleanup_none;
940 +  }
941 +  
942 +  /* Register match. */
943 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
944 +  if (xt_register_match(&account_match)) {
945 +#else    
946 +  if (ipt_register_match(&account_match)) {
947 +#endif  
948 +    ret = -EINVAL;
949 +    goto cleanup_none;
950 +  }
951 +
952 +  /* Create /proc/net/ipt_account/ entry. */
953 +  ipt_account_procdir = proc_mkdir("ipt_account", init_net.proc_net);
954 +  if (!ipt_account_procdir) {   
955 +    printk(KERN_ERR "ipt_account [__init]: ipt_account_procdir = proc_mkdir(\"ipt_account\", init_net.proc_net) failed.\n");
956 +    ret = -ENOMEM;
957 +    goto cleanup_match;
958 +  }
959 +  
960 +  return ret;
961 +
962 +  /* If something goes wrong we end here. */
963 +cleanup_match:
964 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
965 +  xt_unregister_match(&account_match);
966 +#else  
967 +  ipt_unregister_match(&account_match);
968 +#endif  
969 +cleanup_none:
970 +  return ret;
971 +}
972 +
973 +/*
974 + * Module exit function.
975 + */
976 +static void __exit fini(void)
977 +{
978 +  /* Remove /proc/net/ipt_account/ */
979 +  remove_proc_entry(ipt_account_procdir->name, ipt_account_procdir->parent);  
980 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
981 +  xt_unregister_match(&account_match);
982 +#else
983 +  ipt_unregister_match(&account_match);
984 +#endif  
985 +}
986 +
987 +module_init(init);
988 +module_exit(fini);
989 +
990 diff -uNrp linux/include/linux/netfilter_ipv4/ipt_account.h linux/include/linux/netfilter_ipv4/ipt_account.h
991 --- linux/include/linux/netfilter_ipv4/ipt_account.h    1970-01-01 01:00:00.000000000 +0100
992 +++ linux/include/linux/netfilter_ipv4/ipt_account.h    2007-08-04 15:24:49.000000000 +0200
993 @@ -0,0 +1,24 @@
994 +/* Copyright (c) 2004-2007 Piotr 'QuakeR' Gasidlo <quaker@barbara.eu.org>
995 + *
996 + * This program is free software; you can redistribute it and/or modify
997 + * it under the terms of the GNU General Public License version 2 as
998 + * published by the Free Software Foundation.
999 + */
1000 +
1001 +#ifndef _IPT_ACCOUNT_H_
1002 +#define _IPT_ACCOUNT_H_
1003 +
1004 +#define IPT_ACCOUNT_NAME_LEN 64
1005 +
1006 +struct t_ipt_account_table;
1007 +
1008 +struct t_ipt_account_info {
1009 +  char name[IPT_ACCOUNT_NAME_LEN + 1];
1010 +  u_int32_t network, netmask;
1011 +  int shortlisting:1;
1012 +  /* pointer to the table for fast matching */
1013 +  struct t_ipt_account_table *table;
1014 +};
1015 +
1016 +#endif /* _IPT_ACCOUNT_H */
1017 +
1018 diff -uNrp linux/net/ipv4/netfilter/Makefile.a linux/net/ipv4/netfilter/Makefile.b
1019 --- linux/net/ipv4/netfilter/Makefile   1971-01-01 01:00:00.000000000 +0100
1020 +++ linux/net/ipv4/netfilter/Makefile   2007-08-15 13:23:25.375304000 +0200
1021 @@ -0,0 +0,1 @@
1022 +obj-$(CONFIG_IP_NF_MATCH_ACCOUNT) += ipt_account.o
1023 diff -Nur linux/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig
1024 --- linux/net/ipv4/netfilter/Kconfig    2006-05-02 23:38:44.000000000 +0200
1025 +++ linux/net/ipv4/netfilter/Kconfig    2006-05-04 11:23:02.000000000 +0200
1026 @@ -606,5 +606,51 @@
1027           Allows altering the ARP packet payload: source and destination
1028           hardware and network addresses.
1029  
1030 +config IP_NF_MATCH_ACCOUNT
1031 +       tristate "account match support"
1032 +       depends on IP_NF_IPTABLES && PROC_FS
1033 +       help
1034 +         This match is used for accounting traffic for all hosts in
1035 +         defined network/netmask. 
1036 +         
1037 +         Features:
1038 +         - long (one counter per protocol TCP/UDP/IMCP/Other) and short statistics
1039 +         - one iptables rule for all hosts in network/netmask
1040 +         - loading/saving counters (by reading/writting to procfs entries)
1041 +         
1042 +         Example usage:
1043 +         
1044 +         account traffic for/to 192.168.0.0/24 network into table mynetwork:
1045 +         
1046 +         # iptables -A FORWARD -m account --aname mynetwork --aaddr 192.168.0.0/24
1047 +         
1048 +         account traffic for/to WWW serwer for 192.168.0.0/24 network into table 
1049 +         mywwwserver:
1050 +         
1051 +         # iptables -A INPUT -p tcp --dport 80 
1052 +           -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort
1053 +         # iptables -A OUTPUT -p tcp --sport 80
1054 +           -m account --aname mywwwserver --aaddr 192.168.0.0/24 --ashort    
1055 +         
1056 +         read counters:
1057 +         
1058 +         # cat /proc/net/ipt_account/mynetwork
1059 +         # cat /proc/net/ipt_account/mywwwserver
1060 +         
1061 +         set counters:
1062 +         
1063 +         # echo "ip = 192.168.0.1 packets_src = 0" > /proc/net/ipt_account/mywwserver
1064 +         
1065 +         Webpage: 
1066 +           http://www.barbara.eu.org/~quaker/ipt_account/
1067 +
1068 +config IP_NF_MATCH_ACCOUNT_DEBUG
1069 +       bool "account debugging output"
1070 +       depends on IP_NF_MATCH_ACCOUNT
1071 +       help
1072 +         Say Y to get lots of debugging output.
1073 +         
1074 +
1075 +
1076  endmenu
1077  
This page took 0.099133 seconds and 3 git commands to generate.