libxt_connlimit.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
1/* Shared library add-on to iptables to add connection limit support. */
2#include <stdbool.h>
3#include <stdio.h>
4#include <netdb.h>
5#include <string.h>
6#include <stdlib.h>
7#include <stddef.h>
8#include <getopt.h>
9#include <xtables.h>
10#include <linux/netfilter/xt_connlimit.h>
11
12enum {
13	FL_LIMIT = 1 << 0,
14	FL_MASK  = 1 << 1,
15	FL_ADDR  = 1 << 2,
16};
17
18static void connlimit_help(void)
19{
20	printf(
21"connlimit match options:\n"
22"  --connlimit-upto n     match if the number of existing connections is 0..n\n"
23"  --connlimit-above n    match if the number of existing connections is >n\n"
24"  --connlimit-mask n     group hosts using prefix length (default: max len)\n"
25"  --connlimit-saddr      select source address for grouping\n"
26"  --connlimit-daddr      select destination addresses for grouping\n");
27}
28
29static const struct option connlimit_opts[] = {
30	{.name = "connlimit-upto",  .has_arg = true, .val = 'U'},
31	{.name = "connlimit-above", .has_arg = true, .val = 'A'},
32	{.name = "connlimit-mask",  .has_arg = true, .val = 'M'},
33	{.name = "connlimit-saddr", .has_arg = false, .val = 's'},
34	{.name = "connlimit-daddr", .has_arg = false, .val = 'd'},
35	XT_GETOPT_TABLEEND,
36};
37
38static void connlimit_init(struct xt_entry_match *match)
39{
40	struct xt_connlimit_info *info = (void *)match->data;
41
42	/* This will also initialize the v4 mask correctly */
43	memset(info->v6_mask, 0xFF, sizeof(info->v6_mask));
44}
45
46static void prefix_to_netmask(uint32_t *mask, unsigned int prefix_len)
47{
48	if (prefix_len == 0) {
49		mask[0] = mask[1] = mask[2] = mask[3] = 0;
50	} else if (prefix_len <= 32) {
51		mask[0] <<= 32 - prefix_len;
52		mask[1] = mask[2] = mask[3] = 0;
53	} else if (prefix_len <= 64) {
54		mask[1] <<= 32 - (prefix_len - 32);
55		mask[2] = mask[3] = 0;
56	} else if (prefix_len <= 96) {
57		mask[2] <<= 32 - (prefix_len - 64);
58		mask[3] = 0;
59	} else if (prefix_len <= 128) {
60		mask[3] <<= 32 - (prefix_len - 96);
61	}
62	mask[0] = htonl(mask[0]);
63	mask[1] = htonl(mask[1]);
64	mask[2] = htonl(mask[2]);
65	mask[3] = htonl(mask[3]);
66}
67
68static int
69connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
70                struct xt_entry_match **match, unsigned int family)
71{
72	struct xt_connlimit_info *info = (void *)(*match)->data;
73	const unsigned int revision = (*match)->u.user.revision;
74	char *err;
75	int i;
76
77	switch (c) {
78	case 'A': /* --connlimit-above */
79		xtables_param_act(XTF_ONLY_ONCE, "connlimit",
80			"--connlimit-{upto,above}", *flags & FL_LIMIT);
81		*flags |= FL_LIMIT;
82		if (invert)
83			info->flags |= XT_CONNLIMIT_INVERT;
84		info->limit = strtoul(optarg, NULL, 0);
85		return true;
86	case 'U': /* --connlimit-upto */
87		xtables_param_act(XTF_ONLY_ONCE, "connlimit",
88			"--connlimit-{upto,above}", *flags & FL_LIMIT);
89		*flags |= FL_LIMIT;
90		if (!invert)
91			info->flags |= XT_CONNLIMIT_INVERT;
92		info->limit = strtoul(optarg, NULL, 0);
93		return true;
94	case 'M': /* --connlimit-mask */
95		xtables_param_act(XTF_NO_INVERT, "connlimit",
96			"--connlimit-mask", invert);
97		xtables_param_act(XTF_ONLY_ONCE, "connlimit",
98			"--connlimit-mask", *flags & FL_MASK);
99		*flags |= FL_MASK;
100		i = strtoul(optarg, &err, 0);
101		if (family == NFPROTO_IPV6) {
102			if (i > 128 || *err != '\0')
103				xtables_error(PARAMETER_PROBLEM,
104					"--connlimit-mask must be between "
105					"0 and 128");
106			prefix_to_netmask(info->v6_mask, i);
107		} else {
108			if (i > 32 || *err != '\0')
109				xtables_error(PARAMETER_PROBLEM,
110					"--connlimit-mask must be between "
111					"0 and 32");
112			if (i == 0)
113				info->v4_mask = 0;
114			else
115				info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
116		}
117		return true;
118	case 's': /* --connlimit-saddr */
119		info->flags &= ~XT_CONNLIMIT_DADDR;
120		return true;
121	case 'd': /* --connlimit-daddr */
122		if (revision < 1)
123			xtables_error(PARAMETER_PROBLEM,
124				"xt_connlimit.0 does not support "
125				"--connlimit-daddr");
126		info->flags |= XT_CONNLIMIT_DADDR;
127		return true;
128	}
129	return false;
130}
131
132static int connlimit_parse4(int c, char **argv, int invert,
133                            unsigned int *flags, const void *entry,
134                            struct xt_entry_match **match)
135{
136	return connlimit_parse(c, argv, invert, flags, match, NFPROTO_IPV4);
137}
138
139static int connlimit_parse6(int c, char **argv, int invert,
140                            unsigned int *flags, const void *entry,
141                            struct xt_entry_match **match)
142{
143	return connlimit_parse(c, argv, invert, flags, match, NFPROTO_IPV6);
144}
145
146static void connlimit_check(unsigned int flags)
147{
148	if (!(flags & 0x1))
149		xtables_error(PARAMETER_PROBLEM,
150			"You must specify \"--connlimit-above\"");
151}
152
153static unsigned int count_bits4(uint32_t mask)
154{
155	unsigned int bits = 0;
156
157	for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
158		++bits;
159
160	return 32 - bits;
161}
162
163static unsigned int count_bits6(const uint32_t *mask)
164{
165	unsigned int bits = 0, i;
166	uint32_t tmp[4];
167
168	for (i = 0; i < 4; ++i)
169		for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
170			++bits;
171	return 128 - bits;
172}
173
174static void connlimit_print4(const void *ip,
175                             const struct xt_entry_match *match, int numeric)
176{
177	const struct xt_connlimit_info *info = (const void *)match->data;
178
179	printf(" #conn %s/%u %s %u",
180	       (info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src",
181	       count_bits4(info->v4_mask),
182	       (info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit);
183}
184
185static void connlimit_print6(const void *ip,
186                             const struct xt_entry_match *match, int numeric)
187{
188	const struct xt_connlimit_info *info = (const void *)match->data;
189
190	printf(" #conn %s/%u %s %u",
191	       (info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src",
192	       count_bits6(info->v6_mask),
193	       (info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit);
194}
195
196static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
197{
198	const struct xt_connlimit_info *info = (const void *)match->data;
199	const int revision = match->u.user.revision;
200
201	if (info->flags & XT_CONNLIMIT_INVERT)
202		printf(" --connlimit-upto %u", info->limit);
203	else
204		printf(" --connlimit-above %u", info->limit);
205	printf(" --connlimit-mask %u", count_bits4(info->v4_mask));
206	if (revision >= 1) {
207		if (info->flags & XT_CONNLIMIT_DADDR)
208			printf(" --connlimit-daddr");
209		else
210			printf(" --connlimit-saddr");
211	}
212}
213
214static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
215{
216	const struct xt_connlimit_info *info = (const void *)match->data;
217	const int revision = match->u.user.revision;
218
219	if (info->flags & XT_CONNLIMIT_INVERT)
220		printf(" --connlimit-upto %u", info->limit);
221	else
222		printf(" --connlimit-above %u", info->limit);
223	printf(" --connlimit-mask %u", count_bits6(info->v6_mask));
224	if (revision >= 1) {
225		if (info->flags & XT_CONNLIMIT_DADDR)
226			printf(" --connlimit-daddr");
227		else
228			printf(" --connlimit-saddr");
229	}
230}
231
232static struct xtables_match connlimit_mt_reg[] = {
233	{
234		.name          = "connlimit",
235		.revision      = 0,
236		.family        = NFPROTO_IPV4,
237		.version       = XTABLES_VERSION,
238		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
239		.userspacesize = offsetof(struct xt_connlimit_info, data),
240		.help          = connlimit_help,
241		.init          = connlimit_init,
242		.parse         = connlimit_parse4,
243		.final_check   = connlimit_check,
244		.print         = connlimit_print4,
245		.save          = connlimit_save4,
246		.extra_opts    = connlimit_opts,
247	},
248	{
249		.name          = "connlimit",
250		.revision      = 0,
251		.family        = NFPROTO_IPV6,
252		.version       = XTABLES_VERSION,
253		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
254		.userspacesize = offsetof(struct xt_connlimit_info, data),
255		.help          = connlimit_help,
256		.init          = connlimit_init,
257		.parse         = connlimit_parse6,
258		.final_check   = connlimit_check,
259		.print         = connlimit_print6,
260		.save          = connlimit_save6,
261		.extra_opts    = connlimit_opts,
262	},
263	{
264		.name          = "connlimit",
265		.revision      = 1,
266		.family        = NFPROTO_IPV4,
267		.version       = XTABLES_VERSION,
268		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
269		.userspacesize = offsetof(struct xt_connlimit_info, data),
270		.help          = connlimit_help,
271		.init          = connlimit_init,
272		.parse         = connlimit_parse4,
273		.final_check   = connlimit_check,
274		.print         = connlimit_print4,
275		.save          = connlimit_save4,
276		.extra_opts    = connlimit_opts,
277	},
278	{
279		.name          = "connlimit",
280		.revision      = 1,
281		.family        = NFPROTO_IPV6,
282		.version       = XTABLES_VERSION,
283		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
284		.userspacesize = offsetof(struct xt_connlimit_info, data),
285		.help          = connlimit_help,
286		.init          = connlimit_init,
287		.parse         = connlimit_parse6,
288		.final_check   = connlimit_check,
289		.print         = connlimit_print6,
290		.save          = connlimit_save6,
291		.extra_opts    = connlimit_opts,
292	},
293};
294
295void _init(void)
296{
297	xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
298}
299