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_match0 * c,const char * name,const char * sep)449 set_printv3_counter(const struct ip_set_counter_match0 *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 /* Revision 4 */
501 static int
set_parse_v4(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)502 set_parse_v4(int c, char **argv, int invert, unsigned int *flags,
503 	     const void *entry, struct xt_entry_match **match)
504 {
505 	struct xt_set_info_match_v4 *info =
506 		(struct xt_set_info_match_v4 *) (*match)->data;
507 
508 	switch (c) {
509 	case 'a':
510 		if (invert)
511 			info->flags |= IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE;
512 		break;
513 	case '0':
514 		if (info->bytes.op != IPSET_COUNTER_NONE)
515 			xtables_error(PARAMETER_PROBLEM,
516 				      "only one of the --bytes-[eq|lt|gt]"
517 				      " is allowed\n");
518 		if (invert)
519 			xtables_error(PARAMETER_PROBLEM,
520 				      "--bytes-gt option cannot be inverted\n");
521 		info->bytes.op = IPSET_COUNTER_GT;
522 		info->bytes.value = parse_counter(optarg);
523 		break;
524 	case '9':
525 		if (info->bytes.op != IPSET_COUNTER_NONE)
526 			xtables_error(PARAMETER_PROBLEM,
527 				      "only one of the --bytes-[eq|lt|gt]"
528 				      " is allowed\n");
529 		if (invert)
530 			xtables_error(PARAMETER_PROBLEM,
531 				      "--bytes-lt option cannot be inverted\n");
532 		info->bytes.op = IPSET_COUNTER_LT;
533 		info->bytes.value = parse_counter(optarg);
534 		break;
535 	case '8':
536 		if (info->bytes.op != IPSET_COUNTER_NONE)
537 			xtables_error(PARAMETER_PROBLEM,
538 				      "only one of the --bytes-[eq|lt|gt]"
539 				      " is allowed\n");
540 		info->bytes.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
541 		info->bytes.value = parse_counter(optarg);
542 		break;
543 	case '7':
544 		if (info->packets.op != IPSET_COUNTER_NONE)
545 			xtables_error(PARAMETER_PROBLEM,
546 				      "only one of the --packets-[eq|lt|gt]"
547 				      " is allowed\n");
548 		if (invert)
549 			xtables_error(PARAMETER_PROBLEM,
550 				      "--packets-gt option cannot be inverted\n");
551 		info->packets.op = IPSET_COUNTER_GT;
552 		info->packets.value = parse_counter(optarg);
553 		break;
554 	case '6':
555 		if (info->packets.op != IPSET_COUNTER_NONE)
556 			xtables_error(PARAMETER_PROBLEM,
557 				      "only one of the --packets-[eq|lt|gt]"
558 				      " is allowed\n");
559 		if (invert)
560 			xtables_error(PARAMETER_PROBLEM,
561 				      "--packets-lt option cannot be inverted\n");
562 		info->packets.op = IPSET_COUNTER_LT;
563 		info->packets.value = parse_counter(optarg);
564 		break;
565 	case '5':
566 		if (info->packets.op != IPSET_COUNTER_NONE)
567 			xtables_error(PARAMETER_PROBLEM,
568 				      "only one of the --packets-[eq|lt|gt]"
569 				      " is allowed\n");
570 		info->packets.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
571 		info->packets.value = parse_counter(optarg);
572 		break;
573 	case '4':
574 		if (invert)
575 			info->flags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
576 		break;
577 	case '3':
578 		if (invert)
579 			xtables_error(PARAMETER_PROBLEM,
580 				      "--return-nomatch flag cannot be inverted\n");
581 		info->flags |= IPSET_FLAG_RETURN_NOMATCH;
582 		break;
583 	case '2':
584 		fprintf(stderr,
585 			"--set option deprecated, please use --match-set\n");
586 	case '1':		/* --match-set <set> <flag>[,<flag> */
587 		if (info->match_set.dim)
588 			xtables_error(PARAMETER_PROBLEM,
589 				      "--match-set can be specified only once");
590 		if (invert)
591 			info->match_set.flags |= IPSET_INV_MATCH;
592 
593 		if (!argv[optind]
594 		    || argv[optind][0] == '-'
595 		    || argv[optind][0] == '!')
596 			xtables_error(PARAMETER_PROBLEM,
597 				      "--match-set requires two args.");
598 
599 		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
600 			xtables_error(PARAMETER_PROBLEM,
601 				      "setname `%s' too long, max %d characters.",
602 				      optarg, IPSET_MAXNAMELEN - 1);
603 
604 		get_set_byname(optarg, &info->match_set);
605 		parse_dirs(argv[optind], &info->match_set);
606 		DEBUGP("parse: set index %u\n", info->match_set.index);
607 		optind++;
608 
609 		*flags = 1;
610 		break;
611 	}
612 
613 	return 1;
614 }
615 
616 static void
set_printv4_counter(const struct ip_set_counter_match * c,const char * name,const char * sep)617 set_printv4_counter(const struct ip_set_counter_match *c, const char *name,
618 		    const char *sep)
619 {
620 	switch (c->op) {
621 	case IPSET_COUNTER_EQ:
622 		printf(" %s%s-eq %llu", sep, name, c->value);
623 		break;
624 	case IPSET_COUNTER_NE:
625 		printf(" ! %s%s-eq %llu", sep, name, c->value);
626 		break;
627 	case IPSET_COUNTER_LT:
628 		printf(" %s%s-lt %llu", sep, name, c->value);
629 		break;
630 	case IPSET_COUNTER_GT:
631 		printf(" %s%s-gt %llu", sep, name, c->value);
632 		break;
633 	}
634 }
635 
636 static void
set_print_v4_matchinfo(const struct xt_set_info_match_v4 * info,const char * opt,const char * sep)637 set_print_v4_matchinfo(const struct xt_set_info_match_v4 *info,
638 		       const char *opt, const char *sep)
639 {
640 	print_match(opt, &info->match_set);
641 	if (info->flags & IPSET_FLAG_RETURN_NOMATCH)
642 		printf(" %sreturn-nomatch", sep);
643 	if ((info->flags & IPSET_FLAG_SKIP_COUNTER_UPDATE))
644 		printf(" ! %supdate-counters", sep);
645 	if ((info->flags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE))
646 		printf(" ! %supdate-subcounters", sep);
647 	set_printv4_counter(&info->packets, "packets", sep);
648 	set_printv4_counter(&info->bytes, "bytes", sep);
649 }
650 
651 /* Prints out the matchinfo. */
652 static void
set_print_v4(const void * ip,const struct xt_entry_match * match,int numeric)653 set_print_v4(const void *ip, const struct xt_entry_match *match, int numeric)
654 {
655 	const struct xt_set_info_match_v4 *info = (const void *)match->data;
656 
657 	set_print_v4_matchinfo(info, "match-set", "");
658 }
659 
660 static void
set_save_v4(const void * ip,const struct xt_entry_match * match)661 set_save_v4(const void *ip, const struct xt_entry_match *match)
662 {
663 	const struct xt_set_info_match_v4 *info = (const void *)match->data;
664 
665 	set_print_v4_matchinfo(info, "--match-set", "--");
666 }
667 
668 static struct xtables_match set_mt_reg[] = {
669 	{
670 		.name		= "set",
671 		.revision	= 0,
672 		.version	= XTABLES_VERSION,
673 		.family		= NFPROTO_IPV4,
674 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
675 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
676 		.help		= set_help_v0,
677 		.parse		= set_parse_v0,
678 		.final_check	= set_check_v0,
679 		.print		= set_print_v0,
680 		.save		= set_save_v0,
681 		.extra_opts	= set_opts_v0,
682 	},
683 	{
684 		.name		= "set",
685 		.revision	= 1,
686 		.version	= XTABLES_VERSION,
687 		.family		= NFPROTO_UNSPEC,
688 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
689 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
690 		.help		= set_help_v0,
691 		.parse		= set_parse_v1,
692 		.final_check	= set_check_v0,
693 		.print		= set_print_v1,
694 		.save		= set_save_v1,
695 		.extra_opts	= set_opts_v0,
696 	},
697 	{
698 		.name		= "set",
699 		.revision	= 2,
700 		.version	= XTABLES_VERSION,
701 		.family		= NFPROTO_UNSPEC,
702 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
703 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
704 		.help		= set_help_v2,
705 		.parse		= set_parse_v2,
706 		.final_check	= set_check_v0,
707 		.print		= set_print_v2,
708 		.save		= set_save_v2,
709 		.extra_opts	= set_opts_v2,
710 	},
711 	{
712 		.name		= "set",
713 		.revision	= 3,
714 		.version	= XTABLES_VERSION,
715 		.family		= NFPROTO_UNSPEC,
716 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
717 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
718 		.help		= set_help_v3,
719 		.parse		= set_parse_v3,
720 		.final_check	= set_check_v0,
721 		.print		= set_print_v3,
722 		.save		= set_save_v3,
723 		.extra_opts	= set_opts_v3,
724 	},
725 	{
726 		.name		= "set",
727 		.revision	= 4,
728 		.version	= XTABLES_VERSION,
729 		.family		= NFPROTO_UNSPEC,
730 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v4)),
731 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v4)),
732 		.help		= set_help_v3,
733 		.parse		= set_parse_v4,
734 		.final_check	= set_check_v0,
735 		.print		= set_print_v4,
736 		.save		= set_save_v4,
737 		.extra_opts	= set_opts_v3,
738 	},
739 };
740 
_init(void)741 void _init(void)
742 {
743 	xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg));
744 }
745