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