1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * netfilter module to limit the number of parallel tcp 4 * connections per IP address. 5 * (c) 2000 Gerd Knorr <kraxel@bytesex.org> 6 * Nov 2002: Martin Bene <martin.bene@icomedias.com>: 7 * only ignore TIME_WAIT or gone connections 8 * (C) CC Computer Consultants GmbH, 2007 9 * 10 * based on ... 11 * 12 * Kernel module to match connection tracking information. 13 * (C) 1999 Rusty Russell (rusty@rustcorp.com.au). 14 */ 15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16 17 #include <linux/ip.h> 18 #include <linux/ipv6.h> 19 #include <linux/module.h> 20 #include <linux/skbuff.h> 21 #include <linux/netfilter/x_tables.h> 22 #include <linux/netfilter/xt_connlimit.h> 23 24 #include <net/netfilter/nf_conntrack.h> 25 #include <net/netfilter/nf_conntrack_core.h> 26 #include <net/netfilter/nf_conntrack_tuple.h> 27 #include <net/netfilter/nf_conntrack_zones.h> 28 #include <net/netfilter/nf_conntrack_count.h> 29 30 static bool 31 connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) 32 { 33 struct net *net = xt_net(par); 34 const struct xt_connlimit_info *info = par->matchinfo; 35 const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; 36 enum ip_conntrack_info ctinfo; 37 const struct nf_conn *ct; 38 unsigned int connections; 39 u32 key[5]; 40 41 ct = nf_ct_get(skb, &ctinfo); 42 if (ct) 43 zone = nf_ct_zone(ct); 44 45 if (xt_family(par) == NFPROTO_IPV6) { 46 const struct ipv6hdr *iph = ipv6_hdr(skb); 47 union nf_inet_addr addr; 48 unsigned int i; 49 50 memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ? 51 &iph->daddr : &iph->saddr, sizeof(addr.ip6)); 52 53 for (i = 0; i < ARRAY_SIZE(addr.ip6); ++i) 54 addr.ip6[i] &= info->mask.ip6[i]; 55 memcpy(key, &addr, sizeof(addr.ip6)); 56 key[4] = zone->id; 57 } else { 58 const struct iphdr *iph = ip_hdr(skb); 59 60 key[0] = (info->flags & XT_CONNLIMIT_DADDR) ? 61 (__force __u32)iph->daddr : (__force __u32)iph->saddr; 62 key[0] &= (__force __u32)info->mask.ip; 63 key[1] = zone->id; 64 } 65 66 connections = nf_conncount_count_skb(net, skb, xt_family(par), info->data, key); 67 if (connections == 0) 68 /* kmalloc failed or tuple couldn't be found, drop it entirely */ 69 goto hotdrop; 70 71 return (connections > info->limit) ^ !!(info->flags & XT_CONNLIMIT_INVERT); 72 73 hotdrop: 74 par->hotdrop = true; 75 return false; 76 } 77 78 static int connlimit_mt_check(const struct xt_mtchk_param *par) 79 { 80 struct xt_connlimit_info *info = par->matchinfo; 81 unsigned int keylen; 82 int ret; 83 84 keylen = sizeof(u32); 85 if (par->family == NFPROTO_IPV6) 86 keylen += sizeof(struct in6_addr); 87 else 88 keylen += sizeof(struct in_addr); 89 90 ret = nf_ct_netns_get(par->net, par->family); 91 if (ret < 0) { 92 pr_info_ratelimited("cannot load conntrack support for proto=%u\n", 93 par->family); 94 return ret; 95 } 96 97 /* init private data */ 98 info->data = nf_conncount_init(par->net, keylen); 99 if (IS_ERR(info->data)) 100 nf_ct_netns_put(par->net, par->family); 101 102 return PTR_ERR_OR_ZERO(info->data); 103 } 104 105 static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) 106 { 107 const struct xt_connlimit_info *info = par->matchinfo; 108 109 nf_conncount_destroy(par->net, info->data); 110 nf_ct_netns_put(par->net, par->family); 111 } 112 113 static struct xt_match connlimit_mt_reg[] __read_mostly = { 114 { 115 .name = "connlimit", 116 .revision = 1, 117 .family = NFPROTO_IPV4, 118 .checkentry = connlimit_mt_check, 119 .match = connlimit_mt, 120 .matchsize = sizeof(struct xt_connlimit_info), 121 .usersize = offsetof(struct xt_connlimit_info, data), 122 .destroy = connlimit_mt_destroy, 123 .me = THIS_MODULE, 124 }, 125 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 126 { 127 .name = "connlimit", 128 .revision = 1, 129 .family = NFPROTO_IPV6, 130 .checkentry = connlimit_mt_check, 131 .match = connlimit_mt, 132 .matchsize = sizeof(struct xt_connlimit_info), 133 .usersize = offsetof(struct xt_connlimit_info, data), 134 .destroy = connlimit_mt_destroy, 135 .me = THIS_MODULE, 136 }, 137 #endif 138 }; 139 140 static int __init connlimit_mt_init(void) 141 { 142 return xt_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); 143 } 144 145 static void __exit connlimit_mt_exit(void) 146 { 147 xt_unregister_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); 148 } 149 150 module_init(connlimit_mt_init); 151 module_exit(connlimit_mt_exit); 152 MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); 153 MODULE_DESCRIPTION("Xtables: Number of connections matching"); 154 MODULE_LICENSE("GPL"); 155 MODULE_ALIAS("ipt_connlimit"); 156 MODULE_ALIAS("ip6t_connlimit"); 157