diff -u -rN linux-2.4.20rc1/Documentation/Configure.help linux-2.4.20rc1.iplimit/Documentation/Configure.help --- linux-2.4.20rc1/Documentation/Configure.help Sun Nov 10 14:54:03 2002 +++ linux-2.4.20rc1.iplimit/Documentation/Configure.help Sun Nov 10 15:07:35 2002 @@ -2670,6 +2670,14 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +Connections/IP limit match support +CONFIG_IP_NF_MATCH_IPLIMIT + This match allows you to restrict the number of parallel TCP + connections to a server per client IP address (or address block). + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + Unclean match support CONFIG_IP_NF_MATCH_UNCLEAN Unclean packet matching matches any strange or invalid packets, by diff -u -rN linux-2.4.20rc1/include/linux/netfilter_ipv4/ipt_iplimit.h linux-2.4.20rc1.iplimit/include/linux/netfilter_ipv4/ipt_iplimit.h --- linux-2.4.20rc1/include/linux/netfilter_ipv4/ipt_iplimit.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.20rc1.iplimit/include/linux/netfilter_ipv4/ipt_iplimit.h Sun Nov 10 14:57:50 2002 @@ -0,0 +1,12 @@ +#ifndef _IPT_IPLIMIT_H +#define _IPT_IPLIMIT_H + +struct ipt_iplimit_data; + +struct ipt_iplimit_info { + int limit; + int inverse; + u_int32_t mask; + struct ipt_iplimit_data *data; +}; +#endif /* _IPT_IPLIMIT_H */ diff -u -rN linux-2.4.20rc1/net/ipv4/netfilter/Config.in linux-2.4.20rc1.iplimit/net/ipv4/netfilter/Config.in --- linux-2.4.20rc1/net/ipv4/netfilter/Config.in Sun Nov 10 14:54:26 2002 +++ linux-2.4.20rc1.iplimit/net/ipv4/netfilter/Config.in Sun Nov 10 15:04:10 2002 @@ -36,6 +36,7 @@ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then dep_tristate ' Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES dep_tristate ' Connection tracking match support' CONFIG_IP_NF_MATCH_CONNTRACK $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + dep_tristate ' Connections/IP limit match support' CONFIG_IP_NF_MATCH_IPLIMIT $CONFIG_IP_NF_IPTABLES fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES diff -u -rN linux-2.4.20rc1/net/ipv4/netfilter/Makefile linux-2.4.20rc1.iplimit/net/ipv4/netfilter/Makefile --- linux-2.4.20rc1/net/ipv4/netfilter/Makefile Sun Nov 10 14:54:26 2002 +++ linux-2.4.20rc1.iplimit/net/ipv4/netfilter/Makefile Sun Nov 10 15:09:36 2002 @@ -72,6 +72,7 @@ obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_IPLIMIT) += ipt_iplimit.o obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o diff -u -rN linux-2.4.20rc1/net/ipv4/netfilter/ipt_iplimit.c linux-2.4.20rc1.iplimit/net/ipv4/netfilter/ipt_iplimit.c --- linux-2.4.20rc1/net/ipv4/netfilter/ipt_iplimit.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.20rc1.iplimit/net/ipv4/netfilter/ipt_iplimit.c Sun Nov 10 14:57:50 2002 @@ -0,0 +1,230 @@ +/* + * netfilter module to limit the number of parallel tcp + * connections per IP address. + * (c) 2000 Gerd Knorr + * + * based on ... + * + * Kernel module to match connection tracking information. + * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au). + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +MODULE_LICENSE("GPL"); + +/* we'll save the tuples of all connections we care about */ +struct ipt_iplimit_conn +{ + struct list_head list; + struct ip_conntrack_tuple tuple; +}; + +struct ipt_iplimit_data { + spinlock_t lock; + struct list_head iphash[256]; +}; + +static int ipt_iphash(u_int32_t addr) +{ + int hash; + + hash = addr & 0xff; + hash ^= (addr >> 8) & 0xff; + hash ^= (addr >> 16) & 0xff; + hash ^= (addr >> 24) & 0xff; + return hash; +} + +static int count_them(struct ipt_iplimit_data *data, + u_int32_t addr, u_int32_t mask, + struct ip_conntrack *ct) +{ +#if DEBUG + const static char *tcp[] = { "none", "established", "syn_sent", "syn_recv", + "fin_wait", "time_wait", "close", "close_wait", + "last_ack", "listen" }; +#endif + int addit = 1, matches = 0; + struct ip_conntrack_tuple tuple; + struct ip_conntrack_tuple_hash *found; + struct ipt_iplimit_conn *conn; + struct list_head *hash,*lh; + + spin_lock(&data->lock); + tuple = ct->tuplehash[0].tuple; + hash = &data->iphash[ipt_iphash(addr & mask)]; + + /* check the saved connections */ + for (lh = hash->next; lh != hash; lh = lh->next) { + conn = list_entry(lh,struct ipt_iplimit_conn,list); + if (0 == memcmp(&conn->tuple,&tuple,sizeof(tuple))) { + /* Just to be sure we have it only once in the list. + We should'nt see tuples twice unless someone hooks this + into a table without "-p tcp --syn" */ + addit = 0; + } + found = ip_conntrack_find_get(&conn->tuple,ct); +#if DEBUG + printk("ipt_iplimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d %s\n", + ipt_iphash(addr & mask), + NIPQUAD(conn->tuple.src.ip), ntohs(conn->tuple.src.u.tcp.port), + NIPQUAD(conn->tuple.dst.ip), ntohs(conn->tuple.dst.u.tcp.port), + (NULL != found) ? tcp[found->ctrack->proto.tcp.state] : "gone"); +#endif + if (NULL == found) { + /* this one is gone */ + lh = lh->prev; + list_del(lh->next); + kfree(conn); + /* connection in 'gone' state - sumit@elitecore.com */ + matches++; + continue; + } + if (found->ctrack->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) { + /* we don't care about connections which are + closed already -> ditch it */ + lh = lh->prev; + list_del(lh->next); + kfree(conn); + nf_conntrack_put(&found->ctrack->infos[0]); + continue; + } + if ((addr & mask) == (conn->tuple.src.ip & mask)) { + /* same source IP address -> be counted! */ + matches++; + } + nf_conntrack_put(&found->ctrack->infos[0]); + } + if (addit) { + /* save the new connection in our list */ +#if DEBUG + printk("ipt_iplimit [%d]: src=%u.%u.%u.%u:%d dst=%u.%u.%u.%u:%d new\n", + ipt_iphash(addr & mask), + NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), + NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); +#endif + conn = kmalloc(sizeof(*conn),GFP_ATOMIC); + if (NULL == conn) + return -1; + memset(conn,0,sizeof(*conn)); + INIT_LIST_HEAD(&conn->list); + conn->tuple = tuple; + list_add(&conn->list,hash); + matches++; + } + spin_unlock(&data->lock); + return matches; +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ipt_iplimit_info *info = matchinfo; + int connections, match; + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (NULL == ct) { + printk("ipt_iplimit: Oops: invalid ct state ?\n"); + *hotdrop = 1; + return 0; + } + connections = count_them(info->data,skb->nh.iph->saddr,info->mask,ct); + if (-1 == connections) { + printk("ipt_iplimit: Hmm, kmalloc failed :-(\n"); + *hotdrop = 1; /* let's free some memory :-) */ + return 0; + } + match = (info->inverse) ? (connections <= info->limit) : (connections > info->limit); +#if DEBUG + printk("ipt_iplimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u " + "connections=%d limit=%d match=%s\n", + NIPQUAD(skb->nh.iph->saddr), NIPQUAD(info->mask), + connections, info->limit, match ? "yes" : "no"); +#endif + + return match; +} + +static int check(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_iplimit_info *info = matchinfo; + int i; + + /* verify size */ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_iplimit_info))) + return 0; + + /* refuse anything but tcp */ + if (ip->proto != IPPROTO_TCP) + return 0; + + /* init private data */ + info->data = kmalloc(sizeof(struct ipt_iplimit_data),GFP_KERNEL); + spin_lock_init(&(info->data->lock)); + for (i = 0; i < 256; i++) + INIT_LIST_HEAD(&(info->data->iphash[i])); + + return 1; +} + +static void destroy(void *matchinfo, unsigned int matchinfosize) +{ + struct ipt_iplimit_info *info = matchinfo; + struct ipt_iplimit_conn *conn; + struct list_head *hash; + int i; + + /* cleanup */ + for (i = 0; i < 256; i++) { + hash = &(info->data->iphash[i]); + while (hash != hash->next) { + conn = list_entry(hash->next,struct ipt_iplimit_conn,list); + list_del(hash->next); + kfree(conn); + } + } + kfree(info->data); +} + +static struct ipt_match iplimit_match += { { NULL, NULL }, "iplimit", &match, &check, &destroy, THIS_MODULE }; + +static int __init init(void) +{ + /* NULL if ip_conntrack not a module */ + if (ip_conntrack_module) + __MOD_INC_USE_COUNT(ip_conntrack_module); + return ipt_register_match(&iplimit_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&iplimit_match); + if (ip_conntrack_module) + __MOD_DEC_USE_COUNT(ip_conntrack_module); +} + +module_init(init); +module_exit(fini);