libxt_connlimit.c revision f2a77520693f0a6dd1df1f87be4b81913961c1f5
1/* Shared library add-on to iptables to add connection limit support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <stddef.h>
7#include <getopt.h>
8#include <xtables.h>
9#include <linux/netfilter/xt_connlimit.h>
10
11static void connlimit_help(void)
12{
13	printf(
14"connlimit match options:\n"
15"[!] --connlimit-above n        match if the number of existing "
16"                               connections is (not) above n\n"
17"    --connlimit-mask n         group hosts using mask\n");
18}
19
20static const struct option connlimit_opts[] = {
21	{"connlimit-above", 1, NULL, 'A'},
22	{"connlimit-mask",  1, NULL, 'M'},
23	{ .name = NULL }
24};
25
26static void connlimit_init(struct xt_entry_match *match)
27{
28	struct xt_connlimit_info *info = (void *)match->data;
29
30	/* This will also initialize the v4 mask correctly */
31	memset(info->v6_mask, 0xFF, sizeof(info->v6_mask));
32}
33
34static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len)
35{
36	if (prefix_len == 0) {
37		mask[0] = mask[1] = mask[2] = mask[3] = 0;
38	} else if (prefix_len <= 32) {
39		mask[0] <<= 32 - prefix_len;
40		mask[1] = mask[2] = mask[3] = 0;
41	} else if (prefix_len <= 64) {
42		mask[1] <<= 32 - (prefix_len - 32);
43		mask[2] = mask[3] = 0;
44	} else if (prefix_len <= 96) {
45		mask[2] <<= 32 - (prefix_len - 64);
46		mask[3] = 0;
47	} else if (prefix_len <= 128) {
48		mask[3] <<= 32 - (prefix_len - 96);
49	}
50	mask[0] = htonl(mask[0]);
51	mask[1] = htonl(mask[1]);
52	mask[2] = htonl(mask[2]);
53	mask[3] = htonl(mask[3]);
54}
55
56static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags,
57                           struct xt_connlimit_info *info, unsigned int family)
58{
59	char *err;
60	int i;
61
62	switch (c) {
63	case 'A':
64		if (*flags & 0x1)
65			xtables_error(PARAMETER_PROBLEM,
66				"--connlimit-above may be given only once");
67		*flags |= 0x1;
68		xtables_check_inverse(optarg, &invert, &optind, 0);
69		info->limit   = strtoul(argv[optind-1], NULL, 0);
70		info->inverse = invert;
71		break;
72	case 'M':
73		if (*flags & 0x2)
74			xtables_error(PARAMETER_PROBLEM,
75				"--connlimit-mask may be given only once");
76
77		*flags |= 0x2;
78		i = strtoul(argv[optind-1], &err, 0);
79		if (family == NFPROTO_IPV6) {
80			if (i > 128 || *err != '\0')
81				xtables_error(PARAMETER_PROBLEM,
82					"--connlimit-mask must be between "
83					"0 and 128");
84			prefix_to_netmask(info->v6_mask, i);
85		} else {
86			if (i > 32 || *err != '\0')
87				xtables_error(PARAMETER_PROBLEM,
88					"--connlimit-mask must be between "
89					"0 and 32");
90			if (i == 0)
91				info->v4_mask = 0;
92			else
93				info->v4_mask = htonl(0xFFFFFFFF << (32 - i));
94		}
95		break;
96	default:
97		return 0;
98	}
99
100	return 1;
101}
102
103static int connlimit_parse4(int c, char **argv, int invert,
104                            unsigned int *flags, const void *entry,
105                            struct xt_entry_match **match)
106{
107	return connlimit_parse(c, argv, invert, flags,
108	       (void *)(*match)->data, NFPROTO_IPV4);
109}
110
111static int connlimit_parse6(int c, char **argv, int invert,
112                            unsigned int *flags, const void *entry,
113                            struct xt_entry_match **match)
114{
115	return connlimit_parse(c, argv, invert, flags,
116	       (void *)(*match)->data, NFPROTO_IPV6);
117}
118
119static void connlimit_check(unsigned int flags)
120{
121	if (!(flags & 0x1))
122		xtables_error(PARAMETER_PROBLEM,
123			"You must specify \"--connlimit-above\"");
124}
125
126static unsigned int count_bits4(u_int32_t mask)
127{
128	unsigned int bits = 0;
129
130	for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
131		++bits;
132
133	return 32 - bits;
134}
135
136static unsigned int count_bits6(const u_int32_t *mask)
137{
138	unsigned int bits = 0, i;
139	u_int32_t tmp[4];
140
141	for (i = 0; i < 4; ++i)
142		for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
143			++bits;
144	return 128 - bits;
145}
146
147static void connlimit_print4(const void *ip,
148                             const struct xt_entry_match *match, int numeric)
149{
150	const struct xt_connlimit_info *info = (const void *)match->data;
151
152	printf("#conn/%u %s %u ", count_bits4(info->v4_mask),
153	       info->inverse ? "<=" : ">", info->limit);
154}
155
156static void connlimit_print6(const void *ip,
157                             const struct xt_entry_match *match, int numeric)
158{
159	const struct xt_connlimit_info *info = (const void *)match->data;
160	printf("#conn/%u %s %u ", count_bits6(info->v6_mask),
161	       info->inverse ? "<=" : ">", info->limit);
162}
163
164static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
165{
166	const struct xt_connlimit_info *info = (const void *)match->data;
167
168	printf("%s--connlimit-above %u --connlimit-mask %u ",
169	       info->inverse ? "! " : "", info->limit,
170	       count_bits4(info->v4_mask));
171}
172
173static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
174{
175	const struct xt_connlimit_info *info = (const void *)match->data;
176
177	printf("%s--connlimit-above %u --connlimit-mask %u ",
178	       info->inverse ? "! " : "", info->limit,
179	       count_bits6(info->v6_mask));
180}
181
182static struct xtables_match connlimit_mt_reg[] = {
183	{
184		.name          = "connlimit",
185		.family        = NFPROTO_IPV4,
186		.version       = XTABLES_VERSION,
187		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
188		.userspacesize = offsetof(struct xt_connlimit_info, data),
189		.help          = connlimit_help,
190		.init          = connlimit_init,
191		.parse         = connlimit_parse4,
192		.final_check   = connlimit_check,
193		.print         = connlimit_print4,
194		.save          = connlimit_save4,
195		.extra_opts    = connlimit_opts,
196	},
197	{
198		.name          = "connlimit",
199		.family        = NFPROTO_IPV6,
200		.version       = XTABLES_VERSION,
201		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
202		.userspacesize = offsetof(struct xt_connlimit_info, data),
203		.help          = connlimit_help,
204		.init          = connlimit_init,
205		.parse         = connlimit_parse6,
206		.final_check   = connlimit_check,
207		.print         = connlimit_print6,
208		.save          = connlimit_save6,
209		.extra_opts    = connlimit_opts,
210	},
211};
212
213void _init(void)
214{
215	xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
216}
217