All of lore.kernel.org
 help / color / mirror / Atom feed
* xt_connlimit 20070620_2
@ 2007-06-20  9:39 Jan Engelhardt
  2007-06-20 17:21 ` Andrew Beverley
                   ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-20  9:39 UTC (permalink / raw)
  To: kaber; +Cc: Netfilter Developer Mailing List, Andrew Beverley

[-- Attachment #1: Type: TEXT/PLAIN, Size: 11192 bytes --]

Hi,


I fixed the crash. Sometimes, I should just look at the regs :) EBP was 
totally beyond any reasonable value which directed me to the hash 
function which was missing a "% 256".

Patrick, please merge. (xt_u32 as posted is functional and already 
complete, please merge too.)
Andrew, please see if this works for you too now.

(Kernel patch below, iptables patch unchanged)

Thanks!
	Jan
---


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

    *   2007-06-06: Using jhash, as suggested by Eric Dumazet.
        ( https://lists.netfilter.org/pipermail/netfilter-devel/2007-June/028056.html )


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           |  264 +++++++++++++++++++++++++++++++++
 4 files changed, 286 insertions(+)

Index: linux-2.6.22/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22/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/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22.orig/net/netfilter/Kconfig
+++ linux-2.6.22/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/net/netfilter/Makefile
===================================================================
--- linux-2.6.22.orig/net/netfilter/Makefile
+++ linux-2.6.22/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/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22/net/netfilter/xt_connlimit.c
@@ -0,0 +1,264 @@
+/*
+ * 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/jhash.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/random.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 u_int32_t connlimit_iphash_rnd;
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	/*
+	 * Since iphash (in struct xt_connlimit_data) has 256 entries, we need
+	 * to "% 256" it. "& mask" works too, but _only_ if the iphash array
+	 * size is exactly a {power of two} minus 1.
+	 */
+	return jhash_1word(addr, connlimit_iphash_rnd) & 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 */
+	list_for_each(lh, hash) {
+		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 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 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 < ARRAY_SIZE(info->data->iphash); ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct list_head *hash;
+	struct list_head *hash_next;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
+		list_for_each_safe(hash, hash_next, &info->data->iphash[i]) {
+			conn = list_entry(hash, struct xt_connlimit_conn, list);
+			list_del(hash);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+}
+
+static struct xt_match connlimit_reg = {
+	.name       = "connlimit",
+	.family     = AF_INET,
+	.checkentry = connlimit_check,
+	.match      = connlimit_match,
+	.matchsize  = sizeof(struct xt_connlimit_info),
+	.destroy    = connlimit_destroy,
+	.me         = THIS_MODULE,
+};
+
+static int __init xt_connlimit_init(void)
+{
+	get_random_bytes(&connlimit_iphash_rnd, sizeof(connlimit_iphash_rnd));
+	return xt_register_match(&connlimit_reg);
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_match(&connlimit_reg);
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");

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

* Re: xt_connlimit 20070620_2
  2007-06-20  9:39 xt_connlimit 20070620_2 Jan Engelhardt
@ 2007-06-20 17:21 ` Andrew Beverley
  2007-06-22 11:14 ` Patrick McHardy
  2007-06-25 18:51 ` xt_connlimit 20070620_2 Patrick McHardy
  2 siblings, 0 replies; 71+ messages in thread
From: Andrew Beverley @ 2007-06-20 17:21 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, kaber


> I fixed the crash. Sometimes, I should just look at the regs :) EBP was
> totally beyond any reasonable value which directed me to the hash
> function which was missing a "% 256".
>
> Patrick, please merge. (xt_u32 as posted is functional and already
> complete, please merge too.)
> Andrew, please see if this works for you too now.

Thanks Jan. In my very quick initial tests it works now.

Regards,

Andy

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

* Re: xt_connlimit 20070620_2
  2007-06-20  9:39 xt_connlimit 20070620_2 Jan Engelhardt
  2007-06-20 17:21 ` Andrew Beverley
@ 2007-06-22 11:14 ` Patrick McHardy
  2007-06-22 11:36   ` Jan Engelhardt
  2007-06-25 11:11   ` xt_connlimit 20070625 Jan Engelhardt
  2007-06-25 18:51 ` xt_connlimit 20070620_2 Patrick McHardy
  2 siblings, 2 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-22 11:14 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Andrew Beverley

Jan Engelhardt wrote:
> Hi,
> 
> 
> I fixed the crash. Sometimes, I should just look at the regs :) EBP was 
> totally beyond any reasonable value which directed me to the hash 
> function which was missing a "% 256".
> 
> Patrick, please merge. (xt_u32 as posted is functional and already 
> complete, please merge too.)
> Andrew, please see if this works for you too now.
> 
> (Kernel patch below, iptables patch unchanged)
> 
> Thanks!
> 	Jan
> ---
> 
> 
> 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
> 
>     *   2007-06-06: Using jhash, as suggested by Eric Dumazet.
>         ( https://lists.netfilter.org/pipermail/netfilter-devel/2007-June/028056.html )
> 
> 
> 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           |  264 +++++++++++++++++++++++++++++++++
>  4 files changed, 286 insertions(+)
> 
> Index: linux-2.6.22/include/linux/netfilter/xt_connlimit.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6.22/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;


As Pablo mentioned, you need to force 8 byte alignment to
avoid 32/64 bit issues.
> Index: linux-2.6.22/net/netfilter/xt_connlimit.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6.22/net/netfilter/xt_connlimit.c
> +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 */
> +	list_for_each(lh, hash) {
> +		conn     = list_entry(lh, struct xt_connlimit_conn, list);

list_for_each_entry_safe

> +		found    = nf_conntrack_find_get(&conn->tuple, ct);
> +		found_ct = NULL;
> +
> +		if (found != NULL &&
> +		    (found_ct = nf_ct_tuplehash_to_ctrack(found)) != NULL &&


Please no assignments in comparisons.

> +		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
> +		    found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)


It should support other protocols as well. Also nothing guarantees that
you're looking at a TCP connection here.

> +			/*
> +			 * 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;

You can remove this with list_for_each_entry_safe

> +			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 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;


Thats just me I think, but I have an aversion to "rv" for return value.
"err" or "ret"?

> +	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);


Switching the two expressions seems more logical.

> +#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");


Excessive debugging, also shouldn't use ifdef but pr_debug if
absolutely necessary.

> +#endif
> +
> +	return rv;
> +}
> +
> +static bool 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);


This will make it forget its state everytime you add or delete _any_
rule. The alternative sucks as well, keep data on a global list and
lookup based on some identifier ..

> +	spin_lock_init(&info->data->lock);
> +	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
> +		INIT_LIST_HEAD(&info->data->iphash[i]);
> +
> +	return true;
> +}
> +
> +static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
> +{
> +	struct xt_connlimit_info *info = matchinfo;
> +	struct xt_connlimit_conn *conn;
> +	struct list_head *hash;
> +	struct list_head *hash_next;
> +	unsigned int i;
> +
> +	nf_ct_l3proto_module_put(match->family);
> +
> +	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
> +		list_for_each_safe(hash, hash_next, &info->data->iphash[i]) {

list_for_each_entry_safe

> +			conn = list_entry(hash, struct xt_connlimit_conn, list);
> +			list_del(hash);
> +			kfree(conn);
> +		}
> +	}
> +
> +	kfree(info->data);
> +}
> +
> +static struct xt_match connlimit_reg = {
> +	.name       = "connlimit",
> +	.family     = AF_INET,
> +	.checkentry = connlimit_check,
> +	.match      = connlimit_match,
> +	.matchsize  = sizeof(struct xt_connlimit_info),
> +	.destroy    = connlimit_destroy,
> +	.me         = THIS_MODULE,
> +};
> +
> +static int __init xt_connlimit_init(void)
> +{
> +	get_random_bytes(&connlimit_iphash_rnd, sizeof(connlimit_iphash_rnd));
> +	return xt_register_match(&connlimit_reg);
> +}
> +
> +static void __exit xt_connlimit_exit(void)
> +{
> +	xt_unregister_match(&connlimit_reg);
> +}
> +
> +module_init(xt_connlimit_init);
> +module_exit(xt_connlimit_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("ipt_connlimit");

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

* Re: xt_connlimit 20070620_2
  2007-06-22 11:14 ` Patrick McHardy
@ 2007-06-22 11:36   ` Jan Engelhardt
  2007-06-22 11:43     ` Patrick McHardy
  2007-06-25 11:11   ` xt_connlimit 20070625 Jan Engelhardt
  1 sibling, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-22 11:36 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Andrew Beverley


On Jun 22 2007 13:14, Patrick McHardy wrote:
>
>> +		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
>> +		    found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)
>
>
>It should support other protocols as well.

It does support UDP and UDPLITE.

>Also nothing guarantees that you're looking at a TCP connection here.

True that. What is the SCTP state (include/linux/netfilter/nf_conntrack_tcp.h)
I am looking for? (And DCCP does not seem to have states.)

>> +	const struct xt_connlimit_info *info = matchinfo;
>> +	enum ip_conntrack_info ctinfo;
>> +	const struct iphdr *iph;
>> +	int connections, rv;
>
>
>Thats just me I think, but I have an aversion to "rv" for return value.
>"err" or "ret"?

Definitely.

>> +	rv = info->inverse ^ (connections > info->limit);
>
>Switching the two expressions seems more logical.

Like...?
	ret = connections > info->limit;
	ret ^= info->inverse

>> +#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");
>
>Excessive debugging, also shouldn't use ifdef but pr_debug if
>absolutely necessary.

Should it go or not?

>> +static bool 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);
>
>This will make it forget its state everytime you add or delete _any_
>rule.

Sorry, what state?
Each rule keeps its own list of known connections, yes :-/

>The alternative sucks as well, keep data on a global list and
>lookup based on some identifier ..
>
>> +	spin_lock_init(&info->data->lock);
>> +	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
>> +		INIT_LIST_HEAD(&info->data->iphash[i]);
>> +
>> +	return true;
>> +}

Thanks for the feedback, updated patch soon.



	Jan
-- 

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

* Re: xt_connlimit 20070620_2
  2007-06-22 11:36   ` Jan Engelhardt
@ 2007-06-22 11:43     ` Patrick McHardy
  2007-06-22 12:33       ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-22 11:43 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Andrew Beverley

Jan Engelhardt wrote:
> On Jun 22 2007 13:14, Patrick McHardy wrote:
> 
>>>+		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
>>>+		    found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)
>>
>>
>>It should support other protocols as well.
> 
> 
> It does support UDP and UDPLITE.


I didn't realize that. Still, why doesn't it support all protocols?

>>Also nothing guarantees that you're looking at a TCP connection here.
> 
> 
> True that. What is the SCTP state (include/linux/netfilter/nf_conntrack_tcp.h)
> I am looking for? (And DCCP does not seem to have states.)


SCTP states are defined in the .c file itself I think.

>>>+	rv = info->inverse ^ (connections > info->limit);
>>
>>Switching the two expressions seems more logical.
> 
> 
> Like...?
> 	ret = connections > info->limit;
> 	ret ^= info->inverse


Or just "(connections > info->limit) ^ info->inverse".


>>>+#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");
>>
>>Excessive debugging, also shouldn't use ifdef but pr_debug if
>>absolutely necessary.
> 
> 
> Should it go or not?


I certainly don't need it. If there's a bug I prefer to think about
the code instead of recompiling with debugging and getting lots
of spam in my logs. I also have the suspicion that too much debugging
statements cause more bugs because the code gets harder to read.

> 
> 
>>>+static bool 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);
>>
>>This will make it forget its state everytime you add or delete _any_
>>rule.
> 
> 
> Sorry, what state?
> Each rule keeps its own list of known connections, yes :-/


Thats the state. It will forget them every time you change _anything_
in the ruleset. On second though, I guess it doesn't matter because
the state can be entirely reconstructed at runtime?

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

* Re: xt_connlimit 20070620_2
  2007-06-22 11:43     ` Patrick McHardy
@ 2007-06-22 12:33       ` Jan Engelhardt
  2007-06-22 12:42         ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-22 12:33 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Andrew Beverley


On Jun 22 2007 13:43, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> On Jun 22 2007 13:14, Patrick McHardy wrote:
>> 
>>>>+		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
>>>>+		    found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)
>>>
>>>It should support other protocols as well.
>> 
>> It does support UDP and UDPLITE.
>
>I didn't realize that. Still, why doesn't it support all protocols?

It suppers all protocols that Netfilter supports, surprise surprise :)
-A OUTPUT -p udp -m connlimit uses what Netfilter considers an UDP
connection. /proc/net/ip_conntrack shows that. Of course these UDP
"connections" expeire within 30 seconds by default, but that's another problem. 

>>>Also nothing guarantees that you're looking at a TCP connection here.

Which is why I asked how to find out I am actually looking at TCP.
There's tuple.proto or the proto field in struct nf_conn, but the question is,
which one should I use, or are both the same?

>> True that. What is the SCTP state (include/linux/netfilter/nf_conntrack_tcp.h)
>> I am looking for? (And DCCP does not seem to have states.)
>
>SCTP states are defined in the .c file itself I think.

Conntrack states are needed. For TCP this is TCP_CONNTRACK_TIME_WAIT.
Hence I think all states that could possibly exist (as far as conntrack is
concerned) are listed in netfilter/nf_conntrack_<proto>.h

>> Sorry, what state?
>> Each rule keeps its own list of known connections, yes :-/
>
>Thats the state. It will forget them every time you change _anything_
>in the ruleset. On second though, I guess it doesn't matter because
>the state can be entirely reconstructed at runtime?

Yes there is a user-administrator race. For example,
 * host opens N connections (and hits the connlimit limit) and new
   connections are rejected
 * administrator reloads ip tables
 * now, the next N that are seen -- which may not even be the *original*
   connections -- are allowed, so the older ones could get magically
   disconnected.

And: a lot more netfilter matches might be affected by this. hashlimit 
comes to mind. I'd say this is an administrator issue and a justified 
sacrifice to take.

	Jan
-- 

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

* Re: xt_connlimit 20070620_2
  2007-06-22 12:33       ` Jan Engelhardt
@ 2007-06-22 12:42         ` Patrick McHardy
  2007-06-22 13:22           ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-22 12:42 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Andrew Beverley

Jan Engelhardt wrote:
> On Jun 22 2007 13:43, Patrick McHardy wrote:
> 
>>I didn't realize that. Still, why doesn't it support all protocols?
> 
> 
> It suppers all protocols that Netfilter supports, surprise surprise :)
> -A OUTPUT -p udp -m connlimit uses what Netfilter considers an UDP
> connection. /proc/net/ip_conntrack shows that. Of course these UDP
> "connections" expeire within 30 seconds by default, but that's another problem. 


OK, that fine then.


> 
> 
>>>>Also nothing guarantees that you're looking at a TCP connection here.
> 
> 
> Which is why I asked how to find out I am actually looking at TCP.
> There's tuple.proto or the proto field in struct nf_conn, but the question is,
> which one should I use, or are both the same?
> 
> 
>>>True that. What is the SCTP state (include/linux/netfilter/nf_conntrack_tcp.h)
>>>I am looking for? (And DCCP does not seem to have states.)
>>
>>SCTP states are defined in the .c file itself I think.
> 
> 
> Conntrack states are needed. For TCP this is TCP_CONNTRACK_TIME_WAIT.
> Hence I think all states that could possibly exist (as far as conntrack is
> concerned) are listed in netfilter/nf_conntrack_<proto>.h


Indeed, its in include/linux/netfilter/nf_conntrack_sctp.h.
I'm wondering why its looking at the states though, a conntrack
in TIME_WAIT state is still a conntrack.

>>>Sorry, what state?
>>>Each rule keeps its own list of known connections, yes :-/
>>
>>Thats the state. It will forget them every time you change _anything_
>>in the ruleset. On second though, I guess it doesn't matter because
>>the state can be entirely reconstructed at runtime?
> 
> 
> Yes there is a user-administrator race. For example,
>  * host opens N connections (and hits the connlimit limit) and new
>    connections are rejected
>  * administrator reloads ip tables
>  * now, the next N that are seen -- which may not even be the *original*
>    connections -- are allowed, so the older ones could get magically
>    disconnected.


That sucks a bit.

> And: a lot more netfilter matches might be affected by this. hashlimit 
> comes to mind. I'd say this is an administrator issue and a justified 
> sacrifice to take.


No, hashlimit looks up the state from a global list. It behaves
strangely when changing parameters without deleting the table
first though.

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

* Re: xt_connlimit 20070620_2
  2007-06-22 12:42         ` Patrick McHardy
@ 2007-06-22 13:22           ` Jan Engelhardt
  2007-06-22 13:27             ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-22 13:22 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Andrew Beverley


On Jun 22 2007 14:42, Patrick McHardy wrote:
>> 
>> Conntrack states are needed. For TCP this is TCP_CONNTRACK_TIME_WAIT.
>> Hence I think all states that could possibly exist (as far as conntrack is
>> concerned) are listed in netfilter/nf_conntrack_<proto>.h
>
>Indeed, its in include/linux/netfilter/nf_conntrack_sctp.h.
>I'm wondering why its looking at the states though, a conntrack
>in TIME_WAIT state is still a conntrack.

The original author thought that a TIME_WAIT socket does not count
towards the limit anymore (interpreting the code). Seems reasonable.
Does SCTP does not have the concept of TIME_WAIT - does SCTP end
with that is known in TCP land by "LAST_ACK"?
If so, then all is well with SCTP and no extra state check if needed for SCTP.

>> Yes there is a user-administrator race. For example,
>>  * host opens N connections (and hits the connlimit limit) and new
>>    connections are rejected
>>  * administrator reloads ip tables
>>  * now, the next N that are seen -- which may not even be the *original*
>>    connections -- are allowed, so the older ones could get magically
>>    disconnected.
>
>That sucks a bit.

Well give me your stance on this. Do I have to change that now?


Thanks,

	Jan
-- 

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

* Re: xt_connlimit 20070620_2
  2007-06-22 13:22           ` Jan Engelhardt
@ 2007-06-22 13:27             ` Patrick McHardy
  0 siblings, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-22 13:27 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Andrew Beverley

Jan Engelhardt wrote:
> On Jun 22 2007 14:42, Patrick McHardy wrote:
> 
>>>Conntrack states are needed. For TCP this is TCP_CONNTRACK_TIME_WAIT.
>>>Hence I think all states that could possibly exist (as far as conntrack is
>>>concerned) are listed in netfilter/nf_conntrack_<proto>.h
>>
>>Indeed, its in include/linux/netfilter/nf_conntrack_sctp.h.
>>I'm wondering why its looking at the states though, a conntrack
>>in TIME_WAIT state is still a conntrack.
> 
> 
> The original author thought that a TIME_WAIT socket does not count
> towards the limit anymore (interpreting the code). Seems reasonable.
> Does SCTP does not have the concept of TIME_WAIT - does SCTP end
> with that is known in TCP land by "LAST_ACK"?
> If so, then all is well with SCTP and no extra state check if needed for SCTP.


I think the shutdown states correspond to TIME_WAIT/LAST_ACK.

>>>Yes there is a user-administrator race. For example,
>>> * host opens N connections (and hits the connlimit limit) and new
>>>   connections are rejected
>>> * administrator reloads ip tables
>>> * now, the next N that are seen -- which may not even be the *original*
>>>   connections -- are allowed, so the older ones could get magically
>>>   disconnected.
>>
>>That sucks a bit.
> 
> 
> Well give me your stance on this. Do I have to change that now?


If you have a good idea how to fix that ..

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

* xt_connlimit 20070625
  2007-06-22 11:14 ` Patrick McHardy
  2007-06-22 11:36   ` Jan Engelhardt
@ 2007-06-25 11:11   ` Jan Engelhardt
  2007-06-25 11:41     ` Patrick McHardy
  1 sibling, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-25 11:11 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

[-- Attachment #1: Type: TEXT/PLAIN, Size: 11814 bytes --]


On Jun 22 2007 13:14, Patrick McHardy wrote:

>As Pablo mentioned, you need to force 8 byte alignment to
>avoid 32/64 bit issues.
>
>list_for_each_entry_safe
>
>Please no assignments in comparisons.
>
>It should support other protocols as well. Also nothing guarantees that
>you're looking at a TCP connection here.
>
>You can remove this with list_for_each_entry_safe
>
>Thats just me I think, but I have an aversion to "rv" for return value.
>"err" or "ret"?
>
>Switching the two expressions seems more logical.
>
>Excessive debugging, also shouldn't use ifdef but pr_debug if
>absolutely necessary.
>
>list_for_each_entry_safe


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: any-protocol support

    *   2007-06-06: Using jhash, as suggested by Eric Dumazet.
        ( https://lists.netfilter.org/pipermail/netfilter-devel/2007-June/028056.html )


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           |  278 +++++++++++++++++++++++++++++++++
 4 files changed, 300 insertions(+)

Index: linux-2.6.22/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22/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 __attribute__((aligned(8))) *data;
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22.orig/net/netfilter/Kconfig
+++ linux-2.6.22/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/net/netfilter/Makefile
===================================================================
--- linux-2.6.22.orig/net/netfilter/Makefile
+++ linux-2.6.22/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/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22/net/netfilter/xt_connlimit.c
@@ -0,0 +1,278 @@
+/*
+ * 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
+ *   Copyright © 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/jhash.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/random.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>
+
+/* 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 u_int32_t connlimit_iphash_rnd;
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	/*
+	 * Since iphash (in struct xt_connlimit_data) has 256 entries, we need
+	 * to "% 256" it. "& mask" works too, but _only_ if the iphash array
+	 * size is exactly a {power of two} minus 1.
+	 */
+	return jhash_1word(addr, connlimit_iphash_rnd) & 0xFF;
+}
+
+#ifdef DEBUG
+static const char *connection_state(const struct nf_conn *conn)
+{
+	static const char const *tcp_state[] = {
+		"none", "established", "syn_sent", "syn_recv", "fin_wait",
+		"time_wait", "close", "close_wait", "last_ack", "listen"
+	};
+	if (conn == NULL)
+		return "gone";
+	if (conn->tuplehash[0].tuple.dst.protonum == IPPROTO_TCP)
+		return tcp_state[found_ct->proto.tcp.state]);
+	return "";
+}
+#else
+static inline const char *connection_state(const struct nf_conn *conn)
+{
+	return "";
+}
+#endif
+
+static bool already_closed(const struct nf_conn *conn)
+{
+	u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum;
+	if (proto == IPPROTO_TCP)
+		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT;
+	else if (proto == IPPROTO_SCTP)
+		/* ACK_SENT the right thing? */
+		return conn->proto.sctp.state == SCTP_CONNTRACK_SHUTDOWN_ACK_SENT;
+	else
+		return 0;
+}
+
+static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
+		      u_int32_t mask, struct nf_conn *ct)
+{
+	struct nf_conntrack_tuple_hash *found;
+	struct nf_conntrack_tuple tuple;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	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 */
+	list_for_each_entry_safe(conn, tmp, hash, list) {
+		found    = nf_conntrack_find_get(&conn->tuple, ct);
+		found_ct = NULL;
+
+		if (found != NULL)
+			found_ct = nf_ct_tuplehash_to_ctrack(found);
+
+		if (found_ct != NULL &&
+		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
+		    !already_closed(found_ct))
+			/*
+			 * 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;
+
+		pr_debug(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),
+			 connection_state(found_ct));
+
+		if (found == NULL) {
+			/* this one is gone */
+			list_del(&conn->list);
+			kfree(conn);
+			continue;
+		}
+
+		if (already_closed(found_ct)) {
+
+			/*
+			 * we do not care about connections which are
+			 * closed already -> ditch it
+			 */
+			list_del(&conn->list);
+			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 */
+#ifdef DEBUG
+		pr_debug(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 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;
+	struct nf_conn *ct;
+	int connections;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (ct == NULL) {
+		printk(KERN_INFO "xt_connlimit: INVALID connection\n");
+		*hotdrop = true;
+		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;
+	}
+
+	return (connections > info->limit) ^ info->inverse;
+}
+
+static bool 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 < ARRAY_SIZE(info->data->iphash); ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
+		list_for_each_entry_safe(conn, tmp,
+		    &info->data->iphash[i], list) {
+			list_del(&conn->list);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+}
+
+static struct xt_match connlimit_reg = {
+	.name       = "connlimit",
+	.family     = AF_INET,
+	.checkentry = connlimit_check,
+	.match      = connlimit_match,
+	.matchsize  = sizeof(struct xt_connlimit_info),
+	.destroy    = connlimit_destroy,
+	.me         = THIS_MODULE,
+};
+
+static int __init xt_connlimit_init(void)
+{
+	get_random_bytes(&connlimit_iphash_rnd, sizeof(connlimit_iphash_rnd));
+	return xt_register_match(&connlimit_reg);
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_match(&connlimit_reg);
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
+MODULE_DESCRIPTION("netfilter xt_connlimit match module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");

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

* Re: xt_connlimit 20070625
  2007-06-25 11:11   ` xt_connlimit 20070625 Jan Engelhardt
