Home | History | Annotate | Download | only in extensions
      1 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem (at) linux.nu>
      2  *                         Patrick Schaaf <bof (at) bof.de>
      3  *                         Martin Josefsson <gandalf (at) wlug.westbo.se>
      4  * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec (at) 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
     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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    741 void _init(void)
    742 {
    743 	xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg));
    744 }
    745