libxt_iprange.c revision 240eee607ab7e5bb9f671b7ceba40e2940182f61
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 <netinet/in.h>
9#include <xtables.h>
10#include <linux/netfilter.h>
11#include <linux/netfilter/xt_iprange.h>
12#include <linux/netfilter_ipv4/ipt_iprange.h>
13
14enum {
15	F_SRCIP = 1 << 0,
16	F_DSTIP = 1 << 1,
17};
18
19static void iprange_mt_help(void)
20{
21	printf(
22"iprange match options:\n"
23"[!] --src-range ip-ip        Match source IP in the specified range\n"
24"[!] --dst-range ip-ip        Match destination IP in the specified range\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-range */
111		end = strchr(optarg, '-');
112		if (end == NULL)
113			param_act(P_BAD_VALUE, "iprange", "--src-range", optarg);
114		*end = '\0';
115		ia = numeric_to_ipaddr(optarg);
116		if (ia == NULL)
117			param_act(P_BAD_VALUE, "iprange", "--src-range", 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-range", end + 1);
122		memcpy(&info->src_max.in, ia, sizeof(*ia));
123		info->flags |= IPRANGE_SRC;
124		if (invert)
125			info->flags |= IPRANGE_SRC_INV;
126		*flags |= F_SRCIP;
127		return true;
128
129	case '2': /* --dst-range */
130		end = strchr(optarg, '-');
131		if (end == NULL)
132			param_act(P_BAD_VALUE, "iprange", "--dst-range", optarg);
133		*end = '\0';
134		ia = numeric_to_ipaddr(optarg);
135		if (ia == NULL)
136			param_act(P_BAD_VALUE, "iprange", "--dst-range", optarg);
137		memcpy(&info->dst_min.in, ia, sizeof(*ia));
138		ia = numeric_to_ipaddr(end + 1);
139		if (ia == NULL)
140			param_act(P_BAD_VALUE, "iprange", "--dst-range", end + 1);
141		memcpy(&info->dst_max.in, ia, sizeof(*ia));
142		info->flags |= IPRANGE_DST;
143		if (invert)
144			info->flags |= IPRANGE_DST_INV;
145		*flags |= F_DSTIP;
146		return true;
147	}
148	return false;
149}
150
151static int
152iprange_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
153                  const void *entry, struct xt_entry_match **match)
154{
155	struct xt_iprange_mtinfo *info = (void *)(*match)->data;
156	const struct in6_addr *ia;
157	char *end;
158
159	switch (c) {
160	case '1': /* --src-range */
161		end = strchr(optarg, '-');
162		if (end == NULL)
163			param_act(P_BAD_VALUE, "iprange", "--src-range", optarg);
164		*end = '\0';
165		ia = numeric_to_ip6addr(optarg);
166		if (ia == NULL)
167			param_act(P_BAD_VALUE, "iprange", "--src-range", optarg);
168		memcpy(&info->src_min.in, ia, sizeof(*ia));
169		ia = numeric_to_ip6addr(end+1);
170		if (ia == NULL)
171			param_act(P_BAD_VALUE, "iprange", "--src-range", end + 1);
172		memcpy(&info->src_max.in, ia, sizeof(*ia));
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		end = strchr(optarg, '-');
181		if (end == NULL)
182			param_act(P_BAD_VALUE, "iprange", "--dst-range", optarg);
183		*end = '\0';
184		ia = numeric_to_ip6addr(optarg);
185		if (ia == NULL)
186			param_act(P_BAD_VALUE, "iprange", "--dst-range", optarg);
187		memcpy(&info->dst_min.in, ia, sizeof(*ia));
188		ia = numeric_to_ip6addr(end + 1);
189		if (ia == NULL)
190			param_act(P_BAD_VALUE, "iprange", "--dst-range", end + 1);
191		memcpy(&info->dst_max.in, ia, sizeof(*ia));
192		info->flags |= IPRANGE_DST;
193		if (invert)
194			info->flags |= IPRANGE_DST_INV;
195		*flags |= F_DSTIP;
196		return true;
197	}
198	return false;
199}
200
201static void iprange_mt_check(unsigned int flags)
202{
203	if (flags == 0)
204		exit_error(PARAMETER_PROBLEM,
205			   "iprange match: You must specify `--src-range' or `--dst-range'");
206}
207
208static void
209print_iprange(const struct ipt_iprange *range)
210{
211	const unsigned char *byte_min, *byte_max;
212
213	byte_min = (const unsigned char *)&range->min_ip;
214	byte_max = (const unsigned char *)&range->max_ip;
215	printf("%u.%u.%u.%u-%u.%u.%u.%u ",
216		byte_min[0], byte_min[1], byte_min[2], byte_min[3],
217		byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
218}
219
220static void iprange_print(const void *ip, const struct xt_entry_match *match,
221                          int numeric)
222{
223	const struct ipt_iprange_info *info = (const void *)match->data;
224
225	if (info->flags & IPRANGE_SRC) {
226		printf("source IP range ");
227		if (info->flags & IPRANGE_SRC_INV)
228			printf("! ");
229		print_iprange(&info->src);
230	}
231	if (info->flags & IPRANGE_DST) {
232		printf("destination IP range ");
233		if (info->flags & IPRANGE_DST_INV)
234			printf("! ");
235		print_iprange(&info->dst);
236	}
237}
238
239static void
240iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
241                  int numeric)
242{
243	const struct xt_iprange_mtinfo *info = (const void *)match->data;
244
245	if (info->flags & IPRANGE_SRC) {
246		printf("source IP range ");
247		if (info->flags & IPRANGE_SRC_INV)
248			printf("! ");
249		/*
250		 * ipaddr_to_numeric() uses a static buffer, so cannot
251		 * combine the printf() calls.
252		 */
253		printf("%s", ipaddr_to_numeric(&info->src_min.in));
254		printf("-%s ", ipaddr_to_numeric(&info->src_max.in));
255	}
256	if (info->flags & IPRANGE_DST) {
257		printf("destination IP range ");
258		if (info->flags & IPRANGE_DST_INV)
259			printf("! ");
260		printf("%s", ipaddr_to_numeric(&info->dst_min.in));
261		printf("-%s ", ipaddr_to_numeric(&info->dst_max.in));
262	}
263}
264
265static void
266iprange_mt6_print(const void *ip, const struct xt_entry_match *match,
267                  int numeric)
268{
269	const struct xt_iprange_mtinfo *info = (const void *)match->data;
270
271	if (info->flags & IPRANGE_SRC) {
272		printf("source IP range ");
273		if (info->flags & IPRANGE_SRC_INV)
274			printf("! ");
275		/*
276		 * ipaddr_to_numeric() uses a static buffer, so cannot
277		 * combine the printf() calls.
278		 */
279		printf("%s", ip6addr_to_numeric(&info->src_min.in6));
280		printf("-%s ", ip6addr_to_numeric(&info->src_max.in6));
281	}
282	if (info->flags & IPRANGE_DST) {
283		printf("destination IP range ");
284		if (info->flags & IPRANGE_DST_INV)
285			printf("! ");
286		printf("%s", ip6addr_to_numeric(&info->dst_min.in6));
287		printf("-%s ", ip6addr_to_numeric(&info->dst_max.in6));
288	}
289}
290
291static void iprange_save(const void *ip, const struct xt_entry_match *match)
292{
293	const struct ipt_iprange_info *info = (const void *)match->data;
294
295	if (info->flags & IPRANGE_SRC) {
296		if (info->flags & IPRANGE_SRC_INV)
297			printf("! ");
298		printf("--src-range ");
299		print_iprange(&info->src);
300		if (info->flags & IPRANGE_DST)
301			fputc(' ', stdout);
302	}
303	if (info->flags & IPRANGE_DST) {
304		if (info->flags & IPRANGE_DST_INV)
305			printf("! ");
306		printf("--dst-range ");
307		print_iprange(&info->dst);
308	}
309}
310
311static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match)
312{
313	const struct xt_iprange_mtinfo *info = (const void *)match->data;
314
315	if (info->flags & IPRANGE_SRC) {
316		if (info->flags & IPRANGE_SRC_INV)
317			printf("! ");
318		printf("--src-range %s", ipaddr_to_numeric(&info->src_min.in));
319		printf("-%s ", ipaddr_to_numeric(&info->src_max.in));
320	}
321	if (info->flags & IPRANGE_DST) {
322		if (info->flags & IPRANGE_DST_INV)
323			printf("! ");
324		printf("--dst-range %s", ipaddr_to_numeric(&info->dst_min.in));
325		printf("-%s ", ipaddr_to_numeric(&info->dst_max.in));
326	}
327}
328
329static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match)
330{
331	const struct xt_iprange_mtinfo *info = (const void *)match->data;
332
333	if (info->flags & IPRANGE_SRC) {
334		if (info->flags & IPRANGE_SRC_INV)
335			printf("! ");
336		printf("--src-range %s", ip6addr_to_numeric(&info->src_min.in6));
337		printf("-%s ", ip6addr_to_numeric(&info->src_max.in6));
338	}
339	if (info->flags & IPRANGE_DST) {
340		if (info->flags & IPRANGE_DST_INV)
341			printf("! ");
342		printf("--dst-range %s", ip6addr_to_numeric(&info->dst_min.in6));
343		printf("-%s ", ip6addr_to_numeric(&info->dst_max.in6));
344	}
345}
346
347static struct xtables_match iprange_match = {
348	.version       = XTABLES_VERSION,
349	.name          = "iprange",
350	.revision      = 0,
351	.family        = AF_INET,
352	.size          = XT_ALIGN(sizeof(struct ipt_iprange_info)),
353	.userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)),
354	.help          = iprange_mt_help,
355	.parse         = iprange_parse,
356	.final_check   = iprange_mt_check,
357	.print         = iprange_print,
358	.save          = iprange_save,
359	.extra_opts    = iprange_mt_opts,
360};
361
362static struct xtables_match iprange_mt_reg = {
363	.version        = XTABLES_VERSION,
364	.name           = "iprange",
365	.revision       = 1,
366	.family         = AF_INET,
367	.size           = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
368	.userspacesize  = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
369	.help           = iprange_mt_help,
370	.parse          = iprange_mt4_parse,
371	.final_check    = iprange_mt_check,
372	.print          = iprange_mt4_print,
373	.save           = iprange_mt4_save,
374	.extra_opts     = iprange_mt_opts,
375};
376
377static struct xtables_match iprange_mt6_reg = {
378	.version        = XTABLES_VERSION,
379	.name           = "iprange",
380	.revision       = 1,
381	.family         = AF_INET6,
382	.size           = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
383	.userspacesize  = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
384	.help           = iprange_mt_help,
385	.parse          = iprange_mt6_parse,
386	.final_check    = iprange_mt_check,
387	.print          = iprange_mt6_print,
388	.save           = iprange_mt6_save,
389	.extra_opts     = iprange_mt_opts,
390};
391
392void _init(void)
393{
394	xtables_register_match(&iprange_match);
395	xtables_register_match(&iprange_mt_reg);
396	xtables_register_match(&iprange_mt6_reg);
397}
398