@ 2007-06-25 11:41     ` Patrick McHardy
  2007-06-25 11:45       ` Patrick McHardy
                         ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 11:41 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> Subject: Add the connlimit match from POM-NG
>   

We're getting close, final round of nitpicking :)


> +#endif /* _XT_CONNLIMIT_H */
> Index: linux-2.6.22/net/netfilter/Kconfig
> ===================================================================
> --- linux-2.6.22.orig/net/netfilter/Kconfig
> +++ linux-2.6.22/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
>   

This doesn't seem right for an x_tables target. Adding IPv6 looks
trivial, the only thing that really depends on the address length
seems to be the masked comparison.

> +	---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/net/netfilter/Makefile
> ===================================================================
> --- linux-2.6.22.orig/net/netfilter/Makefile
> +++ linux-2.6.22/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/net/netfilter/xt_connlimit.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6.22/net/netfilter/xt_connlimit.c
> @@ -0,0 +1,278 @@
> +/*
> + * 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
> + *   Copyright © 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/jhash.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/random.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>
> +
> +/* 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 u_int32_t connlimit_iphash_rnd;
> +
> +static inline unsigned int connlimit_iphash(u_int32_t addr)
> +{
> +	/*
> +	 * Since iphash (in struct xt_connlimit_data) has 256 entries, we need
> +	 * to "% 256" it. "& mask" works too, but _only_ if the iphash array
> +	 * size is exactly a {power of two} minus 1.
>   
Incorrect comment, it does use &.

> +	 */
> +	return jhash_1word(addr, connlimit_iphash_rnd) & 0xFF;
> +}
> +
> +#ifdef DEBUG
> +static const char *connection_state(const struct nf_conn *conn)
>   
Please name it ct here and below as you did in the remaining code.

> +{
> +	static const char const *tcp_state[] = {
> +		"none", "established", "syn_sent", "syn_recv", "fin_wait",
> +		"time_wait", "close", "close_wait", "last_ack", "listen"
> +	};
> +	if (conn == NULL)
> +		return "gone";
>   
Only caller doesn't pass NULL.

> +	if (conn->tuplehash[0].tuple.dst.protonum == IPPROTO_TCP)
> +		return tcp_state[found_ct->proto.tcp.state]);
> +	return "";
> +}
> +#else
> +static inline const char *connection_state(const struct nf_conn *conn)
> +{
>   
You can avoid this empty function by moving the #ifdefs inside the body
of the debug version.

> +	return "";
> +}
> +#endif
> +
> +static bool already_closed(const struct nf_conn *conn)
> +{
> +	u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum;
> +	if (proto == IPPROTO_TCP)
> +		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT;
> +	else if (proto == IPPROTO_SCTP)
> +		/* ACK_SENT the right thing? */
> +		return conn->proto.sctp.state == SCTP_CONNTRACK_SHUTDOWN_ACK_SENT;
>   
Don't know. If you're not sure, better leave it out and let someone
actually able to test this add it later.

> +	else
> +		return 0;
> +}
> +
> +static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
> +		      u_int32_t mask, struct nf_conn *ct)
> +{
> +	struct nf_conntrack_tuple_hash *found;
> +	struct nf_conntrack_tuple tuple;
> +	struct xt_connlimit_conn *conn;
> +	struct xt_connlimit_conn *tmp;
> +	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 */
> +	list_for_each_entry_safe(conn, tmp, hash, list) {
> +		found    = nf_conntrack_find_get(&conn->tuple, ct);

Something for the hopefully near future: with ct_extend you could
allocate a dummy ct extension and use the destructor to remove
connections, which will avoid this expansive searching.

> +		found_ct = NULL;
> +
> +		if (found != NULL)
> +			found_ct = nf_ct_tuplehash_to_ctrack(found);
> +
> +		if (found_ct != NULL &&
>   
Can't be NULL here.

> +		    memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
>   
We have tuple comparison functions for this.

> +		    !already_closed(found_ct))
> +			/*
> +			 * 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;
> +
> +		pr_debug(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),
> +			 connection_state(found_ct));
> +
> +		if (found == NULL) {
> +			/* this one is gone */
> +			list_del(&conn->list);
> +			kfree(conn);
> +			continue;
> +		}
> +
> +		if (already_closed(found_ct)) {
> +
> +			/*
> +			 * we do not care about connections which are
> +			 * closed already -> ditch it
> +			 */
> +			list_del(&conn->list);
> +			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;
>   

See comment below about pre-increment.

> +
> +		nf_conntrack_put(&found_ct->ct_general);
You're holding the match's private lock here. Would be better
to avoid this, though I don't see how right now.

> +	}
> +
> +	if (addit) {
> +		/* save the new connection in our list */
> +#ifdef DEBUG
>   

Is that #ifdef needed?

> +		pr_debug(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 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;
> +	struct nf_conn *ct;
> +	int connections;
> +
> +	ct = nf_ct_get(skb, &ctinfo);
> +	if (ct == NULL) {
> +		printk(KERN_INFO "xt_connlimit: INVALID connection\n");
> +		*hotdrop = true;
> +		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");
>   
No printing of failed memory allocations please, its not like
its an uncommon error.

> +		*hotdrop = 1;
> +		return false;
> +	}
> +
> +	return (connections > info->limit) ^ info->inverse;
> +}
> +
> +static bool 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 < ARRAY_SIZE(info->data->iphash); ++i)
>   
I prefer post-increment in all cases but those where pre-increment
is really needed.

> +		INIT_LIST_HEAD(&info->data->iphash[i]);
> +
> +	return true;
> +}
> +
> +static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
> +{
> +	struct xt_connlimit_info *info = matchinfo;
> +	struct xt_connlimit_conn *conn;
> +	struct xt_connlimit_conn *tmp;
> +	unsigned int i;
> +
> +	nf_ct_l3proto_module_put(match->family);
> +
> +	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
> +		list_for_each_entry_safe(conn, tmp,
> +		    &info->data->iphash[i], list) {
>   
Maybe fit it on one line by using a
struct list_head *hash = info->data->iphash;

> +			list_del(&conn->list);
> +			kfree(conn);
> +		}
> +	}
> +
> +	kfree(info->data);
> +}
> +
> +static struct xt_match connlimit_reg = {
>   
__read_mostly?

> +	.name       = "connlimit",
> +	.family     = AF_INET,
> +	.checkentry = connlimit_check,
> +	.match      = connlimit_match,
> +	.matchsize  = sizeof(struct xt_connlimit_info),
> +	.destroy    = connlimit_destroy,
> +	.me         = THIS_MODULE,
> +};
> +
> +static int __init xt_connlimit_init(void)
> +{
> +	get_random_bytes(&connlimit_iphash_rnd, sizeof(connlimit_iphash_rnd));
>   

We usually initialize jhash random values at the time the first
hash is calculated because there might be not enough entropy
available during boot.

> +	return xt_register_match(&connlimit_reg);
> +}
> +
> +static void __exit xt_connlimit_exit(void)
> +{
> +	xt_unregister_match(&connlimit_reg);
> +}
> +
> +module_init(xt_connlimit_init);
> +module_exit(xt_connlimit_exit);
> +MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
> +MODULE_DESCRIPTION("netfilter xt_connlimit match module");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("ipt_connlimit");

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

* Re: xt_connlimit 20070625
  2007-06-25 11:41     ` Patrick McHardy
@ 2007-06-25 11:45       ` Patrick McHardy
  2007-06-25 12:36       ` Jan Engelhardt
  2007-06-28 19:23       ` Jan Engelhardt
  2 siblings, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 11:45 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Patrick McHardy wrote:
