12e4e6a17SHarald Welte /* Kernel module to match connection tracking byte counter. 22e4e6a17SHarald Welte * GPL (C) 2002 Martin Devera (devik@cdi.cz). 32e4e6a17SHarald Welte */ 42e4e6a17SHarald Welte #include <linux/module.h> 51977f032SJiri Slaby #include <linux/bitops.h> 62e4e6a17SHarald Welte #include <linux/skbuff.h> 72e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h> 82e4e6a17SHarald Welte #include <linux/netfilter/xt_connbytes.h> 9587aa641SPatrick McHardy #include <net/netfilter/nf_conntrack.h> 102e4e6a17SHarald Welte 112e4e6a17SHarald Welte #include <asm/div64.h> 122e4e6a17SHarald Welte 132e4e6a17SHarald Welte MODULE_LICENSE("GPL"); 142e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); 152e4e6a17SHarald Welte MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection"); 162e4e6a17SHarald Welte MODULE_ALIAS("ipt_connbytes"); 1773aaf935SJan Engelhardt MODULE_ALIAS("ip6t_connbytes"); 182e4e6a17SHarald Welte 191d93a9cbSJan Engelhardt static bool 202e4e6a17SHarald Welte match(const struct sk_buff *skb, 212e4e6a17SHarald Welte const struct net_device *in, 222e4e6a17SHarald Welte const struct net_device *out, 23c4986734SPatrick McHardy const struct xt_match *match, 242e4e6a17SHarald Welte const void *matchinfo, 252e4e6a17SHarald Welte int offset, 262e4e6a17SHarald Welte unsigned int protoff, 27cff533acSJan Engelhardt bool *hotdrop) 282e4e6a17SHarald Welte { 292e4e6a17SHarald Welte const struct xt_connbytes_info *sinfo = matchinfo; 30a47362a2SJan Engelhardt const struct nf_conn *ct; 31587aa641SPatrick McHardy enum ip_conntrack_info ctinfo; 322e4e6a17SHarald Welte u_int64_t what = 0; /* initialize to make gcc happy */ 33fb74a841SPatrick McHardy u_int64_t bytes = 0; 34fb74a841SPatrick McHardy u_int64_t pkts = 0; 352e4e6a17SHarald Welte const struct ip_conntrack_counter *counters; 362e4e6a17SHarald Welte 37587aa641SPatrick McHardy ct = nf_ct_get(skb, &ctinfo); 38587aa641SPatrick McHardy if (!ct) 391d93a9cbSJan Engelhardt return false; 40587aa641SPatrick McHardy counters = ct->counters; 412e4e6a17SHarald Welte 422e4e6a17SHarald Welte switch (sinfo->what) { 432e4e6a17SHarald Welte case XT_CONNBYTES_PKTS: 442e4e6a17SHarald Welte switch (sinfo->direction) { 452e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL: 462e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].packets; 472e4e6a17SHarald Welte break; 482e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY: 492e4e6a17SHarald Welte what = counters[IP_CT_DIR_REPLY].packets; 502e4e6a17SHarald Welte break; 512e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH: 522e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].packets; 532e4e6a17SHarald Welte what += counters[IP_CT_DIR_REPLY].packets; 542e4e6a17SHarald Welte break; 552e4e6a17SHarald Welte } 562e4e6a17SHarald Welte break; 572e4e6a17SHarald Welte case XT_CONNBYTES_BYTES: 582e4e6a17SHarald Welte switch (sinfo->direction) { 592e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL: 602e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].bytes; 612e4e6a17SHarald Welte break; 622e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY: 632e4e6a17SHarald Welte what = counters[IP_CT_DIR_REPLY].bytes; 642e4e6a17SHarald Welte break; 652e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH: 662e4e6a17SHarald Welte what = counters[IP_CT_DIR_ORIGINAL].bytes; 672e4e6a17SHarald Welte what += counters[IP_CT_DIR_REPLY].bytes; 682e4e6a17SHarald Welte break; 692e4e6a17SHarald Welte } 702e4e6a17SHarald Welte break; 712e4e6a17SHarald Welte case XT_CONNBYTES_AVGPKT: 722e4e6a17SHarald Welte switch (sinfo->direction) { 732e4e6a17SHarald Welte case XT_CONNBYTES_DIR_ORIGINAL: 74fb74a841SPatrick McHardy bytes = counters[IP_CT_DIR_ORIGINAL].bytes; 75fb74a841SPatrick McHardy pkts = counters[IP_CT_DIR_ORIGINAL].packets; 762e4e6a17SHarald Welte break; 772e4e6a17SHarald Welte case XT_CONNBYTES_DIR_REPLY: 78fb74a841SPatrick McHardy bytes = counters[IP_CT_DIR_REPLY].bytes; 79fb74a841SPatrick McHardy pkts = counters[IP_CT_DIR_REPLY].packets; 802e4e6a17SHarald Welte break; 812e4e6a17SHarald Welte case XT_CONNBYTES_DIR_BOTH: 822e4e6a17SHarald Welte bytes = counters[IP_CT_DIR_ORIGINAL].bytes + 832e4e6a17SHarald Welte counters[IP_CT_DIR_REPLY].bytes; 842e4e6a17SHarald Welte pkts = counters[IP_CT_DIR_ORIGINAL].packets + 852e4e6a17SHarald Welte counters[IP_CT_DIR_REPLY].packets; 862e4e6a17SHarald Welte break; 872e4e6a17SHarald Welte } 88fb74a841SPatrick McHardy if (pkts != 0) 89fb74a841SPatrick McHardy what = div64_64(bytes, pkts); 902e4e6a17SHarald Welte break; 912e4e6a17SHarald Welte } 922e4e6a17SHarald Welte 932e4e6a17SHarald Welte if (sinfo->count.to) 947c4e36bcSJan Engelhardt return what <= sinfo->count.to && what >= sinfo->count.from; 952e4e6a17SHarald Welte else 967c4e36bcSJan Engelhardt return what >= sinfo->count.from; 972e4e6a17SHarald Welte } 982e4e6a17SHarald Welte 99ccb79bdcSJan Engelhardt static bool check(const char *tablename, 1002e4e6a17SHarald Welte const void *ip, 101c4986734SPatrick McHardy const struct xt_match *match, 1022e4e6a17SHarald Welte void *matchinfo, 1032e4e6a17SHarald Welte unsigned int hook_mask) 1042e4e6a17SHarald Welte { 1052e4e6a17SHarald Welte const struct xt_connbytes_info *sinfo = matchinfo; 1062e4e6a17SHarald Welte 1072e4e6a17SHarald Welte if (sinfo->what != XT_CONNBYTES_PKTS && 1082e4e6a17SHarald Welte sinfo->what != XT_CONNBYTES_BYTES && 1092e4e6a17SHarald Welte sinfo->what != XT_CONNBYTES_AVGPKT) 110ccb79bdcSJan Engelhardt return false; 1112e4e6a17SHarald Welte 1122e4e6a17SHarald Welte if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL && 1132e4e6a17SHarald Welte sinfo->direction != XT_CONNBYTES_DIR_REPLY && 1142e4e6a17SHarald Welte sinfo->direction != XT_CONNBYTES_DIR_BOTH) 115ccb79bdcSJan Engelhardt return false; 1162e4e6a17SHarald Welte 11711078c37SYasuyuki Kozakai if (nf_ct_l3proto_try_module_get(match->family) < 0) { 11811078c37SYasuyuki Kozakai printk(KERN_WARNING "can't load conntrack support for " 11911078c37SYasuyuki Kozakai "proto=%d\n", match->family); 120ccb79bdcSJan Engelhardt return false; 12111078c37SYasuyuki Kozakai } 12211078c37SYasuyuki Kozakai 123ccb79bdcSJan Engelhardt return true; 1242e4e6a17SHarald Welte } 1252e4e6a17SHarald Welte 12611078c37SYasuyuki Kozakai static void 12711078c37SYasuyuki Kozakai destroy(const struct xt_match *match, void *matchinfo) 12811078c37SYasuyuki Kozakai { 12911078c37SYasuyuki Kozakai nf_ct_l3proto_module_put(match->family); 13011078c37SYasuyuki Kozakai } 13111078c37SYasuyuki Kozakai 1329f15c530SPatrick McHardy static struct xt_match xt_connbytes_match[] __read_mostly = { 1334470bbc7SPatrick McHardy { 1342e4e6a17SHarald Welte .name = "connbytes", 135a45049c5SPablo Neira Ayuso .family = AF_INET, 1365d04bff0SPatrick McHardy .checkentry = check, 1374470bbc7SPatrick McHardy .match = match, 13811078c37SYasuyuki Kozakai .destroy = destroy, 1395d04bff0SPatrick McHardy .matchsize = sizeof(struct xt_connbytes_info), 1402e4e6a17SHarald Welte .me = THIS_MODULE 1414470bbc7SPatrick McHardy }, 1424470bbc7SPatrick McHardy { 1434470bbc7SPatrick McHardy .name = "connbytes", 1444470bbc7SPatrick McHardy .family = AF_INET6, 1454470bbc7SPatrick McHardy .checkentry = check, 1464470bbc7SPatrick McHardy .match = match, 14711078c37SYasuyuki Kozakai .destroy = destroy, 1484470bbc7SPatrick McHardy .matchsize = sizeof(struct xt_connbytes_info), 1494470bbc7SPatrick McHardy .me = THIS_MODULE 1504470bbc7SPatrick McHardy }, 1512e4e6a17SHarald Welte }; 1522e4e6a17SHarald Welte 15365b4b4e8SAndrew Morton static int __init xt_connbytes_init(void) 1542e4e6a17SHarald Welte { 1554470bbc7SPatrick McHardy return xt_register_matches(xt_connbytes_match, 1564470bbc7SPatrick McHardy ARRAY_SIZE(xt_connbytes_match)); 1572e4e6a17SHarald Welte } 1582e4e6a17SHarald Welte 15965b4b4e8SAndrew Morton static void __exit xt_connbytes_fini(void) 1602e4e6a17SHarald Welte { 1614470bbc7SPatrick McHardy xt_unregister_matches(xt_connbytes_match, 1624470bbc7SPatrick McHardy ARRAY_SIZE(xt_connbytes_match)); 1632e4e6a17SHarald Welte } 1642e4e6a17SHarald Welte 16565b4b4e8SAndrew Morton module_init(xt_connbytes_init); 16665b4b4e8SAndrew Morton module_exit(xt_connbytes_fini); 167