libxt_iprange.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
1/* Shared library add-on to iptables to add IP range matching support. */
2#include <stdbool.h>
3#include <stdio.h>
4#include <netdb.h>
5#include <string.h>
6#include <stdlib.h>
7#include <getopt.h>
8
9#include <netinet/in.h>
10#include <xtables.h>
11#include <linux/netfilter.h>
12#include <linux/netfilter/xt_iprange.h>
13
14struct ipt_iprange {
15	/* Inclusive: network order. */
16	__be32 min_ip, max_ip;
17};
18
19struct ipt_iprange_info {
20	struct ipt_iprange src;
21	struct ipt_iprange dst;
22
23	/* Flags from above */
24	uint8_t flags;
25};
26
27enum {
28	F_SRCIP = 1 << 0,
29	F_DSTIP = 1 << 1,
30};
31
32static void iprange_mt_help(void)
33{
34	printf(
35"iprange match options:\n"
36"[!] --src-range ip[-ip]    Match source IP in the specified range\n"
37"[!] --dst-range ip[-ip]    Match destination IP in the specified range\n");
38}
39
40static const struct option iprange_mt_opts[] = {
41	{.name = "src-range", .has_arg = true, .val = '1'},
42	{.name = "dst-range", .has_arg = true, .val = '2'},
43	XT_GETOPT_TABLEEND,
44};
45
46static void
47iprange_parse_spec(const char *from, const char *to, union nf_inet_addr *range,
48		   uint8_t family, const char *optname)
49{
50	const char *spec[2] = {from, to};
51	struct in6_addr *ia6;
52	struct in_addr *ia4;
53	unsigned int i;
54
55	memset(range, 0, sizeof(union nf_inet_addr) * 2);
56
57	if (family == NFPROTO_IPV6) {
58		for (i = 0; i < ARRAY_SIZE(spec); ++i) {
59			ia6 = xtables_numeric_to_ip6addr(spec[i]);
60			if (ia6 == NULL)
61				xtables_param_act(XTF_BAD_VALUE, "iprange",
62					optname, spec[i]);
63			range[i].in6 = *ia6;
64		}
65	} else {
66		for (i = 0; i < ARRAY_SIZE(spec); ++i) {
67			ia4 = xtables_numeric_to_ipaddr(spec[i]);
68			if (ia4 == NULL)
69				xtables_param_act(XTF_BAD_VALUE, "iprange",
70					optname, spec[i]);
71			range[i].in = *ia4;
72		}
73	}
74}
75
76static void iprange_parse_range(char *arg, union nf_inet_addr *range,
77				uint8_t family, const char *optname)
78{
79	char *dash;
80
81	dash = strchr(arg, '-');
82	if (dash == NULL) {
83		iprange_parse_spec(arg, arg, range, family, optname);
84		return;
85	}
86
87	*dash = '\0';
88	iprange_parse_spec(arg, dash + 1, range, family, optname);
89	if (memcmp(&range[0], &range[1], sizeof(*range)) > 0)
90		fprintf(stderr, "xt_iprange: range %s-%s is reversed and "
91			"will never match\n", arg, dash + 1);
92}
93
94static int iprange_parse(int c, char **argv, int invert, unsigned int *flags,
95                         const void *entry, struct xt_entry_match **match)
96{
97	struct ipt_iprange_info *info = (struct ipt_iprange_info *)(*match)->data;
98	union nf_inet_addr range[2];
99
100	switch (c) {
101	case '1':
102		if (*flags & IPRANGE_SRC)
103			xtables_error(PARAMETER_PROBLEM,
104				   "iprange match: Only use --src-range ONCE!");
105		*flags |= IPRANGE_SRC;
106
107		info->flags |= IPRANGE_SRC;
108		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
109		if (invert)
110			info->flags |= IPRANGE_SRC_INV;
111		iprange_parse_range(optarg, range, NFPROTO_IPV4, "--src-range");
112		info->src.min_ip = range[0].ip;
113		info->src.max_ip = range[1].ip;
114		break;
115
116	case '2':
117		if (*flags & IPRANGE_DST)
118			xtables_error(PARAMETER_PROBLEM,
119				   "iprange match: Only use --dst-range ONCE!");
120		*flags |= IPRANGE_DST;
121
122		info->flags |= IPRANGE_DST;
123		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
124		if (invert)
125			info->flags |= IPRANGE_DST_INV;
126
127		iprange_parse_range(optarg, range, NFPROTO_IPV4, "--dst-range");
128		info->dst.min_ip = range[0].ip;
129		info->dst.max_ip = range[1].ip;
130		break;
131	}
132	return 1;
133}
134
135static int
136iprange_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
137                  const void *entry, struct xt_entry_match **match)
138{
139	struct xt_iprange_mtinfo *info = (void *)(*match)->data;
140
141	switch (c) {
142	case '1': /* --src-range */
143		iprange_parse_range(optarg, &info->src_min, NFPROTO_IPV4,
144			"--src-range");
145		info->flags |= IPRANGE_SRC;
146		if (invert)
147			info->flags |= IPRANGE_SRC_INV;
148		*flags |= F_SRCIP;
149		return true;
150
151	case '2': /* --dst-range */
152		iprange_parse_range(optarg, &info->dst_min, NFPROTO_IPV4,
153			"--dst-range");
154		info->flags |= IPRANGE_DST;
155		if (invert)
156			info->flags |= IPRANGE_DST_INV;
157		*flags |= F_DSTIP;
158		return true;
159	}
160	return false;
161}
162
163static int
164iprange_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
165                  const void *entry, struct xt_entry_match **match)
166{
167	struct xt_iprange_mtinfo *info = (void *)(*match)->data;
168
169	switch (c) {
170	case '1': /* --src-range */
171		iprange_parse_range(optarg, &info->src_min, NFPROTO_IPV6,
172			"--src-range");
173		info->flags |= IPRANGE_SRC;
174		if (invert)
175			info->flags |= IPRANGE_SRC_INV;
176		*flags |= F_SRCIP;
177		return true;
178
179	case '2': /* --dst-range */
180		iprange_parse_range(optarg, &info->dst_min, NFPROTO_IPV6,
181			"--dst-range");
182		info->flags |= IPRANGE_DST;
183		if (invert)
184			info->flags |= IPRANGE_DST_INV;
185		*flags |= F_DSTIP;
186		return true;
187	}
188	return false;
189}
190
191static void iprange_mt_check(unsigned int flags)
192{
193	if (flags == 0)
194		xtables_error(PARAMETER_PROBLEM,
195			   "iprange match: You must specify `--src-range' or `--dst-range'");
196}
197
198static void
199print_iprange(const struct ipt_iprange *range)
200{
201	const unsigned char *byte_min, *byte_max;
202
203	byte_min = (const unsigned char *)&range->min_ip;
204	byte_max = (const unsigned char *)&range->max_ip;
205	printf(" %u.%u.%u.%u-%u.%u.%u.%u",
206		byte_min[0], byte_min[1], byte_min[2], byte_min[3],
207		byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
208}
209
210static void iprange_print(const void *ip, const struct xt_entry_match *match,
211                          int numeric)
212{
213	const struct ipt_iprange_info *info = (const void *)match->data;
214
215	if (info->flags & IPRANGE_SRC) {
216		printf(" source IP range");
217		if (info->flags & IPRANGE_SRC_INV)
218			printf(" !");
219		print_iprange(&info->src);
220	}
221	if (info->flags & IPRANGE_DST) {
222		printf(" destination IP range");
223		if (info->flags & IPRANGE_DST_INV)
224			printf(" !");
225		print_iprange(&info->dst);
226	}
227}
228
229static void
230iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
231                  int numeric)
232{
233	const struct xt_iprange_mtinfo *info = (const void *)match->data;
234
235	if (info->flags & IPRANGE_SRC) {
236		printf(" source IP range");
237		if (info->flags & IPRANGE_SRC_INV)
238			printf(" !");
239		/*
240		 * ipaddr_to_numeric() uses a static buffer, so cannot
241		 * combine the printf() calls.
242		 */
243		printf(" %s", xtables_ipaddr_to_numeric(&info->src_min.in));
244		printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
245	}
246	if (info->flags & IPRANGE_DST) {
247		printf(" destination IP range");
248		if (info->flags & IPRANGE_DST_INV)
249			printf(" !");
250		printf(" %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
251		printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
252	}
253}
254
255static void
256iprange_mt6_print(const void *ip, const struct xt_entry_match *match,
257                  int numeric)
258{
259	const struct xt_iprange_mtinfo *info = (const void *)match->data;
260
261	if (info->flags & IPRANGE_SRC) {
262		printf(" source IP range");
263		if (info->flags & IPRANGE_SRC_INV)
264			printf(" !");
265		/*
266		 * ipaddr_to_numeric() uses a static buffer, so cannot
267		 * combine the printf() calls.
268		 */
269		printf(" %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
270		printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
271	}
272	if (info->flags & IPRANGE_DST) {
273		printf(" destination IP range");
274		if (info->flags & IPRANGE_DST_INV)
275			printf(" !");
276		printf(" %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
277		printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
278	}
279}
280
281static void iprange_save(const void *ip, const struct xt_entry_match *match)
282{
283	const struct ipt_iprange_info *info = (const void *)match->data;
284
285	if (info->flags & IPRANGE_SRC) {
286		if (info->flags & IPRANGE_SRC_INV)
287			printf(" !");
288		printf(" --src-range");
289		print_iprange(&info->src);
290	}
291	if (info->flags & IPRANGE_DST) {
292		if (info->flags & IPRANGE_DST_INV)
293			printf(" !");
294		printf(" --dst-range");
295		print_iprange(&info->dst);
296	}
297}
298
299static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match)
300{
301	const struct xt_iprange_mtinfo *info = (const void *)match->data;
302
303	if (info->flags & IPRANGE_SRC) {
304		if (info->flags & IPRANGE_SRC_INV)
305			printf(" !");
306		printf(" --src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in));
307		printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
308	}
309	if (info->flags & IPRANGE_DST) {
310		if (info->flags & IPRANGE_DST_INV)
311			printf(" !");
312		printf(" --dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
313		printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
314	}
315}
316
317static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match)
318{
319	const struct xt_iprange_mtinfo *info = (const void *)match->data;
320
321	if (info->flags & IPRANGE_SRC) {
322		if (info->flags & IPRANGE_SRC_INV)
323			printf(" !");
324		printf(" --src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
325		printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
326	}
327	if (info->flags & IPRANGE_DST) {
328		if (info->flags & IPRANGE_DST_INV)
329			printf(" !");
330		printf(" --dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
331		printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
332	}
333}
334
335static struct xtables_match iprange_mt_reg[] = {
336	{
337		.version       = XTABLES_VERSION,
338		.name          = "iprange",
339		.revision      = 0,
340		.family        = NFPROTO_IPV4,
341		.size          = XT_ALIGN(sizeof(struct ipt_iprange_info)),
342		.userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)),
343		.help          = iprange_mt_help,
344		.parse         = iprange_parse,
345		.final_check   = iprange_mt_check,
346		.print         = iprange_print,
347		.save          = iprange_save,
348		.extra_opts    = iprange_mt_opts,
349	},
350	{
351		.version       = XTABLES_VERSION,
352		.name          = "iprange",
353		.revision      = 1,
354		.family        = NFPROTO_IPV4,
355		.size          = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
356		.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
357		.help          = iprange_mt_help,
358		.parse         = iprange_mt4_parse,
359		.final_check   = iprange_mt_check,
360		.print         = iprange_mt4_print,
361		.save          = iprange_mt4_save,
362		.extra_opts    = iprange_mt_opts,
363	},
364	{
365		.version       = XTABLES_VERSION,
366		.name          = "iprange",
367		.revision      = 1,
368		.family        = NFPROTO_IPV6,
369		.size          = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
370		.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
371		.help          = iprange_mt_help,
372		.parse         = iprange_mt6_parse,
373		.final_check   = iprange_mt_check,
374		.print         = iprange_mt6_print,
375		.save          = iprange_mt6_save,
376		.extra_opts    = iprange_mt_opts,
377	},
378};
379
380void _init(void)
381{
382	xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
383}
384