> Jan Engelhardt wrote:
>> +        found_ct = NULL;
>> +
>> +        if (found != NULL)
>> +            found_ct = nf_ct_tuplehash_to_ctrack(found);
>> +
>> +        if (found_ct != NULL &&
>>   
> Can't be NULL here.
>

OK it can. But you should probably just continue for found == NULL above.

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

* Re: xt_connlimit 20070625
  2007-06-25 11:41     ` Patrick McHardy
  2007-06-25 11:45       ` Patrick McHardy
@ 2007-06-25 12:36       ` Jan Engelhardt
  2007-06-25 12:43         ` Patrick McHardy
  2007-06-28 19:23       ` Jan Engelhardt
  2 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-25 12:36 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 25 2007 13:41, Patrick McHardy wrote:
>> +config NETFILTER_XT_MATCH_CONNLIMIT
>> +	tristate '"connlimit" match support"'
>> +	depends on NETFILTER_XTABLES && NF_CONNTRACK_IPV4
>>   
>
> This doesn't seem right for an x_tables target. Adding IPv6 looks
> trivial, the only thing that really depends on the address length
> seems to be the masked comparison.

Indeed - leftovers from its POM ascendant. But do I have to add ipv6 now?

(It would also be quite a duplicate of count_them() and connlimit_match() -
is there a better way without too many if()s?)

>> +static inline unsigned int connlimit_iphash(u_int32_t addr)
>> +{
>> +	/*
>> +	 * Since iphash (in struct xt_connlimit_data) has 256 entries, we need
>> +	 * to "% 256" it. "& mask" works too, but _only_ if the iphash array
>> +	 * size is exactly a {power of two} minus 1.
>>   
> Incorrect comment, it does use &.

I reword it.

>> +		found_ct = NULL;
>> +
>> +		if (found != NULL)
>> +			found_ct = nf_ct_tuplehash_to_ctrack(found);
>> +
>> +		if (found_ct != NULL &&
>>   
> Can't be NULL here.
> OK it can. But you should probably just continue for found == NULL above.

I doubt I can do that. Is is part of the destruction.
A little more below, there is /* this one is gone */
Also, there is the pr_debug() call.

>> +		if (found == NULL) {
>> +			/* this one is gone */
>> +			list_del(&conn->list);
>> +			kfree(conn);
>> +			continue;
>> +		}

>> +		if ((addr & mask) == (conn->tuple.src.u3.ip & mask))
>> +			/* same source network -> be counted! */
>> +			++matches;
>>   
>
> See comment below about pre-increment.
>
>> +
>> +		nf_conntrack_put(&found_ct->ct_general);
> You're holding the match's private lock here. Would be better
> to avoid this, though I don't see how right now.
>
>> +	}
>> +
>> +	if (addit) {
>> +		/* save the new connection in our list */
>> +#ifdef DEBUG
>>   
>
> Is that #ifdef needed?

No, removed.

>> +static bool 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 < ARRAY_SIZE(info->data->iphash); ++i)
>>   
> I prefer post-increment in all cases but those where pre-increment
> is really needed.

Can I ignore that?

>> +static struct xt_match connlimit_reg = {
>>   
> __read_mostly?

No other xt_ modules have it, but I can add it.

>> +static int __init xt_connlimit_init(void)
>> +{
>> +	get_random_bytes(&connlimit_iphash_rnd, sizeof(connlimit_iphash_rnd));
>>   
>
> We usually initialize jhash random values at the time the first
> hash is calculated because there might be not enough entropy
> available during boot.

The routing code (where Eric pointed me to) does it on boot-up.
I have xt_connlimit loaded at boot (in runlevel B), there does not seem to be a
problem.


	Jan
-- 

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

* Re: xt_connlimit 20070625
  2007-06-25 12:36       ` Jan Engelhardt
@ 2007-06-25 12:43         ` Patrick McHardy
  2007-06-25 14:41           ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 12:43 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 25 2007 13:41, Patrick McHardy wrote:
>   
>>> +config NETFILTER_XT_MATCH_CONNLIMIT
>>> +	tristate '"connlimit" match support"'
>>> +	depends on NETFILTER_XTABLES && NF_CONNTRACK_IPV4
>>>   
>>>       
>> This doesn't seem right for an x_tables target. Adding IPv6 looks
>> trivial, the only thing that really depends on the address length
>> seems to be the masked comparison.
>>     
>
> Indeed - leftovers from its POM ascendant. But do I have to add ipv6 now?
>
> (It would also be quite a duplicate of count_them() and connlimit_match() -
> is there a better way without too many if()s?)
>   
I'd use a conditional in count_them and add an connlimit_match6 function.


>   
>>> +static inline unsigned int connlimit_iphash(u_int32_t addr)
>>> +{
>>> +	/*
>>> +	 * Since iphash (in struct xt_connlimit_data) has 256 entries, we need
>>> +	 * to "% 256" it. "& mask" works too, but _only_ if the iphash array
>>> +	 * size is exactly a {power of two} minus 1.
>>>   
>>>       
>> Incorrect comment, it does use &.
>>     
>
> I reword it.
>
>
>   
>> Can't be NULL here.
>> OK it can. But you should probably just continue for found == NULL above.
>>     
>
> I doubt I can do that. Is is part of the destruction.
> A little more below, there is /* this one is gone */
>   
Right.

>>> +	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
>>>   
>>>       
>> I prefer post-increment in all cases but those where pre-increment
>> is really needed.
>>     
>
> Can I ignore that?
>   

Yes.
>   
>>> +static struct xt_match connlimit_reg = {
>>>   
>>>       
>> __read_mostly?
>>     
>
> No other xt_ modules have it, but I can add it.
>   

They don't? I'll add it to the others.
>   
>>> +static int __init xt_connlimit_init(void)
>>> +{
>>> +	get_random_bytes(&connlimit_iphash_rnd, sizeof(connlimit_iphash_rnd));
>>>   
>>>       
>> We usually initialize jhash random values at the time the first
>> hash is calculated because there might be not enough entropy
>> available during boot.
>>     
>
> The routing code (where Eric pointed me to) does it on boot-up.
>   

It does not use get_random_bytes during boot and reinitizalizes the
random value after a flush.

> I have xt_connlimit loaded at boot (in runlevel B), there does not seem to be a
> problem.
>   

Think of a diskless system.

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

* Re: xt_connlimit 20070625
  2007-06-25 12:43         ` Patrick McHardy
@ 2007-06-25 14:41           ` Jan Engelhardt
  2007-06-25 14:46             ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-25 14:41 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 25 2007 14:43, Patrick McHardy wrote:
>>
>> Indeed - leftovers from its POM ascendant. But do I have to add ipv6 now?
>>
>> (It would also be quite a duplicate of count_them() and connlimit_match() -
>> is there a better way without too many if()s?)
>>   
> I'd use a conditional in count_them and add an connlimit_match6 function.

Thing is..
static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
                      u_int32_t mask, struct nf_conn *ct)

I'd need a u_int128_t for IPv6. (Or a clever idea.)
Suggestions for elegant solution?


>> > Incorrect comment, it does use &.
>> I reword it.

        /*
         * The hash needs to be folded down (using the % modulo operator) to
         * actually yield a value that is greater or equal than 0 and smaller
         * than ARRAY_SIZE(iphash). If ARRAY_SIZE(iphash) is exactly a power of
         * two, the (most likely faster) "& mask" operation can also be used,
         * where mask is the size minus one.
         */

Does that read better?

>> > __read_mostly?
>> No other xt_ modules have it, but I can add it.
> They don't? I'll add it to the others.

Not even ipt_ or ip6t_ modules have it.

>> The routing code (where Eric pointed me to) does it on boot-up.
>
> It does not use get_random_bytes during boot and reinitizalizes the
> random value after a flush.

So what should xt_connlimit do? Grab the rnd per xt_connlimit_data
(= per rule)?

> Think of a diskless system.

I would not add the disk subsystem to the entropy pool.


	Jan
-- 

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

* Re: xt_connlimit 20070625
  2007-06-25 14:41           ` Jan Engelhardt
@ 2007-06-25 14:46             ` Patrick McHardy
  2007-06-25 15:01               ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 14:46 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 25 2007 14:43, Patrick McHardy wrote:
> 
>>>Indeed - leftovers from its POM ascendant. But do I have to add ipv6 now?
>>>
>>>(It would also be quite a duplicate of count_them() and connlimit_match() -
>>>is there a better way without too many if()s?)
>>>  
>>
>>I'd use a conditional in count_them and add an connlimit_match6 function.
> 
> 
> Thing is..
> static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
>                       u_int32_t mask, struct nf_conn *ct)
> 
> I'd need a u_int128_t for IPv6. (Or a clever idea.)
> Suggestions for elegant solution?

struct xt_conntrack_address
>>>>Incorrect comment, it does use &.
>>>
>>>I reword it.
> 
> 
>         /*
>          * The hash needs to be folded down (using the % modulo operator) to
>          * actually yield a value that is greater or equal than 0 and smaller
>          * than ARRAY_SIZE(iphash). If ARRAY_SIZE(iphash) is exactly a power of
>          * two, the (most likely faster) "& mask" operation can also be used,
>          * where mask is the size minus one.
>          */
> 
> Does that read better?

That falls into the "i++ /* increment i */" category IMO.
If someone doesn't know how basic operators work he won't
learn it here either .. so I'd just remove it.


> 
> 
>>>>__read_mostly?
>>>
>>>No other xt_ modules have it, but I can add it.
>>
>>They don't? I'll add it to the others.
> 
> 
> Not even ipt_ or ip6t_ modules have it.

I've added it to all matches and tarets.

> 
> 
>>>The routing code (where Eric pointed me to) does it on boot-up.
>>
>>It does not use get_random_bytes during boot and reinitizalizes the
>>random value after a flush.
> 
> 
> So what should xt_connlimit do? Grab the rnd per xt_connlimit_data
> (= per rule)?

That doesn't seem necessary, just do what others do:

static int hash_rnd_initted;
static int hash_rnd;

static int hash(...)
{
	if (unlikely(!hash_rnd_initted)) {
		get_random_bytes(&hash_rnd, 4);
		hash_rnd_initted = 1;
	}

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

* Re: xt_connlimit 20070625
  2007-06-25 14:46             ` Patrick McHardy
@ 2007-06-25 15:01               ` Jan Engelhardt
  2007-06-25 15:05                 ` Patrick McHardy
  2007-06-25 15:05                 ` Patrick McHardy
  0 siblings, 2 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-25 15:01 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 25 2007 16:46, Patrick McHardy wrote:
>> Thing is..
>> static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
>>                       u_int32_t mask, struct nf_conn *ct)
>> 
>> I'd need a u_int128_t for IPv6. (Or a clever idea.)
>> Suggestions for elegant solution?
>
>struct xt_conntrack_address

Is that something I yet have to invent, or is it part of some other git tree?

>static int hash(...)
>{
>	if (unlikely(!hash_rnd_initted)) {
>		get_random_bytes(&hash_rnd, 4);
>		hash_rnd_initted = 1;
>	}
>
Looks expensive, though.


	Jan
-- 

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

* Re: xt_connlimit 20070625
  2007-06-25 15:01               ` Jan Engelhardt
@ 2007-06-25 15:05                 ` Patrick McHardy
  2007-06-25 15:05                 ` Patrick McHardy
  1 sibling, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 15:05 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 25 2007 16:46, Patrick McHardy wrote:
> 
>>>Thing is..
>>>static int count_them(struct xt_connlimit_data *data, u_int32_t addr,
>>>                      u_int32_t mask, struct nf_conn *ct)
>>>
>>>I'd need a u_int128_t for IPv6. (Or a clever idea.)
>>>Suggestions for elegant solution?
>>
>>struct xt_conntrack_address
> 
> 
> Is that something I yet have to invent, or is it part of some other git tree?

nf_conntrack_address. Should be somewhere under include/net I think.

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

* Re: xt_connlimit 20070625
  2007-06-25 15:01               ` Jan Engelhardt
  2007-06-25 15:05                 ` Patrick McHardy
@ 2007-06-25 15:05                 ` Patrick McHardy
  2007-06-25 15:14                   ` Jan Engelhardt
  1 sibling, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 15:05 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 25 2007 16:46, Patrick McHardy wrote:
>>static int hash(...)
>>{
>>	if (unlikely(!hash_rnd_initted)) {
>>		get_random_bytes(&hash_rnd, 4);
>>		hash_rnd_initted = 1;
>>	}
>>
> 
> Looks expensive, though.


Its executed once.

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

* Re: xt_connlimit 20070625
  2007-06-25 15:05                 ` Patrick McHardy
@ 2007-06-25 15:14                   ` Jan Engelhardt
  0 siblings, 0 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-25 15:14 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 25 2007 17:05, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> On Jun 25 2007 16:46, Patrick McHardy wrote:
>>>static int hash(...)
>>>{
>>>	if (unlikely(!hash_rnd_initted)) {
>>>		get_random_bytes(&hash_rnd, 4);
>>>		hash_rnd_initted = 1;
>>>	}
>> 
>> Looks expensive, though.
>
>Its executed once.

I mean the if().


	Jan
-- 

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

* Re: xt_connlimit 20070620_2
  2007-06-20  9:39 xt_connlimit 20070620_2 Jan Engelhardt
  2007-06-20 17:21 ` Andrew Beverley
  2007-06-22 11:14 ` Patrick McHardy
@ 2007-06-25 18:51 ` Patrick McHardy
  2 siblings, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-25 18:51 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Andrew Beverley

Jan Engelhardt wrote:
> +/* we will save the tuples of all connections we care about */
> +struct xt_connlimit_conn {
> +	struct list_head list;
> +	struct nf_conntrack_tuple tuple;
>   

I just applied Yasuyuki's ct_extend patches, what you could do
now is to allocate a ct_extend area where you'll put the list_heads
and a pointer to the conntrack, then you can use the destroy
callback to remove it from the list again to keep it clean without
much effort. Should also simplify the rest memory handling a bit.

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

* Re: xt_connlimit 20070625
  2007-06-25 11:41     ` Patrick McHardy
  2007-06-25 11:45       ` Patrick McHardy
  2007-06-25 12:36       ` Jan Engelhardt
@ 2007-06-28 19:23       ` Jan Engelhardt
  2007-06-28 19:27         ` Patrick McHardy
  2 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-28 19:23 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 25 2007 13:41, Patrick McHardy wrote:

>> +	/* check the saved connections */
>> +	list_for_each_entry_safe(conn, tmp, hash, list) {
>> +		found    = nf_conntrack_find_get(&conn->tuple, ct);
>
> Something for the hopefully near future: with ct_extend you could
> allocate a dummy ct extension and use the destructor to remove
> connections, which will avoid this expansive searching.

There's just one problem... judging from example code
(`a=96ef23541211a66adb0504d1451ee318965ac525; git diff $a^..$a` in your
nf-2.6.23.git tree), the struct nf_ct_ext_type->cleanup() handler is only
called with "struct nf_conn *", but that does not help at all. xt_connlimit
needs the "struct xt_connlimit_data", which is only accessible through
xt_match's void *matchinfo (per-match private info).
Ideas?



Thanks,
	Jan
-- 

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

* Re: xt_connlimit 20070625
  2007-06-28 19:23       ` Jan Engelhardt
@ 2007-06-28 19:27         ` Patrick McHardy
  2007-06-28 19:31           ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-28 19:27 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 25 2007 13:41, Patrick McHardy wrote:
> 
> 
>>>+	/* check the saved connections */
>>>+	list_for_each_entry_safe(conn, tmp, hash, list) {
>>>+		found    = nf_conntrack_find_get(&conn->tuple, ct);
>>
>>Something for the hopefully near future: with ct_extend you could
>>allocate a dummy ct extension and use the destructor to remove
>>connections, which will avoid this expansive searching.
> 
> 
> There's just one problem... judging from example code
> (`a=96ef23541211a66adb0504d1451ee318965ac525; git diff $a^..$a` in your
> nf-2.6.23.git tree), the struct nf_ct_ext_type->cleanup() handler is only
> called with "struct nf_conn *", but that does not help at all. xt_connlimit
> needs the "struct xt_connlimit_data", which is only accessible through
> xt_match's void *matchinfo (per-match private info).
> Ideas?


I though you would store the struct xt_connlimit_conn in the
ct_extend area. The only reason I can think of why you'd need
the xt_connlimit_data is for the lock. So you could make the
lock global (which I think is fine) or add a pointer to the
data you need.

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

* Re: xt_connlimit 20070625
  2007-06-28 19:27         ` Patrick McHardy
@ 2007-06-28 19:31           ` Jan Engelhardt
  2007-06-28 19:33             ` Patrick McHardy
  2007-06-28 19:48             ` Patrick McHardy
  0 siblings, 2 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-28 19:31 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 28 2007 21:27, Patrick McHardy wrote:
>
>I though you would store the struct xt_connlimit_conn in the
>ct_extend area.

Ah, that was the big hint. Thank you.
I'll just put the pointer to xt_connlimit_data there. Just as easy.

>The only reason I can think of why you'd need
>the xt_connlimit_data is for the lock. So you could make the
>lock global (which I think is fine) or add a pointer to the
>data you need.

I'd hate to introduce such a lock, because it would make the _whole_ match
"singleprocessor-sticky".


	Jan
-- 

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

* Re: xt_connlimit 20070625
  2007-06-28 19:31           ` Jan Engelhardt
@ 2007-06-28 19:33             ` Patrick McHardy
  2007-06-28 19:48             ` Patrick McHardy
  1 sibling, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-06-28 19:33 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 28 2007 21:27, Patrick McHardy wrote:
> 
>>I though you would store the struct xt_connlimit_conn in the
>>ct_extend area.
> 
> 
> Ah, that was the big hint. Thank you.
> I'll just put the pointer to xt_connlimit_data there. Just as easy.
> 
> 
>>The only reason I can think of why you'd need
>>the xt_connlimit_data is for the lock. So you could make the
>>lock global (which I think is fine) or add a pointer to the
>>data you need.
> 
> 
> I'd hate to introduce such a lock, because it would make the _whole_ match
> "singleprocessor-sticky".


Use a rwlock or something lockless if you're worried about that.
But that seems like a premature optimization, the time a single
match needs should be very small anyway so its unlikely that
we'll see contention.

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

* Re: xt_connlimit 20070625
  2007-06-28 19:31           ` Jan Engelhardt
  2007-06-28 19:33             ` Patrick McHardy
@ 2007-06-28 19:48             ` Patrick McHardy
  2007-06-28 19:51               ` xt_connlimit 20070628 Jan Engelhardt
  1 sibling, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-28 19:48 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 28 2007 21:27, Patrick McHardy wrote:
> 
>>I though you would store the struct xt_connlimit_conn in the
>>ct_extend area.
> 
> 
> Ah, that was the big hint. Thank you.
> I'll just put the pointer to xt_connlimit_data there. Just as easy.


Actually my entire suggestion may be broken. We can't
allocate extensions once the conntrack is confirmed.
So this would only work for new connections.

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

* xt_connlimit 20070628
  2007-06-28 19:48             ` Patrick McHardy
@ 2007-06-28 19:51               ` Jan Engelhardt
  2007-06-28 19:55                 ` xt_connlimit 20070628 kernel Jan Engelhardt
  2007-06-28 20:08                 ` xt_connlimit 20070628 Jan Engelhardt
  0 siblings, 2 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-28 19:51 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

>Jan Engelhardt wrote:
>> On Jun 28 2007 21:27, Patrick McHardy wrote:
>> 
>>>I though you would store the struct xt_connlimit_conn in the
>>>ct_extend area.
>> 
>> 
>> Ah, that was the big hint. Thank you.
>> I'll just put the pointer to xt_connlimit_data there. Just as easy.
>
>Actually my entire suggestion may be broken. We can't
>allocate extensions once the conntrack is confirmed.
>So this would only work for new connections.

Yes. And the actual connlimit rule may be inserted after the connection 
is already past --ctstate NEW.
So I submit today's xt_connlimit (with IPv6 yay!!)


	Jan
-- 

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

* xt_connlimit 20070628 kernel
  2007-06-28 19:51               ` xt_connlimit 20070628 Jan Engelhardt
@ 2007-06-28 19:55                 ` Jan Engelhardt
  2007-06-29 11:27                   ` Patrick McHardy
  2007-06-28 20:08                 ` xt_connlimit 20070628 Jan Engelhardt
  1 sibling, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-28 19:55 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

[-- Attachment #1: Type: TEXT/PLAIN, Size: 13262 bytes --]


Add the xt_connlimit match

ipt_connlimit has been sitting in POM-NG for a long time.
Here is a new shiny xt_connlimit with:

 * xtables'ified and all that
 * will request the layer3 module
   (previously it hotdropped every packet when it was not loaded)
 * fixed: there was a deadlock in case of an OOM condition
 * support for any layer4 protocol
 * using jhash, as suggested by Eric Dumazet
 * ipv6 support


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

---
 include/linux/netfilter/xt_connlimit.h |   17 +
 net/netfilter/Kconfig                  |    7 
 net/netfilter/Makefile                 |    1 
 net/netfilter/xt_connlimit.c           |  349 +++++++++++++++++++++++++++++++++
 4 files changed, 374 insertions(+)

Index: linux-2.6.22/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,17 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	union {
+		u_int32_t v4_mask;
+		u_int32_t v6_mask[4];
+	};
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22.orig/net/netfilter/Kconfig
+++ linux-2.6.22/net/netfilter/Kconfig
@@ -423,6 +423,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
+	---help---
+	  This match allows you to match against the number of parallel
+	  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/net/netfilter/Makefile
===================================================================
--- linux-2.6.22.orig/net/netfilter/Makefile
+++ linux-2.6.22/net/netfilter/Makefile
@@ -53,6 +53,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/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22/net/netfilter/xt_connlimit.c
@@ -0,0 +1,349 @@
+/*
+ * 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
+ *   Copyright © 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/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/random.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>
+
+/* 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 u_int32_t connlimit_rnd;
+static bool connlimit_rnd_inited;
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	if (unlikely(!connlimit_rnd_inited)) {
+		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+		connlimit_rnd_inited = true;
+	}
+	return jhash_1word(addr, connlimit_rnd) & 0xFF;
+}
+
+static inline unsigned int
+connlimit_iphash6(const union nf_conntrack_address *addr,
+                  const union nf_conntrack_address *mask)
+{
+	union nf_conntrack_address res;
+	unsigned int i;
+
+	if (unlikely(!connlimit_rnd_inited)) {
+		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+		connlimit_rnd_inited = true;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
+		res.ip6[i] = addr->ip6[i] & mask->ip6[i];
+
+	return jhash2(res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
+}
+
+static const char *ct_state(const struct nf_conn *conn)
+{
+#ifdef DEBUG
+	static const char const *tcp_state[] = {
+		"none", "established", "syn_sent", "syn_recv", "fin_wait",
+		"time_wait", "close", "close_wait", "last_ack", "listen"
+	};
+	if (conn == NULL)
+		return "gone";
+	if (conn->tuplehash[0].tuple.dst.protonum == IPPROTO_TCP)
+		return tcp_state[found_ct->proto.tcp.state]);
+#endif
+	return "";
+}
+
+static inline bool already_closed(const struct nf_conn *conn)
+{
+	u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum;
+	if (proto == IPPROTO_TCP)
+		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT;
+	else
+		return 0;
+}
+
+static inline void
+connlimit_debug(const struct xt_connlimit_conn *conn,
+                const struct nf_conn *found_ct,
+                const union nf_conntrack_address *addr,
+                const union nf_conntrack_address *mask,
+                unsigned int family)
+{
+#define U3_NIP6(x) NIP6(*(const struct in6_addr *)&(x))
+	if (family == AF_INET6)
+		pr_debug(KERN_WARNING "xt_connlimit [%u]: src=" NIP6_FMT ":%u "
+		         "dst=" NIP6_FMT ":%u %s\n",
+		         connlimit_iphash6(addr, mask),
+		         U3_NIP6(conn->tuple.src.u3),
+		         ntohs(conn->tuple.src.u.tcp.port),
+		         U3_NIP6(conn->tuple.dst.u3),
+		         ntohs(conn->tuple.dst.u.tcp.port),
+		         ct_state(found_ct));
+	else
+		pr_debug(KERN_WARNING "xt_connlimit [%u]: src=%u.%u.%u.%u:%u "
+		         "dst=%u.%u.%u.%u:%u %s\n",
+		         connlimit_iphash(addr->ip & mask->ip),
+		         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),
+			 ct_state(found_ct));
+#undef U3_NIP6
+}
+
+static inline unsigned int
+same_source_net(const union nf_conntrack_address *addr,
+                const union nf_conntrack_address *mask,
+                const union nf_conntrack_address *u3, unsigned int family)
+{
+	if (family == AF_INET) {
+		return (addr->ip & mask->ip) == (u3->ip & mask->ip);
+	} else {
+		union nf_conntrack_address lh, rh;
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
+			lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
+			rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
+		}
+
+		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
+	}
+}
+
+static int count_them(struct xt_connlimit_data *data,
+                      const union nf_conntrack_address *addr,
+		      const union nf_conntrack_address *mask,
+		      struct nf_conn *ct, unsigned int family)
+{
+	struct nf_conntrack_tuple_hash *found;
+	struct nf_conntrack_tuple tuple;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	struct nf_conn *found_ct;
+	struct list_head *hash;
+	bool addit = true;
+	int matches = 0;
+
+	tuple = ct->tuplehash[0].tuple;
+
+	if (family == AF_INET6)
+		hash = &data->iphash[connlimit_iphash6(addr, mask)];
+	else
+		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
+
+	/* check the saved connections */
+	list_for_each_entry_safe(conn, tmp, hash, list) {
+		found    = nf_conntrack_find_get(&conn->tuple, ct);
+		found_ct = NULL;
+
+		if (found != NULL)
+			found_ct = nf_ct_tuplehash_to_ctrack(found);
+
+		if (found_ct != NULL &&
+		    nf_ct_tuple_equal(&conn->tuple, &tuple) &&
+		    !already_closed(found_ct))
+			/*
+			 * 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;
+
+		connlimit_debug(conn, found_ct, addr, mask, family);
+
+		if (found == NULL) {
+			/* this one is gone */
+			list_del(&conn->list);
+			kfree(conn);
+			continue;
+		}
+
+		if (already_closed(found_ct)) {
+			/*
+			 * we do not care about connections which are
+			 * closed already -> ditch it
+			 */
+			list_del(&conn->list);
+			kfree(conn);
+			nf_conntrack_put(&found_ct->ct_general);
+			continue;
+		}
+
+		if (same_source_net(addr, mask, &conn->tuple.src.u3, family))
+			/* same source network -> be counted! */
+			++matches;
+
+		nf_conntrack_put(&found_ct->ct_general);
+	}
+
+	if (addit) {
+		/* save the new connection in our list */
+		connlimit_debug(conn, found_ct, addr, mask, family);
+		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 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;
+	union nf_conntrack_address addr, mask;
+	enum ip_conntrack_info ctinfo;
+	struct nf_conn *ct;
+	int connections;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (ct == NULL) {
+		printk(KERN_INFO "xt_connlimit: INVALID connection\n");
+		*hotdrop = true;
+		return false;
+	}
+
+	if (match->family == AF_INET6) {
+		const struct ipv6hdr *iph = ipv6_hdr(skb);
+		memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr));
+		memcpy(&mask.ip6, info->v6_mask, sizeof(info->v6_mask));
+	} else {
+		const struct iphdr *iph = ip_hdr(skb);
+		addr.ip = iph->saddr;
+		mask.ip = info->v4_mask;
+	}
+
+	spin_lock_bh(&info->data->lock);
+	connections = count_them(info->data, &addr, &mask, ct, match->family);
+	spin_unlock_bh(&info->data->lock);
+
+	if (connections < 0) {
+		/* kmalloc failed, drop it entirely */
+		*hotdrop = 1;
+		return false;
+	}
+
+	return (connections > info->limit) ^ info->inverse;
+}
+
+static bool 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 < ARRAY_SIZE(info->data->iphash); ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	struct list_head *hash = info->data->iphash;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
+		list_for_each_entry_safe(conn, tmp, &hash[i], list) {
+			list_del(&conn->list);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+}
+
+static struct xt_match connlimit_reg[] __read_mostly = {
+	{
+		.name       = "connlimit",
+		.family     = AF_INET,
+		.checkentry = connlimit_check,
+		.match      = connlimit_match,
+		.matchsize  = sizeof(struct xt_connlimit_info),
+		.destroy    = connlimit_destroy,
+		.me         = THIS_MODULE,
+	},
+	{
+		.name       = "connlimit",
+		.family     = AF_INET6,
+		.checkentry = connlimit_check,
+		.match      = connlimit_match,
+		.matchsize  = sizeof(struct xt_connlimit_info),
+		.destroy    = connlimit_destroy,
+		.me         = THIS_MODULE,
+	},
+};
+
+static int __init xt_connlimit_init(void)
+{
+	return xt_register_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg));
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg));
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
+MODULE_DESCRIPTION("netfilter xt_connlimit match module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");
+MODULE_ALIAS("ip6t_connlimit");

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

* Re: xt_connlimit 20070628
  2007-06-28 19:51               ` xt_connlimit 20070628 Jan Engelhardt
  2007-06-28 19:55                 ` xt_connlimit 20070628 kernel Jan Engelhardt
@ 2007-06-28 20:08                 ` Jan Engelhardt
  1 sibling, 0 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-06-28 20:08 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


Subject: Add connlimit to iptables.

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

---

 symlink libip6t_connlimit.man -> libipt_connlimit.man

 extensions/Makefile                    |    4 
 extensions/libip6t_connlimit.c         |  151 +++++++++++++++++++++++++++++++++
 extensions/libipt_connlimit.c          |  128 +++++++++++++++++++++++++++
 extensions/libipt_connlimit.man        |   27 +++++
 include/linux/netfilter/xt_connlimit.h |   17 +++
 5 files changed, 325 insertions(+), 2 deletions(-)

Index: iptables/extensions/Makefile
===================================================================
--- iptables.orig/extensions/Makefile
+++ iptables/extensions/Makefile
@@ -5,8 +5,8 @@
 # header files are present in the include/linux directory of this iptables
 # package (HW)
 #
-PF_EXT_SLIB:=ah addrtype comment connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype policy realm sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NFQUEUE NOTRACK REDIRECT REJECT SAME SNAT TCPMSS TOS TTL TRACE ULOG
-PF6_EXT_SLIB:=connmark eui64 hl icmp6 length limit mac mark multiport owner physdev policy standard state tcp udp CONNMARK HL LOG NFQUEUE MARK TCPMSS TRACE
+PF_EXT_SLIB:=ah addrtype comment connlimit connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype policy realm sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NFQUEUE NOTRACK REDIRECT REJECT SAME SNAT TCPMSS TOS TTL TRACE ULOG
+PF6_EXT_SLIB:=connlimit connmark eui64 hl icmp6 length limit mac mark multiport owner physdev policy standard state tcp udp CONNMARK HL LOG NFQUEUE MARK TCPMSS TRACE
 
 ifeq ($(DO_SELINUX), 1)
 PF_EXT_SE_SLIB:=SECMARK CONNSECMARK
Index: iptables/extensions/libip6t_connlimit.c
===================================================================
--- /dev/null
+++ iptables/extensions/libip6t_connlimit.c
@@ -0,0 +1,151 @@
+/* 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 <ip6tables.h>
+#include "../include/linux/netfilter/xt_connlimit.h"
+
+/* Function which prints out usage message. */
+static void connlimit_help(void)
+{
+	printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n        match if the number of existing "
+"                               connections is (not) above n\n"
+"    --connlimit-mask n         group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static const struct option connlimit_opts[] = {
+	{"connlimit-above", 1, NULL, 1},
+	{"connlimit-mask",  1, NULL, 2},
+	{NULL},
+};
+
+static void connlimit_init(struct ip6t_entry_match *match, unsigned int *nfc)
+{
+	struct xt_connlimit_info *info = (void *)match->data;
+	info->v6_mask[0] =
+	info->v6_mask[1] =
+	info->v6_mask[2] =
+	info->v6_mask[3] = 0xFFFFFFFF;
+}
+
+static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len)
+{
+	if (prefix_len == 0) {
+		mask[0] = mask[1] = mask[2] = mask[3] = 0;
+	} else if (prefix_len <= 32) {
+		mask[0] <<= 32 - prefix_len;
+		mask[1] = mask[2] = mask[3] = 0;
+	} else if (prefix_len <= 64) {
+		mask[1] <<= 32 - (prefix_len - 32);
+		mask[2] = mask[3] = 0;
+	} else if (prefix_len <= 96) {
+		mask[2] <<= 32 - (prefix_len - 64);
+		mask[3] = 0;
+	} else if (prefix_len <= 128) {
+		mask[3] <<= 32 - (prefix_len - 96);
+	}
+	mask[0] = htonl(mask[0]);
+	mask[1] = htonl(mask[1]);
+	mask[2] = htonl(mask[2]);
+	mask[3] = htonl(mask[3]);
+}
+
+static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
+                           const struct ip6t_entry *entry,
+                           unsigned int *nfcache,
+                           struct ip6t_entry_match **match)
+{
+	struct xt_connlimit_info *info = (void *)(*match)->data;
+	char *err;
+	int i;
+
+	if (*flags & c)
+		exit_error(PARAMETER_PROBLEM,
+		           "--connlimit-above and/or --connlimit-mask may "
+			   "only be given once");
+
+	switch (c) {
+	case 1:
+		check_inverse(optarg, &invert, &optind, 0);
+		info->limit   = strtoul(argv[optind-1], NULL, 0);
+		info->inverse = invert;
+		break;
+	case 2:
+		i = strtoul(argv[optind-1], &err, 0);
+		if (i > 128 || *err != '\0')
+			exit_error(PARAMETER_PROBLEM,
+				"--connlimit-mask must be between 0 and 128");
+		prefix_to_netmask(info->v6_mask, i);
+		break;
+	default:
+		return 0;
+	}
+
+	*flags |= c;
+	return 1;
+}
+
+/* Final check */
+static void connlimit_check(unsigned int flags)
+{
+	if (!(flags & 1))
+		exit_error(PARAMETER_PROBLEM,
+		           "You must specify \"--connlimit-above\"");
+}
+
+static unsigned int count_bits(const u_int32_t *mask)
+{
+	unsigned int bits = 0, i;
+	u_int32_t tmp[4];
+
+	for (i = 0; i < 4; ++i)
+		for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
+			++bits;
+	return 128 - bits;
+}
+
+/* Prints out the matchinfo. */
+static void connlimit_print(const struct ip6t_ip6 *ip,
+                            const struct ip6t_entry_match *match, int numeric)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+
+	printf("#conn/%u %s %u ", count_bits(info->v6_mask),
+	       info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void connlimit_save(const struct ip6t_ip6 *ip,
+                           const struct ip6t_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->v6_mask));
+}
+
+static struct ip6tables_match connlimit_reg = {
+	.name          = "connlimit",
+	.version       = IPTABLES_VERSION,
+	.size          = IP6T_ALIGN(sizeof(struct xt_connlimit_info)),
+	.userspacesize = offsetof(struct xt_connlimit_info, data),
+	.help          = connlimit_help,
+	.init          = connlimit_init,
+	.parse         = connlimit_parse,
+	.final_check   = connlimit_check,
+	.print         = connlimit_print,
+	.save          = connlimit_save,
+	.extra_opts    = connlimit_opts,
+};
+
+static __attribute__((constructor)) void libipt_connlimit_init(void)
+{
+	register_match6(&connlimit_reg);
+}
Index: iptables/extensions/libipt_connlimit.c
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.c
@@ -0,0 +1,128 @@
+/* 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 "../include/linux/netfilter/xt_connlimit.h"
+
+/* Function which prints out usage message. */
+static void connlimit_help(void)
+{
+	printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n        match if the number of existing "
+"                               connections is (not) above n\n"
+"    --connlimit-mask n         group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static const struct option connlimit_opts[] = {
+	{"connlimit-above", 1, NULL, 1},
+	{"connlimit-mask",  1, NULL, 2},
+	{NULL},
+};
+
+static void connlimit_init(struct ipt_entry_match *match, unsigned int *nfc)
+{
+	struct xt_connlimit_info *info = (void *)match->data;
+	info->v4_mask = 0xFFFFFFFF;
+}
+
+static int connlimit_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;
+	char *err;
+	int i;
+
+	if (*flags & c)
+		exit_error(PARAMETER_PROBLEM,
+		           "--connlimit-above and/or --connlimit-mask may "
+			   "only be given once");
+
+	switch (c) {
+	case 1:
+		check_inverse(optarg, &invert, &optind, 0);
+		info->limit   = strtoul(argv[optind-1], NULL, 0);
+		info->inverse = invert;
+		break;
+	case 2:
+		i = strtoul(argv[optind-1], &err, 0);
+		if (i > 32 || *err != '\0')
+			exit_error(PARAMETER_PROBLEM,
+				"--connlimit-mask must be between 0 and 32");
+		if (i == 0)
+			info->v4_mask = 0;
+		else
+			info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
+		break;
+	default:
+		return 0;
+	}
+
+	*flags |= c;
+	return 1;
+}
+
+/* Final check */
+static void connlimit_check(unsigned int flags)
+{
+	if (!(flags & 1))
+		exit_error(PARAMETER_PROBLEM,
+		           "You must specify \"--connlimit-above\"");
+}
+
+static unsigned int count_bits(u_int32_t mask)
+{
+	unsigned int bits = 0;
+
+	for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
+		++bits;
+
+	return 32 - bits;
+}
+
+/* Prints out the matchinfo. */
+static void connlimit_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/%u %s %u ", count_bits(info->v4_mask),
+	       info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void connlimit_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->v4_mask));
+}
+
+static struct iptables_match connlimit_reg = {
+	.name          = "connlimit",
+	.version       = IPTABLES_VERSION,
+	.size          = IPT_ALIGN(sizeof(struct xt_connlimit_info)),
+	.userspacesize = offsetof(struct xt_connlimit_info, data),
+	.help          = connlimit_help,
+	.init          = connlimit_init,
+	.parse         = connlimit_parse,
+	.final_check   = connlimit_check,
+	.print         = connlimit_print,
+	.save          = connlimit_save,
+	.extra_opts    = connlimit_opts,
+};
+
+static __attribute__((constructor)) void libipt_connlimit_init(void)
+{
+	register_match(&connlimit_reg);
+}
Index: iptables/extensions/libipt_connlimit.man
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.man
@@ -0,0 +1,27 @@
+Allows you to restrict the number of parallel connections to a server per
+client IP address (or client address block).
+.TP
+[\fB!\fR] \fB--connlimit-above \fIn\fR
+Match if the number of existing connections is (not) above \fIn\fR.
+.TP
+\fB--connlimit-mask\fR \fIprefix_length\fR
+Group hosts using the prefix length. For IPv4, this must be a number between
+(including) 0 and 32. For IPv6, between 0 and 128.
+.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 number 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
+.TP
+# limit the number of parallel HTTP requests to 16 for the link local network \
+(ipv6)
+ip6tables -p tcp --syn --dport 80 -s fe80::/64 -m connlimit --connlimit-above
+16 --connlimit-mask 64 -j REJECT
Index: iptables/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ iptables/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,17 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	union {
+		u_int32_t v4_mask;
+		u_int32_t v6_mask[4];
+	};
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONNLIMIT_H */

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

