All of lore.kernel.org
 help / color / mirror / Atom feed
* xt_connlimit 20070605
@ 2007-06-05 11:15 Jan Engelhardt
  2007-06-05 11:16 ` xt_connlimit 20070605 (kernel) Jan Engelhardt
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Jan Engelhardt @ 2007-06-05 11:15 UTC (permalink / raw)
  To: kaber; +Cc: Netfilter Developer Mailing List

Hi!


xt_connlimit cleanups as you requested. I hope I did not miss any 
either.

    *   2007-06-05: fixed: deadlock after OOM

    *   2007-06-05: UDP support


Thanks,
	Jan
-- 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* xt_connlimit 20070605 (kernel)
  2007-06-05 11:15 xt_connlimit 20070605 Jan Engelhardt
@ 2007-06-05 11:16 ` Jan Engelhardt
  2007-06-05 11:17 ` xt_connlimit 20070605 (iptables) Jan Engelhardt
  2007-06-05 11:41 ` xt_connlimit 20070605 Patrick McHardy
  2 siblings, 0 replies; 7+ messages in thread
From: Jan Engelhardt @ 2007-06-05 11:16 UTC (permalink / raw)
  To: kaber; +Cc: Netfilter Developer Mailing List, Andrew Beverley


Subject: Add the connlimit match from POM-NG

Along comes... the connlimit match that has been in POM-NG for a long time.
Plus:

    *	2007-06-02: works with 2.6.22, xtables'ified and all that

    *	2007-06-02: will request nf_conntrack_ipv4 upon load
	(otherwise it hotdrops every packet - a glitch that goes back
	to at least 2.6.20.2)

    *   2007-06-05: fixed: deadlock after OOM

    *   2007-06-05: UDP support


Signed-off-by: Jan Engelhardt <jengelh@gmx.de>

---
 include/linux/netfilter/xt_connlimit.h |   14 +
 net/netfilter/Kconfig                  |    7 
 net/netfilter/Makefile                 |    1 
 net/netfilter/xt_connlimit.c           |  257 +++++++++++++++++++++++++++++++++
 4 files changed, 279 insertions(+)

Index: linux-2.6.22-rc4/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22-rc4/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,14 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	uint32_t mask;
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data;
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22-rc4/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22-rc4.orig/net/netfilter/Kconfig
+++ linux-2.6.22-rc4/net/netfilter/Kconfig
@@ -411,6 +411,13 @@ config NETFILTER_XT_MATCH_CONNBYTES
 	  If you want to compile it as a module, say M here and read
 	  <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_CONNLIMIT
+	tristate '"connlimit" match support"'
+	depends on NETFILTER_XTABLES && NF_CONNTRACK_IPV4
+	---help---
+	  This match allows you to match against the number of parallel TCP
+	  connections to a server per client IP address (or address block).
+
 config NETFILTER_XT_MATCH_CONNMARK
 	tristate  '"connmark" connection mark match support'
 	depends on NETFILTER_XTABLES
Index: linux-2.6.22-rc4/net/netfilter/Makefile
===================================================================
--- linux-2.6.22-rc4.orig/net/netfilter/Makefile
+++ linux-2.6.22-rc4/net/netfilter/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSEC
 # matches
 obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNMARK) += xt_connmark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o
