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