* Re: xt_connlimit 20070628 kernel
  2007-06-28 19:55                 ` xt_connlimit 20070628 kernel Jan Engelhardt
@ 2007-06-29 11:27                   ` Patrick McHardy
  2007-07-01 14:11                     ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-06-29 11:27 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> Add the xt_connlimit match

There's still one bug and a few things that seem suboptimal, please
see below.


> 
> +static inline unsigned int connlimit_iphash(u_int32_t addr)
> +{
> +	if (unlikely(!connlimit_rnd_inited)) {
> +		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
> +		connlimit_rnd_inited = true;
> +	}
> +	return jhash_1word(addr, connlimit_rnd) & 0xFF;
> +}
> +
> +static inline unsigned int
> +connlimit_iphash6(const union nf_conntrack_address *addr,
> +                  const union nf_conntrack_address *mask)
> +{
> +	union nf_conntrack_address res;
> +	unsigned int i;
> +
> +	if (unlikely(!connlimit_rnd_inited)) {
> +		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
> +		connlimit_rnd_inited = true;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
> +		res.ip6[i] = addr->ip6[i] & mask->ip6[i];
> +
> +	return jhash2(res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
> +}


Are two hash functions really necessary? A single hash would have
the advantage that it would make it easier to deal with IPv4 mapped
addresses (thats assuming that an IPv4 mapped address and a regular
address should be counted as the same thing).

> +
> +static const char *ct_state(const struct nf_conn *conn)
> +{
> +#ifdef DEBUG
> +	static const char const *tcp_state[] = {
> +		"none", "established", "syn_sent", "syn_recv", "fin_wait",
> +		"time_wait", "close", "close_wait", "last_ack", "listen"
> +	};

^newline after local variables

> +	if (conn == NULL)
> +		return "gone";
> +	if (conn->tuplehash[0].tuple.dst.protonum == IPPROTO_TCP)
> +		return tcp_state[found_ct->proto.tcp.state]);
> +#endif
> +	return "";
> +}
> +
> +static inline bool already_closed(const struct nf_conn *conn)
> +{
> +	u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum;

^newline

> +	if (proto == IPPROTO_TCP)
> +		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT;
> +	else
> +		return 0;
> +}
> +
> +static inline void
> +connlimit_debug(const struct xt_connlimit_conn *conn,
> +                const struct nf_conn *found_ct,
> +                const union nf_conntrack_address *addr,
> +                const union nf_conntrack_address *mask,
> +                unsigned int family)
> +{
> +#define U3_NIP6(x) NIP6(*(const struct in6_addr *)&(x))
> +	if (family == AF_INET6)
> +		pr_debug(KERN_WARNING "xt_connlimit [%u]: src=" NIP6_FMT ":%u "
> +		         "dst=" NIP6_FMT ":%u %s\n",
> +		         connlimit_iphash6(addr, mask),
> +		         U3_NIP6(conn->tuple.src.u3),
> +		         ntohs(conn->tuple.src.u.tcp.port),
> +		         U3_NIP6(conn->tuple.dst.u3),
> +		         ntohs(conn->tuple.dst.u.tcp.port),
> +		         ct_state(found_ct));
> +	else
> +		pr_debug(KERN_WARNING "xt_connlimit [%u]: src=%u.%u.%u.%u:%u "
> +		         "dst=%u.%u.%u.%u:%u %s\n",
> +		         connlimit_iphash(addr->ip & mask->ip),
> +		         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),
> +			 ct_state(found_ct));
> +#undef U3_NIP6
> +}


Thats a lot of debugging considering that this is something quite
simple. I trust you tested this match, is it really necessary to
keep this?

> +
> +static inline unsigned int
> +same_source_net(const union nf_conntrack_address *addr,
> +                const union nf_conntrack_address *mask,
> +                const union nf_conntrack_address *u3, unsigned int family)
> +{
> +	if (family == AF_INET) {
> +		return (addr->ip & mask->ip) == (u3->ip & mask->ip);
> +	} else {
> +		union nf_conntrack_address lh, rh;
> +		unsigned int i;
> +
> +		for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
> +			lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
> +			rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
> +		}
> +
> +		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
> +	}
> +}
> +
> +static int count_them(struct xt_connlimit_data *data,
> +                      const union nf_conntrack_address *addr,
> +		      const union nf_conntrack_address *mask,
> +		      struct nf_conn *ct, unsigned int family)
> +{
> +	struct nf_conntrack_tuple_hash *found;
> +	struct nf_conntrack_tuple tuple;
> +	struct xt_connlimit_conn *conn;
> +	struct xt_connlimit_conn *tmp;
> +	struct nf_conn *found_ct;
> +	struct list_head *hash;
> +	bool addit = true;
> +	int matches = 0;
> +
> +	tuple = ct->tuplehash[0].tuple;
> +
> +	if (family == AF_INET6)
> +		hash = &data->iphash[connlimit_iphash6(addr, mask)];
> +	else
> +		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
> +
> +	/* check the saved connections */
> +	list_for_each_entry_safe(conn, tmp, hash, list) {
> +		found    = nf_conntrack_find_get(&conn->tuple, ct);


I didn't notice this before. I just removed this "ignored_conntrack"
argument from nf_conntrack_find_get because it was unused so far and
seems to imply someone doing more complicated hash table fiddling
than they should be. Why exactly are you using it?

Another thing is that you're grabbing and releasing nf_conntrack_lock
once for each call, additionally you have an atomic_inc and an
atomic_dec_and_test per entry. Since you were worried about speed,
that part is what you should worry about. I'd suggest to hold
nf_conntrack_lock around the entire iteration and use
__nf_conntrack_find.


> +		found_ct = NULL;
> +
> +		if (found != NULL)
> +			found_ct = nf_ct_tuplehash_to_ctrack(found);
> +
> +		if (found_ct != NULL &&
> +		    nf_ct_tuple_equal(&conn->tuple, &tuple) &&
> +		    !already_closed(found_ct))
> +			/*
> +			 * 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;
> +
> +		connlimit_debug(conn, found_ct, addr, mask, family);
> +
> +		if (found == NULL) {
> +			/* this one is gone */
> +			list_del(&conn->list);
> +			kfree(conn);

Minor optimization possible: you could reuse this memory in case
addit = true

> +			continue;
> +		}
> +
> +		if (already_closed(found_ct)) {
> +			/*
> +			 * we do not care about connections which are
> +			 * closed already -> ditch it
> +			 */
> +			list_del(&conn->list);
> +			kfree(conn);
> +			nf_conntrack_put(&found_ct->ct_general);
> +			continue;
> +		}
> +
> +		if (same_source_net(addr, mask, &conn->tuple.src.u3, family))
> +			/* same source network -> be counted! */
> +			++matches;
> +
> +		nf_conntrack_put(&found_ct->ct_general);
> +	}
> +
> +	if (addit) {
> +		/* save the new connection in our list */
> +		connlimit_debug(conn, found_ct, addr, mask, family);
> +		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 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;
> +	union nf_conntrack_address addr, mask;
> +	enum ip_conntrack_info ctinfo;
> +	struct nf_conn *ct;
> +	int connections;
> +
> +	ct = nf_ct_get(skb, &ctinfo);
> +	if (ct == NULL) {
> +		printk(KERN_INFO "xt_connlimit: INVALID connection\n");


No printks without net_ratelimit, this one seems like it shouldn't exist
at all. And hotdropping is quite unfriendly, it seems that as long as
you're able to read the addresses (which you're always), you can still
count the other connections.

> +		*hotdrop = true;
> +		return false;
> +	}
> +
> +	if (match->family == AF_INET6) {
> +		const struct ipv6hdr *iph = ipv6_hdr(skb);
> +		memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr));
> +		memcpy(&mask.ip6, info->v6_mask, sizeof(info->v6_mask));
> +	} else {
> +		const struct iphdr *iph = ip_hdr(skb);
> +		addr.ip = iph->saddr;
> +		mask.ip = info->v4_mask;
> +	}
> +
> +	spin_lock_bh(&info->data->lock);
> +	connections = count_them(info->data, &addr, &mask, ct, match->family);
> +	spin_unlock_bh(&info->data->lock);
> +
> +	if (connections < 0) {
> +		/* kmalloc failed, drop it entirely */
> +		*hotdrop = 1;

That one is fine.

> +		return false;
> +	}
> +
> +	return (connections > info->limit) ^ info->inverse;
> +}
> +
> +static bool 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);

Missing check for failure (and don't forget to release the module
reference).

> +	spin_lock_init(&info->data->lock);
> +	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
> +		INIT_LIST_HEAD(&info->data->iphash[i]);
> +
> +	return true;
> +}

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

* Re: xt_connlimit 20070628 kernel
  2007-06-29 11:27                   ` Patrick McHardy
@ 2007-07-01 14:11                     ` Jan Engelhardt
  2007-07-02 12:27                       ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-01 14:11 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

Hi,

On Jun 29 2007 13:27, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> Add the xt_connlimit match
>
>There's still one bug and a few things that seem suboptimal, please
>see below.
>
>> +static inline unsigned int connlimit_iphash(u_int32_t addr)
>> +{
[...]
>> +	return jhash_1word(addr, connlimit_rnd) & 0xFF;
>> +}
>> +
>> +static inline unsigned int
>> +connlimit_iphash6(const union nf_conntrack_address *addr,
>> +                  const union nf_conntrack_address *mask)
>> +{
[...]
>> +	return jhash2(res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
>> +}
>
>Are two hash functions really necessary?

It is the same hash. jhash_1word just does it over 32 bits, while the jhash2()
call does it over 4x32 bits. If I wanted, I could use jhash(), but jhash_1word
and jhash2() are optimized variants.

>A single hash would have the advantage that it would make it easier to deal
>with IPv4 mapped addresses (thats assuming that an IPv4 mapped address and a
>regular address should be counted as the same thing).

Huwee :)
Mathematically seen, all that is required is a hash function that is pure (GCC
slang for "produces always the same for same input") for a tuple of
<ipaddress, struct xt_connlimit_data>. So I could use xhash for ipv4 and yhash
for ipv6 even and a per-connlimit_data rnd.

Right, to the topic: I think we're fine here.

>> +static const char *ct_state(const struct nf_conn *conn)
>> +{
[..].
>> +}
>> +
>> +static inline void
>> +connlimit_debug(const struct xt_connlimit_conn *conn,
>> +                const struct nf_conn *found_ct,
>> +                const union nf_conntrack_address *addr,
>> +                const union nf_conntrack_address *mask,
>> +                unsigned int family)
>> +{
>> +}
>
>Thats a lot of debugging considering that this is something quite
>simple. I trust you tested this match, is it really necessary to
>keep this?

I certainly don't need it. Though, it's #ifdefed anyway, so... roll a dice and
tell me :)

>> +static inline unsigned int
>> +same_source_net(const union nf_conntrack_address *addr,
>> +                const union nf_conntrack_address *mask,
>> +                const union nf_conntrack_address *u3, unsigned int family)
>> +{
>> +	if (family == AF_INET) {
>> +		return (addr->ip & mask->ip) == (u3->ip & mask->ip);
>> +	} else {
>> +		union nf_conntrack_address lh, rh;
>> +		unsigned int i;
>> +
>> +		for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
>> +			lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
>> +			rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
>> +		}
>> +
>> +		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
>> +	}
>> +}
>> +
>> +static int count_them(struct xt_connlimit_data *data,
>> +                      const union nf_conntrack_address *addr,
>> +		      const union nf_conntrack_address *mask,
>> +		      struct nf_conn *ct, unsigned int family)
>> +{
>> +	struct nf_conntrack_tuple_hash *found;
>> +	struct nf_conntrack_tuple tuple;
>> +	struct xt_connlimit_conn *conn;
>> +	struct xt_connlimit_conn *tmp;
>> +	struct nf_conn *found_ct;
>> +	struct list_head *hash;
>> +	bool addit = true;
>> +	int matches = 0;
>> +
>> +	tuple = ct->tuplehash[0].tuple;
>> +
>> +	if (family == AF_INET6)
>> +		hash = &data->iphash[connlimit_iphash6(addr, mask)];
>> +	else
>> +		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
>> +
>> +	/* check the saved connections */
>> +	list_for_each_entry_safe(conn, tmp, hash, list) {
>> +		found    = nf_conntrack_find_get(&conn->tuple, ct);
>
>
>I didn't notice this before. I just removed this "ignored_conntrack"
>argument from nf_conntrack_find_get because it was unused so far and
>seems to imply someone doing more complicated hash table fiddling
>than they should be. Why exactly are you using it?

This is "original code" (from POM). I can replace the last argument with NULL
if that looks better.

>Another thing is that you're grabbing and releasing nf_conntrack_lock
>once for each call, additionally you have an atomic_inc and an
>atomic_dec_and_test per entry. Since you were worried about speed,
>that part is what you should worry about. I'd suggest to hold
>nf_conntrack_lock around the entire iteration and use
>__nf_conntrack_find.

Does this look reasonable? (Just removing the ///nf_conntrack_put lines and
doing the locking ourselves.)

        read_lock_bh(&nf_conntrack_lock);

        /* check the saved connections */
        list_for_each_entry_safe(conn, tmp, hash, list) {
                found    = __nf_conntrack_find(&conn->tuple, NULL);
                found_ct = NULL;

                if (found != NULL)
                        found_ct = nf_ct_tuplehash_to_ctrack(found);

                if (found_ct != NULL &&
                    nf_ct_tuple_equal(&conn->tuple, &tuple) &&
                    !already_closed(found_ct))
                        /*
                         * 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;

                connlimit_debug(conn, found_ct, addr, mask, family);

                if (found == NULL) {
                        /* this one is gone */
                        list_del(&conn->list);
                        kfree(conn);
                        continue;
                }

                if (already_closed(found_ct)) {
                        /*
                         * we do not care about connections which are
                         * closed already -> ditch it
                         */
                        list_del(&conn->list);
                        kfree(conn);
                        ////nf_conntrack_put(&found_ct->ct_general);
                        continue;
                }

                if (same_source_net(addr, mask, &conn->tuple.src.u3, family))
                        /* same source network -> be counted! */
                        ++matches;

                ////nf_conntrack_put(&found_ct->ct_general);
        }

        read_unlock_bh(&nf_conntrack_lock);


>> +		connlimit_debug(conn, found_ct, addr, mask, family);
>> +
>> +		if (found == NULL) {
>> +			/* this one is gone */
>> +			list_del(&conn->list);
>> +			kfree(conn);
>
>Minor optimization possible: you could reuse this memory in case
>addit = true

I think that would make it more complex - need to have the last pointer around.

>> +static bool 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;
>> +	union nf_conntrack_address addr, mask;
>> +	enum ip_conntrack_info ctinfo;
>> +	struct nf_conn *ct;
>> +	int connections;
>> +
>> +	ct = nf_ct_get(skb, &ctinfo);
>> +	if (ct == NULL) {
>> +		printk(KERN_INFO "xt_connlimit: INVALID connection\n");
>
>
>No printks without net_ratelimit, this one seems like it shouldn't exist
>at all.
Removed.

>And hotdropping is quite unfriendly, it seems that as long as
>you're able to read the addresses (which you're always), you can still
>count the other connections.

This is nf_ct_get that can fail. Without a connection, we can't figure
anything.

>> +static bool connlimit_check(const char *tablename, const void *ip,
>> +			    const struct xt_match *match, void *matchinfo,
>> +			    unsigned int hook_mask)
>> +{
[...]
>> +	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
[...]
>> +	/* init private data */
>> +	info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
>
>Missing check for failure (and don't forget to release the module
>reference).

Ok.



Thanks,
	Jan
-- 

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

* Re: xt_connlimit 20070628 kernel
  2007-07-01 14:11                     ` Jan Engelhardt
@ 2007-07-02 12:27                       ` Patrick McHardy
  2007-07-02 15:38                         ` Jan Engelhardt
  2007-07-04  8:55                         ` xt_connlimit 20070628 kernel Yasuyuki KOZAKAI
  0 siblings, 2 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-07-02 12:27 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 29 2007 13:27, Patrick McHardy wrote:
> 
>>A single hash would have the advantage that it would make it easier to deal
>>with IPv4 mapped addresses (thats assuming that an IPv4 mapped address and a
>>regular address should be counted as the same thing).
> 
> 
> Huwee :)
> Mathematically seen, all that is required is a hash function that is pure (GCC
> slang for "produces always the same for same input") for a tuple of
> <ipaddress, struct xt_connlimit_data>. So I could use xhash for ipv4 and yhash
> for ipv6 even and a per-connlimit_data rnd.
> 
> Right, to the topic: I think we're fine here.


That didn't answer my question. Should IPv6 mapped IPv4 addresses be
counted as the same address as the mapped IPv4 address or not?

>>Thats a lot of debugging considering that this is something quite
>>simple. I trust you tested this match, is it really necessary to
>>keep this?
> 
> 
> I certainly don't need it. Though, it's #ifdefed anyway, so... roll a dice and
> tell me :)


My one-sided dice tells me "better remove it" :)


