diff -Nru a/connbytes/iptables/extensions/libipt_connbytes.c b/connbytes/iptables/extensions/libipt_connbytes.c --- a/connbytes/iptables/extensions/libipt_connbytes.c 1970-01-01 01:00:00.000000000 +0100 +++ b/connbytes/iptables/extensions/libipt_connbytes.c 2004-11-15 22:17:46.000000000 +0100 @@ -0,0 +1,256 @@ +/* Shared library add-on to iptables to add byte tracking support. + * + * 2004-11-11 - Piotr Chytla + * - Some adaptation to backported module with per-conntrack accounting + * - Added connpkts/connavgpkt/direction , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function which prints out usage message. */ +static void +help(void) +{ + printf( +"connbytes v%s options:\n" +" [!] --connbytes from:[to]\n" +" Transfered bytes range to match\n" +" [!] --connpkts from:[to]\n" +" Transfered number of packets range to match\n" +" [!] --connavgpkt from:[to]\n" +" Transfered average packet size range to match\n" +" --direction original|reply|both\n" +" Match only from direction \n" +"\n", IPTABLES_VERSION); +} + +static struct option opts[] = { + { "connpkts", 1, 0, '1' }, + { "connbytes", 1, 0, '2' }, + { "connavgpkt", 1, 0, '3' }, + { "direction", 1, 0, '4'}, + {0} +}; + +static char *sinfo_names[] = { + "connpkts", + "connbytes", + "connavgpkt", + NULL +}; + +static char *sinfo_directions[] = { + "original", + "replay", + "both", + NULL +}; + +#define IPT_CONNBYTES_PKTS 0x1 +#define IPT_CONNBYTES_BYTES 0x2 +#define IPT_CONNBYTES_AVGPKT 0x4 +#define IPT_CONNBYTES_ORIGINAL 0x8 +#define IPT_CONNBYTES_REPLY 0x10 +#define IPT_CONNBYTES_BOTH 0x20 + +/* Initialize the match. */ +static void +init(struct ipt_entry_match *m, unsigned int *nfcache) +{ + /* Can't cache this */ + *nfcache |= NFC_UNKNOWN; +} + +static void +parse_range(const char *arg, struct ipt_connbytes_info *si) +{ + char *colon,*p; + + si->count.from = strtoul(arg,&colon,10); + if (*colon != ':') + exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg); + si->count.to = strtoul(colon+1,&p,10); + if (p == colon+1) { + /* second number omited */ + si->count.to = 0xffffffff; + } + if (si->count.from > si->count.to) + exit_error(PARAMETER_PROBLEM, "%llu should be less than %llu", si->count.from,si->count.to); +} + +static int +parse_direction(const char *direction,size_t strlen,struct ipt_connbytes_info *sinfo,unsigned int *flags) +{ + if (strncasecmp(direction,"original",strlen) == 0) + { + sinfo->direction=IPT_CONNBYTES_DIR_ORIGINAL; + *flags |= IPT_CONNBYTES_ORIGINAL; + } else if (strncasecmp(direction,"both",strlen) == 0) + { + sinfo->direction=IPT_CONNBYTES_DIR_BOTH; + *flags |= IPT_CONNBYTES_BOTH; + } else if (strncasecmp(direction,"replay",strlen) == 0) + { + sinfo->direction=IPT_CONNBYTES_DIR_REPLY; + *flags |= IPT_CONNBYTES_REPLY; + } else return 0; + return 1; +} + + +/* Function which parses command options; returns true if it + ate an option */ +static int +parse(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + unsigned int *nfcache, + struct ipt_entry_match **match) +{ + struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)(*match)->data; + unsigned long i; + switch (c) { + case '2': + if (*flags & IPT_CONNBYTES_BYTES) + exit_error(PARAMETER_PROBLEM, + "Can't specify --connbytes twice"); + if (*flags & IPT_CONNBYTES_PKTS) + exit_error(PARAMETER_PROBLEM, + "Can't use --connpkts and --connbytes together"); + if (*flags & IPT_CONNBYTES_AVGPKT) + exit_error(PARAMETER_PROBLEM, + "Can't use --connavgpkt and --connbytes together"); + + if (check_inverse(optarg, &invert, &optind, 0)) + optind++; + + parse_range(argv[optind-1], sinfo); + if (invert) { + i = sinfo->count.from; + sinfo->count.from = sinfo->count.to; + sinfo->count.to = i; + } + sinfo->what=IPT_CONNBYTES_WHAT_BYTES; + sinfo->direction=IPT_CONNBYTES_DIR_BOTH; + *flags |= IPT_CONNBYTES_BYTES; + break; + case '1': + if (*flags & IPT_CONNBYTES_PKTS) + exit_error(PARAMETER_PROBLEM, + "Can't specify --connpkts twice"); + if (*flags & IPT_CONNBYTES_BYTES) + exit_error(PARAMETER_PROBLEM, + "Can't use --connbytes and --connpkts together"); + if (*flags & IPT_CONNBYTES_AVGPKT) + exit_error(PARAMETER_PROBLEM, + "Can't use --connavgpkt and --connpkts together"); + + if (check_inverse(optarg,&invert,&optind,0)) + optind++; + + parse_range(argv[optind-1],sinfo); + if (invert) { + i = sinfo->count.from; + sinfo->count.from = sinfo->count.to; + sinfo->count.to = i; + } + sinfo->what=IPT_CONNBYTES_WHAT_PKTS; + sinfo->direction=IPT_CONNBYTES_DIR_BOTH; + *flags |= IPT_CONNBYTES_PKTS; + break; + case '3': + if (*flags & IPT_CONNBYTES_AVGPKT) + exit_error(PARAMETER_PROBLEM, + "Can't specify --connavgpkt twice"); + if (*flags & IPT_CONNBYTES_PKTS) + exit_error(PARAMETER_PROBLEM, + "Can't use --connpkts and --connavgpkt together"); + if (*flags & IPT_CONNBYTES_BYTES) + exit_error(PARAMETER_PROBLEM, + "Can't use --connbytes and --connavgpkt together"); + + if (check_inverse(optarg,&invert,&optind,0)) + optind++; + + parse_range(argv[optind-1],sinfo); + if (invert) { + i = sinfo->count.from; + sinfo->count.from = sinfo->count.to; + sinfo->count.to = i; + } + sinfo->what=IPT_CONNBYTES_WHAT_AVGPKT; + sinfo->direction=IPT_CONNBYTES_DIR_BOTH; + *flags |= IPT_CONNBYTES_AVGPKT; + break; + case '4': + if (*flags & (IPT_CONNBYTES_ORIGINAL|IPT_CONNBYTES_BOTH|IPT_CONNBYTES_REPLY)) + exit_error(PARAMETER_PROBLEM, + "Can't specify --direction twice"); + if (!parse_direction(argv[optind-1],strlen(argv[optind-1]),sinfo,flags)) + exit_error(PARAMETER_PROBLEM, "Bad direction '%s'",argv[optind-1]); + + break; + default: + return 0; + } + return 1; +} + +static void final_check(unsigned int flags) +{ + if (!flags) + exit_error(PARAMETER_PROBLEM, "You must specify `--connbytes or --connpkts or--connavgpkt'"); +} + +/* Prints out the matchinfo. */ +static void +print(const struct ipt_ip *ip, + const struct ipt_entry_match *match, + int numeric) +{ + struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data; + + if (sinfo->count.from > sinfo->count.to) + printf("%s ! %llu:%llu direction:%s",sinfo_names[sinfo->what],sinfo->count.to,sinfo->count.from,sinfo_directions[sinfo->direction]); + else + printf("%s %llu:%llu direction:%s",sinfo_names[sinfo->what],sinfo->count.from,sinfo->count.to,sinfo_directions[sinfo->direction]); +} + +/* Saves the matchinfo in parsable form to stdout. */ +static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) +{ + struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data; + + if (sinfo->count.from > sinfo->count.to) + printf("! --%s %llu:%llu --direction %s",sinfo_names[sinfo->what],sinfo->count.to,sinfo->count.from,sinfo_directions[sinfo->direction]); + else + printf("--%s %llu:%llu --direction %s",sinfo_names[sinfo->what],sinfo->count.from,sinfo->count.to,sinfo_directions[sinfo->direction]); +} + +static +struct iptables_match state += { NULL, + "connbytes", + IPTABLES_VERSION, + IPT_ALIGN(sizeof(struct ipt_connbytes_info)), + IPT_ALIGN(sizeof(struct ipt_connbytes_info)), + &help, + &init, + &parse, + &final_check, + &print, + &save, + opts +}; + +void _init(void) +{ + register_match(&state); +} diff -Nru a/connbytes/linux-2.4/include/linux/netfilter_ipv4/ipt_connbytes.h b/connbytes/linux-2.4/include/linux/netfilter_ipv4/ipt_connbytes.h --- a/connbytes/linux-2.4/include/linux/netfilter_ipv4/ipt_connbytes.h 2004-04-07 13:04:28.000000000 +0200 +++ b/connbytes/linux-2.4/include/linux/netfilter_ipv4/ipt_connbytes.h 2004-11-11 21:05:52.000000000 +0100 @@ -1,10 +1,25 @@ #ifndef _IPT_CONNBYTES_H #define _IPT_CONNBYTES_H +enum ipt_connbytes_what { + IPT_CONNBYTES_WHAT_PKTS, + IPT_CONNBYTES_WHAT_BYTES, + IPT_CONNBYTES_WHAT_AVGPKT, +}; + +enum ipt_connbytes_direction { + IPT_CONNBYTES_DIR_ORIGINAL, + IPT_CONNBYTES_DIR_REPLY, + IPT_CONNBYTES_DIR_BOTH, +}; struct ipt_connbytes_info { - /* if from <= to then it matches the range; if from > to then - inverse range is matched */ - unsigned long from, to; + struct { + u_int64_t from; /* count to be matched */ + u_int64_t to; /* count to be matched */ + } count; + u_int8_t what; /* ipt_connbytes_what */ + u_int8_t direction; /* ipt_connbytes_direction */ }; + #endif diff -Nru a/connbytes/linux-2.4/net/ipv4/netfilter/ipt_connbytes.c b/connbytes/linux-2.4/net/ipv4/netfilter/ipt_connbytes.c --- a/connbytes/linux-2.4/net/ipv4/netfilter/ipt_connbytes.c 2004-04-07 13:04:28.000000000 +0200 +++ b/connbytes/linux-2.4/net/ipv4/netfilter/ipt_connbytes.c 2004-11-15 22:11:20.000000000 +0100 @@ -1,12 +1,32 @@ /* Kernel module to match connection tracking byte counter. * GPL (C) 2002 Martin Devera (devik@cdi.cz). + * + * 2004-07-20 Harald Welte + * - 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 + * + * 2004-10-24 Piotr Chytla + * - Connbytes with per-connection accouting backported to 2.4 + * */ + #include #include +#include #include #include #include +#include + +static u_int64_t mydiv(u_int64_t arg1,u_int32_t arg2) +{ + do_div(arg1,arg2); + return arg1; +} + static int match(const struct sk_buff *skb, const struct net_device *in, @@ -17,17 +37,89 @@ u_int16_t datalen, int *hotdrop) { + static u_int64_t what; const struct ipt_connbytes_info *sinfo = matchinfo; enum ip_conntrack_info ctinfo; struct ip_conntrack *ct; if (!(ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo))) return 0; /* no match */ - - if (sinfo->from > sinfo->to) - return (ct->bytes < sinfo->to || ct->bytes > sinfo->from); - else - return (ct->bytes >= sinfo->from && ct->bytes <= sinfo->to); + switch (sinfo->what) { + case IPT_CONNBYTES_WHAT_PKTS: + switch (sinfo->direction) { + case IPT_CONNBYTES_DIR_ORIGINAL: + what = ct->counters[IP_CT_DIR_ORIGINAL].packets; + break; + case IPT_CONNBYTES_DIR_REPLY: + what = ct->counters[IP_CT_DIR_REPLY].packets; + break; + case IPT_CONNBYTES_DIR_BOTH: + what = ct->counters[IP_CT_DIR_ORIGINAL].packets; + what += ct->counters[IP_CT_DIR_REPLY].packets; + break; + } + break; + case IPT_CONNBYTES_WHAT_BYTES: + switch (sinfo->direction) { + case IPT_CONNBYTES_DIR_ORIGINAL: + what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; + break; + case IPT_CONNBYTES_DIR_REPLY: + what = ct->counters[IP_CT_DIR_REPLY].bytes; + break; + case IPT_CONNBYTES_DIR_BOTH: + what = ct->counters[IP_CT_DIR_ORIGINAL].bytes; + what += ct->counters[IP_CT_DIR_REPLY].bytes; + break; + } + break; + case IPT_CONNBYTES_WHAT_AVGPKT: + switch (sinfo->direction) { + case IPT_CONNBYTES_DIR_ORIGINAL: + { + u_int32_t pkts32; + + if (ct->counters[IP_CT_DIR_ORIGINAL].packets > 0xfffffffff) + pkts32 = 0xffffffff; + else + pkts32 = ct->counters[IP_CT_DIR_ORIGINAL].packets; + what = mydiv(ct->counters[IP_CT_DIR_ORIGINAL].bytes,pkts32); + } + break; + case IPT_CONNBYTES_DIR_REPLY: + { + u_int32_t pkts32; + + if (ct->counters[IP_CT_DIR_REPLY].packets > 0xffffffff) + pkts32 = 0xffffffff; + else + pkts32 = ct->counters[IP_CT_DIR_REPLY].packets; + what = mydiv(ct->counters[IP_CT_DIR_REPLY].bytes,pkts32); + } + break; + case IPT_CONNBYTES_DIR_BOTH: + { + u_int64_t bytes; + u_int64_t pkts; + u_int32_t pkts32; + bytes = ct->counters[IP_CT_DIR_ORIGINAL].bytes + + ct->counters[IP_CT_DIR_REPLY].bytes; + pkts = ct->counters[IP_CT_DIR_ORIGINAL].packets + + ct->counters[IP_CT_DIR_REPLY].packets; + if (pkts > 0xffffffff) + pkts32 = 0xffffffff; + else + pkts32 = pkts; + what = mydiv(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, @@ -36,8 +128,19 @@ unsigned int matchsize, unsigned int hook_mask) { + const struct ipt_connbytes_info *sinfo = matchinfo; + if (matchsize != IPT_ALIGN(sizeof(struct ipt_connbytes_info))) return 0; + if (sinfo->what != IPT_CONNBYTES_WHAT_PKTS && + sinfo->what != IPT_CONNBYTES_WHAT_BYTES && + sinfo->what != IPT_CONNBYTES_WHAT_AVGPKT) + return 0; + + if (sinfo->direction != IPT_CONNBYTES_DIR_ORIGINAL && + sinfo->direction != IPT_CONNBYTES_DIR_REPLY && + sinfo->direction != IPT_CONNBYTES_DIR_BOTH) + return 0; return 1; }