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