summaryrefslogtreecommitdiffstats
path: root/net/netfilter/xt_connlimit.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/netfilter/xt_connlimit.c')
-rw-r--r--net/netfilter/xt_connlimit.c137
1 files changed, 137 insertions, 0 deletions
diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c
new file mode 100644
index 000000000..5d04ef80a
--- /dev/null
+++ b/net/netfilter/xt_connlimit.c
@@ -0,0 +1,137 @@
+/*
+ * 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
+ * (C) CC Computer Consultants GmbH, 2007
+ *
+ * based on ...
+ *
+ * Kernel module to match connection tracking information.
+ * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/module.h>
+#include <linux/skbuff.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>
+#include <net/netfilter/nf_conntrack_zones.h>
+#include <net/netfilter/nf_conntrack_count.h>
+
+static bool
+connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ struct net *net = xt_net(par);
+ const struct xt_connlimit_info *info = par->matchinfo;
+ struct nf_conntrack_tuple tuple;
+ const struct nf_conntrack_tuple *tuple_ptr = &tuple;
+ const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
+ enum ip_conntrack_info ctinfo;
+ const struct nf_conn *ct;
+ unsigned int connections;
+ u32 key[5];
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct != NULL) {
+ tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+ zone = nf_ct_zone(ct);
+ } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
+ xt_family(par), net, &tuple)) {
+ goto hotdrop;
+ }
+
+ if (xt_family(par) == NFPROTO_IPV6) {
+ const struct ipv6hdr *iph = ipv6_hdr(skb);
+ union nf_inet_addr addr;
+ unsigned int i;
+
+ memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ?
+ &iph->daddr : &iph->saddr, sizeof(addr.ip6));
+
+ for (i = 0; i < ARRAY_SIZE(addr.ip6); ++i)
+ addr.ip6[i] &= info->mask.ip6[i];
+ memcpy(key, &addr, sizeof(addr.ip6));
+ key[4] = zone->id;
+ } else {
+ const struct iphdr *iph = ip_hdr(skb);
+
+ key[0] = (info->flags & XT_CONNLIMIT_DADDR) ?
+ (__force __u32)iph->daddr : (__force __u32)iph->saddr;
+ key[0] &= (__force __u32)info->mask.ip;
+ key[1] = zone->id;
+ }
+
+ connections = nf_conncount_count(net, info->data, key, tuple_ptr,
+ zone);
+ if (connections == 0)
+ /* kmalloc failed, drop it entirely */
+ goto hotdrop;
+
+ return (connections > info->limit) ^ !!(info->flags & XT_CONNLIMIT_INVERT);
+
+ hotdrop:
+ par->hotdrop = true;
+ return false;
+}
+
+static int connlimit_mt_check(const struct xt_mtchk_param *par)
+{
+ struct xt_connlimit_info *info = par->matchinfo;
+ unsigned int keylen;
+
+ keylen = sizeof(u32);
+ if (par->family == NFPROTO_IPV6)
+ keylen += sizeof(struct in6_addr);
+ else
+ keylen += sizeof(struct in_addr);
+
+ /* init private data */
+ info->data = nf_conncount_init(par->net, par->family, keylen);
+
+ return PTR_ERR_OR_ZERO(info->data);
+}
+
+static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
+{
+ const struct xt_connlimit_info *info = par->matchinfo;
+
+ nf_conncount_destroy(par->net, par->family, info->data);
+}
+
+static struct xt_match connlimit_mt_reg __read_mostly = {
+ .name = "connlimit",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = connlimit_mt_check,
+ .match = connlimit_mt,
+ .matchsize = sizeof(struct xt_connlimit_info),
+ .usersize = offsetof(struct xt_connlimit_info, data),
+ .destroy = connlimit_mt_destroy,
+ .me = THIS_MODULE,
+};
+
+static int __init connlimit_mt_init(void)
+{
+ return xt_register_match(&connlimit_mt_reg);
+}
+
+static void __exit connlimit_mt_exit(void)
+{
+ xt_unregister_match(&connlimit_mt_reg);
+}
+
+module_init(connlimit_mt_init);
+module_exit(connlimit_mt_exit);
+MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
+MODULE_DESCRIPTION("Xtables: Number of connections matching");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_connlimit");
+MODULE_ALIAS("ip6t_connlimit");