>>>+		found    = nf_conntrack_find_get(&conn->tuple, ct);
>>
>>
>>I didn't notice this before. I just removed this "ignored_conntrack"
>>argument from nf_conntrack_find_get because it was unused so far and
>>seems to imply someone doing more complicated hash table fiddling
>>than they should be. Why exactly are you using it?
> 
> 
> This is "original code" (from POM). I can replace the last argument with NULL
> if that looks better.


Its not about looks. Do you need it or not?
(Looking below I guess not).

>>Another thing is that you're grabbing and releasing nf_conntrack_lock
>>once for each call, additionally you have an atomic_inc and an
>>atomic_dec_and_test per entry. Since you were worried about speed,
>>that part is what you should worry about. I'd suggest to hold
>>nf_conntrack_lock around the entire iteration and use
>>__nf_conntrack_find.
> 
> 
> Does this look reasonable? (Just removing the ///nf_conntrack_put lines and
> doing the locking ourselves.)
> 
>         read_lock_bh(&nf_conntrack_lock);
> 
>         /* check the saved connections */
>         list_for_each_entry_safe(conn, tmp, hash, list) {
>                 found    = __nf_conntrack_find(&conn->tuple, NULL);
>                 found_ct = NULL;
> 
>[...]
>         }
> 
>         read_unlock_bh(&nf_conntrack_lock);

Seems fine.

>>And hotdropping is quite unfriendly, it seems that as long as
>>you're able to read the addresses (which you're always), you can still
>>count the other connections.
> 
> 
> This is nf_ct_get that can fail. Without a connection, we can't figure
> anything.


You still have the addresses and port numbers to do a lookup. In
fact the most reasonable place to use this match is in the raw
table, before any resources are consumed. So it would make a lot
of sense to simply use the values from the headers (or call the
conntrack functions for tuple decoding if that makes it easier).

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

* Re: xt_connlimit 20070628 kernel
  2007-07-02 12:27                       ` Patrick McHardy
@ 2007-07-02 15:38                         ` Jan Engelhardt
  2007-07-02 15:40                           ` Patrick McHardy
  2007-07-04  8:55                         ` xt_connlimit 20070628 kernel Yasuyuki KOZAKAI
  1 sibling, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-02 15:38 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 2 2007 14:27, Patrick McHardy wrote:
>
>That didn't answer my question. Should IPv6 mapped IPv4 addresses be
>counted as the same address as the mapped IPv4 address or not?

No. (It is not needed.)

>>>And hotdropping is quite unfriendly, it seems that as long as
>>>you're able to read the addresses (which you're always), you can still
>>>count the other connections.
>> 
>> This is nf_ct_get that can fail. Without a connection, we can't figure
>> anything.
>
>You still have the addresses and port numbers to do a lookup.
>In fact the most reasonable place to use this match is in the raw 
>table, before any resources are consumed. So it would make a lot of 
>sense to simply use the values from the headers (or call the conntrack 
>functions for tuple decoding if that makes it easier).
>
To look up what? I am sorry I don't quite get what you are trying to 
tell me.
I do recognize that -t raw -A CHAIN -p tcp --syn -m connlimit
--connlimit-above X -j ACTION is something to keep resources minimal.


	Jan

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

* Re: xt_connlimit 20070628 kernel
  2007-07-02 15:38                         ` Jan Engelhardt
@ 2007-07-02 15:40                           ` Patrick McHardy
  2007-07-02 19:53                             ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-02 15:40 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 2 2007 14:27, Patrick McHardy wrote:
>> That didn't answer my question. Should IPv6 mapped IPv4 addresses be
>> counted as the same address as the mapped IPv4 address or not?
>
> No. (It is not needed.)


And why isn't it needed? The IPv4 address space is contained in IPv6,
so it seems only logical to count real IPv4 addresses and mapped IPv6
addresses as the same thing.

>   
>>>> And hotdropping is quite unfriendly, it seems that as long as
>>>> you're able to read the addresses (which you're always), you can still
>>>> count the other connections.
>>>>         
>>> This is nf_ct_get that can fail. Without a connection, we can't figure
>>> anything.
>>>       
>> You still have the addresses and port numbers to do a lookup.
>> In fact the most reasonable place to use this match is in the raw 
>> table, before any resources are consumed. So it would make a lot of 
>> sense to simply use the values from the headers (or call the conntrack 
>> functions for tuple decoding if that makes it easier).
>>
>>     
> To look up what? I am sorry I don't quite get what you are trying to 
> tell me.
>   

To look up similar connections. Connections are identifier by their
tuples, you can derive them yourself and do a lookup based on that.

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

* Re: xt_connlimit 20070628 kernel
  2007-07-02 15:40                           ` Patrick McHardy
@ 2007-07-02 19:53                             ` Jan Engelhardt
  2007-07-03 11:14                               ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-02 19:53 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 2 2007 17:40, Patrick McHardy wrote:
> Jan Engelhardt wrote:
>> On Jul 2 2007 14:27, Patrick McHardy wrote:
>> > That didn't answer my question. Should IPv6 mapped IPv4 addresses be
>> > counted as the same address as the mapped IPv4 address or not?
>>
>> No. (It is not needed.)
>
> And why isn't it needed? The IPv4 address space is contained in IPv6,
> so it seems only logical to count real IPv4 addresses and mapped IPv6
> addresses as the same thing.


Each struct xt_connlimit_data is allowed to have a different hash 
function, as long as each function is injective.

A struct xt_connlimit_{info,data} is never fed both AF_INET and
AF_INET6 connections.
Hence it may use different hash functions for AF_INET and AF_INET6 
connections.

Does that help?

>> > You still have the addresses and port numbers to do a lookup.
>> > In fact the most reasonable place to use this match is in the raw table,
>> > before any resources are consumed. So it would make a lot of sense to
>> > simply use the values from the headers (or call the conntrack functions for
>> > tuple decoding if that makes it easier).
>> >
>> To look up what? I don't quite get what you are trying to tell me.
>
> To look up similar connections.

What do you mean by "similar"?

> Connections are identifier by their tuples, you can derive them 
> yourself and do a lookup based on that.

connlimit uses nf_ct_get(skb,...)->tuplehash[0].tuple to get at the 
tuple. nf_ct_get() can fail.
How else should I derive it?


Sorry if this sounds all stupid and basic, but I certainly do not want 
to parse the skb by hand to get at the source address.


	Jan
-- 

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

* Re: xt_connlimit 20070628 kernel
  2007-07-02 19:53                             ` Jan Engelhardt
@ 2007-07-03 11:14                               ` Patrick McHardy
  2007-07-03 11:31                                 ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-03 11:14 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> Each struct xt_connlimit_data is allowed to have a different hash 
> function, as long as each function is injective.
> 
> A struct xt_connlimit_{info,data} is never fed both AF_INET and
> AF_INET6 connections.
> Hence it may use different hash functions for AF_INET and AF_INET6 
> connections.
> 
> Does that help?


Not really, you described the situation that led me to this question.
I can see that you're not using the same hash for both and I question
that. Anyway, lets drop this question, I don't care that much.

>>>>You still have the addresses and port numbers to do a lookup.
>>>>In fact the most reasonable place to use this match is in the raw table,
>>>>before any resources are consumed. So it would make a lot of sense to
>>>>simply use the values from the headers (or call the conntrack functions for
>>>>tuple decoding if that makes it easier).
>>>>
>>>
>>>To look up what? I don't quite get what you are trying to tell me.
>>
>>To look up similar connections.
> 
> 
> What do you mean by "similar"?


Same source tuple.

>>Connections are identifier by their tuples, you can derive them 
>>yourself and do a lookup based on that.
> 
> 
> connlimit uses nf_ct_get(skb,...)->tuplehash[0].tuple to get at the 
> tuple. nf_ct_get() can fail.
> How else should I derive it?
> 
> 
> Sorry if this sounds all stupid and basic, but I certainly do not want 
> to parse the skb by hand to get at the source address.


Use the conntrack tuple if one is available, otherwise use
nf_ct_get_tuple().

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

* Re: xt_connlimit 20070628 kernel
  2007-07-03 11:14                               ` Patrick McHardy
@ 2007-07-03 11:31                                 ` Jan Engelhardt
  2007-07-03 11:34                                   ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-03 11:31 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 3 2007 13:14, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> Each struct xt_connlimit_data is allowed to have a different hash 
>> function, as long as each function is injective.
>> 
>> A struct xt_connlimit_{info,data} is never fed both AF_INET and
>> AF_INET6 connections.
>> Hence it may use different hash functions for AF_INET and AF_INET6 
>> connections.
>> 
>> Does that help?
>
>
>Not really, you described the situation that led me to this question.
>I can see that you're not using the same hash for both and I question
>that.

See the patch below. It may help to understanding. (The code itself is
redundant, because the BUG_ONs will (should) never trigger.)

>>>Connections are identifier by their tuples, you can derive them 
>>>yourself and do a lookup based on that.
>> 
>> connlimit uses nf_ct_get(skb,...)->tuplehash[0].tuple to get at the 
>> tuple. nf_ct_get() can fail.
>> How else should I derive it?
>
>Use the conntrack tuple if one is available, otherwise use
>nf_ct_get_tuple().

So you are saying I should use...

  nf_ct_get_tuple(skb, 0, 0, match->family, match->proto, &tuple,
                  what_l3, what_l4);

at the top of count_them() and get rid of the nf_ct_get() in connlimit_match?


Thanks,
	Jan

---
 net/netfilter/xt_connlimit.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

Index: linux-2.6.22/net/netfilter/xt_connlimit.c
===================================================================
--- linux-2.6.22.orig/net/netfilter/xt_connlimit.c
+++ linux-2.6.22/net/netfilter/xt_connlimit.c
@@ -36,6 +36,7 @@ struct xt_connlimit_conn {
 
 struct xt_connlimit_data {
 	struct list_head iphash[256];
+	u_int16_t family;
 	spinlock_t lock;
 };
 
@@ -115,10 +116,13 @@ static int count_them(struct xt_connlimi
 
 	tuple = xct->tuplehash[0].tuple;
 
-	if (family == AF_INET6)
+	if (family == AF_INET6) {
+		BUG_ON(data->family != AF_INET6);
 		hash = &data->iphash[connlimit_iphash6(addr, mask)];
-	else
+	} else {
+		BUG_ON(data->family != AF_INET);
 		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
+	}
 
 	read_lock_bh(&nf_conntrack_lock);
 
@@ -245,6 +249,8 @@ static bool connlimit_check(const char *
 	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
 		INIT_LIST_HEAD(&info->data->iphash[i]);
 
+	/* Tag private structure with the type it is going to be used */
+	info->data->family = match->family;
 	return true;
 }
 

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

* Re: xt_connlimit 20070628 kernel
  2007-07-03 11:31                                 ` Jan Engelhardt
@ 2007-07-03 11:34                                   ` Patrick McHardy
  2007-07-04 10:56                                     ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-03 11:34 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 3 2007 13:14, Patrick McHardy wrote:
> 
>>>>Connections are identifier by their tuples, you can derive them 
>>>>yourself and do a lookup based on that.
>>>
>>>connlimit uses nf_ct_get(skb,...)->tuplehash[0].tuple to get at the 
>>>tuple. nf_ct_get() can fail.
>>>How else should I derive it?
>>
>>Use the conntrack tuple if one is available, otherwise use
>>nf_ct_get_tuple().
> 
> 
> So you are saying I should use...
> 
>   nf_ct_get_tuple(skb, 0, 0, match->family, match->proto, &tuple,
>                   what_l3, what_l4);
> 
> at the top of count_them() and get rid of the nf_ct_get() in connlimit_match?


You could do both, if the tuple is already derived there is no need
to repeat that work. Manually deriving it as fallback would allow
to use the match in the raw table.

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

* Re: xt_connlimit 20070628 kernel
  2007-07-02 12:27                       ` Patrick McHardy
  2007-07-02 15:38                         ` Jan Engelhardt
@ 2007-07-04  8:55                         ` Yasuyuki KOZAKAI
  2007-07-04 14:52                           ` Patrick McHardy
  1 sibling, 1 reply; 71+ messages in thread
From: Yasuyuki KOZAKAI @ 2007-07-04  8:55 UTC (permalink / raw)
  To: kaber; +Cc: jengelh, netfilter-devel


Hi,

It seems old discussion, anyway,

From: Patrick McHardy <kaber@trash.net>
Date: Mon, 02 Jul 2007 14:27:49 +0200

> Jan Engelhardt wrote:
> > On Jun 29 2007 13:27, Patrick McHardy wrote:
> > 
> >>A single hash would have the advantage that it would make it easier to deal
> >>with IPv4 mapped addresses (thats assuming that an IPv4 mapped address and a
> >>regular address should be counted as the same thing).
> > 
> > 
> > Huwee :)
> > Mathematically seen, all that is required is a hash function that is pure (GCC
> > slang for "produces always the same for same input") for a tuple of
> > <ipaddress, struct xt_connlimit_data>. So I could use xhash for ipv4 and yhash
> > for ipv6 even and a per-connlimit_data rnd.
> > 
> > Right, to the topic: I think we're fine here.
> 
> 
> That didn't answer my question. Should IPv6 mapped IPv4 addresses be
> counted as the same address as the mapped IPv4 address or not?

Logically, IPv6 packets including (almost) mapped addresses can be
assumed that they belong to IPv4 connection.

But now I don't want to do that because mapped address can cause security
issues.

	http://www.ietf.org/internet-drafts/draft-ietf-v6ops-security-overview-06.txt
	(2.2.  IPv4-mapped IPv6 Addresses)

These issues arise because IPv6 packets including mapped address are handled as
IPv4 packets. So, to avoid new security issue we don't know yet, I think
that it's safe not to merge IPv4 connection and IPv6 connection.


P.S. That's the reason why hash function of nf_conntrack takes address family.

-- Yasuyuki Kozakai

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

* Re: xt_connlimit 20070628 kernel
  2007-07-03 11:34                                   ` Patrick McHardy
@ 2007-07-04 10:56                                     ` Jan Engelhardt
  2007-07-04 14:52                                       ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-04 10:56 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 3 2007 13:34, Patrick McHardy wrote:
>>>
>>>Use the conntrack tuple if one is available, otherwise use
>>>nf_ct_get_tuple().
>> 
>> So you are saying I should use...
>> 
>>   nf_ct_get_tuple(skb, 0, 0, match->family, match->proto, &tuple,
>>                   what_l3, what_l4);
>> 
>> at the top of count_them() and get rid of the nf_ct_get() in connlimit_match?
>
>
>You could do both, if the tuple is already derived there is no need
>to repeat that work.

So the netfilter connection tracking system itself does nf_ct_get_tuple() at
some point?
That would make sense .. finally :)

>Manually deriving it as fallback would allow
>to use the match in the raw table.


	Jan
-- 

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

* Re: xt_connlimit 20070628 kernel
  2007-07-04 10:56                                     ` Jan Engelhardt
@ 2007-07-04 14:52                                       ` Patrick McHardy
  2007-07-04 15:11                                         ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-04 14:52 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 3 2007 13:34, Patrick McHardy wrote:
> 
>>>>Use the conntrack tuple if one is available, otherwise use
>>>>nf_ct_get_tuple().
>>>
>>>So you are saying I should use...
>>>
>>>  nf_ct_get_tuple(skb, 0, 0, match->family, match->proto, &tuple,
>>>                  what_l3, what_l4);
>>>
>>>at the top of count_them() and get rid of the nf_ct_get() in connlimit_match?
>>
>>
>>You could do both, if the tuple is already derived there is no need
>>to repeat that work.
> 
> 
> So the netfilter connection tracking system itself does nf_ct_get_tuple() at
> some point?


Right, when the packet hits connection tracking.

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

* Re: xt_connlimit 20070628 kernel
  2007-07-04  8:55                         ` xt_connlimit 20070628 kernel Yasuyuki KOZAKAI
@ 2007-07-04 14:52                           ` Patrick McHardy
  0 siblings, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-07-04 14:52 UTC (permalink / raw)
  To: Yasuyuki KOZAKAI; +Cc: jengelh, netfilter-devel

Yasuyuki KOZAKAI wrote:
> Logically, IPv6 packets including (almost) mapped addresses can be
> assumed that they belong to IPv4 connection.
> 
> But now I don't want to do that because mapped address can cause security
> issues.
> 
> 	http://www.ietf.org/internet-drafts/draft-ietf-v6ops-security-overview-06.txt
> 	(2.2.  IPv4-mapped IPv6 Addresses)
> 
> These issues arise because IPv6 packets including mapped address are handled as
> IPv4 packets. So, to avoid new security issue we don't know yet, I think
> that it's safe not to merge IPv4 connection and IPv6 connection.
> 
> 
> P.S. That's the reason why hash function of nf_conntrack takes address family.


Thanks for the explanation Yasuyuki.

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

* Re: xt_connlimit 20070628 kernel
  2007-07-04 14:52                                       ` Patrick McHardy
@ 2007-07-04 15:11                                         ` Jan Engelhardt
  2007-07-06 13:05                                           ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-04 15:11 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 4 2007 16:52, Patrick McHardy wrote:
>>>>
>>>>So you are saying I should use...
>>>>
>>>>  nf_ct_get_tuple(skb, 0, 0, match->family, match->proto, &tuple,
>>>>                  what_l3, what_l4);
>>>>
>>>>at the top of count_them() and get rid of the nf_ct_get() in connlimit_match?
>>>
[...]
>Right, when the packet hits connection tracking.

Ok so what should I put in for nhoff, dataoff, what_l3 and what_l4?


Thanks,
	Jan
-- 

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

* Re: xt_connlimit 20070628 kernel
  2007-07-04 15:11                                         ` Jan Engelhardt
@ 2007-07-06 13:05                                           ` Patrick McHardy
  2007-07-07 17:51                                             ` xt_connlimit 20070707 kernel Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-06 13:05 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 4 2007 16:52, Patrick McHardy wrote:
> 
>>>>>So you are saying I should use...
>>>>>
>>>>> nf_ct_get_tuple(skb, 0, 0, match->family, match->proto, &tuple,
>>>>>                 what_l3, what_l4);
>>>>>
>>>>>at the top of count_them() and get rid of the nf_ct_get() in connlimit_match?
>>>>
> [...]
> 
>>Right, when the packet hits connection tracking.
> 
> 
> Ok so what should I put in for nhoff, dataoff, what_l3 and what_l4?


Check out nf_conntrack_in() and the IPv4/IPv6 prepare functions.

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

* xt_connlimit 20070707 kernel
  2007-07-06 13:05                                           ` Patrick McHardy
@ 2007-07-07 17:51                                             ` Jan Engelhardt
  2007-07-09 14:30                                               ` Patrick McHardy
                                                                 ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-07 17:51 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