Index: linux-2.6.22-rc4/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22-rc4/net/netfilter/xt_connlimit.c
@@ -0,0 +1,257 @@
+/*
+ * netfilter module to limit the number of parallel tcp
+ * connections per IP address.
+ *   (c) 2000 Gerd Knorr <kraxel@bytesex.org>
+ *   Nov 2002: Martin Bene <martin.bene@icomedias.com>:
+ *		only ignore TIME_WAIT or gone connections
+ *   © Jan Engelhardt <jengelh@gmx.de>, 2007
+ *
+ * based on ...
+ *
+ * Kernel module to match connection tracking information.
+ * GPL (C) 1999  Rusty Russell (rusty@rustcorp.com.au).
+ */
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/netfilter/nf_conntrack_tcp.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_connlimit.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+
+#define DEBUG 0
+
+/* we will save the tuples of all connections we care about */
+struct xt_connlimit_conn {
+	struct list_head list;
+	struct nf_conntrack_tuple tuple;
+};
+
+struct xt_connlimit_data {
+	struct list_head iphash[256];
+	spinlock_t lock;
+};
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	return (addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff;
+}
+
+static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
+		      u_int32_t mask, struct nf_conn *ct)
+{
+#if DEBUG
+	static const char const *tcp_state[] = {
+		"none", "established", "syn_sent", "syn_recv", "fin_wait",
+		"time_wait", "close", "close_wait", "last_ack", "listen"
+	};
+#endif
+	struct nf_conntrack_tuple_hash *found;
+	struct nf_conntrack_tuple tuple;
+	struct xt_connlimit_conn *conn;
+	const struct list_head *lh;
+	struct nf_conn *found_ct;
+	struct list_head *hash;
+	bool addit = true;
+	int matches = 0;
+
+	tuple = ct->tuplehash[0].tuple;
+	hash  = &data->iphash[connlimit_iphash(addr & mask)];
+
+	/* check the saved connections */
+	for (lh = hash->next; lh != hash; lh = lh->next) {
+		conn     = list_entry(lh, struct xt_connlimit_conn, list);
+		found    = nf_conntrack_find_get(&conn->tuple, ct);
+		found_ct = NULL;
+
+		if (found != NULL &&
+		    (found_ct = nf_ct_tuplehash_to_ctrack(found)) != NULL &&
+		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
+		    found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)
+			/*
+			 * Just to be sure we have it only once in the list.
+			 * We should not see tuples twice unless someone hooks
+			 * this into a table without "-p tcp --syn".
+			 */
+			addit = false;
+
+#if DEBUG
+		printk(KERN_WARNING "xt_connlimit [%u]: src=%u.%u.%u.%u:%u "
+		       "dst=%u.%u.%u.%u:%d %s\n",
+		       connlimit_iphash(addr & mask),
+		       NIPQUAD(conn->tuple.src.u3.ip),
+		       ntohs(conn->tuple.src.u.tcp.port),
+		       NIPQUAD(conn->tuple.dst.u3.ip),
+		       ntohs(conn->tuple.dst.u.tcp.port),
+		       (found == NULL) ? "gone" :
+				tcp_state[found_ct->proto.tcp.state]);
+#endif
+
+		if (found == NULL) {
+			/* this one is gone */
+			lh = lh->prev;
+			list_del(lh->next);
+			kfree(conn);
+			continue;
+		}
+
+		if (found_ct->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {
+			/*
+			 * we do not care about connections which are
+			 * closed already -> ditch it
+			 */
+			lh = lh->prev;
+			list_del(lh->next);
+			kfree(conn);
+			nf_conntrack_put(&found_ct->ct_general);
+			continue;
+		}
+
+		if ((addr & mask) == (conn->tuple.src.u3.ip & mask))
+			/* same source network -> be counted! */
+			++matches;
+
+		nf_conntrack_put(&found_ct->ct_general);
+	}
+
+	if (addit) {
+		/* save the new connection in our list */
+#if DEBUG
+		printk(KERN_WARNING "xt_connlimit [%u]: src=%u.%u.%u.%u:%u "
+		       "dst=%u.%u.%u.%u:%u new\n",
+		       connlimit_iphash(addr & mask),
+		       NIPQUAD(tuple.src.u3.ip), ntohs(tuple.src.u.tcp.port),
+		       NIPQUAD(tuple.dst.u3.ip), ntohs(tuple.dst.u.tcp.port));
+#endif
+
+		conn = kzalloc(sizeof(*conn), GFP_ATOMIC);
+		if (conn == NULL)
+			return -ENOMEM;
+
+		INIT_LIST_HEAD(&conn->list);
+		conn->tuple = tuple;
+		list_add(&conn->list, hash);
+		++matches;
+	}
+
+	return matches;
+}
+
+static bool xt_connlimit_match(const struct sk_buff *skb,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       const struct xt_match *match,
+			       const void *matchinfo, int offset,
+			       unsigned int protoff, bool *hotdrop)
+{
+	const struct xt_connlimit_info *info = matchinfo;
+	enum ip_conntrack_info ctinfo;
+	const struct iphdr *iph;
+	int connections, rv;
+	struct nf_conn *ct;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (ct == NULL) {
+		printk(KERN_INFO "xt_connlimit: INVALID connection\n");
+		*hotdrop = 1;
+		return false;
+	}
+
+	iph = ip_hdr(skb);
+	spin_lock_bh(&info->data->lock);
+	connections = count_them(info->data, iph->saddr, info->mask, ct);
+	spin_unlock_bh(&info->data->lock);
+
+	if (connections < 0) {
+		/* kmalloc failed, drop it entirely */
+		printk(KERN_DEBUG "xt_connlimit: kmalloc failed\n");
+		*hotdrop = 1;
+		return false;
+	}
+
+	rv = info->inverse ^ (connections > info->limit);
+#if DEBUG
+	printk(KERN_DEBUG "xt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "
+	       "connections=%d limit=%u match=%s\n",
+	       NIPQUAD(iph->saddr), NIPQUAD(info->mask),
+	       connections, info->limit, match ? "yes" : "no");
+#endif
+
+	return rv;
+}
+
+static bool xt_connlimit_check(const char *tablename, const void *ip,
+			       const struct xt_match *match, void *matchinfo,
+			       unsigned int hook_mask)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	unsigned int i;
+
+	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
+		printk(KERN_WARNING "cannot load conntrack support for "
+		       "address family %u\n", match->family);
+		return false;
+	}
+
+	/* init private data */
+	info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
+	spin_lock_init(&info->data->lock);
+	for (i = 0; i < 256; ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void xt_connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct list_head *hash;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < 256; ++i) {
+		hash = &info->data->iphash[i];
+		while (hash != hash->next) {
+			conn = list_entry(hash->next,
+			       struct xt_connlimit_conn, list);
+			list_del(hash->next);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+	return;
+}
+
+static struct xt_match xt_connlimit_reg = {
+	.name       = "connlimit",
+	.family     = AF_INET,
+	.checkentry = xt_connlimit_check,
+	.match      = xt_connlimit_match,
+	.matchsize  = sizeof(struct xt_connlimit_info),
+	.destroy    = xt_connlimit_destroy,
+	.me         = THIS_MODULE,
+};
+
+static int __init xt_connlimit_init(void)
+{
+	return xt_register_match(&xt_connlimit_reg);
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_match(&xt_connlimit_reg);
+	return;
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");

^ permalink raw reply	[flat|nested] 7+ messages in thread

* xt_connlimit 20070605 (iptables)
  2007-06-05 11:15 xt_connlimit 20070605 Jan Engelhardt
  2007-06-05 11:16 ` xt_connlimit 20070605 (kernel) Jan Engelhardt
@ 2007-06-05 11:17 ` Jan Engelhardt
  2007-06-05 11:41 ` xt_connlimit 20070605 Patrick McHardy
  2 siblings, 0 replies; 7+ messages in thread
From: Jan Engelhardt @ 2007-06-05 11:17 UTC (permalink / raw)
  To: kaber; +Cc: Netfilter Developer Mailing List


Subject: Add connlimit to iptables.

Signed-off-by: Jan Engelhardt <jengelh@gmx.de>

---
 extensions/.connlimit-test      |    2 
 extensions/libipt_connlimit.c   |  129 ++++++++++++++++++++++++++++++++++++++++
 extensions/libipt_connlimit.man |   21 ++++++
 3 files changed, 152 insertions(+)

Index: iptables/extensions/.connlimit-test
===================================================================
--- /dev/null
+++ iptables/extensions/.connlimit-test
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f "$KERNEL_DIR/include/linux/netfilter/xt_connlimit.h" ] && echo connlimit
Index: iptables/extensions/libipt_connlimit.c
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.c
@@ -0,0 +1,129 @@
+/* Shared library add-on to iptables to add connection limit support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter/xt_connlimit.h>
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+	printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n		match if the number of existing tcp connections is (not) above n\n"
+" --connlimit-mask n		group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+	{"connlimit-above", 1, NULL, '1'},
+	{"connlimit-mask",  1, NULL, '2'},
+	{NULL},
+};
+
+/* 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 xt_connlimit_info *info = (void *)(*match)->data;
+	int i;
+
+	if (!(*flags & 2))
+		/*
+		 * set default mask unless we have already seen a mask option
+		 */
+		info->mask = htonl(0xFFFFFFFF);
+
+	switch (c) {
+	case '1':
+		check_inverse(optarg, &invert, &optind, 0);
+		info->limit   = strtoul(argv[optind-1], NULL, 0);
+		info->inverse = invert;
+		*flags |= 1;
+		break;
+
+	case '2':
+		i = strtol(argv[optind-1], NULL, 0);
+		if (i < 0 || i > 32)
+			exit_error(PARAMETER_PROBLEM,
+				"--connlimit-mask must be between 0 and 32");
+
+		if (i == 0)
+			info->mask = 0;
+		else
+			info->mask = htonl(0xFFFFFFFF << (32 - i));
+
+		*flags |= 2;
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Final check */
+static void final_check(unsigned int flags)
+{
+	if (!(flags & 1))
+		exit_error(PARAMETER_PROBLEM, "You must specify `--connlimit-above'");
+}
+
+static int count_bits(u_int32_t mask)
+{
+	int i, bits;
+
+	for (bits = 0, i = 31; i >= 0; i--) {
+		if (mask & htonl((u_int32_t)1 << i)) {
+			bits++;
+			continue;
+		}
+		break;
+	}
+	return bits;
+}
+
+/* Prints out the matchinfo. */
+static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match,
+		  int numeric)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+
+	printf("#conn/%d %s %d ", count_bits(info->mask),
+	       info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+
+	printf("%s--connlimit-above %u --connlimit-mask %u ",
+	       info->inverse ? "! " : "", info->limit,
+	       count_bits(info->mask));
+}
+
+static struct iptables_match connlimit = {
+	.name		= "connlimit",
+	.version	= IPTABLES_VERSION,
+	.size		= IPT_ALIGN(sizeof(struct xt_connlimit_info)),
+	.userspacesize 	= offsetof(struct xt_connlimit_info, data),
+	.help		= help,
+	.parse 		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.save		= save,
+	.extra_opts	= opts,
+};
+
+static __attribute__((constructor)) void libipt_connlimit_init(void)
+{
+	register_match(&connlimit);
+}
Index: iptables/extensions/libipt_connlimit.man
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.man
@@ -0,0 +1,21 @@
+Allows you to restrict the number of parallel TCP connections to a
+server per client IP address (or address block).
+.TP
+[\fB!\fR] \fB--connlimit-above \fIn\fR
+match if the number of existing tcp connections is (not) above n
+.TP
+.BI "--connlimit-mask " "bits"
+group hosts using mask
+.P
+Examples:
+.TP
+# allow 2 telnet connections per client host
+iptables -A INPUT -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT
+.TP
+# you can also match the other way around:
+iptables -A INPUT -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT
+.TP
+# limit the nr of parallel http requests to 16 per class C sized \
+network (24 bit netmask)
+iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16
+--connlimit-mask 24 -j REJECT

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xt_connlimit 20070605
  2007-06-05 11:15 xt_connlimit 20070605 Jan Engelhardt
  2007-06-05 11:16 ` xt_connlimit 20070605 (kernel) Jan Engelhardt
  2007-06-05 11:17 ` xt_connlimit 20070605 (iptables) Jan Engelhardt
