Commit | Line | Data |
---|---|---|
370786f9 JE |
1 | /* |
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 | |
ba5dc275 | 7 | * (C) CC Computer Consultants GmbH, 2007 |
370786f9 JE |
8 | * |
9 | * based on ... | |
10 | * | |
11 | * Kernel module to match connection tracking information. | |
12 | * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au). | |
13 | */ | |
8bee4bad | 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
625c5561 | 15 | |
40d102cd JS |
16 | #include <linux/ip.h> |
17 | #include <linux/ipv6.h> | |
370786f9 | 18 | #include <linux/module.h> |
370786f9 | 19 | #include <linux/skbuff.h> |
370786f9 JE |
20 | #include <linux/netfilter/x_tables.h> |
21 | #include <linux/netfilter/xt_connlimit.h> | |
625c5561 | 22 | |
370786f9 JE |
23 | #include <net/netfilter/nf_conntrack.h> |
24 | #include <net/netfilter/nf_conntrack_core.h> | |
25 | #include <net/netfilter/nf_conntrack_tuple.h> | |
5d0aa2cc | 26 | #include <net/netfilter/nf_conntrack_zones.h> |
625c5561 | 27 | #include <net/netfilter/nf_conntrack_count.h> |
15cfd528 | 28 | |
d3c5ee6d | 29 | static bool |
62fc8051 | 30 | connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) |
370786f9 | 31 | { |
613dbd95 | 32 | struct net *net = xt_net(par); |
f7108a20 | 33 | const struct xt_connlimit_info *info = par->matchinfo; |
370786f9 JE |
34 | struct nf_conntrack_tuple tuple; |
35 | const struct nf_conntrack_tuple *tuple_ptr = &tuple; | |
308ac914 | 36 | const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; |
370786f9 JE |
37 | enum ip_conntrack_info ctinfo; |
38 | const struct nf_conn *ct; | |
7d084877 | 39 | unsigned int connections; |
625c5561 | 40 | u32 key[5]; |
370786f9 JE |
41 | |
42 | ct = nf_ct_get(skb, &ctinfo); | |
e59ea3df | 43 | if (ct != NULL) { |
8183e3a8 | 44 | tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; |
e59ea3df FW |
45 | zone = nf_ct_zone(ct); |
46 | } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), | |
613dbd95 | 47 | xt_family(par), net, &tuple)) { |
370786f9 | 48 | goto hotdrop; |
e59ea3df | 49 | } |
370786f9 | 50 | |
613dbd95 | 51 | if (xt_family(par) == NFPROTO_IPV6) { |
370786f9 | 52 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
625c5561 | 53 | union nf_inet_addr addr; |
b1fc1372 FW |
54 | unsigned int i; |
55 | ||
cc4fc022 JE |
56 | memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ? |
57 | &iph->daddr : &iph->saddr, sizeof(addr.ip6)); | |
b1fc1372 FW |
58 | |
59 | for (i = 0; i < ARRAY_SIZE(addr.ip6); ++i) | |
60 | addr.ip6[i] &= info->mask.ip6[i]; | |
625c5561 FW |
61 | memcpy(key, &addr, sizeof(addr.ip6)); |
62 | key[4] = zone->id; | |
370786f9 JE |
63 | } else { |
64 | const struct iphdr *iph = ip_hdr(skb); | |
625c5561 | 65 | key[0] = (info->flags & XT_CONNLIMIT_DADDR) ? |
cc4fc022 | 66 | iph->daddr : iph->saddr; |
b1fc1372 | 67 | |
625c5561 FW |
68 | key[0] &= info->mask.ip; |
69 | key[1] = zone->id; | |
370786f9 JE |
70 | } |
71 | ||
6aec2087 YHW |
72 | connections = nf_conncount_count(net, info->data, key, tuple_ptr, |
73 | zone); | |
7d084877 | 74 | if (connections == 0) |
370786f9 | 75 | /* kmalloc failed, drop it entirely */ |
1cc34c30 | 76 | goto hotdrop; |
370786f9 | 77 | |
625c5561 | 78 | return (connections > info->limit) ^ !!(info->flags & XT_CONNLIMIT_INVERT); |
370786f9 JE |
79 | |
80 | hotdrop: | |
b4ba2611 | 81 | par->hotdrop = true; |
370786f9 JE |
82 | return false; |
83 | } | |
84 | ||
b0f38452 | 85 | static int connlimit_mt_check(const struct xt_mtchk_param *par) |
370786f9 | 86 | { |
9b4fce7a | 87 | struct xt_connlimit_info *info = par->matchinfo; |
625c5561 | 88 | unsigned int keylen; |
370786f9 | 89 | |
625c5561 FW |
90 | keylen = sizeof(u32); |
91 | if (par->family == NFPROTO_IPV6) | |
92 | keylen += sizeof(struct in6_addr); | |
93 | else | |
94 | keylen += sizeof(struct in_addr); | |
370786f9 JE |
95 | |
96 | /* init private data */ | |
625c5561 | 97 | info->data = nf_conncount_init(par->net, par->family, keylen); |
370786f9 | 98 | |
33b78aaa | 99 | return PTR_ERR_OR_ZERO(info->data); |
370786f9 JE |
100 | } |
101 | ||
7d084877 FW |
102 | static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) |
103 | { | |
104 | const struct xt_connlimit_info *info = par->matchinfo; | |
370786f9 | 105 | |
625c5561 | 106 | nf_conncount_destroy(par->net, par->family, info->data); |
370786f9 JE |
107 | } |
108 | ||
68c07cb6 CW |
109 | static 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), | |
ec231890 | 116 | .usersize = offsetof(struct xt_connlimit_info, data), |
68c07cb6 CW |
117 | .destroy = connlimit_mt_destroy, |
118 | .me = THIS_MODULE, | |
370786f9 JE |
119 | }; |
120 | ||
d3c5ee6d | 121 | static int __init connlimit_mt_init(void) |
370786f9 | 122 | { |
625c5561 | 123 | return xt_register_match(&connlimit_mt_reg); |
370786f9 JE |
124 | } |
125 | ||
d3c5ee6d | 126 | static void __exit connlimit_mt_exit(void) |
370786f9 | 127 | { |
68c07cb6 | 128 | xt_unregister_match(&connlimit_mt_reg); |
370786f9 JE |
129 | } |
130 | ||
d3c5ee6d JE |
131 | module_init(connlimit_mt_init); |
132 | module_exit(connlimit_mt_exit); | |
92f3b2b1 | 133 | MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); |
2ae15b64 | 134 | MODULE_DESCRIPTION("Xtables: Number of connections matching"); |
370786f9 JE |
135 | MODULE_LICENSE("GPL"); |
136 | MODULE_ALIAS("ipt_connlimit"); | |
137 | MODULE_ALIAS("ip6t_connlimit"); |