1 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
2  *                         Patrick Schaaf <bof@bof.de>
3  *                         Martin Josefsson <gandalf@wlug.westbo.se>
4  * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 
11 /* Shared library add-on to iptables to add IP set matching. */
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <netdb.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <getopt.h>
18 #include <ctype.h>
19 #include <errno.h>
20 
21 #include <xtables.h>
22 #include <linux/netfilter/xt_set.h>
23 #include "libxt_set.h"
24 
25 /* Revision 0 */
26 
27 static void
set_help_v0(void)28 set_help_v0(void)
29 {
30 	printf("set match options:\n"
31 	       " [!] --match-set name flags\n"
32 	       "		 'name' is the set name from to match,\n"
33 	       "		 'flags' are the comma separated list of\n"
34 	       "		 'src' and 'dst' specifications.\n");
35 }
36 
37 static const struct option set_opts_v0[] = {
38 	{.name = "match-set", .has_arg = true, .val = '1'},
39 	{.name = "set",       .has_arg = true, .val = '2'},
40 	XT_GETOPT_TABLEEND,
41 };
42 
43 static void
set_check_v0(unsigned int flags)44 set_check_v0(unsigned int flags)
45 {
46 	if (!flags)
47 		xtables_error(PARAMETER_PROBLEM,
48 			"You must specify `--match-set' with proper arguments");
49 }
50 
51 static int
set_parse_v0(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)52 set_parse_v0(int c, char **argv, int invert, unsigned int *flags,
53 	     const void *entry, struct xt_entry_match **match)
54 {
55 	struct xt_set_info_match_v0 *myinfo =
56 		(struct xt_set_info_match_v0 *) (*match)->data;
57 	struct xt_set_info_v0 *info = &myinfo->match_set;
58 
59 	switch (c) {
60 	case '2':
61 		fprintf(stderr,
62 			"--set option deprecated, please use --match-set\n");
63 	case '1':		/* --match-set <set> <flag>[,<flag> */
64 		if (info->u.flags[0])
65 			xtables_error(PARAMETER_PROBLEM,
66 				      "--match-set can be specified only once");
67 		if (invert)
68 			info->u.flags[0] |= IPSET_MATCH_INV;
69 
70 		if (!argv[optind]
71 		    || argv[optind][0] == '-'
72 		    || argv[optind][0] == '!')
73 			xtables_error(PARAMETER_PROBLEM,
74 				      "--match-set requires two args.");
75 
76 		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
77 			xtables_error(PARAMETER_PROBLEM,
78 				      "setname `%s' too long, max %d characters.",
79 				      optarg, IPSET_MAXNAMELEN - 1);
80 
81 		get_set_byname(optarg, (struct xt_set_info *)info);
82 		parse_dirs_v0(argv[optind], info);
83 		DEBUGP("parse: set index %u\n", info->index);
84 		optind++;
85 
86 		*flags = 1;
87 		break;
88 	}
89 
90 	return 1;
91 }
92 
93 static void
print_match_v0(const char * prefix,const struct xt_set_info_v0 * info)94 print_match_v0(const char *prefix, const struct xt_set_info_v0 *info)
95 {
96 	int i;
97 	char setname[IPSET_MAXNAMELEN];
98 
99 	get_set_byid(setname, info->index);
100 	printf("%s %s %s",
101 	       (info->u.flags[0] & IPSET_MATCH_INV) ? " !" : "",
102 	       prefix,
103 	       setname);
104 	for (i = 0; i < IPSET_DIM_MAX; i++) {
105 		if (!info->u.flags[i])
106 			break;
107 		printf("%s%s",
108 		       i == 0 ? " " : ",",
109 		       info->u.flags[i] & IPSET_SRC ? "src" : "dst");
110 	}
111 }
112 
113 /* Prints out the matchinfo. */
114 static void
set_print_v0(const void * ip,const struct xt_entry_match * match,int numeric)115 set_print_v0(const void *ip, const struct xt_entry_match *match, int numeric)
116 {
117 	const struct xt_set_info_match_v0 *info = (const void *)match->data;
118 
119 	print_match_v0("match-set", &info->match_set);
120 }
121 
122 static void
set_save_v0(const void * ip,const struct xt_entry_match * match)123 set_save_v0(const void *ip, const struct xt_entry_match *match)
124 {
125 	const struct xt_set_info_match_v0 *info = (const void *)match->data;
126 
127 	print_match_v0("--match-set", &info->match_set);
128 }
129 
130 /* Revision 1 */
131 static int
set_parse_v1(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)132 set_parse_v1(int c, char **argv, int invert, unsigned int *flags,
133 	     const void *entry, struct xt_entry_match **match)
134 {
135 	struct xt_set_info_match_v1 *myinfo =
136 		(struct xt_set_info_match_v1 *) (*match)->data;
137 	struct xt_set_info *info = &myinfo->match_set;
138 
139 	switch (c) {
140 	case '2':
141 		fprintf(stderr,
142 			"--set option deprecated, please use --match-set\n");
143 	case '1':		/* --match-set <set> <flag>[,<flag> */
144 		if (info->dim)
145 			xtables_error(PARAMETER_PROBLEM,
146 				      "--match-set can be specified only once");
147 		if (invert)
148 			info->flags |= IPSET_INV_MATCH;
149 
150 		if (!argv[optind]
151 		    || argv[optind][0] == '-'
152 		    || argv[optind][0] == '!')
153 			xtables_error(PARAMETER_PROBLEM,
154 				      "--match-set requires two args.");
155 
156 		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
157 			xtables_error(PARAMETER_PROBLEM,
158 				      "setname `%s' too long, max %d characters.",
159 				      optarg, IPSET_MAXNAMELEN - 1);
160 
161 		get_set_byname(optarg, info);
162 		parse_dirs(argv[optind], info);
163 		DEBUGP("parse: set index %u\n", info->index);
164 		optind++;
165 
166 		*flags = 1;
167 		break;
168 	}
169 
170 	return 1;
171 }
172 
173 static void
print_match(const char * prefix,const struct xt_set_info * info)174 print_match(const char *prefix, const struct xt_set_info *info)
175 {
176 	int i;
177 	char setname[IPSET_MAXNAMELEN];
178 
179 	get_set_byid(setname, info->index);
180 	printf("%s %s %s",
181 	       (info->flags & IPSET_INV_MATCH) ? " !" : "",
182 	       prefix,
183 	       setname);
184 	for (i = 1; i <= info->dim; i++) {
185 		printf("%s%s",
186 		       i == 1 ? " " : ",",
187 		       info->flags & (1 << i) ? "src" : "dst");
188 	}
189 }
190 
191 /* Prints out the matchinfo. */
192 static void
set_print_v1(const void * ip,const struct xt_entry_match * match,int numeric)193 set_print_v1(const void *ip, const struct xt_entry_match *match, int numeric)
194 {
195 	const struct xt_set_info_match_v1 *info = (const void *)match->data;
196 
197 	print_match("match-set", &info->match_set);
198 }
199 
200 static void
set_save_v1(const void * ip,const struct xt_entry_match * match)201 set_save_v1(const void *ip, const struct xt_entry_match *match)
202 {
203 	const struct xt_set_info_match_v1 *info = (const void *)match->data;
204 
205 	print_match("--match-set", &info->match_set);
206 }
207 
208 /* Revision 2 */
209 static void
set_help_v2(void)210 set_help_v2(void)
211 {
212 	printf("set match options:\n"
213 	       " [!] --match-set name flags [--return-nomatch]\n"
214 	       "		 'name' is the set name from to match,\n"
215 	       "		 'flags' are the comma separated list of\n"
216 	       "		 'src' and 'dst' specifications.\n");
217 }
218 
219 static const struct option set_opts_v2[] = {
220 	{.name = "match-set",		.has_arg = true,	.val = '1'},
221 	{.name = "set",			.has_arg = true,	.val = '2'},
222 	{.name = "return-nomatch",	.has_arg = false,	.val = '3'},
223 	XT_GETOPT_TABLEEND,
224 };
225 
226 static int
set_parse_v2(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)227 set_parse_v2(int c, char **argv, int invert, unsigned int *flags,
228 	     const void *entry, struct xt_entry_match **match)
229 {
230 	struct xt_set_info_match_v1 *myinfo =
231 		(struct xt_set_info_match_v1 *) (*match)->data;
232 	struct xt_set_info *info = &myinfo->match_set;
233 
234 	switch (c) {
235 	case '3':
236 		info->flags |= IPSET_RETURN_NOMATCH;
237 		break;
238 	case '2':
239 		fprintf(stderr,
240 			"--set option deprecated, please use --match-set\n");
241 	case '1':		/* --match-set <set> <flag>[,<flag> */
242 		if (info->dim)
243 			xtables_error(PARAMETER_PROBLEM,
244 				      "--match-set can be specified only once");
245 		if (invert)
246 			info->flags |= IPSET_INV_MATCH;
247 
248 		if (!argv[optind]
249 		    || argv[optind][0] == '-'
250 		    || argv[optind][0] == '!')
251 			xtables_error(PARAMETER_PROBLEM,
252 				      "--match-set requires two args.");
253 
254 		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
255 			xtables_error(PARAMETER_PROBLEM,
256 				      "setname `%s' too long, max %d characters.",
257 				      optarg, IPSET_MAXNAMELEN - 1);
258 
259 		get_set_byname(optarg, info);
260 		parse_dirs(argv[optind], info);
261 		DEBUGP("parse: set index %u\n", info->index);
262 		optind++;
263 
264 		*flags = 1;
265 		break;
266 	}
267 
268 	return 1;
269 }
270 
271 /* Prints out the matchinfo. */
272 static void
set_print_v2(const void * ip,const struct xt_entry_match * match,int numeric)273 set_print_v2(const void *ip, const struct xt_entry_match *match, int numeric)
274 {
275 	const struct xt_set_info_match_v1 *info = (const void *)match->data;
276 
277 	print_match("match-set", &info->match_set);
278 	if (info->match_set.flags & IPSET_RETURN_NOMATCH)
279 		printf(" return-nomatch");
280 }
281 
282 static void
set_save_v2(const void * ip,const struct xt_entry_match * match)283 set_save_v2(const void *ip, const struct xt_entry_match *match)
284 {
285 	const struct xt_set_info_match_v1 *info = (const void *)match->data;
286 
287 	print_match("--match-set", &info->match_set);
288 	if (info->match_set.flags & IPSET_RETURN_NOMATCH)
289 		printf(" --return-nomatch");
290 }
291 
292 /* Revision 3 */
293 static void
set_help_v3(void)294 set_help_v3(void)
295 {
296 	printf("set match options:\n"
297 	       " [!] --match-set name flags [--return-nomatch]\n"
298 	       "   [! --update-counters] [! --update-subcounters]\n"
299 	       "   [[!] --packets-eq value | --packets-lt value | --packets-gt value\n"
300 	       "   [[!] --bytes-eq value | --bytes-lt value | --bytes-gt value\n"
301 	       "		 'name' is the set name from to match,\n"
302 	       "		 'flags' are the comma separated list of\n"
303 	       "		 'src' and 'dst' specifications.\n");
304 }
305 
306 static const struct option set_opts_v3[] = {
307 	{.name = "match-set",		.has_arg = true,	.val = '1'},
308 	{.name = "set",			.has_arg = true,	.val = '2'},
309 	{.name = "return-nomatch",	.has_arg = false,	.val = '3'},
310 	{.name = "update-counters",	.has_arg = false,	.val = '4'},
311 	{.name = "packets-eq",		.has_arg = true,	.val = '5'},
312 	{.name = "packets-lt",		.has_arg = true,	.val = '6'},
313 	{.name = "packets-gt",		.has_arg = true,	.val = '7'},
314 	{.name = "bytes-eq",		.has_arg = true,	.val = '8'},
315 	{.name = "bytes-lt",		.has_arg = true,	.val = '9'},
316 	{.name = "bytes-gt",		.has_arg = true,	.val = '0'},
317 	{.name = "update-subcounters",	.has_arg = false,	.val = 'a'},
318 	XT_GETOPT_TABLEEND,
319 };
320 
321 static uint64_t
parse_counter(const char * opt)322 parse_counter(const char *opt)
323 {
324 	uintmax_t value;
325 
326 	if (!xtables_strtoul(opt, NULL, &value, 0, UINT64_MAX))
327 		xtables_error(PARAMETER_PROBLEM,
328 			      "Cannot parse %s as a counter value\n",
329 			      opt);
330 	return (uint64_t)value;
331 }
332 
333 static int
set_parse_v3(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)334 set_parse_v3(int c, char **argv, int invert, unsigned int *flags,
335 	     const void *entry, struct xt_entry_match **match)
336 {
337 	struct xt_set_info_match_v3 *info =
338 		(struct xt_set_info_match_v3 *) (*match)->data;
339 
340 	switch (c) {
341 	case 'a':
342 		if (invert)
343 			info->flags |= IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE;
344 		break;
345 	case '0':
346 		if (info->bytes.op != IPSET_COUNTER_NONE)
347 			xtables_error(PARAMETER_PROBLEM,
348 				      "only one of the --bytes-[eq|lt|gt]"
349 				      " is allowed\n");
350 		if (invert)
351 			xtables_error(PARAMETER_PROBLEM,
352 				      "--bytes-gt option cannot be inverted\n");
353 		info->bytes.op = IPSET_COUNTER_GT;
354 		info->bytes.value = parse_counter(optarg);
355 		break;
356 	case '9':
357 		if (info->bytes.op != IPSET_COUNTER_NONE)
358 			xtables_error(PARAMETER_PROBLEM,
359 				      "only one of the --bytes-[eq|lt|gt]"
360 				      " is allowed\n");
361 		if (invert)
362 			xtables_error(PARAMETER_PROBLEM,
363 				      "--bytes-lt option cannot be inverted\n");
364 		info->bytes.op = IPSET_COUNTER_LT;
365 		info->bytes.value = parse_counter(optarg);
366 		break;
367 	case '8':
368 		if (info->bytes.op != IPSET_COUNTER_NONE)
369 			xtables_error(PARAMETER_PROBLEM,
370 				      "only one of the --bytes-[eq|lt|gt]"
371 				      " is allowed\n");
372 		info->bytes.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
373 		info->bytes.value = parse_counter(optarg);
374 		break;
375 	case '7':
376 		if (info->packets.op != IPSET_COUNTER_NONE)
377 			xtables_error(PARAMETER_PROBLEM,
378 				      "only one of the --packets-[eq|lt|gt]"
379 				      " is allowed\n");
380 		if (invert)
381 			xtables_error(PARAMETER_PROBLEM,
382 				      "--packets-gt option cannot be inverted\n");
383 		info->packets.op = IPSET_COUNTER_GT;
384 		info->packets.value = parse_counter(optarg);
385 		break;
386 	case '6':
387 		if (info->packets.op != IPSET_COUNTER_NONE)
388 			xtables_error(PARAMETER_PROBLEM,
389 				      "only one of the --packets-[eq|lt|gt]"
390 				      " is allowed\n");
391 		if (invert)
392 			xtables_error(PARAMETER_PROBLEM,
393 				      "--packets-lt option cannot be inverted\n");
394 		info->packets.op = IPSET_COUNTER_LT;
395 		info->packets.value = parse_counter(optarg);
396 		break;
397 	case '5':
398 		if (info->packets.op != IPSET_COUNTER_NONE)
399 			xtables_error(PARAMETER_PROBLEM,
400 				      "only one of the --packets-[eq|lt|gt]"
401 				      " is allowed\n");
402 		info->packets.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
403 		info->packets.value = parse_counter(optarg);
404 		break;
405 	case '4':
406 		if (invert)
407 			info->flags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
408 		break;
409 	case '3':
410 		if (invert)
411 			xtables_error(PARAMETER_PROBLEM,
412 				      "--return-nomatch flag cannot be inverted\n");
413 		info->flags |= IPSET_FLAG_RETURN_NOMATCH;
414 		break;
415 	case '2':
416 		fprintf(stderr,
417 			"--set option deprecated, please use --match-set\n");
418 	case '1':		/* --match-set <set> <flag>[,<flag> */
419 		if (info->match_set.dim)
420 			xtables_error(PARAMETER_PROBLEM,
421 				      "--match-set can be specified only once");
422 		if (invert)
423 			info->match_set.flags |= IPSET_INV_MATCH;
424 
425 		if (!argv[optind]
426 		    || argv[optind][0] == '-'
427 		    || argv[optind][0] == '!')
428 			xtables_error(PARAMETER_PROBLEM,
429 				      "--match-set requires two args.");
430 
431 		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
432 			xtables_error(PARAMETER_PROBLEM,
433 				      "setname `%s' too long, max %d characters.",
434 				      optarg, IPSET_MAXNAMELEN - 1);
435 
436 		get_set_byname(optarg, &info->match_set);
437 		parse_dirs(argv[optind], &info->match_set);
438 		DEBUGP("parse: set index %u\n", info->match_set.index);
439 		optind++;
440 
441 		*flags = 1;
442 		break;
443 	}
444 
445 	return 1;
446 }
447 
448 static void
set_printv3_counter(const struct ip_set_counter_match * c,const char * name,const char * sep)449 set_printv3_counter(const struct ip_set_counter_match *c, const char *name,
450 		    const char *sep)
451 {
452 	switch (c->op) {
453 	case IPSET_COUNTER_EQ:
454 		printf(" %s%s-eq %llu", sep, name, c->value);
455 		break;
456 	case IPSET_COUNTER_NE:
457 		printf(" ! %s%s-eq %llu", sep, name, c->value);
458 		break;
459 	case IPSET_COUNTER_LT:
460 		printf(" %s%s-lt %llu", sep, name, c->value);
461 		break;
462 	case IPSET_COUNTER_GT:
463 		printf(" %s%s-gt %llu", sep, name, c->value);
464 		break;
465 	}
466 }
467 
468 static void
set_print_v3_matchinfo(const struct xt_set_info_match_v3 * info,const char * opt,const char * sep)469 set_print_v3_matchinfo(const struct xt_set_info_match_v3 *info,
470 		       const char *opt, const char *sep)
471 {
472 	print_match(opt, &info->match_set);
473 	if (info->flags & IPSET_FLAG_RETURN_NOMATCH)
474 		printf(" %sreturn-nomatch", sep);
475 	if ((info->flags & IPSET_FLAG_SKIP_COUNTER_UPDATE))
476 		printf(" ! %supdate-counters", sep);
477 	if ((info->flags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE))
478 		printf(" ! %supdate-subcounters", sep);
479 	set_printv3_counter(&info->packets, "packets", sep);
480 	set_printv3_counter(&info->bytes, "bytes", sep);
481 }
482 
483 /* Prints out the matchinfo. */
484 static void
set_print_v3(const void * ip,const struct xt_entry_match * match,int numeric)485 set_print_v3(const void *ip, const struct xt_entry_match *match, int numeric)
486 {
487 	const struct xt_set_info_match_v3 *info = (const void *)match->data;
488 
489 	set_print_v3_matchinfo(info, "match-set", "");
490 }
491 
492 static void
set_save_v3(const void * ip,const struct xt_entry_match * match)493 set_save_v3(const void *ip, const struct xt_entry_match *match)
494 {
495 	const struct xt_set_info_match_v3 *info = (const void *)match->data;
496 
497 	set_print_v3_matchinfo(info, "--match-set", "--");
498 }
499 
500 static struct xtables_match set_mt_reg[] = {
501 	{
502 		.name		= "set",
503 		.revision	= 0,
504 		.version	= XTABLES_VERSION,
505 		.family		= NFPROTO_IPV4,
506 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
507 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
508 		.help		= set_help_v0,
509 		.parse		= set_parse_v0,
510 		.final_check	= set_check_v0,
511 		.print		= set_print_v0,
512 		.save		= set_save_v0,
513 		.extra_opts	= set_opts_v0,
514 	},
515 	{
516 		.name		= "set",
517 		.revision	= 1,
518 		.version	= XTABLES_VERSION,
519 		.family		= NFPROTO_UNSPEC,
520 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
521 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
522 		.help		= set_help_v0,
523 		.parse		= set_parse_v1,
524 		.final_check	= set_check_v0,
525 		.print		= set_print_v1,
526 		.save		= set_save_v1,
527 		.extra_opts	= set_opts_v0,
528 	},
529 	{
530 		.name		= "set",
531 		.revision	= 2,
532 		.version	= XTABLES_VERSION,
533 		.family		= NFPROTO_UNSPEC,
534 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
535 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
536 		.help		= set_help_v2,
537 		.parse		= set_parse_v2,
538 		.final_check	= set_check_v0,
539 		.print		= set_print_v2,
540 		.save		= set_save_v2,
541 		.extra_opts	= set_opts_v2,
542 	},
543 	{
544 		.name		= "set",
545 		.revision	= 3,
546 		.version	= XTABLES_VERSION,
547 		.family		= NFPROTO_UNSPEC,
548 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
549 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
550 		.help		= set_help_v3,
551 		.parse		= set_parse_v3,
552 		.final_check	= set_check_v0,
553 		.print		= set_print_v3,
554 		.save		= set_save_v3,
555 		.extra_opts	= set_opts_v3,
556 	},
557 };
558 
_init(void)559 void _init(void)
560 {
561 	xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg));
562 }
563