@ 2007-06-05 11:41 ` Patrick McHardy
  2007-06-05 12:06   ` Jan Engelhardt
  2 siblings, 1 reply; 7+ messages in thread
From: Patrick McHardy @ 2007-06-05 11:41 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> Hi!
> 
> 
> xt_connlimit cleanups as you requested. I hope I did not miss any 
> either.
> 
>     *   2007-06-05: fixed: deadlock after OOM
> 
>     *   2007-06-05: UDP support

Seem my previous mail please.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xt_connlimit 20070605
  2007-06-05 11:41 ` xt_connlimit 20070605 Patrick McHardy
@ 2007-06-05 12:06   ` Jan Engelhardt
  2007-06-05 12:37     ` Eric Dumazet
  0 siblings, 1 reply; 7+ messages in thread
From: Jan Engelhardt @ 2007-06-05 12:06 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 5 2007 13:41, Patrick McHardy wrote:
>> 
>> xt_connlimit cleanups as you requested. I hope I did not miss any 
>> either.
>> 
>>     *   2007-06-05: fixed: deadlock after OOM
>> 
>>     *   2007-06-05: UDP support
>
>Seem my previous mail please.




Signed-off-by: Jan Engelhardt <jengelh@gmx.de>

---
 include/linux/netfilter/xt_connlimit.h |   14 +
 net/netfilter/Kconfig                  |    7 
 net/netfilter/Makefile                 |    1 
 net/netfilter/xt_connlimit.c           |  255 +++++++++++++++++++++++++++++++++
 4 files changed, 277 insertions(+)

Index: linux-2.6.22-rc4/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22-rc4/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,14 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	u_int32_t mask;
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data;
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22-rc4/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22-rc4.orig/net/netfilter/Kconfig
+++ linux-2.6.22-rc4/net/netfilter/Kconfig
@@ -411,6 +411,13 @@ config NETFILTER_XT_MATCH_CONNBYTES
 	  If you want to compile it as a module, say M here and read
 	  <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_CONNLIMIT
+	tristate '"connlimit" match support"'
+	depends on NETFILTER_XTABLES && NF_CONNTRACK_IPV4
+	---help---
+	  This match allows you to match against the number of parallel TCP
+	  connections to a server per client IP address (or address block).
+
 config NETFILTER_XT_MATCH_CONNMARK
 	tristate  '"connmark" connection mark match support'
 	depends on NETFILTER_XTABLES
Index: linux-2.6.22-rc4/net/netfilter/Makefile
===================================================================
--- linux-2.6.22-rc4.orig/net/netfilter/Makefile
+++ linux-2.6.22-rc4/net/netfilter/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSEC
 # matches
 obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNMARK) += xt_connmark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o
Index: linux-2.6.22-rc4/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22-rc4/net/netfilter/xt_connlimit.c
@@ -0,0 +1,255 @@
+/*
+ * netfilter module to limit the number of parallel tcp
+ * connections per IP address.
+ *   (c) 2000 Gerd Knorr <kraxel@bytesex.org>
+ *   Nov 2002: Martin Bene <martin.bene@icomedias.com>:
+ *		only ignore TIME_WAIT or gone connections
+ *   © Jan Engelhardt <jengelh@gmx.de>, 2007
+ *
+ * based on ...
+ *
+ * Kernel module to match connection tracking information.
+ * GPL (C) 1999  Rusty Russell (rusty@rustcorp.com.au).
+ */
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/netfilter/nf_conntrack_tcp.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_connlimit.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+
+#define DEBUG 0
+
+/* we will save the tuples of all connections we care about */
+struct xt_connlimit_conn {
+	struct list_head list;
+	struct nf_conntrack_tuple tuple;
+};
+
+struct xt_connlimit_data {
+	struct list_head iphash[256];
+	spinlock_t lock;
+};
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	return (addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff;
+}
+
+static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
+		      u_int32_t mask, struct nf_conn *ct)
+{
+#if DEBUG
+	static const char const *tcp_state[] = {
+		"none", "established", "syn_sent", "syn_recv", "fin_wait",
+		"time_wait", "close", "close_wait", "last_ack", "listen"
+	};
+#endif
+	struct nf_conntrack_tuple_hash *found;
+	struct nf_conntrack_tuple tuple;
+	struct xt_connlimit_conn *conn;
+	const struct list_head *lh;
+	struct nf_conn *found_ct;
+	struct list_head *hash;
+	bool addit = true;
+	int matches = 0;
+
+	tuple = ct->tuplehash[0].tuple;
+	hash  = &data->iphash[connlimit_iphash(addr & mask)];
+
+	/* check the saved connections */
+	for (lh = hash->next; lh != hash; lh = lh->next) {
+		conn     = list_entry(lh, struct xt_connlimit_conn, list);
+		found    = nf_conntrack_find_get(&conn->tuple, ct);
+		found_ct = NULL;
+
+		if (found != NULL &&
+		    (found_ct = nf_ct_tuplehash_to_ctrack(found)) != NULL &&
+		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
+		    found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)
+			/*
+			 * Just to be sure we have it only once in the list.
+			 * We should not see tuples twice unless someone hooks
+			 * this into a table without "-p tcp --syn".
+			 */
+			addit = false;
+
+#if DEBUG
+		printk(KERN_WARNING "xt_connlimit [%u]: src=%u.%u.%u.%u:%u "
+		       "dst=%u.%u.%u.%u:%d %s\n",
+		       connlimit_iphash(addr & mask),
+		       NIPQUAD(conn->tuple.src.u3.ip),
+		       ntohs(conn->tuple.src.u.tcp.port),
+		       NIPQUAD(conn->tuple.dst.u3.ip),
+		       ntohs(conn->tuple.dst.u.tcp.port),
+		       (found == NULL) ? "gone" :
+				tcp_state[found_ct->proto.tcp.state]);
+#endif
+
+		if (found == NULL) {
+			/* this one is gone */
+			lh = lh->prev;
+			list_del(lh->next);
+			kfree(conn);
+			continue;
+		}
+
+		if (found_ct->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT) {
+			/*
+			 * we do not care about connections which are
+			 * closed already -> ditch it
+			 */
+			lh = lh->prev;
+			list_del(lh->next);
+			kfree(conn);
+			nf_conntrack_put(&found_ct->ct_general);
+			continue;
+		}
+
+		if ((addr & mask) == (conn->tuple.src.u3.ip & mask))
+			/* same source network -> be counted! */
+			++matches;
+
+		nf_conntrack_put(&found_ct->ct_general);
+	}
+
+	if (addit) {
+		/* save the new connection in our list */
+#if DEBUG
+		printk(KERN_WARNING "xt_connlimit [%u]: src=%u.%u.%u.%u:%u "
+		       "dst=%u.%u.%u.%u:%u new\n",
+		       connlimit_iphash(addr & mask),
+		       NIPQUAD(tuple.src.u3.ip), ntohs(tuple.src.u.tcp.port),
+		       NIPQUAD(tuple.dst.u3.ip), ntohs(tuple.dst.u.tcp.port));
+#endif
+
+		conn = kzalloc(sizeof(*conn), GFP_ATOMIC);
+		if (conn == NULL)
+			return -ENOMEM;
+
+		INIT_LIST_HEAD(&conn->list);
+		conn->tuple = tuple;
+		list_add(&conn->list, hash);
+		++matches;
+	}
+
+	return matches;
+}
+
+static bool xt_connlimit_match(const struct sk_buff *skb,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       const struct xt_match *match,
+			       const void *matchinfo, int offset,
+			       unsigned int protoff, bool *hotdrop)
+{
+	const struct xt_connlimit_info *info = matchinfo;
+	enum ip_conntrack_info ctinfo;
+	const struct iphdr *iph;
+	int connections, rv;
+	struct nf_conn *ct;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (ct == NULL) {
+		printk(KERN_INFO "xt_connlimit: INVALID connection\n");
+		*hotdrop = 1;
+		return false;
+	}
+
+	iph = ip_hdr(skb);
+	spin_lock_bh(&info->data->lock);
+	connections = count_them(info->data, iph->saddr, info->mask, ct);
+	spin_unlock_bh(&info->data->lock);
+
+	if (connections < 0) {
+		/* kmalloc failed, drop it entirely */
+		printk(KERN_DEBUG "xt_connlimit: kmalloc failed\n");
+		*hotdrop = 1;
+		return false;
+	}
+
+	rv = info->inverse ^ (connections > info->limit);
+#if DEBUG
+	printk(KERN_DEBUG "xt_connlimit: src=%u.%u.%u.%u mask=%u.%u.%u.%u "
+	       "connections=%d limit=%u match=%s\n",
+	       NIPQUAD(iph->saddr), NIPQUAD(info->mask),
+	       connections, info->limit, match ? "yes" : "no");
+#endif
+
+	return rv;
+}
+
+static bool xt_connlimit_check(const char *tablename, const void *ip,
+			       const struct xt_match *match, void *matchinfo,
+			       unsigned int hook_mask)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	unsigned int i;
+
+	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
+		printk(KERN_WARNING "cannot load conntrack support for "
+		       "address family %u\n", match->family);
+		return false;
+	}
+
+	/* init private data */
+	info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
+	spin_lock_init(&info->data->lock);
+	for (i = 0; i < 256; ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void xt_connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct list_head *hash;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < 256; ++i) {
+		hash = &info->data->iphash[i];
+		while (hash != hash->next) {
+			conn = list_entry(hash->next,
+			       struct xt_connlimit_conn, list);
+			list_del(hash->next);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+}
+
+static struct xt_match xt_connlimit_reg = {
+	.name       = "connlimit",
+	.family     = AF_INET,
+	.checkentry = xt_connlimit_check,
+	.match      = xt_connlimit_match,
+	.matchsize  = sizeof(struct xt_connlimit_info),
+	.destroy    = xt_connlimit_destroy,
+	.me         = THIS_MODULE,
+};
+
+static int __init xt_connlimit_init(void)
+{
+	return xt_register_match(&xt_connlimit_reg);
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_match(&xt_connlimit_reg);
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xt_connlimit 20070605
  2007-06-05 12:06   ` Jan Engelhardt
