blob: 46fcac75f7268678019e4d79900e8ef39a47d6f4 [file] [log] [blame]
Jan Engelhardt370786f2007-07-14 20:47:26 -07001/*
2 * netfilter module to limit the number of parallel tcp
3 * connections per IP address.
4 * (c) 2000 Gerd Knorr <kraxel@bytesex.org>
5 * Nov 2002: Martin Bene <martin.bene@icomedias.com>:
6 * only ignore TIME_WAIT or gone connections
Jan Engelhardtba5dc272007-11-05 20:35:56 -08007 * (C) CC Computer Consultants GmbH, 2007
Jan Engelhardt370786f2007-07-14 20:47:26 -07008 *
9 * based on ...
10 *
11 * Kernel module to match connection tracking information.
12 * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
13 */
Jan Engelhardt8bee4ba2010-03-17 16:04:40 +010014#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Florian Westphal625c5562017-12-09 21:01:08 +010015
Jeremy Sowden40d102c2019-09-13 09:13:05 +010016#include <linux/ip.h>
17#include <linux/ipv6.h>
Jan Engelhardt370786f2007-07-14 20:47:26 -070018#include <linux/module.h>
Jan Engelhardt370786f2007-07-14 20:47:26 -070019#include <linux/skbuff.h>
Jan Engelhardt370786f2007-07-14 20:47:26 -070020#include <linux/netfilter/x_tables.h>
21#include <linux/netfilter/xt_connlimit.h>
Florian Westphal625c5562017-12-09 21:01:08 +010022
Jan Engelhardt370786f2007-07-14 20:47:26 -070023#include <net/netfilter/nf_conntrack.h>
24#include <net/netfilter/nf_conntrack_core.h>
25#include <net/netfilter/nf_conntrack_tuple.h>
Patrick McHardy5d0aa2c2010-02-15 18:13:33 +010026#include <net/netfilter/nf_conntrack_zones.h>
Florian Westphal625c5562017-12-09 21:01:08 +010027#include <net/netfilter/nf_conntrack_count.h>
Florian Westphal15cfd522014-03-07 14:37:09 +010028
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -080029static bool
Jan Engelhardt62fc8052009-07-07 20:42:08 +020030connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
Jan Engelhardt370786f2007-07-14 20:47:26 -070031{
Pablo Neira Ayuso613dbd92016-11-03 10:56:21 +010032 struct net *net = xt_net(par);
Jan Engelhardtf7108a202008-10-08 11:35:18 +020033 const struct xt_connlimit_info *info = par->matchinfo;
Jan Engelhardt370786f2007-07-14 20:47:26 -070034 struct nf_conntrack_tuple tuple;
35 const struct nf_conntrack_tuple *tuple_ptr = &tuple;
Daniel Borkmann308ac912015-08-08 21:40:01 +020036 const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
Jan Engelhardt370786f2007-07-14 20:47:26 -070037 enum ip_conntrack_info ctinfo;
38 const struct nf_conn *ct;
Florian Westphal7d084872014-03-12 23:49:51 +010039 unsigned int connections;
Florian Westphal625c5562017-12-09 21:01:08 +010040 u32 key[5];
Jan Engelhardt370786f2007-07-14 20:47:26 -070041
42 ct = nf_ct_get(skb, &ctinfo);
Florian Westphale59ea3d2014-11-14 13:21:48 +010043 if (ct != NULL) {
Changli Gao8183e3a2011-03-15 13:23:28 +010044 tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
Florian Westphale59ea3d2014-11-14 13:21:48 +010045 zone = nf_ct_zone(ct);
46 } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
Pablo Neira Ayuso613dbd92016-11-03 10:56:21 +010047 xt_family(par), net, &tuple)) {
Jan Engelhardt370786f2007-07-14 20:47:26 -070048 goto hotdrop;
Florian Westphale59ea3d2014-11-14 13:21:48 +010049 }
Jan Engelhardt370786f2007-07-14 20:47:26 -070050
Pablo Neira Ayuso613dbd92016-11-03 10:56:21 +010051 if (xt_family(par) == NFPROTO_IPV6) {
Jan Engelhardt370786f2007-07-14 20:47:26 -070052 const struct ipv6hdr *iph = ipv6_hdr(skb);
Florian Westphal625c5562017-12-09 21:01:08 +010053 union nf_inet_addr addr;
Florian Westphalb1fc1372017-11-02 12:50:38 +010054 unsigned int i;
55
Jan Engelhardtcc4fc022011-01-18 17:32:40 +010056 memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ?
57 &iph->daddr : &iph->saddr, sizeof(addr.ip6));
Florian Westphalb1fc1372017-11-02 12:50:38 +010058
59 for (i = 0; i < ARRAY_SIZE(addr.ip6); ++i)
60 addr.ip6[i] &= info->mask.ip6[i];
Florian Westphal625c5562017-12-09 21:01:08 +010061 memcpy(key, &addr, sizeof(addr.ip6));
62 key[4] = zone->id;
Jan Engelhardt370786f2007-07-14 20:47:26 -070063 } else {
64 const struct iphdr *iph = ip_hdr(skb);
Florian Westphal625c5562017-12-09 21:01:08 +010065 key[0] = (info->flags & XT_CONNLIMIT_DADDR) ?
Jan Engelhardtcc4fc022011-01-18 17:32:40 +010066 iph->daddr : iph->saddr;
Florian Westphalb1fc1372017-11-02 12:50:38 +010067
Florian Westphal625c5562017-12-09 21:01:08 +010068 key[0] &= info->mask.ip;
69 key[1] = zone->id;
Jan Engelhardt370786f2007-07-14 20:47:26 -070070 }
71
Yi-Hung Wei6aec2082018-03-04 15:29:51 -080072 connections = nf_conncount_count(net, info->data, key, tuple_ptr,
73 zone);
Florian Westphal7d084872014-03-12 23:49:51 +010074 if (connections == 0)
Jan Engelhardt370786f2007-07-14 20:47:26 -070075 /* kmalloc failed, drop it entirely */
Richard Weinberger1cc34c32011-01-18 01:36:57 +010076 goto hotdrop;
Jan Engelhardt370786f2007-07-14 20:47:26 -070077
Florian Westphal625c5562017-12-09 21:01:08 +010078 return (connections > info->limit) ^ !!(info->flags & XT_CONNLIMIT_INVERT);
Jan Engelhardt370786f2007-07-14 20:47:26 -070079
80 hotdrop:
Jan Engelhardtb4ba2612009-07-07 20:54:30 +020081 par->hotdrop = true;
Jan Engelhardt370786f2007-07-14 20:47:26 -070082 return false;
83}
84
Jan Engelhardtb0f38452010-03-19 17:16:42 +010085static int connlimit_mt_check(const struct xt_mtchk_param *par)
Jan Engelhardt370786f2007-07-14 20:47:26 -070086{
Jan Engelhardt9b4fce72008-10-08 11:35:18 +020087 struct xt_connlimit_info *info = par->matchinfo;
Florian Westphal625c5562017-12-09 21:01:08 +010088 unsigned int keylen;
Jan Engelhardt370786f2007-07-14 20:47:26 -070089
Florian Westphal625c5562017-12-09 21:01:08 +010090 keylen = sizeof(u32);
91 if (par->family == NFPROTO_IPV6)
92 keylen += sizeof(struct in6_addr);
93 else
94 keylen += sizeof(struct in_addr);
Jan Engelhardt370786f2007-07-14 20:47:26 -070095
96 /* init private data */
Florian Westphal625c5562017-12-09 21:01:08 +010097 info->data = nf_conncount_init(par->net, par->family, keylen);
Jan Engelhardt370786f2007-07-14 20:47:26 -070098
YueHaibing33b78aa2018-07-19 21:20:09 +080099 return PTR_ERR_OR_ZERO(info->data);
Jan Engelhardt370786f2007-07-14 20:47:26 -0700100}
101
Jan Engelhardt6be3d852008-10-08 11:35:19 +0200102static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
Jan Engelhardt370786f2007-07-14 20:47:26 -0700103{
Jan Engelhardt6be3d852008-10-08 11:35:19 +0200104 const struct xt_connlimit_info *info = par->matchinfo;
Jan Engelhardt370786f2007-07-14 20:47:26 -0700105
Florian Westphal625c5562017-12-09 21:01:08 +0100106 nf_conncount_destroy(par->net, par->family, info->data);
Jan Engelhardt370786f2007-07-14 20:47:26 -0700107}
108
Cong Wang68c07cb2012-05-19 04:39:01 +0000109static struct xt_match connlimit_mt_reg __read_mostly = {
110 .name = "connlimit",
111 .revision = 1,
112 .family = NFPROTO_UNSPEC,
113 .checkentry = connlimit_mt_check,
114 .match = connlimit_mt,
115 .matchsize = sizeof(struct xt_connlimit_info),
Willem de Bruijnec231892017-01-02 17:19:46 -0500116 .usersize = offsetof(struct xt_connlimit_info, data),
Cong Wang68c07cb2012-05-19 04:39:01 +0000117 .destroy = connlimit_mt_destroy,
118 .me = THIS_MODULE,
Jan Engelhardt370786f2007-07-14 20:47:26 -0700119};
120
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800121static int __init connlimit_mt_init(void)
Jan Engelhardt370786f2007-07-14 20:47:26 -0700122{
Florian Westphal625c5562017-12-09 21:01:08 +0100123 return xt_register_match(&connlimit_mt_reg);
Jan Engelhardt370786f2007-07-14 20:47:26 -0700124}
125
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800126static void __exit connlimit_mt_exit(void)
Jan Engelhardt370786f2007-07-14 20:47:26 -0700127{
Cong Wang68c07cb2012-05-19 04:39:01 +0000128 xt_unregister_match(&connlimit_mt_reg);
Jan Engelhardt370786f2007-07-14 20:47:26 -0700129}
130
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800131module_init(connlimit_mt_init);
132module_exit(connlimit_mt_exit);
Jan Engelhardt92f3b2b2008-10-08 11:35:20 +0200133MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
Jan Engelhardt2ae15b62008-01-14 23:42:28 -0800134MODULE_DESCRIPTION("Xtables: Number of connections matching");
Jan Engelhardt370786f2007-07-14 20:47:26 -0700135MODULE_LICENSE("GPL");
136MODULE_ALIAS("ipt_connlimit");
137MODULE_ALIAS("ip6t_connlimit");