Hello, Recently i needed to be able to do exactly what iptables with hashlimit does, but instead of hashing by IP i wanted it to hash on arbitrary network mask. So, i've hacked up this, and i hope that you could find it useful. It adds one more hashlimit option : "--hashlimit-ipmask", and if it is supplied all src/dst ip addresses are masked with it, and so you can easily do hashlimit on a per /24(or any else between /1 and /31) network basis. One example : iptables -A INPUT -p tcp --dport 22 -m state --state NEW \ -m hashlimit --hashlimit-mode srcip --hashlimit-ipmask 24 \ --hashlimit-name ssh --hashlimit-burst 3 --hashlimit 12/hour \ --hashlimit-htable-expire 1800000 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j DROP P.S. : There is no ipv6 support, and the manpage is not updated to reflect the new hashlimit option --hashlimit-ipmask P.S.2: I know that this probably can be done with connlimit, but hashlimit can also do limiting on the destination ip (dstip mode) which i think is not possible with connlimit. -- Niki
diff -ur iptables-1.4.0rc1/extensions/libxt_hashlimit.c iptables-1.4.0rc1-hashlimitmask/extensions/libxt_hashlimit.c --- iptables-1.4.0rc1/extensions/libxt_hashlimit.c 2007-10-15 15:45:09.000000000 +0300 +++ iptables-1.4.0rc1-hashlimitmask/extensions/libxt_hashlimit.c 2008-02-28 13:41:54.000000000 +0200 @@ -37,6 +37,7 @@ "--hashlimit-mode <mode> mode is a comma-separated list of\n" " dstip,srcip,dstport,srcport\n" "--hashlimit-name <name> name for /proc/net/ipt_hashlimit/\n" +"[--hashlimit-ipmask <bits>] mask to apply on ip addresses\n" "[--hashlimit-burst <num>] number to match in a burst, default %u\n" "[--hashlimit-htable-size <num>] number of hashtable buckets\n" "[--hashlimit-htable-max <num>] number of hashtable entries\n" @@ -54,6 +55,7 @@ { "hashlimit-htable-expire", 1, NULL, ')' }, { "hashlimit-mode", 1, NULL, '_' }, { "hashlimit-name", 1, NULL, '"' }, + { "hashlimit-ipmask", 1, NULL, '!' }, { } }; @@ -93,6 +95,25 @@ return 1; } +static +int parse_ipmask(const char *ipmask, u_int32_t *val) +{ + int i; + uint bits; + u_int32_t mask; + + mask = 0; + + if (string_to_number(ipmask, 1, 31, &bits) == -1) + return 0; + + for (i = 0; i < bits; i++) + mask |= (1 << (31 - i)); + + *val = mask; + return 1; +} + /* Initialize the match. */ static void hashlimit_init(struct xt_entry_match *m) { @@ -101,6 +122,7 @@ r->cfg.burst = XT_HASHLIMIT_BURST; r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; r->cfg.expire = XT_HASHLIMIT_EXPIRE; + r->cfg.ipmask = 0; /* disabled by default */ } @@ -144,6 +166,7 @@ #define PARAM_MAX 0x00000020 #define PARAM_GCINTERVAL 0x00000040 #define PARAM_EXPIRE 0x00000080 +#define PARAM_IPMASK 0x00000100 /* Function which parses command options; returns true if it ate an option */ @@ -221,6 +244,13 @@ strncpy(r->name, optarg, sizeof(r->name)); *flags |= PARAM_NAME; break; + case '!': + if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; + if (!parse_ipmask(optarg, &r->cfg.ipmask)) + exit_error(PARAMETER_PROBLEM, + "bad --hashlimit-ipmask: `%s'", optarg); + *flags |= PARAM_IPMASK; + break; default: return 0; } @@ -268,6 +298,21 @@ printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name); } +static void print_ipmask(u_int32_t ipmask) +{ + unsigned int i; + unsigned int bits; + + bits = 1; + + for (i=0; i < 31; i++) { + if (ipmask & (1 << i)) + bits++; + } + + printf("%u ", bits); +} + static void print_mode(const struct xt_hashlimit_info *r, char separator) { int prevmode = 0; @@ -308,6 +353,10 @@ printf("burst %u ", r->cfg.burst); fputs("mode ", stdout); print_mode(r, '-'); + if (r->cfg.ipmask) { + fputs("ipmask: ", stdout); + print_ipmask(r->cfg.ipmask); + } if (r->cfg.size) printf("htable-size %u ", r->cfg.size); if (r->cfg.max) @@ -328,6 +377,11 @@ if (r->cfg.burst != XT_HASHLIMIT_BURST) printf("--hashlimit-burst %u ", r->cfg.burst); + if (r->cfg.ipmask) { + fputs("--hashlimit-ipmask ", stdout); + print_ipmask(r->cfg.ipmask); + } + fputs("--hashlimit-mode ", stdout); print_mode(r, ','); diff -ur iptables-1.4.0rc1/include/linux/netfilter/xt_hashlimit.h iptables-1.4.0rc1-hashlimitmask/include/linux/netfilter/xt_hashlimit.h --- iptables-1.4.0rc1/include/linux/netfilter/xt_hashlimit.h 2007-08-06 11:51:02.000000000 +0300 +++ iptables-1.4.0rc1-hashlimitmask/include/linux/netfilter/xt_hashlimit.h 2008-02-28 11:48:36.000000000 +0200 @@ -24,6 +24,7 @@ u_int32_t max; /* max number of entries */ u_int32_t gc_interval; /* gc interval */ u_int32_t expire; /* when do entries expire? */ + u_int32_t ipmask; /* ip mask */ }; struct xt_hashlimit_info {
--- linux/net/netfilter/xt_hashlimit.c.orig 2008-02-28 12:32:27.000000000 +0200 +++ linux/net/netfilter/xt_hashlimit.c 2008-02-28 13:25:57.000000000 +0200 @@ -385,9 +385,18 @@ switch (hinfo->family) { case AF_INET: if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP) - dst->addr.ip.dst = ip_hdr(skb)->daddr; + if (hinfo->cfg.ipmask) + dst->addr.ip.dst = (ip_hdr(skb)->daddr & + htonl(hinfo->cfg.ipmask)); + else + dst->addr.ip.dst = ip_hdr(skb)->daddr; + if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP) - dst->addr.ip.src = ip_hdr(skb)->saddr; + if (hinfo->cfg.ipmask) + dst->addr.ip.src = (ip_hdr(skb)->saddr & + htonl(hinfo->cfg.ipmask)); + else + dst->addr.ip.src = ip_hdr(skb)->saddr; if (!(hinfo->cfg.mode & (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT))) --- linux/include/linux/netfilter/xt_hashlimit.h.orig 2008-02-28 12:32:49.000000000 +0200 +++ linux/include/linux/netfilter/xt_hashlimit.h 2008-02-28 11:48:56.000000000 +0200 @@ -24,6 +24,7 @@ u_int32_t max; /* max number of entries */ u_int32_t gc_interval; /* gc interval */ u_int32_t expire; /* when do entries expire? */ + u_int32_t ipmask; /* ip mask */ }; struct xt_hashlimit_info {