@ 2007-06-05 12:37     ` Eric Dumazet
  2007-06-05 16:47       ` Jan Engelhardt
  0 siblings, 1 reply; 7+ messages in thread
From: Eric Dumazet @ 2007-06-05 12:37 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Patrick McHardy

On Tue, 5 Jun 2007 14:06:50 +0200 (MEST)
Jan Engelhardt <jengelh@linux01.gwdg.de> wrote:

>
> +static inline unsigned int connlimit_iphash(u_int32_t addr)
> +{
> +	return (addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff;
> +}
> +
> +static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
> +		      u_int32_t mask, struct nf_conn *ct)

There is some problem with this hash function :

An attacker can easily send on your machine SYN packets where addr 
is carefully chosen so that your hash function returns the same value.

Time to scan 1.000 elements in a chain is too long.

I suggest you use jhash and a random value (initialized at boot time), so that
the attacker has hard work to guess your hash function.

See for examples :

 net/ipv4/route.c , function rt_hash_code()

 net/netfilter/nf_conntrack_core.c , function __hash_conntrack()

 net/ipv4/netfilter/ipt_recent.c, function recent_entry_hash()

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xt_connlimit 20070605
  2007-06-05 12:37     ` Eric Dumazet
@ 2007-06-05 16:47       ` Jan Engelhardt
  0 siblings, 0 replies; 7+ messages in thread
