12e4e6a17SHarald Welte /* Kernel module to match connection tracking byte counter. 22e4e6a17SHarald Welte * GPL (C) 2002 Martin Devera (devik@cdi.cz). 32e4e6a17SHarald Welte * 42e4e6a17SHarald Welte * 2004-07-20 Harald Welte <laforge@netfilter.org> 52e4e6a17SHarald Welte * - reimplemented to use per-connection accounting counters 62e4e6a17SHarald Welte * - add functionality to match number of packets 72e4e6a17SHarald Welte * - add functionality to match average packet size 82e4e6a17SHarald Welte * - add support to match directions seperately 92e4e6a17SHarald Welte * 2005-10-16 Harald Welte <laforge@netfilter.org> 102e4e6a17SHarald Welte * - Port to x_tables 112e4e6a17SHarald Welte * 122e4e6a17SHarald Welte */ 132e4e6a17SHarald Welte #include <linux/module.h> 142e4e6a17SHarald Welte #include <linux/skbuff.h> 152e4e6a17SHarald Welte #include <net/netfilter/nf_conntrack_compat.h> 162e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h> 172e4e6a17SHarald Welte #include <linux/netfilter/xt_connbytes.h> 182e4e6a17SHarald Welte 192e4e6a17SHarald Welte #include <asm/div64.h> 202e4e6a17SHarald Welte #include <asm/bitops.h> 212e4e6a17SHarald Welte 222e4e6a17SHarald Welte MODULE_LICENSE("GPL"); 232e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); 242e4e6a17SHarald Welte MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection"); 252e4e6a17SHarald Welte MODULE_ALIAS("ipt_connbytes"); 262e4e6a17SHarald Welte 272e4e6a17SHarald Welte /* 64bit divisor, dividend and result. dynamic precision */ 282e4e6a17SHarald Welte static u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor) 292e4e6a17SHarald Welte { 302e4e6a17SHarald Welte u_int32_t d = divisor; 312e4e6a17SHarald Welte 322e4e6a17SHarald Welte if (divisor > 0xffffffffULL) { 332e4e6a17SHarald Welte unsigned int shift = fls(divisor >> 32); 342e4e6a17SHarald Welte 352e4e6a17SHarald Welte d = divisor >> shift; 362e4e6a17SHarald Welte dividend >>= shift; 372e4e6a17SHarald Welte } 382e4e6a17SHarald Welte 392e4e6a17SHarald Welte do_div(dividend, d); 402e4e6a17SHarald Welte return dividend; 412e4e6a17SHarald Welte } 422e4e6a17SHarald Welte 432e4e6a17SHarald Welte static int 442e4e6a17SHarald Welte match(const struct sk_buff *skb, 452e4e6a17SHarald Welte const struct net_device *in, 462e4e6a17SHarald Welte const struct net_device *out, 47c4986734SPatrick McHardy const struct xt_match *match, 482e4e6a17SHarald Welte const void *matchinfo, 492e4e6a17SHarald Welte int offset, 502e4e6a17SHarald Welte unsigned int protoff, 512e4e6a17SHarald Welte int *hotdrop) 522e4e6a17SHarald Welte { 532e4e6a17SHarald Welte const struct xt_connbytes_info *sinfo = matchinfo; 542e4e6a17SHarald Welte u_int64_t what = 0; /* initialize to make gcc happy */ 552e4e6a17SHarald Welte const struct ip_conntrack_counter *counters; 562e4e6a17SHarald Welte 572e4e6a17SHarald Welte if (!(counters = nf_ct_get_counters(skb))) 582e4e6a17SHarald Welte return 0; /* no match */ 592e4e6a17SHarald Welte 602e4e6a17SHarald Welte switch (sinfo->what) { 612e4e6a17SHarald Welte case XT_CONNBYTES_PKTS: 622e4e6a17SHarald Welte switch (sinfo->direction) { 632e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL: 642e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].packets; 652e4e6a17SHarald Welte break; 662e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY: 672e4e6a17SHarald Welte what = counters[IP_CT_DIR_REPLY].packets; 682e4e6a17SHarald Welte break; 692e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH: 702e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].packets; 712e4e6a17SHarald Welte what += counters[IP_CT_DIR_REPLY].packets; 722e4e6a17SHarald Welte break; 732e4e6a17SHarald Welte } 742e4e6a17SHarald Welte break; 752e4e6a17SHarald Welte case XT_CONNBYTES_BYTES: 762e4e6a17SHarald Welte switch (sinfo->direction) { 772e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL: 782e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].bytes; 792e4e6a17SHarald Welte break; 802e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY: 812e4e6a17SHarald Welte what = counters[IP_CT_DIR_REPLY].bytes; 822e4e6a17SHarald Welte break; 832e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH: 842e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].bytes; 852e4e6a17SHarald Welte what += counters[IP_CT_DIR_REPLY].bytes; 862e4e6a17SHarald Welte break; 872e4e6a17SHarald Welte } 882e4e6a17SHarald Welte break; 892e4e6a17SHarald Welte case XT_CONNBYTES_AVGPKT: 902e4e6a17SHarald Welte switch (sinfo->direction) { 912e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL: 922e4e6a17SHarald Welte what = div64_64(counters[IP_CT_DIR_ORIGINAL].bytes, 932e4e6a17SHarald Welte counters[IP_CT_DIR_ORIGINAL].packets); 942e4e6a17SHarald Welte break; 952e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY: 962e4e6a17SHarald Welte what = div64_64(counters[IP_CT_DIR_REPLY].bytes, 972e4e6a17SHarald Welte counters[IP_CT_DIR_REPLY].packets); 982e4e6a17SHarald Welte break; 992e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH: 1002e4e6a17SHarald Welte { 1012e4e6a17SHarald Welte u_int64_t bytes; 1022e4e6a17SHarald Welte u_int64_t pkts; 1032e4e6a17SHarald Welte bytes = counters[IP_CT_DIR_ORIGINAL].bytes + 1042e4e6a17SHarald Welte counters[IP_CT_DIR_REPLY].bytes; 1052e4e6a17SHarald Welte pkts = counters[IP_CT_DIR_ORIGINAL].packets+ 1062e4e6a17SHarald Welte counters[IP_CT_DIR_REPLY].packets; 1072e4e6a17SHarald Welte 1082e4e6a17SHarald Welte /* FIXME_THEORETICAL: what to do if sum 1092e4e6a17SHarald Welte * overflows ? */ 1102e4e6a17SHarald Welte 1112e4e6a17SHarald Welte what = div64_64(bytes, pkts); 1122e4e6a17SHarald Welte } 1132e4e6a17SHarald Welte break; 1142e4e6a17SHarald Welte } 1152e4e6a17SHarald Welte break; 1162e4e6a17SHarald Welte } 1172e4e6a17SHarald Welte 1182e4e6a17SHarald Welte if (sinfo->count.to) 1192e4e6a17SHarald Welte return (what <= sinfo->count.to && what >= sinfo->count.from); 1202e4e6a17SHarald Welte else 1212e4e6a17SHarald Welte return (what >= sinfo->count.from); 1222e4e6a17SHarald Welte } 1232e4e6a17SHarald Welte 1242e4e6a17SHarald Welte static int check(const char *tablename, 1252e4e6a17SHarald Welte const void *ip, 126c4986734SPatrick McHardy const struct xt_match *match, 1272e4e6a17SHarald Welte void *matchinfo, 1282e4e6a17SHarald Welte unsigned int matchsize, 1292e4e6a17SHarald Welte unsigned int hook_mask) 1302e4e6a17SHarald Welte { 1312e4e6a17SHarald Welte const struct xt_connbytes_info *sinfo = matchinfo; 1322e4e6a17SHarald Welte 1332e4e6a17SHarald Welte if (sinfo->what != XT_CONNBYTES_PKTS && 1342e4e6a17SHarald Welte sinfo->what != XT_CONNBYTES_BYTES && 1352e4e6a17SHarald Welte sinfo->what != XT_CONNBYTES_AVGPKT) 1362e4e6a17SHarald Welte return 0; 1372e4e6a17SHarald Welte 1382e4e6a17SHarald Welte if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL && 1392e4e6a17SHarald Welte sinfo->direction != XT_CONNBYTES_DIR_REPLY && 1402e4e6a17SHarald Welte sinfo->direction != XT_CONNBYTES_DIR_BOTH) 1412e4e6a17SHarald Welte return 0; 1422e4e6a17SHarald Welte 1432e4e6a17SHarald Welte return 1; 1442e4e6a17SHarald Welte } 1452e4e6a17SHarald Welte 1464470bbc7SPatrick McHardy static struct xt_match xt_connbytes_match = { 1474470bbc7SPatrick McHardy { 1482e4e6a17SHarald Welte .name = "connbytes", 149a45049c5SPablo Neira Ayuso .family = AF_INET, 1505d04bff0SPatrick McHardy .checkentry = check, 1514470bbc7SPatrick McHardy .match = match, 1525d04bff0SPatrick McHardy .matchsize = sizeof(struct xt_connbytes_info), 1532e4e6a17SHarald Welte .me = THIS_MODULE 1544470bbc7SPatrick McHardy }, 1554470bbc7SPatrick McHardy { 1564470bbc7SPatrick McHardy .name = "connbytes", 1574470bbc7SPatrick McHardy .family = AF_INET6, 1584470bbc7SPatrick McHardy .checkentry = check, 1594470bbc7SPatrick McHardy .match = match, 1604470bbc7SPatrick McHardy .matchsize = sizeof(struct xt_connbytes_info), 1614470bbc7SPatrick McHardy .me = THIS_MODULE 1624470bbc7SPatrick McHardy }, 1632e4e6a17SHarald Welte }; 1642e4e6a17SHarald Welte 16565b4b4e8SAndrew Morton static int __init xt_connbytes_init(void) 1662e4e6a17SHarald Welte { 1674470bbc7SPatrick McHardy return xt_register_matches(xt_connbytes_match, 1684470bbc7SPatrick McHardy ARRAY_SIZE(xt_connbytes_match)); 1692e4e6a17SHarald Welte } 1702e4e6a17SHarald Welte 17165b4b4e8SAndrew Morton static void __exit xt_connbytes_fini(void) 1722e4e6a17SHarald Welte { 1734470bbc7SPatrick McHardy xt_unregister_matches(xt_connbytes_match, 1744470bbc7SPatrick McHardy ARRAY_SIZE(xt_connbytes_match)); 1752e4e6a17SHarald Welte } 1762e4e6a17SHarald Welte 17765b4b4e8SAndrew Morton module_init(xt_connbytes_init); 17865b4b4e8SAndrew Morton module_exit(xt_connbytes_fini); 179