[-- Attachment #1: Type: TEXT/PLAIN, Size: 12685 bytes --]


On Jul 6 2007 15:05, Patrick McHardy wrote:
>> 
>> Ok so what should I put in for nhoff, dataoff, what_l3 and what_l4?
>
>
>Check out nf_conntrack_in() and the IPv4/IPv6 prepare functions.
>

Does this look ok?

Thanks,
	Jan
===

Add the xt_connlimit match

ipt_connlimit has been sitting in POM-NG for a long time.
Here is a new shiny xt_connlimit with:

 * xtables'ified and all that
 * will request the layer3 module
   (previously it hotdropped every packet when it was not loaded)
 * fixed: there was a deadlock in case of an OOM condition
 * support for any layer4 protocol (e.g. UDP/SCTP)
 * using jhash, as suggested by Eric Dumazet
 * ipv6 support


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

---
 include/linux/netfilter/xt_connlimit.h |   17 +
 net/netfilter/Kconfig                  |    7 
 net/netfilter/Makefile                 |    1 
 net/netfilter/xt_connlimit.c           |  334 +++++++++++++++++++++++++++++++++
 4 files changed, 359 insertions(+)

Index: linux-2.6.22/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,17 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	union {
+		u_int32_t v4_mask;
+		u_int32_t v6_mask[4];
+	};
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22.orig/net/netfilter/Kconfig
+++ linux-2.6.22/net/netfilter/Kconfig
@@ -423,6 +423,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
+	---help---
+	  This match allows you to match against the number of parallel
+	  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/net/netfilter/Makefile
===================================================================
--- linux-2.6.22.orig/net/netfilter/Makefile
+++ linux-2.6.22/net/netfilter/Makefile
@@ -53,6 +53,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/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22/net/netfilter/xt_connlimit.c
@@ -0,0 +1,334 @@
+/*
+ * 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
+ *   Copyright © 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/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/random.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>
+
+/* 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 u_int32_t connlimit_rnd;
+static bool connlimit_rnd_inited;
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	if (unlikely(!connlimit_rnd_inited)) {
+		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+		connlimit_rnd_inited = true;
+	}
+	return jhash_1word(addr, connlimit_rnd) & 0xFF;
+}
+
+static inline unsigned int
+connlimit_iphash6(const union nf_conntrack_address *addr,
+                  const union nf_conntrack_address *mask)
+{
+	union nf_conntrack_address res;
+	unsigned int i;
+
+	if (unlikely(!connlimit_rnd_inited)) {
+		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+		connlimit_rnd_inited = true;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
+		res.ip6[i] = addr->ip6[i] & mask->ip6[i];
+
+	return jhash2(res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
+}
+
+static inline bool already_closed(const struct nf_conn *conn)
+{
+	u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum;
+
+	if (proto == IPPROTO_TCP)
+		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT;
+	else
+		return 0;
+}
+
+static inline unsigned int
+same_source_net(const union nf_conntrack_address *addr,
+                const union nf_conntrack_address *mask,
+                const union nf_conntrack_address *u3, unsigned int family)
+{
+	if (family == AF_INET) {
+		return (addr->ip & mask->ip) == (u3->ip & mask->ip);
+	} else {
+		union nf_conntrack_address lh, rh;
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
+			lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
+			rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
+		}
+
+		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
+	}
+}
+
+static int count_them(struct xt_connlimit_data *data,
+                      const struct nf_conntrack_tuple *tuple,
+                      const union nf_conntrack_address *addr,
+                      const union nf_conntrack_address *mask,
+                      const struct xt_match *match)
+{
+	struct nf_conntrack_tuple_hash *found;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	struct nf_conn *found_ct;
+	struct list_head *hash;
+	bool addit = true;
+	int matches = 0;
+
+
+	if (match->family == AF_INET6)
+		hash = &data->iphash[connlimit_iphash6(addr, mask)];
+	else
+		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
+
+	read_lock_bh(&nf_conntrack_lock);
+
+	/* check the saved connections */
+	list_for_each_entry_safe(conn, tmp, hash, list) {
+		found    = __nf_conntrack_find(&conn->tuple, NULL);
+		found_ct = NULL;
+
+		if (found != NULL)
+			found_ct = nf_ct_tuplehash_to_ctrack(found);
+
+		if (found_ct != NULL &&
+		    nf_ct_tuple_equal(&conn->tuple, tuple) &&
+		    !already_closed(found_ct))
+			/*
+			 * 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 (found == NULL) {
+			/* this one is gone */
+			list_del(&conn->list);
+			kfree(conn);
+			continue;
+		}
+
+		if (already_closed(found_ct)) {
+			/*
+			 * we do not care about connections which are
+			 * closed already -> ditch it
+			 */
+			list_del(&conn->list);
+			kfree(conn);
+			continue;
+		}
+
+		if (same_source_net(addr, mask, &conn->tuple.src.u3,
+		    match->family))
+			/* same source network -> be counted! */
+			++matches;
+	}
+
+	read_unlock_bh(&nf_conntrack_lock);
+
+	if (addit) {
+		/* save the new connection in our list */
+		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 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;
+	const struct nf_conntrack_tuple *tuple_ptr;
+	union nf_conntrack_address addr, mask;
+	struct nf_conntrack_tuple tuple;
+	enum ip_conntrack_info ctinfo;
+	const struct nf_conn *ct;
+	int connections;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (ct != NULL) {
+		tuple_ptr = &ct->tuplehash[0].tuple;
+	} else {
+		struct nf_conntrack_l3proto *l3proto;
+		struct nf_conntrack_l4proto *l4proto;
+
+		l3proto = nf_ct_l3proto_find_get(match->family);
+		if (l3proto == NULL) {
+			*hotdrop = true;
+			return false;
+		}
+		l4proto = nf_ct_l4proto_find_get(match->family, match->proto);
+		if (l4proto == NULL) {
+			nf_ct_l3proto_put(l3proto);
+			*hotdrop = true;
+			return false;
+		}
+		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
+		    &tuple, NULL, NULL) != 0) {
+			nf_ct_l3proto_put(l3proto);
+			nf_ct_l4proto_put(l4proto);
+			*hotdrop = true;
+			return false;
+		}
+		tuple_ptr = &tuple;
+		nf_ct_l3proto_put(l3proto);
+		nf_ct_l4proto_put(l4proto);
+	}
+
+	if (match->family == AF_INET6) {
+		const struct ipv6hdr *iph = ipv6_hdr(skb);
+		memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr));
+		memcpy(&mask.ip6, info->v6_mask, sizeof(info->v6_mask));
+	} else {
+		const struct iphdr *iph = ip_hdr(skb);
+		addr.ip = iph->saddr;
+		mask.ip = info->v4_mask;
+	}
+
+	spin_lock_bh(&info->data->lock);
+	connections = count_them(info->data, tuple_ptr, &addr, &mask, match);
+	spin_unlock_bh(&info->data->lock);
+
+	if (connections < 0) {
+		/* kmalloc failed, drop it entirely */
+		*hotdrop = 1;
+		return false;
+	}
+
+	return (connections > info->limit) ^ info->inverse;
+}
+
+static bool 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);
+	if (info->data == NULL) {
+		nf_ct_l3proto_module_put(match->family);
+		return false;
+	}
+
+	spin_lock_init(&info->data->lock);
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	struct list_head *hash = info->data->iphash;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
+		list_for_each_entry_safe(conn, tmp, &hash[i], list) {
+			list_del(&conn->list);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+}
+
+static struct xt_match connlimit_reg[] __read_mostly = {
+	{
+		.name       = "connlimit",
+		.family     = AF_INET,
+		.checkentry = connlimit_check,
+		.match      = connlimit_match,
+		.matchsize  = sizeof(struct xt_connlimit_info),
+		.destroy    = connlimit_destroy,
+		.me         = THIS_MODULE,
+	},
+	{
+		.name       = "connlimit",
+		.family     = AF_INET6,
+		.checkentry = connlimit_check,
+		.match      = connlimit_match,
+		.matchsize  = sizeof(struct xt_connlimit_info),
+		.destroy    = connlimit_destroy,
+		.me         = THIS_MODULE,
+	},
+};
+
+static int __init xt_connlimit_init(void)
+{
+	return xt_register_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg));
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg));
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
+MODULE_DESCRIPTION("netfilter xt_connlimit match module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");
+MODULE_ALIAS("ip6t_connlimit");

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

* Re: xt_connlimit 20070707 kernel
  2007-07-07 17:51                                             ` xt_connlimit 20070707 kernel Jan Engelhardt
@ 2007-07-09 14:30                                               ` Patrick McHardy
  2007-07-09 15:10                                                 ` Jan Engelhardt
  2007-07-10  6:30                                               ` Yasuyuki KOZAKAI
       [not found]                                               ` <200707100630.l6A6UBM1021597@toshiba.co.jp>
  2 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-09 14:30 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 6 2007 15:05, Patrick McHardy wrote:
> 
>>>Ok so what should I put in for nhoff, dataoff, what_l3 and what_l4?
>>
>>
>>Check out nf_conntrack_in() and the IPv4/IPv6 prepare functions.
>>
> 
> 
> Does this look ok?


Yes, just two small things left:

> +	read_unlock_bh(&nf_conntrack_lock);
> +
> +	if (addit) {
> +		/* save the new connection in our list */
> +		conn = kzalloc(sizeof(*conn), GFP_ATOMIC);
> +		if (conn == NULL)
> +			return -ENOMEM;
> +
> +		INIT_LIST_HEAD(&conn->list);

No need to initialize member list_heads.

> +		conn->tuple = *tuple;
> +		list_add(&conn->list, hash);
> +		++matches;
> +	}
> +
> +	return matches;
> +}
> +
> +static bool 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;
> +	const struct nf_conntrack_tuple *tuple_ptr;
> +	union nf_conntrack_address addr, mask;
> +	struct nf_conntrack_tuple tuple;
> +	enum ip_conntrack_info ctinfo;
> +	const struct nf_conn *ct;
> +	int connections;
> +
> +	ct = nf_ct_get(skb, &ctinfo);
> +	if (ct != NULL) {
> +		tuple_ptr = &ct->tuplehash[0].tuple;
> +	} else {
> +		struct nf_conntrack_l3proto *l3proto;
> +		struct nf_conntrack_l4proto *l4proto;
> +
> +		l3proto = nf_ct_l3proto_find_get(match->family);
> +		if (l3proto == NULL) {
> +			*hotdrop = true;
> +			return false;
> +		}
> +		l4proto = nf_ct_l4proto_find_get(match->family, match->proto);


The module reference taking functions should not be used in the
packet processing path. Please use __nf_ct_l3proto_find and
__nf_ct_l4proto_find. Since the l3proto is static for one
instance of the match you could also store it info->data and
only do the lookup once (for then you need to take the module
reference of course).

> +
> +	if (match->family == AF_INET6) {
> +		const struct ipv6hdr *iph = ipv6_hdr(skb);
> +		memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr));
> +		memcpy(&mask.ip6, info->v6_mask, sizeof(info->v6_mask));
> +	} else {
> +		const struct iphdr *iph = ip_hdr(skb);
> +		addr.ip = iph->saddr;
> +		mask.ip = info->v4_mask;
> +	}
> +
> +	spin_lock_bh(&info->data->lock);
> +	connections = count_them(info->data, tuple_ptr, &addr, &mask, match);
> +	spin_unlock_bh(&info->data->lock);
> +
> +	if (connections < 0) {
> +		/* kmalloc failed, drop it entirely */
> +		*hotdrop = 1;


This should be "= true" I guess ..

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 14:30                                               ` Patrick McHardy
@ 2007-07-09 15:10                                                 ` Jan Engelhardt
  2007-07-09 15:20                                                   ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-09 15:10 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

[-- Attachment #1: Type: TEXT/PLAIN, Size: 13112 bytes --]

Hi,

On Jul 9 2007 16:30, Patrick McHardy wrote:
>> Does this look ok?
>Yes, just two small things left:
>
>> +		struct nf_conntrack_l3proto *l3proto;
>> +		struct nf_conntrack_l4proto *l4proto;
>> +
>> +		l3proto = nf_ct_l3proto_find_get(match->family);
>> +		if (l3proto == NULL) {
>> +			*hotdrop = true;
>> +			return false;
>> +		}
>> +		l4proto = nf_ct_l4proto_find_get(match->family, match->proto);
>
>The module reference taking functions should not be used in the
>packet processing path. Please use __nf_ct_l3proto_find and
>__nf_ct_l4proto_find. Since the l3proto is static for one
>instance of the match you could also store it info->data and
>only do the lookup once (for then you need to take the module
>reference of course).

Normally, the ct=NULL condition should not happen (so often), so that I think
just using the non-refcounted variant is fine.



Thank you,
	Jan
===

Add the xt_connlimit match

ipt_connlimit has been sitting in POM-NG for a long time.
Here is a new shiny xt_connlimit with:

 * xtables'ified
 * will request the layer3 module
   (previously it hotdropped every packet when it was not loaded)
 * fixed: there was a deadlock in case of an OOM condition
 * support for any layer4 protocol (e.g. UDP/SCTP)
 * using jhash, as suggested by Eric Dumazet
 * ipv6 support


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

---
 include/linux/netfilter/xt_connlimit.h |   17 +
 net/netfilter/Kconfig                  |    7 
 net/netfilter/Makefile                 |    1 
 net/netfilter/xt_connlimit.c           |  325 +++++++++++++++++++++++++++++++++
 4 files changed, 350 insertions(+)

Index: linux-2.6.22/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,17 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	union {
+		u_int32_t v4_mask;
+		u_int32_t v6_mask[4];
+	};
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22.orig/net/netfilter/Kconfig
+++ linux-2.6.22/net/netfilter/Kconfig
@@ -423,6 +423,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
+	---help---
+	  This match allows you to match against the number of parallel
+	  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/net/netfilter/Makefile
===================================================================
--- linux-2.6.22.orig/net/netfilter/Makefile
+++ linux-2.6.22/net/netfilter/Makefile
@@ -53,6 +53,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/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22/net/netfilter/xt_connlimit.c
@@ -0,0 +1,325 @@
+/*
+ * 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
+ *   Copyright © 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/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/random.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>
+
+/* 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 u_int32_t connlimit_rnd;
+static bool connlimit_rnd_inited;
+
+static inline unsigned int connlimit_iphash(u_int32_t addr)
+{
+	if (unlikely(!connlimit_rnd_inited)) {
+		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+		connlimit_rnd_inited = true;
+	}
+	return jhash_1word(addr, connlimit_rnd) & 0xFF;
+}
+
+static inline unsigned int
+connlimit_iphash6(const union nf_conntrack_address *addr,
+                  const union nf_conntrack_address *mask)
+{
+	union nf_conntrack_address res;
+	unsigned int i;
+
+	if (unlikely(!connlimit_rnd_inited)) {
+		get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+		connlimit_rnd_inited = true;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
+		res.ip6[i] = addr->ip6[i] & mask->ip6[i];
+
+	return jhash2(res.ip6, ARRAY_SIZE(res.ip6), connlimit_rnd) & 0xFF;
+}
+
+static inline bool already_closed(const struct nf_conn *conn)
+{
+	u_int16_t proto = conn->tuplehash[0].tuple.dst.protonum;
+
+	if (proto == IPPROTO_TCP)
+		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT;
+	else
+		return 0;
+}
+
+static inline unsigned int
+same_source_net(const union nf_conntrack_address *addr,
+                const union nf_conntrack_address *mask,
+                const union nf_conntrack_address *u3, unsigned int family)
+{
+	if (family == AF_INET) {
+		return (addr->ip & mask->ip) == (u3->ip & mask->ip);
+	} else {
+		union nf_conntrack_address lh, rh;
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i) {
+			lh.ip6[i] = addr->ip6[i] & mask->ip6[i];
+			rh.ip6[i] = u3->ip6[i] & mask->ip6[i];
+		}
+
+		return memcmp(&lh.ip6, &rh.ip6, sizeof(lh.ip6)) == 0;
+	}
+}
+
+static int count_them(struct xt_connlimit_data *data,
+                      const struct nf_conntrack_tuple *tuple,
+                      const union nf_conntrack_address *addr,
+                      const union nf_conntrack_address *mask,
+                      const struct xt_match *match)
+{
+	struct nf_conntrack_tuple_hash *found;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	struct nf_conn *found_ct;
+	struct list_head *hash;
+	bool addit = true;
+	int matches = 0;
+
+
+	if (match->family == AF_INET6)
+		hash = &data->iphash[connlimit_iphash6(addr, mask)];
+	else
+		hash = &data->iphash[connlimit_iphash(addr->ip & mask->ip)];
+
+	read_lock_bh(&nf_conntrack_lock);
+
+	/* check the saved connections */
+	list_for_each_entry_safe(conn, tmp, hash, list) {
+		found    = __nf_conntrack_find(&conn->tuple, NULL);
+		found_ct = NULL;
+
+		if (found != NULL)
+			found_ct = nf_ct_tuplehash_to_ctrack(found);
+
+		if (found_ct != NULL &&
+		    nf_ct_tuple_equal(&conn->tuple, tuple) &&
+		    !already_closed(found_ct))
+			/*
+			 * 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 (found == NULL) {
+			/* this one is gone */
+			list_del(&conn->list);
+			kfree(conn);
+			continue;
+		}
+
+		if (already_closed(found_ct)) {
+			/*
+			 * we do not care about connections which are
+			 * closed already -> ditch it
+			 */
+			list_del(&conn->list);
+			kfree(conn);
+			continue;
+		}
+
+		if (same_source_net(addr, mask, &conn->tuple.src.u3,
+		    match->family))
+			/* same source network -> be counted! */
+			++matches;
+	}
+
+	read_unlock_bh(&nf_conntrack_lock);
+
+	if (addit) {
+		/* save the new connection in our list */
+		conn = kzalloc(sizeof(*conn), GFP_ATOMIC);
+		if (conn == NULL)
+			return -ENOMEM;
+		conn->tuple = *tuple;
+		list_add(&conn->list, hash);
+		++matches;
+	}
+
+	return matches;
+}
+
+static bool 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;
+	const struct nf_conntrack_tuple *tuple_ptr;
+	union nf_conntrack_address addr, mask;
+	struct nf_conntrack_tuple tuple;
+	enum ip_conntrack_info ctinfo;
+	const struct nf_conn *ct;
+	int connections;
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (ct != NULL) {
+		tuple_ptr = &ct->tuplehash[0].tuple;
+	} else {
+		struct nf_conntrack_l3proto *l3proto;
+		struct nf_conntrack_l4proto *l4proto;
+
+		l3proto = __nf_ct_l3proto_find(match->family);
+		if (l3proto == NULL)
+			goto hotdrop;
+		l4proto = __nf_ct_l4proto_find(match->family, match->proto);
+		if (l4proto == NULL)
+			goto hotdrop;
+		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
+		    &tuple, NULL, NULL) != 0)
+			goto hotdrop;
+		tuple_ptr = &tuple;
+	}
+
+	if (match->family == AF_INET6) {
+		const struct ipv6hdr *iph = ipv6_hdr(skb);
+		memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr));
+		memcpy(&mask.ip6, info->v6_mask, sizeof(info->v6_mask));
+	} else {
+		const struct iphdr *iph = ip_hdr(skb);
+		addr.ip = iph->saddr;
+		mask.ip = info->v4_mask;
+	}
+
+	spin_lock_bh(&info->data->lock);
+	connections = count_them(info->data, tuple_ptr, &addr, &mask, match);
+	spin_unlock_bh(&info->data->lock);
+
+	if (connections < 0) {
+		/* kmalloc failed, drop it entirely */
+		*hotdrop = true;
+		return false;
+	}
+
+	return (connections > info->limit) ^ info->inverse;
+
+ hotdrop:
+ 	*hotdrop = true;
+	return false;
+}
+
+static bool 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);
+	if (info->data == NULL) {
+		nf_ct_l3proto_module_put(match->family);
+		return false;
+	}
+
+	spin_lock_init(&info->data->lock);
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i)
+		INIT_LIST_HEAD(&info->data->iphash[i]);
+
+	return true;
+}
+
+static void connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+	struct xt_connlimit_info *info = matchinfo;
+	struct xt_connlimit_conn *conn;
+	struct xt_connlimit_conn *tmp;
+	struct list_head *hash = info->data->iphash;
+	unsigned int i;
+
+	nf_ct_l3proto_module_put(match->family);
+
+	for (i = 0; i < ARRAY_SIZE(info->data->iphash); ++i) {
+		list_for_each_entry_safe(conn, tmp, &hash[i], list) {
+			list_del(&conn->list);
+			kfree(conn);
+		}
+	}
+
+	kfree(info->data);
+}
+
+static struct xt_match connlimit_reg[] __read_mostly = {
+	{
+		.name       = "connlimit",
+		.family     = AF_INET,
+		.checkentry = connlimit_check,
+		.match      = connlimit_match,
+		.matchsize  = sizeof(struct xt_connlimit_info),
+		.destroy    = connlimit_destroy,
+		.me         = THIS_MODULE,
+	},
+	{
+		.name       = "connlimit",
+		.family     = AF_INET6,
+		.checkentry = connlimit_check,
+		.match      = connlimit_match,
+		.matchsize  = sizeof(struct xt_connlimit_info),
+		.destroy    = connlimit_destroy,
+		.me         = THIS_MODULE,
+	},
+};
+
+static int __init xt_connlimit_init(void)
+{
+	return xt_register_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg));
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+	xt_unregister_matches(connlimit_reg, ARRAY_SIZE(connlimit_reg));
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
+MODULE_DESCRIPTION("netfilter xt_connlimit match module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");
+MODULE_ALIAS("ip6t_connlimit");

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:10                                                 ` Jan Engelhardt
@ 2007-07-09 15:20                                                   ` Patrick McHardy
  2007-07-09 15:29                                                     ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-09 15:20 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 9 2007 16:30, Patrick McHardy wrote:
> 
>>The module reference taking functions should not be used in the
>>packet processing path. Please use __nf_ct_l3proto_find and
>>__nf_ct_l4proto_find. Since the l3proto is static for one
>>instance of the match you could also store it info->data and
>>only do the lookup once (for then you need to take the module
>>reference of course).
> 
> 
> Normally, the ct=NULL condition should not happen (so often), so that I think
> just using the non-refcounted variant is fine.


It will happen *always* when used in the raw table, which is the
most useful position for this match IMO. And you already take
a l3 proto module reference anyways.

But I don't mind, this can also be changed afterwards. But I get
rejects for some reason:

patching file include/linux/netfilter/xt_connlimit.h
patching file net/netfilter/Kconfig
Hunk #1 FAILED at 423.
1 out of 1 hunk FAILED -- saving rejects to file net/netfilter/Kconfig.rej
patching file net/netfilter/Makefile
Hunk #1 FAILED at 53.
1 out of 1 hunk FAILED -- saving rejects to file net/netfilter/Makefile.rej
patching file net/netfilter/xt_connlimit.c

Please rediff against net-2.6.23.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:20                                                   ` Patrick McHardy
@ 2007-07-09 15:29                                                     ` Patrick McHardy
  2007-07-09 15:32                                                       ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-09 15:29 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Patrick McHardy wrote:
> But I don't mind, this can also be changed afterwards. But I get
> rejects for some reason:
> 
> patching file include/linux/netfilter/xt_connlimit.h
> patching file net/netfilter/Kconfig
> Hunk #1 FAILED at 423.
> 1 out of 1 hunk FAILED -- saving rejects to file net/netfilter/Kconfig.rej
> patching file net/netfilter/Makefile
> Hunk #1 FAILED at 53.
> 1 out of 1 hunk FAILED -- saving rejects to file net/netfilter/Makefile.rej
> patching file net/netfilter/xt_connlimit.c
> 
> Please rediff against net-2.6.23.


My mistake. Applied, thanks Jan.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:29                                                     ` Patrick McHardy
@ 2007-07-09 15:32                                                       ` Jan Engelhardt
  2007-07-09 15:33                                                         ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-09 15:32 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 9 2007 17:29, Patrick McHardy wrote:
>> 1 out of 1 hunk FAILED -- saving rejects to file net/netfilter/Makefile.rej
>> patching file net/netfilter/xt_connlimit.c
>> 
>> Please rediff against net-2.6.23.
>
>My mistake. Applied, thanks Jan.
>

Hey, I was already through with the clone :p Anyway..

Does it still make it into 2.6.23-rc1?


Thanks,
	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:32                                                       ` Jan Engelhardt
