|  | /* Kernel module to match connection tracking byte counter. | 
|  | * GPL (C) 2002 Martin Devera (devik@cdi.cz). | 
|  | * | 
|  | * 2004-07-20 Harald Welte <laforge@netfilter.org> | 
|  | * 	- reimplemented to use per-connection accounting counters | 
|  | * 	- add functionality to match number of packets | 
|  | * 	- add functionality to match average packet size | 
|  | * 	- add support to match directions seperately | 
|  | * 2005-10-16 Harald Welte <laforge@netfilter.org> | 
|  | * 	- Port to x_tables | 
|  | * | 
|  | */ | 
|  | #include <linux/module.h> | 
|  | #include <linux/skbuff.h> | 
|  | #include <net/netfilter/nf_conntrack_compat.h> | 
|  | #include <linux/netfilter/x_tables.h> | 
|  | #include <linux/netfilter/xt_connbytes.h> | 
|  |  | 
|  | #include <asm/div64.h> | 
|  | #include <asm/bitops.h> | 
|  |  | 
|  | MODULE_LICENSE("GPL"); | 
|  | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | 
|  | MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection"); | 
|  | MODULE_ALIAS("ipt_connbytes"); | 
|  |  | 
|  | /* 64bit divisor, dividend and result. dynamic precision */ | 
|  | static u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor) | 
|  | { | 
|  | u_int32_t d = divisor; | 
|  |  | 
|  | if (divisor > 0xffffffffULL) { | 
|  | unsigned int shift = fls(divisor >> 32); | 
|  |  | 
|  | d = divisor >> shift; | 
|  | dividend >>= shift; | 
|  | } | 
|  |  | 
|  | do_div(dividend, d); | 
|  | return dividend; | 
|  | } | 
|  |  | 
|  | static int | 
|  | match(const struct sk_buff *skb, | 
|  | const struct net_device *in, | 
|  | const struct net_device *out, | 
|  | const struct xt_match *match, | 
|  | const void *matchinfo, | 
|  | int offset, | 
|  | unsigned int protoff, | 
|  | int *hotdrop) | 
|  | { | 
|  | const struct xt_connbytes_info *sinfo = matchinfo; | 
|  | u_int64_t what = 0;	/* initialize to make gcc happy */ | 
|  | const struct ip_conntrack_counter *counters; | 
|  |  | 
|  | if (!(counters = nf_ct_get_counters(skb))) | 
|  | return 0; /* no match */ | 
|  |  | 
|  | switch (sinfo->what) { | 
|  | case XT_CONNBYTES_PKTS: | 
|  | switch (sinfo->direction) { | 
|  | case XT_CONNBYTES_DIR_ORIGINAL: | 
|  | what = counters[IP_CT_DIR_ORIGINAL].packets; | 
|  | break; | 
|  | case XT_CONNBYTES_DIR_REPLY: | 
|  | what = counters[IP_CT_DIR_REPLY].packets; | 
|  | break; | 
|  | case XT_CONNBYTES_DIR_BOTH: | 
|  | what = counters[IP_CT_DIR_ORIGINAL].packets; | 
|  | what += counters[IP_CT_DIR_REPLY].packets; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case XT_CONNBYTES_BYTES: | 
|  | switch (sinfo->direction) { | 
|  | case XT_CONNBYTES_DIR_ORIGINAL: | 
|  | what = counters[IP_CT_DIR_ORIGINAL].bytes; | 
|  | break; | 
|  | case XT_CONNBYTES_DIR_REPLY: | 
|  | what = counters[IP_CT_DIR_REPLY].bytes; | 
|  | break; | 
|  | case XT_CONNBYTES_DIR_BOTH: | 
|  | what = counters[IP_CT_DIR_ORIGINAL].bytes; | 
|  | what += counters[IP_CT_DIR_REPLY].bytes; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case XT_CONNBYTES_AVGPKT: | 
|  | switch (sinfo->direction) { | 
|  | case XT_CONNBYTES_DIR_ORIGINAL: | 
|  | what = div64_64(counters[IP_CT_DIR_ORIGINAL].bytes, | 
|  | counters[IP_CT_DIR_ORIGINAL].packets); | 
|  | break; | 
|  | case XT_CONNBYTES_DIR_REPLY: | 
|  | what = div64_64(counters[IP_CT_DIR_REPLY].bytes, | 
|  | counters[IP_CT_DIR_REPLY].packets); | 
|  | break; | 
|  | case XT_CONNBYTES_DIR_BOTH: | 
|  | { | 
|  | u_int64_t bytes; | 
|  | u_int64_t pkts; | 
|  | bytes = counters[IP_CT_DIR_ORIGINAL].bytes + | 
|  | counters[IP_CT_DIR_REPLY].bytes; | 
|  | pkts = counters[IP_CT_DIR_ORIGINAL].packets+ | 
|  | counters[IP_CT_DIR_REPLY].packets; | 
|  |  | 
|  | /* FIXME_THEORETICAL: what to do if sum | 
|  | * overflows ? */ | 
|  |  | 
|  | what = div64_64(bytes, pkts); | 
|  | } | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (sinfo->count.to) | 
|  | return (what <= sinfo->count.to && what >= sinfo->count.from); | 
|  | else | 
|  | return (what >= sinfo->count.from); | 
|  | } | 
|  |  | 
|  | static int check(const char *tablename, | 
|  | const void *ip, | 
|  | const struct xt_match *match, | 
|  | void *matchinfo, | 
|  | unsigned int matchsize, | 
|  | unsigned int hook_mask) | 
|  | { | 
|  | const struct xt_connbytes_info *sinfo = matchinfo; | 
|  |  | 
|  | if (sinfo->what != XT_CONNBYTES_PKTS && | 
|  | sinfo->what != XT_CONNBYTES_BYTES && | 
|  | sinfo->what != XT_CONNBYTES_AVGPKT) | 
|  | return 0; | 
|  |  | 
|  | if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL && | 
|  | sinfo->direction != XT_CONNBYTES_DIR_REPLY && | 
|  | sinfo->direction != XT_CONNBYTES_DIR_BOTH) | 
|  | return 0; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static struct xt_match connbytes_match = { | 
|  | .name		= "connbytes", | 
|  | .match		= match, | 
|  | .checkentry	= check, | 
|  | .matchsize	= sizeof(struct xt_connbytes_info), | 
|  | .family		= AF_INET, | 
|  | .me		= THIS_MODULE | 
|  | }; | 
|  | static struct xt_match connbytes6_match = { | 
|  | .name		= "connbytes", | 
|  | .match		= match, | 
|  | .checkentry	= check, | 
|  | .matchsize	= sizeof(struct xt_connbytes_info), | 
|  | .family		= AF_INET6, | 
|  | .me		= THIS_MODULE | 
|  | }; | 
|  |  | 
|  | static int __init xt_connbytes_init(void) | 
|  | { | 
|  | int ret; | 
|  | ret = xt_register_match(&connbytes_match); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = xt_register_match(&connbytes6_match); | 
|  | if (ret) | 
|  | xt_unregister_match(&connbytes_match); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void __exit xt_connbytes_fini(void) | 
|  | { | 
|  | xt_unregister_match(&connbytes_match); | 
|  | xt_unregister_match(&connbytes6_match); | 
|  | } | 
|  |  | 
|  | module_init(xt_connbytes_init); | 
|  | module_exit(xt_connbytes_fini); |