1 /* Copyright 2000-2002 Joakim Axelsson (gozem@linux.nu)
2 * Patrick Schaaf (bof@bof.de)
3 * Copyright 2003-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 #include <assert.h> /* assert */
10 #include <ctype.h> /* isspace */
11 #include <errno.h> /* errno */
12 #include <stdarg.h> /* va_* */
13 #include <stdbool.h> /* bool */
14 #include <stdio.h> /* fprintf, fgets */
15 #include <stdlib.h> /* exit */
16 #include <string.h> /* str* */
20 #include <libipset/debug.h> /* D() */
21 #include <libipset/data.h> /* enum ipset_data */
22 #include <libipset/parse.h> /* ipset_parse_* */
23 #include <libipset/session.h> /* ipset_session_* */
24 #include <libipset/types.h> /* struct ipset_type */
25 #include <libipset/ui.h> /* core options, commands */
26 #include <libipset/utils.h> /* STREQ */
28 static char program_name[] = PACKAGE;
29 static char program_version[] = PACKAGE_VERSION;
31 static struct ipset_session *session;
32 static uint32_t restore_line;
33 static bool interactive;
34 static char cmdline[1024];
35 static char *newargv[255];
37 static FILE *fd = NULL;
38 static const char *filename = NULL;
48 static int __attribute__((format(printf, 2, 3)))
49 exit_error(int status, const char *msg, ...)
51 bool quiet = !interactive &&
53 ipset_envopt_test(session, IPSET_ENV_QUIET);
55 if (status && msg && !quiet) {
58 fprintf(stderr, "%s v%s: ", program_name, program_version);
60 vfprintf(stderr, msg, args);
62 if (status != SESSION_PROBLEM)
63 fprintf(stderr, "\n");
65 if (status == PARAMETER_PROBLEM)
67 "Try `%s help' for more information.\n",
70 /* Ignore errors in interactive mode */
71 if (status && interactive) {
73 ipset_session_report_reset(session);
78 ipset_session_fini(session);
80 D("status: %u", status);
83 exit(status > VERSION_PROBLEM ? OTHER_PROBLEM : status);
91 if (ipset_session_warning(session) &&
92 !ipset_envopt_test(session, IPSET_ENV_QUIET))
93 fprintf(stderr, "Warning: %s\n",
94 ipset_session_warning(session));
95 if (ipset_session_error(session))
96 return exit_error(SESSION_PROBLEM, "%s",
97 ipset_session_error(session));
100 ipset_session_fini(session);
106 ipset_session_report_reset(session);
113 const struct ipset_commands *c;
114 const struct ipset_envopts *opt = ipset_envopts;
117 "Usage: %s [options] COMMAND\n\nCommands:\n",
118 program_name, program_version, program_name);
120 for (c = ipset_commands; c->cmd; c++)
121 printf("%s %s\n", c->name[0], c->help);
122 printf("\nOptions:\n");
126 printf("%s %s\n", opt->name[0], opt->help);
132 ipset_parse_file(struct ipset_session *s UNUSED,
133 int opt UNUSED, const char *str)
135 if (filename != NULL)
136 return exit_error(PARAMETER_PROBLEM,
137 "-file option can be specified once");
144 int __attribute__ ((format (printf, 1, 2)))
145 ipset_print_file(const char *fmt, ...)
152 len = vfprintf(fd, fmt, args);
158 /* Build faked argv from parsed line */
160 build_argv(char *buffer)
167 for (i = 1; i < newargc; i++) {
174 arg = calloc(strlen(buffer) + 1, sizeof(*buffer));
175 for (tmp = buffer, i = 0; *tmp; tmp++) {
176 if ((newargc + 1) == (int)(sizeof(newargv)/sizeof(char *))) {
177 exit_error(PARAMETER_PROBLEM,
178 "Line is too long to parse.");
201 if (!*(tmp+1) && quoted) {
202 exit_error(PARAMETER_PROBLEM, "Missing close quote!");
207 newargv[newargc] = calloc(strlen(arg) + 1, sizeof(*arg));
208 ipset_strlcpy(newargv[newargc++], arg, strlen(arg) + 1);
209 memset(arg, 0, strlen(arg) + 1);
215 /* Main parser function, workhorse */
216 int parse_commandline(int argc, char *argv[]);
219 * Performs a restore from stdin
228 /* Initialize newargv/newargc */
230 newargv[newargc] = calloc(strlen(argv0) + 1, sizeof(*argv0));
231 ipset_strlcpy(newargv[newargc++], argv0, strlen(argv0) + 1);
233 fd = fopen(filename, "r");
235 return exit_error(OTHER_PROBLEM,
236 "Cannot open %s for reading: %s",
237 filename, strerror(errno));
242 while (fgets(cmdline, sizeof(cmdline), rfd)) {
245 while (isspace(c[0]))
247 if (c[0] == '\0' || c[0] == '#')
249 else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) {
250 ret = ipset_commit(session);
255 /* Build faked argv, argc */
259 ret = parse_commandline(newargc, newargv);
263 /* implicit "COMMIT" at EOF */
264 ret = ipset_commit(session);
272 static bool do_parse(const struct ipset_arg *arg, bool family)
274 return !((family == true) ^ (arg->opt == IPSET_OPT_FAMILY));
278 call_parser(int *argc, char *argv[], const struct ipset_type *type,
279 enum ipset_adt cmd, bool family)
281 const struct ipset_arg *arg;
283 const struct ipset_type *t = type;
284 uint8_t revision = type->revision;
285 int ret = 0, i = 1, j;
287 /* Currently CREATE and ADT may have got additional arguments */
288 if (type->cmd[cmd].args[0] == IPSET_ARG_NONE && *argc > 1)
289 return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'",
294 for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) {
295 arg = ipset_keyword(type->cmd[cmd].args[j]);
296 D("argc: %u, %s vs %s", i, argv[i], arg->name[0]);
297 if (!(ipset_match_option(argv[i], arg->name)))
302 D("match %s, argc %u, i %u, %s",
303 arg->name[0], *argc, i + 1,
304 do_parse(arg, family) ? "parse" : "skip");
307 switch (arg->has_arg) {
308 case IPSET_MANDATORY_ARG:
310 return exit_error(PARAMETER_PROBLEM,
311 "Missing mandatory argument "
315 case IPSET_OPTIONAL_ARG:
316 if (*argc - i >= 1) {
317 if (do_parse(arg, family)) {
318 ret = ipset_call_parser(
319 session, arg, argv[i]);
328 if (do_parse(arg, family)) {
329 ret = ipset_call_parser(
330 session, arg, optstr);
345 while ((type = ipset_type_higher_rev(t)) != t) {
346 for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) {
347 arg = ipset_keyword(type->cmd[cmd].args[j]);
348 D("argc: %u, %s vs %s", i, argv[i], arg->name[0]);
349 if (ipset_match_option(argv[i], arg->name))
350 return exit_error(PARAMETER_PROBLEM,
351 "Argument `%s' is supported in the kernel module "
352 "of the set type %s starting from the revision %u "
353 "and you have installed revision %u only. "
354 "Your kernel is behind your ipset utility.",
356 type->revision, revision);
360 return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", argv[i]);
363 static enum ipset_adt
373 case IPSET_CMD_CREATE:
381 check_mandatory(const struct ipset_type *type, enum ipset_cmd command)
383 enum ipset_adt cmd = cmd2cmd(command);
384 uint64_t flags = ipset_data_flags(ipset_session_data(session));
385 uint64_t mandatory = type->cmd[cmd].need;
386 const struct ipset_arg *arg;
389 /* Range can be expressed by ip/cidr */
390 if (flags & IPSET_FLAG(IPSET_OPT_CIDR))
391 flags |= IPSET_FLAG(IPSET_OPT_IP_TO);
396 if (type->cmd[cmd].args[0] == IPSET_ARG_NONE) {
397 exit_error(OTHER_PROBLEM,
398 "There are missing mandatory flags "
399 "but can't check them. "
400 "It's a bug, please report the problem.");
404 for (i = 0; type->cmd[cmd].args[i] != IPSET_ARG_NONE; i++) {
405 arg = ipset_keyword(type->cmd[cmd].args[i]);
406 if (mandatory & IPSET_FLAG(arg->opt)) {
407 exit_error(PARAMETER_PROBLEM,
408 "Mandatory option `%s' is missing",
416 cmd2name(enum ipset_cmd cmd)
418 const struct ipset_commands *c;
420 for (c = ipset_commands; c->cmd; c++)
423 return "unknown command";
429 switch (ipset_data_family(ipset_session_data(session))) {
440 check_allowed(const struct ipset_type *type, enum ipset_cmd command)
442 uint64_t flags = ipset_data_flags(ipset_session_data(session));
443 enum ipset_adt cmd = cmd2cmd(command);
444 uint64_t allowed = type->cmd[cmd].full;
445 uint64_t cmdflags = command == IPSET_CMD_CREATE
446 ? IPSET_CREATE_FLAGS : IPSET_ADT_FLAGS;
447 const struct ipset_arg *arg;
451 /* Range can be expressed by ip/cidr or from-to */
452 if (allowed & IPSET_FLAG(IPSET_OPT_IP_TO))
453 allowed |= IPSET_FLAG(IPSET_OPT_CIDR);
455 for (i = IPSET_OPT_IP; i < IPSET_OPT_FLAGS; i++) {
456 if (!(cmdflags & IPSET_FLAG(i)) ||
457 (allowed & IPSET_FLAG(i)) ||
458 !(flags & IPSET_FLAG(i)))
460 /* Not allowed element-expressions */
463 exit_error(OTHER_PROBLEM,
464 "IP/CIDR range is not allowed in command %s "
465 "with set type %s and family %s",
466 cmd2name(command), type->name,
469 case IPSET_OPT_IP_TO:
470 exit_error(OTHER_PROBLEM,
471 "FROM-TO IP range is not allowed in command %s "
472 "with set type %s and family %s",
473 cmd2name(command), type->name,
476 case IPSET_OPT_PORT_TO:
477 exit_error(OTHER_PROBLEM,
478 "FROM-TO port range is not allowed in command %s "
479 "with set type %s and family %s",
480 cmd2name(command), type->name,
487 if (type->cmd[cmd].args[0] == IPSET_ARG_NONE) {
488 exit_error(OTHER_PROBLEM,
489 "There are not allowed options (%u) "
490 "but option list is empty. "
491 "It's a bug, please report the problem.", i);
494 for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) {
495 arg = ipset_keyword(type->cmd[cmd].args[j]);
498 exit_error(OTHER_PROBLEM,
499 "%s parameter is not allowed in command %s "
500 "with set type %s and family %s",
502 cmd2name(command), type->name,
506 exit_error(OTHER_PROBLEM,
507 "There are not allowed options (%u) "
508 "but can't resolve them. "
509 "It's a bug, please report the problem.", i);
514 static const struct ipset_type *
515 type_find(const char *name)
517 const struct ipset_type *t = ipset_types();
520 if (ipset_match_typename(name, t))
527 static enum ipset_adt cmd_help_order[] = {
535 static const char *cmd_prefix[] = {
536 [IPSET_CREATE] = "create SETNAME",
537 [IPSET_ADD] = "add SETNAME",
538 [IPSET_DEL] = "del SETNAME",
539 [IPSET_TEST] = "test SETNAME",
544 parse_commandline(int argc, char *argv[])
547 enum ipset_cmd cmd = IPSET_CMD_NONE;
549 char *arg0 = NULL, *arg1 = NULL, *c;
550 const struct ipset_envopts *opt;
551 const struct ipset_commands *command;
552 const struct ipset_type *type;
554 /* Set session lineno to report parser errors correctly */
555 ipset_session_lineno(session, restore_line);
557 /* Commandline parsing, somewhat similar to that of 'ip' */
559 /* First: parse core options */
560 for (opt = ipset_envopts; opt->flag; opt++) {
561 for (i = 1; i < argc; ) {
562 if (!ipset_match_envopt(argv[i], opt->name)) {
566 /* Shift off matched option */
567 ipset_shift_argv(&argc, argv, i);
568 switch (opt->has_arg) {
569 case IPSET_MANDATORY_ARG:
571 return exit_error(PARAMETER_PROBLEM,
572 "Missing mandatory argument "
576 case IPSET_OPTIONAL_ARG:
578 ret = opt->parse(session, opt->flag,
581 return handle_error();
582 ipset_shift_argv(&argc, argv, i);
586 ret = opt->parse(session, opt->flag,
589 return handle_error();
597 /* Second: parse command */
598 for (command = ipset_commands;
599 argc > 1 && command->cmd && cmd == IPSET_CMD_NONE;
601 if (!ipset_match_cmd(argv[1], command->name))
604 if (restore_line != 0 &&
605 (command->cmd == IPSET_CMD_RESTORE ||
606 command->cmd == IPSET_CMD_VERSION ||
607 command->cmd == IPSET_CMD_HELP))
608 return exit_error(PARAMETER_PROBLEM,
609 "Command `%s' is invalid "
612 if (interactive && command->cmd == IPSET_CMD_RESTORE) {
613 printf("Restore command ignored "
614 "in interactive mode\n");
618 /* Shift off matched command arg */
619 ipset_shift_argv(&argc, argv, 1);
621 switch (command->has_arg) {
622 case IPSET_MANDATORY_ARG:
623 case IPSET_MANDATORY_ARG2:
625 return exit_error(PARAMETER_PROBLEM,
626 "Missing mandatory argument "
630 case IPSET_OPTIONAL_ARG:
633 /* Shift off first arg */
634 ipset_shift_argv(&argc, argv, 1);
639 if (command->has_arg == IPSET_MANDATORY_ARG2) {
641 return exit_error(PARAMETER_PROBLEM,
642 "Missing second mandatory "
643 "argument to command %s",
646 /* Shift off second arg */
647 ipset_shift_argv(&argc, argv, 1);
652 /* Third: catch interactive mode, handle help, version */
656 printf("No command specified\n");
658 ipset_envopt_parse(session, 0, "reset");
661 if (argc > 1 && STREQ(argv[1], "-")) {
663 printf("%s> ", program_name);
664 /* Initialize newargv/newargc */
665 newargv[newargc++] = program_name;
666 while (fgets(cmdline, sizeof(cmdline), stdin)) {
668 while (isspace(c[0]))
670 if (c[0] == '\0' || c[0] == '#') {
671 printf("%s> ", program_name);
674 /* Build fake argv, argc */
676 /* Execute line: ignore soft errors */
677 if (parse_commandline(newargc, newargv) < 0)
679 printf("%s> ", program_name);
681 return exit_error(NO_PROBLEM, NULL);
684 return exit_error(PARAMETER_PROBLEM,
685 "No command specified: unknown argument %s",
687 return exit_error(PARAMETER_PROBLEM, "No command specified.");
688 case IPSET_CMD_VERSION:
689 printf("%s v%s, protocol version: %u\n",
690 program_name, program_version, IPSET_PROTOCOL);
693 return exit_error(NO_PROBLEM, NULL);
698 !ipset_envopt_test(session, IPSET_ENV_QUIET)) {
700 const struct ipset_arg *arg;
703 /* Type-specific help, without kernel checking */
704 type = type_find(arg0);
706 return exit_error(PARAMETER_PROBLEM,
707 "Unknown settype: `%s'", arg0);
708 printf("\n%s type specific options:\n\n", type->name);
709 for (i = 0; cmd_help_order[i] != IPSET_CADT_MAX; i++) {
710 cmd = cmd_help_order[i];
712 cmd_prefix[cmd], type->name, type->cmd[cmd].help);
713 for (k = 0; type->cmd[cmd].args[k] != IPSET_ARG_NONE; k++) {
714 arg = ipset_keyword(type->cmd[cmd].args[k]);
715 if (!arg->help || arg->help[0] == '\0')
717 printf(" %s\n", arg->help);
720 printf("\n%s\n", type->usage);
723 if (type->family == NFPROTO_UNSPEC)
724 printf("\nType %s is family neutral.\n",
726 else if (type->family == NFPROTO_IPSET_IPV46)
727 printf("\nType %s supports inet "
731 printf("\nType %s supports family "
734 type->family == NFPROTO_IPV4
737 printf("\nSupported set types:\n");
738 type = ipset_types();
740 printf(" %s\t%s%u\t%s\n",
742 strlen(type->name) < 12 ? "\t" : "",
751 return exit_error(NO_PROBLEM, NULL);
753 return exit_error(NO_PROBLEM, NULL);
758 /* Forth: parse command args and issue the command */
760 case IPSET_CMD_CREATE:
761 /* Args: setname typename [type specific options] */
762 ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
764 return handle_error();
766 ret = ipset_parse_typename(session, IPSET_OPT_TYPENAME, arg1);
768 return handle_error();
770 type = ipset_type_get(session, cmd);
772 return handle_error();
774 /* Parse create options: first check INET family */
775 ret = call_parser(&argc, argv, type, IPSET_CREATE, true);
777 return handle_error();
781 /* Parse create options: then check all options */
782 ret = call_parser(&argc, argv, type, IPSET_CREATE, false);
784 return handle_error();
788 /* Check mandatory, then allowed options */
789 check_mandatory(type, cmd);
790 check_allowed(type, cmd);
795 if (filename != NULL) {
796 fd = fopen(filename, "w");
798 return exit_error(OTHER_PROBLEM,
799 "Cannot open %s for writing: "
802 ipset_session_outfn(session, ipset_print_file);
804 case IPSET_CMD_DESTROY:
805 case IPSET_CMD_FLUSH:
806 /* Args: [setname] */
808 ret = ipset_parse_setname(session,
809 IPSET_SETNAME, arg0);
811 return handle_error();
815 case IPSET_CMD_RENAME:
817 /* Args: from-setname to-setname */
818 ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
820 return handle_error();
821 ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1);
823 return handle_error();
826 case IPSET_CMD_RESTORE:
829 return exit_error(PARAMETER_PROBLEM,
830 "Unknown argument %s", argv[1]);
831 return restore(argv[0]);
835 D("ADT: setname %s", arg0);
836 /* Args: setname ip [options] */
837 ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
839 return handle_error();
841 type = ipset_type_get(session, cmd);
843 return handle_error();
845 ret = ipset_parse_elem(session, type->last_elem_optional, arg1);
847 return handle_error();
849 /* Parse additional ADT options */
850 ret = call_parser(&argc, argv, type, cmd2cmd(cmd), false);
852 return handle_error();
856 /* Check mandatory, then allowed options */
857 check_mandatory(type, cmd);
858 check_allowed(type, cmd);
866 return exit_error(PARAMETER_PROBLEM,
867 "Unknown argument %s", argv[1]);
868 ret = ipset_cmd(session, cmd, restore_line);
870 /* Special case for TEST and non-quiet mode */
871 if (cmd == IPSET_CMD_TEST && ipset_session_warning(session)) {
872 if (!ipset_envopt_test(session, IPSET_ENV_QUIET))
873 fprintf(stderr, "%s", ipset_session_warning(session));
874 ipset_session_report_reset(session);
883 main(int argc, char *argv[])
890 /* Initialize session */
891 session = ipset_session_init(printf);
893 return exit_error(OTHER_PROBLEM,
894 "Cannot initialize ipset session, aborting.");
896 ret = parse_commandline(argc, argv);
898 ipset_session_fini(session);