@ 2007-07-09 15:33                                                         ` Patrick McHardy
  2007-07-09 15:34                                                           ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-09 15:33 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 9 2007 17:29, Patrick McHardy wrote:
> 
>>>1 out of 1 hunk FAILED -- saving rejects to file net/netfilter/Makefile.rej
>>>patching file net/netfilter/xt_connlimit.c
>>>
>>>Please rediff against net-2.6.23.
>>
>>My mistake. Applied, thanks Jan.
>>
> 
> 
> Hey, I was already through with the clone :p Anyway..
> 
> Does it still make it into 2.6.23-rc1?


Yes, unless it fails during testing :)

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:33                                                         ` Patrick McHardy
@ 2007-07-09 15:34                                                           ` Patrick McHardy
  2007-07-09 15:38                                                             ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-09 15:34 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Patrick McHardy wrote:
> Jan Engelhardt wrote:
> 
>>Does it still make it into 2.6.23-rc1?
> 
> 
> 
> Yes, unless it fails during testing :)


Speaking of which, could you resend the latest version of the
corresponding iptables patch please?

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:34                                                           ` Patrick McHardy
@ 2007-07-09 15:38                                                             ` Jan Engelhardt
  2007-07-09 15:43                                                               ` Patrick McHardy
                                                                                 ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-09 15:38 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jul 9 2007 17:34, Patrick McHardy wrote:
>>>Does it still make it into 2.6.23-rc1?
>
>> Yes, unless it fails during testing :)
>
>Speaking of which, could you resend the latest version of the
>corresponding iptables patch please?
>
At your service!

(Note the symlink, or how [sw]ould this be done?)


Thanks,
	Jan
===

Subject: Add connlimit to iptables.

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

---

 symlink libip6t_connlimit.man -> libipt_connlimit.man

 extensions/Makefile                    |    4 
 extensions/libip6t_connlimit.c         |  151 +++++++++++++++++++++++++++++++++
 extensions/libipt_connlimit.c          |  128 +++++++++++++++++++++++++++
 extensions/libipt_connlimit.man        |   27 +++++
 include/linux/netfilter/xt_connlimit.h |   17 +++
 5 files changed, 325 insertions(+), 2 deletions(-)

Index: iptables/extensions/Makefile
===================================================================
--- iptables.orig/extensions/Makefile
+++ iptables/extensions/Makefile
@@ -5,8 +5,8 @@
 # header files are present in the include/linux directory of this iptables
 # package (HW)
 #
-PF_EXT_SLIB:=ah addrtype comment connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype policy realm sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NFQUEUE NOTRACK REDIRECT REJECT SAME SNAT TCPMSS TOS TTL TRACE ULOG
-PF6_EXT_SLIB:=connmark eui64 hl icmp6 length limit mac mark multiport owner physdev policy standard state tcp udp CONNMARK HL LOG NFQUEUE MARK TCPMSS TRACE
+PF_EXT_SLIB:=ah addrtype comment connlimit connmark conntrack dscp ecn esp hashlimit helper icmp iprange length limit mac mark multiport owner physdev pkttype policy realm sctp standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NFQUEUE NOTRACK REDIRECT REJECT SAME SNAT TCPMSS TOS TTL TRACE ULOG
+PF6_EXT_SLIB:=connlimit connmark eui64 hl icmp6 length limit mac mark multiport owner physdev policy standard state tcp udp CONNMARK HL LOG NFQUEUE MARK TCPMSS TRACE
 
 ifeq ($(DO_SELINUX), 1)
 PF_EXT_SE_SLIB:=SECMARK CONNSECMARK
Index: iptables/extensions/libip6t_connlimit.c
===================================================================
--- /dev/null
+++ iptables/extensions/libip6t_connlimit.c
@@ -0,0 +1,151 @@
+/* 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 <ip6tables.h>
+#include "../include/linux/netfilter/xt_connlimit.h"
+
+/* Function which prints out usage message. */
+static void connlimit_help(void)
+{
+	printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n        match if the number of existing "
+"                               connections is (not) above n\n"
+"    --connlimit-mask n         group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static const struct option connlimit_opts[] = {
+	{"connlimit-above", 1, NULL, 1},
+	{"connlimit-mask",  1, NULL, 2},
+	{NULL},
+};
+
+static void connlimit_init(struct ip6t_entry_match *match, unsigned int *nfc)
+{
+	struct xt_connlimit_info *info = (void *)match->data;
+	info->v6_mask[0] =
+	info->v6_mask[1] =
+	info->v6_mask[2] =
+	info->v6_mask[3] = 0xFFFFFFFF;
+}
+
+static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len)
+{
+	if (prefix_len == 0) {
+		mask[0] = mask[1] = mask[2] = mask[3] = 0;
+	} else if (prefix_len <= 32) {
+		mask[0] <<= 32 - prefix_len;
+		mask[1] = mask[2] = mask[3] = 0;
+	} else if (prefix_len <= 64) {
+		mask[1] <<= 32 - (prefix_len - 32);
+		mask[2] = mask[3] = 0;
+	} else if (prefix_len <= 96) {
+		mask[2] <<= 32 - (prefix_len - 64);
+		mask[3] = 0;
+	} else if (prefix_len <= 128) {
+		mask[3] <<= 32 - (prefix_len - 96);
+	}
+	mask[0] = htonl(mask[0]);
+	mask[1] = htonl(mask[1]);
+	mask[2] = htonl(mask[2]);
+	mask[3] = htonl(mask[3]);
+}
+
+static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
+                           const struct ip6t_entry *entry,
+                           unsigned int *nfcache,
+                           struct ip6t_entry_match **match)
+{
+	struct xt_connlimit_info *info = (void *)(*match)->data;
+	char *err;
+	int i;
+
+	if (*flags & c)
+		exit_error(PARAMETER_PROBLEM,
+		           "--connlimit-above and/or --connlimit-mask may "
+			   "only be given once");
+
+	switch (c) {
+	case 1:
+		check_inverse(optarg, &invert, &optind, 0);
+		info->limit   = strtoul(argv[optind-1], NULL, 0);
+		info->inverse = invert;
+		break;
+	case 2:
+		i = strtoul(argv[optind-1], &err, 0);
+		if (i > 128 || *err != '\0')
+			exit_error(PARAMETER_PROBLEM,
+				"--connlimit-mask must be between 0 and 128");
+		prefix_to_netmask(info->v6_mask, i);
+		break;
+	default:
+		return 0;
+	}
+
+	*flags |= c;
+	return 1;
+}
+
+/* Final check */
+static void connlimit_check(unsigned int flags)
+{
+	if (!(flags & 1))
+		exit_error(PARAMETER_PROBLEM,
+		           "You must specify \"--connlimit-above\"");
+}
+
+static unsigned int count_bits(const u_int32_t *mask)
+{
+	unsigned int bits = 0, i;
+	u_int32_t tmp[4];
+
+	for (i = 0; i < 4; ++i)
+		for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
+			++bits;
+	return 128 - bits;
+}
+
+/* Prints out the matchinfo. */
+static void connlimit_print(const struct ip6t_ip6 *ip,
+                            const struct ip6t_entry_match *match, int numeric)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+
+	printf("#conn/%u %s %u ", count_bits(info->v6_mask),
+	       info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void connlimit_save(const struct ip6t_ip6 *ip,
+                           const struct ip6t_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->v6_mask));
+}
+
+static struct ip6tables_match connlimit_reg = {
+	.name          = "connlimit",
+	.version       = IPTABLES_VERSION,
+	.size          = IP6T_ALIGN(sizeof(struct xt_connlimit_info)),
+	.userspacesize = offsetof(struct xt_connlimit_info, data),
+	.help          = connlimit_help,
+	.init          = connlimit_init,
+	.parse         = connlimit_parse,
+	.final_check   = connlimit_check,
+	.print         = connlimit_print,
+	.save          = connlimit_save,
+	.extra_opts    = connlimit_opts,
+};
+
+static __attribute__((constructor)) void libipt_connlimit_init(void)
+{
+	register_match6(&connlimit_reg);
+}
Index: iptables/extensions/libipt_connlimit.c
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.c
@@ -0,0 +1,128 @@
+/* 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 "../include/linux/netfilter/xt_connlimit.h"
+
+/* Function which prints out usage message. */
+static void connlimit_help(void)
+{
+	printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n        match if the number of existing "
+"                               connections is (not) above n\n"
+"    --connlimit-mask n         group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static const struct option connlimit_opts[] = {
+	{"connlimit-above", 1, NULL, 1},
+	{"connlimit-mask",  1, NULL, 2},
+	{NULL},
+};
+
+static void connlimit_init(struct ipt_entry_match *match, unsigned int *nfc)
+{
+	struct xt_connlimit_info *info = (void *)match->data;
+	info->v4_mask = 0xFFFFFFFF;
+}
+
+static int connlimit_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;
+	char *err;
+	int i;
+
+	if (*flags & c)
+		exit_error(PARAMETER_PROBLEM,
+		           "--connlimit-above and/or --connlimit-mask may "
+			   "only be given once");
+
+	switch (c) {
+	case 1:
+		check_inverse(optarg, &invert, &optind, 0);
+		info->limit   = strtoul(argv[optind-1], NULL, 0);
+		info->inverse = invert;
+		break;
+	case 2:
+		i = strtoul(argv[optind-1], &err, 0);
+		if (i > 32 || *err != '\0')
+			exit_error(PARAMETER_PROBLEM,
+				"--connlimit-mask must be between 0 and 32");
+		if (i == 0)
+			info->v4_mask = 0;
+		else
+			info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
+		break;
+	default:
+		return 0;
+	}
+
+	*flags |= c;
+	return 1;
+}
+
+/* Final check */
+static void connlimit_check(unsigned int flags)
+{
+	if (!(flags & 1))
+		exit_error(PARAMETER_PROBLEM,
+		           "You must specify \"--connlimit-above\"");
+}
+
+static unsigned int count_bits(u_int32_t mask)
+{
+	unsigned int bits = 0;
+
+	for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
+		++bits;
+
+	return 32 - bits;
+}
+
+/* Prints out the matchinfo. */
+static void connlimit_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/%u %s %u ", count_bits(info->v4_mask),
+	       info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void connlimit_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->v4_mask));
+}
+
+static struct iptables_match connlimit_reg = {
+	.name          = "connlimit",
+	.version       = IPTABLES_VERSION,
+	.size          = IPT_ALIGN(sizeof(struct xt_connlimit_info)),
+	.userspacesize = offsetof(struct xt_connlimit_info, data),
+	.help          = connlimit_help,
+	.init          = connlimit_init,
+	.parse         = connlimit_parse,
+	.final_check   = connlimit_check,
+	.print         = connlimit_print,
+	.save          = connlimit_save,
+	.extra_opts    = connlimit_opts,
+};
+
+static __attribute__((constructor)) void libipt_connlimit_init(void)
+{
+	register_match(&connlimit_reg);
+}
Index: iptables/extensions/libipt_connlimit.man
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.man
@@ -0,0 +1,27 @@
+Allows you to restrict the number of parallel connections to a server per
+client IP address (or client address block).
+.TP
+[\fB!\fR] \fB--connlimit-above \fIn\fR
+Match if the number of existing connections is (not) above \fIn\fR.
+.TP
+\fB--connlimit-mask\fR \fIprefix_length\fR
+Group hosts using the prefix length. For IPv4, this must be a number between
+(including) 0 and 32. For IPv6, between 0 and 128.
+.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 number 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
+.TP
+# limit the number of parallel HTTP requests to 16 for the link local network \
+(ipv6)
+ip6tables -p tcp --syn --dport 80 -s fe80::/64 -m connlimit --connlimit-above
+16 --connlimit-mask 64 -j REJECT
Index: iptables/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ iptables/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,17 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+	union {
+		u_int32_t v4_mask;
+		u_int32_t v6_mask[4];
+	};
+	unsigned int limit, inverse;
+
+	/* this needs to be at the end */
+	struct xt_connlimit_data *data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONNLIMIT_H */

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:38                                                             ` Jan Engelhardt
@ 2007-07-09 15:43                                                               ` Patrick McHardy
  2007-07-13 14:18                                                               ` Yasuyuki KOZAKAI
       [not found]                                                               ` <200707131418.l6DEIudN010879@toshiba.co.jp>
  2 siblings, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-07-09 15:43 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jul 9 2007 17:34, Patrick McHardy wrote:
> 
>>>>Does it still make it into 2.6.23-rc1?
>>
>>>Yes, unless it fails during testing :)
>>
>>Speaking of which, could you resend the latest version of the
>>corresponding iptables patch please?
>>
> 
> At your service!
> 
> (Note the symlink, or how [sw]ould this be done?)


Without symlinks (the patch was correct though), we'll fix these
things up once we have proper x_tables support. I've copied the
IPv4 man page and did s/iptables/ip6tables.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-07 17:51                                             ` xt_connlimit 20070707 kernel Jan Engelhardt
  2007-07-09 14:30                                               ` Patrick McHardy
@ 2007-07-10  6:30                                               ` Yasuyuki KOZAKAI
  2007-07-11 17:37                                                 ` Jan Engelhardt
       [not found]                                               ` <200707100630.l6A6UBM1021597@toshiba.co.jp>
  2 siblings, 1 reply; 71+ messages in thread
From: Yasuyuki KOZAKAI @ 2007-07-10  6:30 UTC (permalink / raw)
  To: jengelh; +Cc: netfilter-devel, kaber


From: Jan Engelhardt <jengelh@computergmbh.de>
Date: Mon, 9 Jul 2007 17:10:52 +0200 (CEST)

> +	} else {
> +		struct nf_conntrack_l3proto *l3proto;
> +		struct nf_conntrack_l4proto *l4proto;
> +
> +		l3proto = __nf_ct_l3proto_find(match->family);
> +		if (l3proto == NULL)
> +			goto hotdrop;
> +		l4proto = __nf_ct_l4proto_find(match->family, match->proto);
> +		if (l4proto == NULL)
> +			goto hotdrop;
> +		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
> +		    &tuple, NULL, NULL) != 0)
> +			goto hotdrop;

The second argument is the offset to network protocol header,
skb_network_offset(skb) (it is 0 in most cases, but it is more logical).

The third argument is the offset to the transport protocol header. In the case
of IPv4, you can use ip_hdrlen(). About IPv6, you can use
nf_ct_ipv6_skip_exthdr(), but you also need to export the symbol of
nf_ct_ipv6_skip_exthdr().

IIRC nf_conntrack_proto_icmp[v6].c do same things. If I have any time
at late night today, I'll write a generic function for them.

-- Yasuyuki Kozakai

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

* Re: xt_connlimit 20070707 kernel
       [not found]                                               ` <200707100630.l6A6UBM1021597@toshiba.co.jp>
@ 2007-07-11 13:23                                                 ` Patrick McHardy
  0 siblings, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-07-11 13:23 UTC (permalink / raw)
  To: Yasuyuki KOZAKAI; +Cc: jengelh, netfilter-devel

Yasuyuki KOZAKAI wrote:
> From: Jan Engelhardt <jengelh@computergmbh.de>
> Date: Mon, 9 Jul 2007 17:10:52 +0200 (CEST)
> 
> 
>>+	} else {
>>+		struct nf_conntrack_l3proto *l3proto;
>>+		struct nf_conntrack_l4proto *l4proto;
>>+
>>+		l3proto = __nf_ct_l3proto_find(match->family);
>>+		if (l3proto == NULL)
>>+			goto hotdrop;
>>+		l4proto = __nf_ct_l4proto_find(match->family, match->proto);
>>+		if (l4proto == NULL)
>>+			goto hotdrop;
>>+		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
>>+		    &tuple, NULL, NULL) != 0)
>>+			goto hotdrop;
> 
> 
> The second argument is the offset to network protocol header,
> skb_network_offset(skb) (it is 0 in most cases, but it is more logical).
> 
> The third argument is the offset to the transport protocol header. In the case
> of IPv4, you can use ip_hdrlen(). About IPv6, you can use
> nf_ct_ipv6_skip_exthdr(), but you also need to export the symbol of
> nf_ct_ipv6_skip_exthdr().


Good point. Another question: Jan, why are you looking up l3proto and
l4proto and don't use them for anything? This doesn't look like you
even tested it ..

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

* Re: xt_connlimit 20070707 kernel
  2007-07-10  6:30                                               ` Yasuyuki KOZAKAI
@ 2007-07-11 17:37                                                 ` Jan Engelhardt
  2007-07-11 18:04                                                   ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-11 17:37 UTC (permalink / raw)
  To: Yasuyuki KOZAKAI; +Cc: Netfilter Developer Mailing List, kaber

On Jul 10 2007 15:30, Yasuyuki KOZAKAI wrote:
>> +		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
>> +		    &tuple, NULL, NULL) != 0)
>> +			goto hotdrop;
>
>The second argument is the offset to network protocol header,
>skb_network_offset(skb) (it is 0 in most cases, but it is more logical).
>
>IIRC nf_conntrack_proto_icmp[v6].c do same things. If I have any time
>at late night today, I'll write a generic function for them.

I have applied your three patches to receive the nf_ct_get_tuplepr() 
function, and connlimit now reads:


---
 net/netfilter/xt_connlimit.c |   20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

Index: net-2.6/net/netfilter/xt_connlimit.c
===================================================================
--- net-2.6.orig/net/netfilter/xt_connlimit.c
+++ net-2.6/net/netfilter/xt_connlimit.c
@@ -185,9 +185,9 @@ static bool connlimit_match(const struct
 			    unsigned int protoff, bool *hotdrop)
 {
 	const struct xt_connlimit_info *info = matchinfo;
-	const struct nf_conntrack_tuple *tuple_ptr;
 	union nf_conntrack_address addr, mask;
 	struct nf_conntrack_tuple tuple;
+	const struct nf_conntrack_tuple *tuple_ptr = &tuple;
 	enum ip_conntrack_info ctinfo;
 	const struct nf_conn *ct;
 	int connections;
@@ -196,19 +196,13 @@ static bool connlimit_match(const struct
 	if (ct != NULL) {
 		tuple_ptr = &ct->tuplehash[0].tuple;
 	} else {
-		struct nf_conntrack_l3proto *l3proto;
-		struct nf_conntrack_l4proto *l4proto;
-
-		l3proto = __nf_ct_l3proto_find(match->family);
-		if (l3proto == NULL)
-			goto hotdrop;
-		l4proto = __nf_ct_l4proto_find(match->family, match->proto);
-		if (l4proto == NULL)
+		int ret;
+		printk(KERN_WARNING "ct==NULL\n");
+		if ((ret = nf_ct_get_tuplepr(skb, skb_network_offset(skb),
+		    match->family, &tuple)) != 0) {
+			printk(KERN_WARNING "drop with %d\n", ret);
 			goto hotdrop;
-		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
-		    &tuple, NULL, NULL) != 0)
-			goto hotdrop;
-		tuple_ptr = &tuple;
+		}
 	}
 
 	if (match->family == AF_INET6) {


But, nf_ct_get_tuplepr() returns with 1, and I am not sure why.



	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
  2007-07-11 17:37                                                 ` Jan Engelhardt
