libxt_set.c revision 34844da8f53ec80b34ad094f2fca2519a7079ec2
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
27static void
28set_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
37static 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
43static void
44set_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
51static int
52set_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
93static void
94print_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. */
114static void
115set_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
122static void
123set_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 */
131static int
132set_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
173static void
174print_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. */
192static void
193set_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
200static void
201set_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 */
209static void
210set_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
219static 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
226static int
227set_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. */
272static void
273set_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
282static void
283set_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 */
293static void
294set_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
306static 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
321static uint64_t
322parse_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
333static int
334set_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
448static void
449set_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
468static void
469set_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. */
484static void
485set_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
492static void
493set_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
500static 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
559void _init(void)
560{
561	xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg));
562}
563