* [PATCH 1/2] xt_connlimit (kernel) - connection limiting
2007-06-03 11:12 [PATCH 0/2] xt_connlimit - connection limiting Jan Engelhardt
@ 2007-06-03 11:12 ` Jan Engelhardt
2007-06-03 11:46 ` Yasuyuki KOZAKAI
` (2 more replies)
2007-06-03 11:14 ` [PATCH 2/2] xt_connlimit (iptables) " Jan Engelhardt
1 sibling, 3 replies; 9+ messages in thread
From: Jan Engelhardt @ 2007-06-03 11:12 UTC (permalink / raw)
To: Netfilter Developer Mailing List
Cc: Netfilter Mailing List, Linux Kernel Mailing List
Adds the connlimit match that has been in POM-NG for a long time.
* works with 2.6.22, xtables'ified and all that
* will request nf_conntrack_ipv4 upon load
(otherwise it hotdrops every packet - a glitch that goes back
to at least 2.6.20.2)
Signed-off-by: Jan Engelhardt <jengelh@gmx.de>
---
include/linux/netfilter/nf_conntrack_common.h | 1
include/linux/netfilter/xt_connlimit.h | 14 +
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 5
net/netfilter/Kconfig | 7
net/netfilter/Makefile | 1
net/netfilter/xt_connlimit.c | 250 +++++++++++++++++++++++++
6 files changed, 278 insertions(+)
Index: linux-2.6.22-rc3-git6/include/linux/netfilter/nf_conntrack_common.h
===================================================================
--- linux-2.6.22-rc3-git6.orig/include/linux/netfilter/nf_conntrack_common.h
+++ linux-2.6.22-rc3-git6/include/linux/netfilter/nf_conntrack_common.h
@@ -164,6 +164,7 @@ struct ip_conntrack_stat
/* call to create an explicit dependency on nf_conntrack. */
extern void need_conntrack(void);
+extern void need_conntrack_ipv4(void);
#endif /* __KERNEL__ */
Index: linux-2.6.22-rc3-git6/include/linux/netfilter/xt_connlimit.h
===================================================================
--- /dev/null
+++ linux-2.6.22-rc3-git6/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,14 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+struct xt_connlimit_data;
+
+struct xt_connlimit_info {
+ uint32_t mask;
+ unsigned int limit, inverse;
+
+ /* this needs to be at the end */
+ struct xt_connlimit_data *data;
+};
+
+#endif /* _XT_CONNLIMIT_H */
Index: linux-2.6.22-rc3-git6/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ linux-2.6.22-rc3-git6/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -519,3 +519,8 @@ static void __exit nf_conntrack_l3proto_
module_init(nf_conntrack_l3proto_ipv4_init);
module_exit(nf_conntrack_l3proto_ipv4_fini);
+
+void need_conntrack_ipv4(void)
+{
+}
+EXPORT_SYMBOL(need_conntrack_ipv4);
Index: linux-2.6.22-rc3-git6/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/netfilter/Kconfig
+++ linux-2.6.22-rc3-git6/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-rc3-git6/net/netfilter/Makefile
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/netfilter/Makefile
+++ linux-2.6.22-rc3-git6/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-rc3-git6/net/netfilter/xt_connlimit.c
===================================================================
--- /dev/null
+++ linux-2.6.22-rc3-git6/net/netfilter/xt_connlimit.c
@@ -0,0 +1,250 @@
+/*
+ * netfilter module to limit the number of parallel tcp
+ * connections per IP address.
+ * (c) 2000 Gerd Knorr <kraxel@bytesex.org>
+ * Nov 2002: Martin Bene <martin.bene@icomedias.com>:
+ * only ignore TIME_WAIT or gone connections
+ * © Jan Engelhardt <jengelh@gmx.de>, 2007
+ *
+ * based on ...
+ *
+ * Kernel module to match connection tracking information.
+ * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
+ */
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tcp.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_connlimit.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+
+#define DEBUG 0
+
+/* we will save the tuples of all connections we care about */
+struct xt_connlimit_conn {
+ struct list_head list;
+ struct nf_conntrack_tuple tuple;
+};
+
+struct xt_connlimit_data {
+ struct list_head iphash[256];
+ spinlock_t lock;
+};
+
+static inline unsigned int connlimit_iphash(uint32_t addr)
+{
+ return (addr ^ (addr >> 8) ^ (addr >> 16) ^ (addr >> 24)) & 0xff;
+}
+
+static int count_them(struct xt_connlimit_data *data, uint32_t addr,
+ uint32_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;
+ struct list_head *hash, *lh;
+ int addit = 1, matches = 0;
+ struct nf_conn *found_ct;
+
+ spin_lock_bh(&data->lock);
+ tuple = ct->tuplehash[0].tuple;
+ hash = &data->iphash[connlimit_iphash(addr & mask)];
+
+ /* check the saved connections */
+ for (lh = hash->next; lh != hash; lh = lh->next) {
+ conn = list_entry(lh, struct xt_connlimit_conn, list);
+ found = nf_conntrack_find_get(&conn->tuple, ct);
+ found_ct = NULL;
+
+ if (found != NULL &&
+ (found_ct = nf_ct_tuplehash_to_ctrack(found)) != NULL &&
+ memcmp(&conn->tuple, &tuple, sizeof(tuple)) == 0 &&
+ found_ct->proto.tcp.state != TCP_CONNTRACK_TIME_WAIT)
+ /*
+ * Just to be sure we have it only once in the list.
+ * We should not see tuples twice unless someone hooks
+ * this into a table without "-p tcp --syn".
+ */
+ addit = 0;
+
+#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 IP address -> 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;
+ }
+
+ spin_unlock_bh(&data->lock);
+ return matches;
+}
+
+static int xt_connlimit_match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct xt_match *match,
+ const void *matchinfo, int offset,
+ unsigned int protoff, int *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 or "
+ "nf_conntrack_ipv4 not loaded\n");
+ *hotdrop = 1;
+ return false;
+ }
+
+ iph = ip_hdr(skb);
+ connections = count_them(info->data, iph->saddr, info->mask, ct);
+ 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 int xt_connlimit_check(const char *tablename, const void *ip,
+ const struct xt_match *match, void *matchinfo,
+ unsigned int hook_mask)
+{
+ struct xt_connlimit_info *info = matchinfo;
+ unsigned int i;
+
+ /* init private data */
+ info->data = kmalloc(sizeof(struct xt_connlimit_data), GFP_KERNEL);
+ spin_lock_init(&info->data->lock);
+ for (i = 0; i < 256; ++i)
+ INIT_LIST_HEAD(&info->data->iphash[i]);
+
+ return 1;
+}
+
+static void xt_connlimit_destroy(const struct xt_match *match, void *matchinfo)
+{
+ struct xt_connlimit_info *info = matchinfo;
+ struct xt_connlimit_conn *conn;
+ struct list_head *hash;
+ unsigned int i;
+
+ for (i = 0; i < 256; ++i) {
+ hash = &info->data->iphash[i];
+ while (hash != hash->next) {
+ conn = list_entry(hash->next,
+ struct xt_connlimit_conn, list);
+ list_del(hash->next);
+ kfree(conn);
+ }
+ }
+
+ kfree(info->data);
+ return;
+}
+
+static struct xt_match xt_connlimit_reg = {
+ .name = "connlimit",
+ .family = AF_INET,
+ .proto = IPPROTO_TCP,
+ .checkentry = xt_connlimit_check,
+ .match = xt_connlimit_match,
+ .matchsize = sizeof(struct xt_connlimit_info),
+ .destroy = xt_connlimit_destroy,
+ .me = THIS_MODULE,
+};
+
+static int __init xt_connlimit_init(void)
+{
+ need_conntrack_ipv4();
+ return xt_register_match(&xt_connlimit_reg);
+}
+
+static void __exit xt_connlimit_exit(void)
+{
+ xt_unregister_match(&xt_connlimit_reg);
+ return;
+}
+
+module_init(xt_connlimit_init);
+module_exit(xt_connlimit_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 2/2] xt_connlimit (iptables) - connection limiting
2007-06-03 11:12 [PATCH 0/2] xt_connlimit - connection limiting Jan Engelhardt
2007-06-03 11:12 ` [PATCH 1/2] xt_connlimit (kernel) " Jan Engelhardt
@ 2007-06-03 11:14 ` Jan Engelhardt
2007-06-03 11:23 ` Jan Engelhardt
1 sibling, 1 reply; 9+ messages in thread
From: Jan Engelhardt @ 2007-06-03 11:14 UTC (permalink / raw)
To: Netfilter Developer Mailing List
Cc: Netfilter Mailing List, Linux Kernel Mailing List
Adds connlimit to iptables.
Signed-off-by: Jan Engelhardt <jengelh@gmx.de>
---
extensions/.connlimit-test | 2
extensions/libipt_connlimit.c | 129 ++++++++++++++++++++++++++++++++++++++++
extensions/libipt_connlimit.man | 21 ++++++
3 files changed, 152 insertions(+)
Index: iptables/extensions/.connlimit-test
===================================================================
--- /dev/null
+++ iptables/extensions/.connlimit-test
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f "$KERNEL_DIR/include/linux/netfilter/xt_connlimit.h" ] && echo connlimit
Index: iptables/extensions/libipt_connlimit.c
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.c
@@ -0,0 +1,129 @@
+/* Shared library add-on to iptables to add connection limit support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/xt_connlimit.h>
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+ printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n match if the number of existing tcp connections is (not) above n\n"
+" --connlimit-mask n group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+ {"connlimit-above", 1, NULL, '1'},
+ {"connlimit-mask", 1, NULL, '2'},
+ {NULL},
+};
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry, unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct xt_connlimit_info *info = (void *)(*match)->data;
+ int i;
+
+ if (!(*flags & 2))
+ /*
+ * set default mask unless we have already seen a mask option
+ */
+ info->mask = htonl(0xFFFFFFFF);
+
+ switch (c) {
+ case '1':
+ check_inverse(optarg, &invert, &optind, 0);
+ info->limit = strtoul(argv[optind-1], NULL, 0);
+ info->inverse = invert;
+ *flags |= 1;
+ break;
+
+ case '2':
+ i = strtol(argv[optind-1], NULL, 0);
+ if (i < 0 || i > 32)
+ exit_error(PARAMETER_PROBLEM,
+ "--connlimit-mask must be between 0 and 32");
+
+ if (i == 0)
+ info->mask = 0;
+ else
+ info->mask = htonl(0xFFFFFFFF << (32 - i));
+
+ *flags |= 2;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check */
+static void final_check(unsigned int flags)
+{
+ if (!(flags & 1))
+ exit_error(PARAMETER_PROBLEM, "You must specify `--connlimit-above'");
+}
+
+static int count_bits(u_int32_t mask)
+{
+ int i, bits;
+
+ for (bits = 0, i = 31; i >= 0; i--) {
+ if (mask & htonl((u_int32_t)1 << i)) {
+ bits++;
+ continue;
+ }
+ break;
+ }
+ return bits;
+}
+
+/* Prints out the matchinfo. */
+static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match,
+ int numeric)
+{
+ const struct xt_connlimit_info *info = (const void *)match->data;
+
+ printf("#conn/%d %s %d ", count_bits(info->mask),
+ info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ const struct xt_connlimit_info *info = (const void *)match->data;
+
+ printf("%s--connlimit-above %u --connlimit-mask %u ",
+ info->inverse ? "! " : "", info->limit,
+ count_bits(info->mask));
+}
+
+static struct iptables_match connlimit = {
+ .name = "connlimit",
+ .version = IPTABLES_VERSION,
+ .size = IPT_ALIGN(sizeof(struct xt_connlimit_info)),
+ .userspacesize = offsetof(struct xt_connlimit_info, data),
+ .help = help,
+ .parse = parse,
+ .final_check = final_check,
+ .print = print,
+ .save = save,
+ .extra_opts = opts,
+};
+
+static __attribute__((constructor)) void libipt_connlimit_init(void)
+{
+ register_match(&connlimit);
+}
Index: iptables/extensions/libipt_connlimit.man
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_connlimit.man
@@ -0,0 +1,21 @@
+Allows you to restrict the number of parallel TCP connections to a
+server per client IP address (or address block).
+.TP
+[\fB!\fR] \fB--connlimit-above \fIn\fR
+match if the number of existing tcp connections is (not) above n
+.TP
+.BI "--connlimit-mask " "bits"
+group hosts using mask
+.P
+Examples:
+.TP
+# allow 2 telnet connections per client host
+iptables -A INPUT -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT
+.TP
+# you can also match the other way around:
+iptables -A INPUT -p tcp --syn --dport 23 -m connlimit ! --connlimit-above 2 -j ACCEPT
+.TP
+# limit the nr of parallel http requests to 16 per class C sized \
+network (24 bit netmask)
+iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 16
+--connlimit-mask 24 -j REJECT
^ permalink raw reply [flat|nested] 9+ messages in thread