@ 2007-07-11 18:04                                                   ` Patrick McHardy
  2007-07-11 18:18                                                     ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-11 18:04 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Yasuyuki KOZAKAI

Jan Engelhardt wrote:
> On Jul 10 2007 15:30, Yasuyuki KOZAKAI wrote:
> 
>>>+		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
>>>+		    &tuple, NULL, NULL) != 0)
>>>+			goto hotdrop;
>>
>>The second argument is the offset to network protocol header,
>>skb_network_offset(skb) (it is 0 in most cases, but it is more logical).
>>
>>IIRC nf_conntrack_proto_icmp[v6].c do same things. If I have any time
>>at late night today, I'll write a generic function for them.
> 
> 
> I have applied your three patches to receive the nf_ct_get_tuplepr() 
> function, and connlimit now reads:
> 
> [...]
> 
> But, nf_ct_get_tuplepr() returns with 1, and I am not sure why.


Thats expected I guess, 1 is usually "success" for the tuple parsing
functions.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-11 18:04                                                   ` Patrick McHardy
@ 2007-07-11 18:18                                                     ` Jan Engelhardt
  2007-07-11 18:19                                                       ` Jan Engelhardt
  2007-07-11 18:25                                                       ` Patrick McHardy
  0 siblings, 2 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-11 18:18 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Yasuyuki KOZAKAI


On Jul 11 2007 20:04, Patrick McHardy wrote:
>> [...]
>> 
>> But, nf_ct_get_tuplepr() returns with 1, and I am not sure why.
>
>Thats expected I guess, 1 is usually "success" for the tuple parsing
>functions.
>
Such functions should at best return bool :(
Anyway, please take this patch (to be folded onto xt_connlimit) -
runtime-'tested' this time.

Thanks,
	Jan
===
---
 net/netfilter/xt_connlimit.c |   22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

Index: net-2.6/net/netfilter/xt_connlimit.c
===================================================================
--- net-2.6.orig/net/netfilter/xt_connlimit.c
+++ net-2.6/net/netfilter/xt_connlimit.c
@@ -185,31 +185,19 @@ static bool connlimit_match(const struct
 			    unsigned int protoff, bool *hotdrop)
 {
 	const struct xt_connlimit_info *info = matchinfo;
-	const struct nf_conntrack_tuple *tuple_ptr;
 	union nf_conntrack_address addr, mask;
 	struct nf_conntrack_tuple tuple;
+	const struct nf_conntrack_tuple *tuple_ptr = &tuple;
 	enum ip_conntrack_info ctinfo;
 	const struct nf_conn *ct;
 	int connections;
 
 	ct = nf_ct_get(skb, &ctinfo);
-	if (ct != NULL) {
+	if (ct != NULL)
 		tuple_ptr = &ct->tuplehash[0].tuple;
-	} else {
-		struct nf_conntrack_l3proto *l3proto;
-		struct nf_conntrack_l4proto *l4proto;
-
-		l3proto = __nf_ct_l3proto_find(match->family);
-		if (l3proto == NULL)
-			goto hotdrop;
-		l4proto = __nf_ct_l4proto_find(match->family, match->proto);
-		if (l4proto == NULL)
-			goto hotdrop;
-		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
-		    &tuple, NULL, NULL) != 0)
-			goto hotdrop;
-		tuple_ptr = &tuple;
-	}
+	else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
+	    match->family, &tuple))
+		goto hotdrop;
 
 	if (match->family == AF_INET6) {
 		const struct ipv6hdr *iph = ipv6_hdr(skb);

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

* Re: xt_connlimit 20070707 kernel
  2007-07-11 18:18                                                     ` Jan Engelhardt
@ 2007-07-11 18:19                                                       ` Jan Engelhardt
  2007-07-11 18:25                                                       ` Patrick McHardy
  1 sibling, 0 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-11 18:19 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Yasuyuki KOZAKAI


On Jul 11 2007 20:18, Jan Engelhardt wrote:
>>Thats expected I guess, 1 is usually "success" for the tuple parsing
>>functions.
>>
>Such functions should at best return bool :(
>Anyway, please take this patch (to be folded onto xt_connlimit) -
>runtime-'tested' this time.
>
>Thanks,
>	Jan
>===

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

>---
> net/netfilter/xt_connlimit.c |   22 +++++-----------------
> 1 file changed, 5 insertions(+), 17 deletions(-)
>
>Index: net-2.6/net/netfilter/xt_connlimit.c
>===================================================================
>--- net-2.6.orig/net/netfilter/xt_connlimit.c
>+++ net-2.6/net/netfilter/xt_connlimit.c
>@@ -185,31 +185,19 @@ static bool connlimit_match(const struct
> 			    unsigned int protoff, bool *hotdrop)
> {
> 	const struct xt_connlimit_info *info = matchinfo;
>-	const struct nf_conntrack_tuple *tuple_ptr;
> 	union nf_conntrack_address addr, mask;
> 	struct nf_conntrack_tuple tuple;
>+	const struct nf_conntrack_tuple *tuple_ptr = &tuple;
> 	enum ip_conntrack_info ctinfo;
> 	const struct nf_conn *ct;
> 	int connections;
> 
> 	ct = nf_ct_get(skb, &ctinfo);
>-	if (ct != NULL) {
>+	if (ct != NULL)
> 		tuple_ptr = &ct->tuplehash[0].tuple;
>-	} else {
>-		struct nf_conntrack_l3proto *l3proto;
>-		struct nf_conntrack_l4proto *l4proto;
>-
>-		l3proto = __nf_ct_l3proto_find(match->family);
>-		if (l3proto == NULL)
>-			goto hotdrop;
>-		l4proto = __nf_ct_l4proto_find(match->family, match->proto);
>-		if (l4proto == NULL)
>-			goto hotdrop;
>-		if (nf_ct_get_tuple(skb, 0, 0, match->family, match->proto,
>-		    &tuple, NULL, NULL) != 0)
>-			goto hotdrop;
>-		tuple_ptr = &tuple;
>-	}
>+	else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
>+	    match->family, &tuple))
>+		goto hotdrop;
> 
> 	if (match->family == AF_INET6) {
> 		const struct ipv6hdr *iph = ipv6_hdr(skb);
>

	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
  2007-07-11 18:18                                                     ` Jan Engelhardt
  2007-07-11 18:19                                                       ` Jan Engelhardt
@ 2007-07-11 18:25                                                       ` Patrick McHardy
  1 sibling, 0 replies; 71+ messages in thread
From: Patrick McHardy @ 2007-07-11 18:25 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List, Yasuyuki KOZAKAI

Jan Engelhardt wrote:
> On Jul 11 2007 20:04, Patrick McHardy wrote:
> 
>>>[...]
>>>
>>>But, nf_ct_get_tuplepr() returns with 1, and I am not sure why.
>>
>>Thats expected I guess, 1 is usually "success" for the tuple parsing
>>functions.
>>
> 
> Such functions should at best return bool :(
> Anyway, please take this patch (to be folded onto xt_connlimit) -
> runtime-'tested' this time.


Looks good. I've folded it into the original patch, thanks Jan.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-09 15:38                                                             ` Jan Engelhardt
  2007-07-09 15:43                                                               ` Patrick McHardy
@ 2007-07-13 14:18                                                               ` Yasuyuki KOZAKAI
       [not found]                                                               ` <200707131418.l6DEIudN010879@toshiba.co.jp>
  2 siblings, 0 replies; 71+ messages in thread
From: Yasuyuki KOZAKAI @ 2007-07-13 14:18 UTC (permalink / raw)
  To: jengelh; +Cc: netfilter-devel, kaber


Hi,

From: Jan Engelhardt <jengelh@computergmbh.de>
Date: Mon, 9 Jul 2007 17:38:52 +0200 (CEST)

> On Jul 9 2007 17:34, Patrick McHardy wrote:
> >>>Does it still make it into 2.6.23-rc1?
> >
> >> Yes, unless it fails during testing :)
> >
> >Speaking of which, could you resend the latest version of the
> >corresponding iptables patch please?
> >
> At your service!
> 
> (Note the symlink, or how [sw]ould this be done?)
> 
> 
> Thanks,
> 	Jan
> ===
> 
> Subject: Add connlimit to iptables.
> 
> Signed-off-by: Jan Engelhardt <jengelh@gmx.de>
> 
> ---
> 

[...]

> +static __attribute__((constructor)) void libipt_connlimit_init(void)
> +{
> +	register_match(&connlimit_reg);
> +}

JFYI: I've fixed compile error in the case of NO_SHARED_LIBS=1. The
function name for initialization needs to be _init, not
libipt_connlimit_init.

-- Yasuyuki Kozakai

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

* Re: xt_connlimit 20070707 kernel
       [not found]                                                               ` <200707131418.l6DEIudN010879@toshiba.co.jp>
@ 2007-07-13 15:01                                                                 ` Jan Engelhardt
  2007-07-13 15:03                                                                   ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-13 15:01 UTC (permalink / raw)
  To: Yasuyuki KOZAKAI; +Cc: netfilter-devel, kaber


On Jul 13 2007 23:18, Yasuyuki KOZAKAI wrote:
>
>> +static __attribute__((constructor)) void libipt_connlimit_init(void)
>> +{
>> +	register_match(&connlimit_reg);
>> +}
>
>JFYI: I've fixed compile error in the case of NO_SHARED_LIBS=1. The
>function name for initialization needs to be _init, not
>libipt_connlimit_init.

Uhm, that's why there is __attribute__((constructor)). Because *having* an
_init in a shared library has often failed on me. Example:


$ cat test.c
#include <stdio.h>
void _init(void)
{
        printf("Hello world\n");
}

$ cc test.c -shared -o test.so -Wall
/tmp/ccBDCmOL.o: In function `_init':
test.c:(.text+0x0): multiple definition of `_init'
/usr/lib/gcc/i586-suse-linux/4.1.2/../../../crti.o:/usr/src/packages/BUILD/glibc-2.5/cc-nptl/csu/crti.S:36:
first defined here
collect2: ld returned 1 exit status


And I certainly do not want to build without the GNU runtime,
because that makes things a bit more unportable (trying to
get the right linker switch, for ex.)




	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:01                                                                 ` Jan Engelhardt
@ 2007-07-13 15:03                                                                   ` Patrick McHardy
  2007-07-13 15:13                                                                     ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-13 15:03 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: netfilter-devel, Yasuyuki KOZAKAI

Jan Engelhardt wrote:
> On Jul 13 2007 23:18, Yasuyuki KOZAKAI wrote:
> 
>>JFYI: I've fixed compile error in the case of NO_SHARED_LIBS=1. The
>>function name for initialization needs to be _init, not
>>libipt_connlimit_init.
> 
> 
> Uhm, that's why there is __attribute__((constructor)). Because *having* an
> _init in a shared library has often failed on me. Example:
> 
> [...]


It works for other extensions, so it will also work for this one.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:03                                                                   ` Patrick McHardy
@ 2007-07-13 15:13                                                                     ` Jan Engelhardt
  2007-07-13 15:16                                                                       ` Patrick McHardy
                                                                                         ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-13 15:13 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netfilter-devel, Yasuyuki KOZAKAI


On Jul 13 2007 17:03, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> On Jul 13 2007 23:18, Yasuyuki KOZAKAI wrote:
>> 
>>>JFYI: I've fixed compile error in the case of NO_SHARED_LIBS=1. The
>>>function name for initialization needs to be _init, not
>>>libipt_connlimit_init.
>> 
>> 
>> Uhm, that's why there is __attribute__((constructor)). Because *having* an
>> _init in a shared library has often failed on me. Example:
>> 
>> [...]
>
>It works for other extensions, so it will also work for this one.
>

Yes of course.

#define _init __attribute__((constructor)) my_init

That's exactly the same as before (for the NO_SHARED_LIBS=0 case).
For NO_SHARED_LIBS=1, _init is replaced by connlimit_init instead. Also ok.
Well, connlimit_init is not that far from libipt_connlimit_init,
and I'd prefer the latter because the former is already used for

  .init = connlimit_init,


Thanks,
	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:13                                                                     ` Jan Engelhardt
@ 2007-07-13 15:16                                                                       ` Patrick McHardy
  2007-07-13 15:31                                                                         ` Jan Engelhardt
  2007-07-13 15:44                                                                       ` Yasuyuki KOZAKAI
       [not found]                                                                       ` <200707131544.l6DFivSf011446@toshiba.co.jp>
  2 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-13 15:16 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: netfilter-devel, Yasuyuki KOZAKAI

Jan Engelhardt wrote:
> On Jul 13 2007 17:03, Patrick McHardy wrote:
> 
>>It works for other extensions, so it will also work for this one.
>>
> 
> 
> Yes of course.
> 
> #define _init __attribute__((constructor)) my_init
> 
> That's exactly the same as before (for the NO_SHARED_LIBS=0 case).
> For NO_SHARED_LIBS=1, _init is replaced by connlimit_init instead. Also ok.
> Well, connlimit_init is not that far from libipt_connlimit_init,
> and I'd prefer the latter because the former is already used for
> 
>   .init = connlimit_init,


Frankly, I'm not too interested in this as long as it works.
So if you have any dislikes with the current code, please
send a patch.

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:16                                                                       ` Patrick McHardy
@ 2007-07-13 15:31                                                                         ` Jan Engelhardt
  2007-07-13 15:42                                                                           ` Patrick McHardy
  0 siblings, 1 reply; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-13 15:31 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netfilter-devel, Yasuyuki KOZAKAI


On Jul 13 2007 17:16, Patrick McHardy wrote:
>> Yes of course.
>> 
>> #define _init __attribute__((constructor)) my_init
>> 
>> That's exactly the same as before (for the NO_SHARED_LIBS=0 case).
>> For NO_SHARED_LIBS=1, _init is replaced by connlimit_init instead. Also ok.
>> Well, connlimit_init is not that far from libipt_connlimit_init,
>> and I'd prefer the latter because the former is already used for
>> 
>>   .init = connlimit_init,
>
>Frankly, I'm not too interested in this as long as it works.
>So if you have any dislikes with the current code, please
>send a patch.
>
Alright... later.

Could you set some SVN properties?
On ".", "libiptc" and "extensions", svn:ignore with
*.d
*.so
*.o



	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:31                                                                         ` Jan Engelhardt
@ 2007-07-13 15:42                                                                           ` Patrick McHardy
  2007-07-13 16:08                                                                             ` Jan Engelhardt
  0 siblings, 1 reply; 71+ messages in thread
From: Patrick McHardy @ 2007-07-13 15:42 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: netfilter-devel, Yasuyuki KOZAKAI

Jan Engelhardt wrote:
> Could you set some SVN properties?
> On ".", "libiptc" and "extensions", svn:ignore with
> *.d
> *.so
> *.o


If you tell me how to do that ..

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:13                                                                     ` Jan Engelhardt
  2007-07-13 15:16                                                                       ` Patrick McHardy
@ 2007-07-13 15:44                                                                       ` Yasuyuki KOZAKAI
       [not found]                                                                       ` <200707131544.l6DFivSf011446@toshiba.co.jp>
  2 siblings, 0 replies; 71+ messages in thread
From: Yasuyuki KOZAKAI @ 2007-07-13 15:44 UTC (permalink / raw)
  To: jengelh; +Cc: netfilter-devel, kaber, yasuyuki.kozakai


From: Jan Engelhardt <jengelh@computergmbh.de>
Date: Fri, 13 Jul 2007 17:13:19 +0200 (CEST)

> Yes of course.
> 
> #define _init __attribute__((constructor)) my_init
> 
> That's exactly the same as before (for the NO_SHARED_LIBS=0 case).
> For NO_SHARED_LIBS=1, _init is replaced by connlimit_init instead. Also ok.
> Well, connlimit_init is not that far from libipt_connlimit_init,
> and I'd prefer the latter because the former is already used for
> 
>   .init = connlimit_init,

It's unnecessary to change it. _init is replaced by ipt_connlimit_init, not
connlimit_init for NO_SHARED_LIBS=1. That's why I could compile connlimit
match without error.

-- Yasuyuki Kozakai

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

* Re: xt_connlimit 20070707 kernel
  2007-07-13 15:42                                                                           ` Patrick McHardy
@ 2007-07-13 16:08                                                                             ` Jan Engelhardt
  0 siblings, 0 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-13 16:08 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netfilter-devel, Yasuyuki KOZAKAI


On Jul 13 2007 17:42, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> Could you set some SVN properties?
>> On ".", "libiptc" and "extensions", svn:ignore with
>> *.d
>> *.so
>> *.o
>
>If you tell me how to do that ..
>

svn propedit svn:ignore . libiptc extensions

will open $EDITOR three times (one after another). It's just textfiles,
that's all.

svn commit



	Jan
-- 

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

* Re: xt_connlimit 20070707 kernel
       [not found]                                                                       ` <200707131544.l6DFivSf011446@toshiba.co.jp>
@ 2007-07-13 16:09                                                                         ` Jan Engelhardt
  0 siblings, 0 replies; 71+ messages in thread
From: Jan Engelhardt @ 2007-07-13 16:09 UTC (permalink / raw)
  To: Yasuyuki KOZAKAI; +Cc: netfilter-devel, kaber


On Jul 14 2007 00:44, Yasuyuki KOZAKAI wrote:
>> Yes of course.
>> 
>> #define _init __attribute__((constructor)) my_init
>> 
>> That's exactly the same as before (for the NO_SHARED_LIBS=0 case).
>> For NO_SHARED_LIBS=1, _init is replaced by connlimit_init instead. Also ok.
>> Well, connlimit_init is not that far from libipt_connlimit_init,
>> and I'd prefer the latter because the former is already used for
>> 
>>   .init = connlimit_init,
>
>It's unnecessary to change it. _init is replaced by ipt_connlimit_init, not
>connlimit_init for NO_SHARED_LIBS=1. That's why I could compile connlimit
>match without error.

Very good.
Thank you for the NO_SHARED_LIBS test.



	Jan
-- 

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

end of thread, other threads:[~2007-07-13 16:09 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-20  9:39 xt_connlimit 20070620_2 Jan Engelhardt
2007-06-20 17:21 ` Andrew Beverley
2007-06-22 11:14 ` Patrick McHardy
2007-06-22 11:36   ` Jan Engelhardt
2007-06-22 11:43     ` Patrick McHardy
2007-06-22 12:33       ` Jan Engelhardt
2007-06-22 12:42         ` Patrick McHardy
2007-06-22 13:22           ` Jan Engelhardt
2007-06-22 13:27             ` Patrick McHardy
2007-06-25 11:11   ` xt_connlimit 20070625 Jan Engelhardt
2007-06-25 11:41     ` Patrick McHardy
2007-06-25 11:45       ` Patrick McHardy
2007-06-25 12:36       ` Jan Engelhardt
2007-06-25 12:43         ` Patrick McHardy
2007-06-25 14:41           ` Jan Engelhardt
2007-06-25 14:46             ` Patrick McHardy
2007-06-25 15:01               ` Jan Engelhardt
2007-06-25 15:05                 ` Patrick McHardy
2007-06-25 15:05                 ` Patrick McHardy
2007-06-25 15:14                   ` Jan Engelhardt
2007-06-28 19:23       ` Jan Engelhardt
2007-06-28 19:27         ` Patrick McHardy
2007-06-28 19:31           ` Jan Engelhardt
2007-06-28 19:33             ` Patrick McHardy
2007-06-28 19:48             ` Patrick McHardy
2007-06-28 19:51               ` xt_connlimit 20070628 Jan Engelhardt
2007-06-28 19:55                 ` xt_connlimit 20070628 kernel Jan Engelhardt
2007-06-29 11:27                   ` Patrick McHardy
2007-07-01 14:11                     ` Jan Engelhardt
2007-07-02 12:27                       ` Patrick McHardy
2007-07-02 15:38                         ` Jan Engelhardt
2007-07-02 15:40                           ` Patrick McHardy
2007-07-02 19:53                             ` Jan Engelhardt
2007-07-03 11:14                               ` Patrick McHardy
2007-07-03 11:31                                 ` Jan Engelhardt
2007-07-03 11:34                                   ` Patrick McHardy
2007-07-04 10:56                                     ` Jan Engelhardt
2007-07-04 14:52                                       ` Patrick McHardy
2007-07-04 15:11                                         ` Jan Engelhardt
2007-07-06 13:05                                           ` Patrick McHardy
2007-07-07 17:51                                             ` xt_connlimit 20070707 kernel Jan Engelhardt
2007-07-09 14:30                                               ` Patrick McHardy
2007-07-09 15:10                                                 ` Jan Engelhardt
2007-07-09 15:20                                                   ` Patrick McHardy
2007-07-09 15:29                                                     ` Patrick McHardy
2007-07-09 15:32                                                       ` Jan Engelhardt
2007-07-09 15:33                                                         ` Patrick McHardy
2007-07-09 15:34                                                           ` Patrick McHardy
2007-07-09 15:38                                                             ` Jan Engelhardt
2007-07-09 15:43                                                               ` Patrick McHardy
2007-07-13 14:18                                                               ` Yasuyuki KOZAKAI
     [not found]                                                               ` <200707131418.l6DEIudN010879@toshiba.co.jp>
2007-07-13 15:01                                                                 ` Jan Engelhardt
2007-07-13 15:03                                                                   ` Patrick McHardy
2007-07-13 15:13                                                                     ` Jan Engelhardt
2007-07-13 15:16                                                                       ` Patrick McHardy
2007-07-13 15:31                                                                         ` Jan Engelhardt
2007-07-13 15:42                                                                           ` Patrick McHardy
2007-07-13 16:08                                                                             ` Jan Engelhardt
2007-07-13 15:44                                                                       ` Yasuyuki KOZAKAI
     [not found]                                                                       ` <200707131544.l6DFivSf011446@toshiba.co.jp>
2007-07-13 16:09                                                                         ` Jan Engelhardt
2007-07-10  6:30                                               ` Yasuyuki KOZAKAI
2007-07-11 17:37                                                 ` Jan Engelhardt
2007-07-11 18:04                                                   ` Patrick McHardy
2007-07-11 18:18                                                     ` Jan Engelhardt
2007-07-11 18:19                                                       ` Jan Engelhardt
2007-07-11 18:25                                                       ` Patrick McHardy
     [not found]                                               ` <200707100630.l6A6UBM1021597@toshiba.co.jp>
2007-07-11 13:23                                                 ` Patrick McHardy
2007-07-04  8:55                         ` xt_connlimit 20070628 kernel Yasuyuki KOZAKAI
2007-07-04 14:52                           ` Patrick McHardy
2007-06-28 20:08                 ` xt_connlimit 20070628 Jan Engelhardt
2007-06-25 18:51 ` xt_connlimit 20070620_2 Patrick McHardy

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.