1#include <stdint.h>
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5#include <xtables.h>
6#include <linux/netfilter.h>
7#include <linux/netfilter/xt_iprange.h>
8
9struct ipt_iprange {
10	/* Inclusive: network order. */
11	__be32 min_ip, max_ip;
12};
13
14struct ipt_iprange_info {
15	struct ipt_iprange src;
16	struct ipt_iprange dst;
17
18	/* Flags from above */
19	uint8_t flags;
20};
21
22enum {
23	O_SRC_RANGE = 0,
24	O_DST_RANGE,
25};
26
27static void iprange_mt_help(void)
28{
29	printf(
30"iprange match options:\n"
31"[!] --src-range ip[-ip]    Match source IP in the specified range\n"
32"[!] --dst-range ip[-ip]    Match destination IP in the specified range\n");
33}
34
35static const struct xt_option_entry iprange_mt_opts[] = {
36	{.name = "src-range", .id = O_SRC_RANGE, .type = XTTYPE_STRING,
37	 .flags = XTOPT_INVERT},
38	{.name = "dst-range", .id = O_DST_RANGE, .type = XTTYPE_STRING,
39	 .flags = XTOPT_INVERT},
40	XTOPT_TABLEEND,
41};
42
43static void
44iprange_parse_spec(const char *from, const char *to, union nf_inet_addr *range,
45		   uint8_t family, const char *optname)
46{
47	const char *spec[2] = {from, to};
48	struct in6_addr *ia6;
49	struct in_addr *ia4;
50	unsigned int i;
51
52	memset(range, 0, sizeof(union nf_inet_addr) * 2);
53
54	if (family == NFPROTO_IPV6) {
55		for (i = 0; i < ARRAY_SIZE(spec); ++i) {
56			ia6 = xtables_numeric_to_ip6addr(spec[i]);
57			if (ia6 == NULL)
58				xtables_param_act(XTF_BAD_VALUE, "iprange",
59					optname, spec[i]);
60			range[i].in6 = *ia6;
61		}
62	} else {
63		for (i = 0; i < ARRAY_SIZE(spec); ++i) {
64			ia4 = xtables_numeric_to_ipaddr(spec[i]);
65			if (ia4 == NULL)
66				xtables_param_act(XTF_BAD_VALUE, "iprange",
67					optname, spec[i]);
68			range[i].in = *ia4;
69		}
70	}
71}
72
73static void iprange_parse_range(const char *oarg, union nf_inet_addr *range,
74				uint8_t family, const char *optname)
75{
76	char *arg = strdup(oarg);
77	char *dash;
78
79	if (arg == NULL)
80		xtables_error(RESOURCE_PROBLEM, "strdup");
81	dash = strchr(arg, '-');
82	if (dash == NULL) {
83		iprange_parse_spec(arg, arg, range, family, optname);
84		free(arg);
85		return;
86	}
87
88	*dash = '\0';
89	iprange_parse_spec(arg, dash + 1, range, family, optname);
90	if (memcmp(&range[0], &range[1], sizeof(*range)) > 0)
91		fprintf(stderr, "xt_iprange: range %s-%s is reversed and "
92			"will never match\n", arg, dash + 1);
93	free(arg);
94}
95
96static void iprange_parse(struct xt_option_call *cb)
97{
98	struct ipt_iprange_info *info = cb->data;
99	union nf_inet_addr range[2];
100
101	xtables_option_parse(cb);
102	switch (cb->entry->id) {
103	case O_SRC_RANGE:
104		info->flags |= IPRANGE_SRC;
105		if (cb->invert)
106			info->flags |= IPRANGE_SRC_INV;
107		iprange_parse_range(cb->arg, range,
108				    NFPROTO_IPV4, "--src-range");
109		info->src.min_ip = range[0].ip;
110		info->src.max_ip = range[1].ip;
111		break;
112	case O_DST_RANGE:
113		info->flags |= IPRANGE_DST;
114		if (cb->invert)
115			info->flags |= IPRANGE_DST_INV;
116		iprange_parse_range(cb->arg, range,
117				    NFPROTO_IPV4, "--dst-range");
118		info->dst.min_ip = range[0].ip;
119		info->dst.max_ip = range[1].ip;
120		break;
121	}
122}
123
124static void iprange_mt_parse(struct xt_option_call *cb, uint8_t nfproto)
125{
126	struct xt_iprange_mtinfo *info = cb->data;
127
128	xtables_option_parse(cb);
129	switch (cb->entry->id) {
130	case O_SRC_RANGE:
131		iprange_parse_range(cb->arg, &info->src_min, nfproto,
132			"--src-range");
133		info->flags |= IPRANGE_SRC;
134		if (cb->invert)
135			info->flags |= IPRANGE_SRC_INV;
136		break;
137	case O_DST_RANGE:
138		iprange_parse_range(cb->arg, &info->dst_min, nfproto,
139			"--dst-range");
140		info->flags |= IPRANGE_DST;
141		if (cb->invert)
142			info->flags |= IPRANGE_DST_INV;
143		break;
144	}
145}
146
147static void iprange_mt4_parse(struct xt_option_call *cb)
148{
149	iprange_mt_parse(cb, NFPROTO_IPV4);
150}
151
152static void iprange_mt6_parse(struct xt_option_call *cb)
153{
154	iprange_mt_parse(cb, NFPROTO_IPV6);
155}
156
157static void iprange_mt_check(struct xt_fcheck_call *cb)
158{
159	if (cb->xflags == 0)
160		xtables_error(PARAMETER_PROBLEM,
161			   "iprange match: You must specify `--src-range' or `--dst-range'");
162}
163
164static void
165print_iprange(const struct ipt_iprange *range)
166{
167	const unsigned char *byte_min, *byte_max;
168
169	byte_min = (const unsigned char *)&range->min_ip;
170	byte_max = (const unsigned char *)&range->max_ip;
171	printf(" %u.%u.%u.%u-%u.%u.%u.%u",
172		byte_min[0], byte_min[1], byte_min[2], byte_min[3],
173		byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
174}
175
176static void iprange_print(const void *ip, const struct xt_entry_match *match,
177			  int numeric)
178{
179	const struct ipt_iprange_info *info = (const void *)match->data;
180
181	if (info->flags & IPRANGE_SRC) {
182		printf(" source IP range");
183		if (info->flags & IPRANGE_SRC_INV)
184			printf(" !");
185		print_iprange(&info->src);
186	}
187	if (info->flags & IPRANGE_DST) {
188		printf(" destination IP range");
189		if (info->flags & IPRANGE_DST_INV)
190			printf(" !");
191		print_iprange(&info->dst);
192	}
193}
194
195static void
196iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
197		  int numeric)
198{
199	const struct xt_iprange_mtinfo *info = (const void *)match->data;
200
201	if (info->flags & IPRANGE_SRC) {
202		printf(" source IP range");
203		if (info->flags & IPRANGE_SRC_INV)
204			printf(" !");
205		/*
206		 * ipaddr_to_numeric() uses a static buffer, so cannot
207		 * combine the printf() calls.
208		 */
209		printf(" %s", xtables_ipaddr_to_numeric(&info->src_min.in));
210		printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
211	}
212	if (info->flags & IPRANGE_DST) {
213		printf(" destination IP range");
214		if (info->flags & IPRANGE_DST_INV)
215			printf(" !");
216		printf(" %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
217		printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
218	}
219}
220
221static void
222iprange_mt6_print(const void *ip, const struct xt_entry_match *match,
223		  int numeric)
224{
225	const struct xt_iprange_mtinfo *info = (const void *)match->data;
226
227	if (info->flags & IPRANGE_SRC) {
228		printf(" source IP range");
229		if (info->flags & IPRANGE_SRC_INV)
230			printf(" !");
231		/*
232		 * ipaddr_to_numeric() uses a static buffer, so cannot
233		 * combine the printf() calls.
234		 */
235		printf(" %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
236		printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
237	}
238	if (info->flags & IPRANGE_DST) {
239		printf(" destination IP range");
240		if (info->flags & IPRANGE_DST_INV)
241			printf(" !");
242		printf(" %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
243		printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
244	}
245}
246
247static void iprange_save(const void *ip, const struct xt_entry_match *match)
248{
249	const struct ipt_iprange_info *info = (const void *)match->data;
250
251	if (info->flags & IPRANGE_SRC) {
252		if (info->flags & IPRANGE_SRC_INV)
253			printf(" !");
254		printf(" --src-range");
255		print_iprange(&info->src);
256	}
257	if (info->flags & IPRANGE_DST) {
258		if (info->flags & IPRANGE_DST_INV)
259			printf(" !");
260		printf(" --dst-range");
261		print_iprange(&info->dst);
262	}
263}
264
265static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match)
266{
267	const struct xt_iprange_mtinfo *info = (const void *)match->data;
268
269	if (info->flags & IPRANGE_SRC) {
270		if (info->flags & IPRANGE_SRC_INV)
271			printf(" !");
272		printf(" --src-range %s",
273		       xtables_ipaddr_to_numeric(&info->src_min.in));
274		printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
275	}
276	if (info->flags & IPRANGE_DST) {
277		if (info->flags & IPRANGE_DST_INV)
278			printf(" !");
279		printf(" --dst-range %s",
280		       xtables_ipaddr_to_numeric(&info->dst_min.in));
281		printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
282	}
283}
284
285static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match)
286{
287	const struct xt_iprange_mtinfo *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 %s",
293		       xtables_ip6addr_to_numeric(&info->src_min.in6));
294		printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
295	}
296	if (info->flags & IPRANGE_DST) {
297		if (info->flags & IPRANGE_DST_INV)
298			printf(" !");
299		printf(" --dst-range %s",
300		       xtables_ip6addr_to_numeric(&info->dst_min.in6));
301		printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
302	}
303}
304
305static void
306print_iprange_xlate(const struct ipt_iprange *range,
307		    struct xt_xlate *xl)
308{
309	const unsigned char *byte_min, *byte_max;
310
311	byte_min = (const unsigned char *)&range->min_ip;
312	byte_max = (const unsigned char *)&range->max_ip;
313	xt_xlate_add(xl, " %u.%u.%u.%u-%u.%u.%u.%u ",
314		   byte_min[0], byte_min[1], byte_min[2], byte_min[3],
315		   byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
316}
317
318static int iprange_xlate(struct xt_xlate *xl,
319			 const struct xt_xlate_mt_params *params)
320{
321	const struct ipt_iprange_info *info = (const void *)params->match->data;
322	char *space = "";
323
324	if (info->flags & IPRANGE_SRC) {
325		xt_xlate_add(xl, "ip saddr%s",
326			     info->flags & IPRANGE_SRC_INV ? " !=" : "");
327		print_iprange_xlate(&info->src, xl);
328		space = " ";
329	}
330	if (info->flags & IPRANGE_DST) {
331		xt_xlate_add(xl, "%sip daddr%s", space,
332			     info->flags & IPRANGE_DST_INV ? " !=" : "");
333		print_iprange_xlate(&info->dst, xl);
334	}
335
336	return 1;
337}
338
339static int iprange_mt4_xlate(struct xt_xlate *xl,
340			     const struct xt_xlate_mt_params *params)
341{
342	const struct xt_iprange_mtinfo *info =
343		(const void *)params->match->data;
344	char *space = "";
345
346	if (info->flags & IPRANGE_SRC) {
347		xt_xlate_add(xl, "ip saddr%s %s",
348			     info->flags & IPRANGE_SRC_INV ? " !=" : "",
349			     xtables_ipaddr_to_numeric(&info->src_min.in));
350		xt_xlate_add(xl, "-%s",
351			     xtables_ipaddr_to_numeric(&info->src_max.in));
352		space = " ";
353	}
354	if (info->flags & IPRANGE_DST) {
355		xt_xlate_add(xl, "%sip daddr%s %s", space,
356			     info->flags & IPRANGE_DST_INV ? " !=" : "",
357			     xtables_ipaddr_to_numeric(&info->dst_min.in));
358		xt_xlate_add(xl, "-%s",
359			     xtables_ipaddr_to_numeric(&info->dst_max.in));
360	}
361
362	return 1;
363}
364
365static int iprange_mt6_xlate(struct xt_xlate *xl,
366			     const struct xt_xlate_mt_params *params)
367{
368	const struct xt_iprange_mtinfo *info =
369		(const void *)params->match->data;
370	char *space = "";
371
372	if (info->flags & IPRANGE_SRC) {
373		xt_xlate_add(xl, "ip6 saddr%s %s",
374			     info->flags & IPRANGE_SRC_INV ? " !=" : "",
375			     xtables_ip6addr_to_numeric(&info->src_min.in6));
376		xt_xlate_add(xl, "-%s",
377			     xtables_ip6addr_to_numeric(&info->src_max.in6));
378		space = " ";
379	}
380	if (info->flags & IPRANGE_DST) {
381		xt_xlate_add(xl, "%sip6 daddr%s %s", space,
382			     info->flags & IPRANGE_DST_INV ? " !=" : "",
383			     xtables_ip6addr_to_numeric(&info->dst_min.in6));
384		xt_xlate_add(xl, "-%s",
385			     xtables_ip6addr_to_numeric(&info->dst_max.in6));
386	}
387
388	return 1;
389}
390
391static struct xtables_match iprange_mt_reg[] = {
392	{
393		.version       = XTABLES_VERSION,
394		.name          = "iprange",
395		.revision      = 0,
396		.family        = NFPROTO_IPV4,
397		.size          = XT_ALIGN(sizeof(struct ipt_iprange_info)),
398		.userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)),
399		.help          = iprange_mt_help,
400		.x6_parse      = iprange_parse,
401		.x6_fcheck     = iprange_mt_check,
402		.print         = iprange_print,
403		.save          = iprange_save,
404		.x6_options    = iprange_mt_opts,
405		.xlate	       = iprange_xlate,
406	},
407	{
408		.version       = XTABLES_VERSION,
409		.name          = "iprange",
410		.revision      = 1,
411		.family        = NFPROTO_IPV4,
412		.size          = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
413		.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
414		.help          = iprange_mt_help,
415		.x6_parse      = iprange_mt4_parse,
416		.x6_fcheck     = iprange_mt_check,
417		.print         = iprange_mt4_print,
418		.save          = iprange_mt4_save,
419		.x6_options    = iprange_mt_opts,
420		.xlate	       = iprange_mt4_xlate,
421	},
422	{
423		.version       = XTABLES_VERSION,
424		.name          = "iprange",
425		.revision      = 1,
426		.family        = NFPROTO_IPV6,
427		.size          = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
428		.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
429		.help          = iprange_mt_help,
430		.x6_parse      = iprange_mt6_parse,
431		.x6_fcheck     = iprange_mt_check,
432		.print         = iprange_mt6_print,
433		.save          = iprange_mt6_save,
434		.x6_options    = iprange_mt_opts,
435		.xlate	       = iprange_mt6_xlate,
436	},
437};
438
439void _init(void)
440{
441	xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
442}
443