From: Jan Engelhardt @ 2007-06-05 16:47 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Netfilter Developer Mailing List, Patrick McHardy

Hi Eric,

On Jun 5 2007 14:37, Eric Dumazet wrote:
>>
>> +static inline unsigned int connlimit_iphash(u_int32_t addr)
>> +{
>> +	return (addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff;
>> +}
>> +
>> +static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
>> +		      u_int32_t mask, struct nf_conn *ct)
>
>I suggest you use jhash and a random value (initialized at boot time), so that
>the attacker has hard work to guess your hash function.

Do you think it is secure enough to use % 256 (or & 0xFF) on jhash()?
E.g. in xt_connlimit.c:count_them():

        hash  = &data->iphash[jhash_1word(addr, IV) & 0xFF];                   



Thanks,
	Jan
-- 

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2007-06-05 16:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-06-05 11:15 xt_connlimit 20070605 Jan Engelhardt
2007-06-05 11:16 ` xt_connlimit 20070605 (kernel) Jan Engelhardt
2007-06-05 11:17 ` xt_connlimit 20070605 (iptables) Jan Engelhardt
2007-06-05 11:41 ` xt_connlimit 20070605 Patrick McHardy
2007-06-05 12:06   ` Jan Engelhardt
2007-06-05 12:37     ` Eric Dumazet
2007-06-05 16:47       ` Jan Engelhardt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.