]> granicus.if.org Git - ipset/blob - src/ipset.c
Userspace revision handling is reworked
[ipset] / src / ipset.c
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)
4  *
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.
8  */
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* */
17
18 #include <config.h>
19
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 */
27
28 static char program_name[] = PACKAGE;
29 static char program_version[] = PACKAGE_VERSION;
30
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];
36 static int newargc;
37 static FILE *fd = NULL;
38 static const char *filename = NULL;
39
40 enum exittype {
41         NO_PROBLEM = 0,
42         OTHER_PROBLEM,
43         PARAMETER_PROBLEM,
44         VERSION_PROBLEM,
45         SESSION_PROBLEM,
46 };
47
48 static int __attribute__((format(printf, 2, 3)))
49 exit_error(int status, const char *msg, ...)
50 {
51         bool quiet = !interactive &&
52                      session &&
53                      ipset_envopt_test(session, IPSET_ENV_QUIET);
54
55         if (status && msg && !quiet) {
56                 va_list args;
57
58                 fprintf(stderr, "%s v%s: ", program_name, program_version);
59                 va_start(args, msg);
60                 vfprintf(stderr, msg, args);
61                 va_end(args);
62                 if (status != SESSION_PROBLEM)
63                         fprintf(stderr, "\n");
64
65                 if (status == PARAMETER_PROBLEM)
66                         fprintf(stderr,
67                                 "Try `%s help' for more information.\n",
68                                 program_name);
69         }
70         /* Ignore errors in interactive mode */
71         if (status && interactive) {
72                 if (session)
73                         ipset_session_report_reset(session);
74                 return -1;
75         }
76
77         if (session)
78                 ipset_session_fini(session);
79
80         D("status: %u", status);
81         if (fd)
82                 fclose(fd);
83         exit(status > VERSION_PROBLEM ? OTHER_PROBLEM : status);
84         /* Unreached */
85         return -1;
86 }
87
88 static int
89 handle_error(void)
90 {
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));
98
99         if (!interactive) {
100                 ipset_session_fini(session);
101                 if (fd)
102                         fclose(fd);
103                 exit(OTHER_PROBLEM);
104         }
105
106         ipset_session_report_reset(session);
107         return -1;
108 }
109
110 static void
111 help(void)
112 {
113         const struct ipset_commands *c;
114         const struct ipset_envopts *opt = ipset_envopts;
115
116         printf("%s v%s\n\n"
117                "Usage: %s [options] COMMAND\n\nCommands:\n",
118                program_name, program_version, program_name);
119
120         for (c = ipset_commands; c->cmd; c++)
121                 printf("%s %s\n", c->name[0], c->help);
122         printf("\nOptions:\n");
123
124         while (opt->flag) {
125                 if (opt->help)
126                         printf("%s %s\n", opt->name[0], opt->help);
127                 opt++;
128         }
129 }
130
131 int
132 ipset_parse_file(struct ipset_session *s UNUSED,
133                  int opt UNUSED, const char *str)
134 {
135         if (filename != NULL)
136                 return exit_error(PARAMETER_PROBLEM,
137                                   "-file option can be specified once");
138         filename = str;
139
140         return 0;
141 }
142
143 static
144 int __attribute__ ((format (printf, 1, 2)))
145 ipset_print_file(const char *fmt, ...)
146 {
147         int len;
148         va_list args;
149
150         assert(fd != NULL);
151         va_start(args, fmt);
152         len = vfprintf(fd, fmt, args);
153         va_end(args);
154
155         return len;
156 }
157
158 /* Build faked argv from parsed line */
159 static void
160 build_argv(char *buffer)
161 {
162         char *tmp, *arg;
163         int i;
164         bool quoted = false;
165
166         /* Reset */
167         for (i = 1; i < newargc; i++) {
168                 if (newargv[i])
169                         free(newargv[i]);
170                 newargv[i] = NULL;
171         }
172         newargc = 1;
173
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.");
179                         return;
180                 }
181                 switch (*tmp) {
182                 case '"':
183                         quoted = !quoted;
184                         if (*(tmp+1))
185                                 continue;
186                         break;
187                 case ' ':
188                 case '\r':
189                 case '\n':
190                 case '\t':
191                         if (!quoted)
192                                 break;
193                         arg[i++] = *tmp;
194                         continue;
195                 default:
196                         arg[i++] = *tmp;
197                         if (*(tmp+1))
198                                 continue;
199                         break;
200                 }
201                 if (!*(tmp+1) && quoted) {
202                         exit_error(PARAMETER_PROBLEM, "Missing close quote!");
203                         return;
204                 }
205                 if (!*arg)
206                         continue;
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);
210                 i = 0;
211         }
212         free(arg);
213 }
214
215 /* Main parser function, workhorse */
216 int parse_commandline(int argc, char *argv[]);
217
218 /*
219  * Performs a restore from stdin
220  */
221 static int
222 restore(char *argv0)
223 {
224         int ret = 0;
225         char *c;
226         FILE *rfd = stdin;
227
228         /* Initialize newargv/newargc */
229         newargc = 0;
230         newargv[newargc] = calloc(strlen(argv0) + 1, sizeof(*argv0));
231         ipset_strlcpy(newargv[newargc++], argv0, strlen(argv0) + 1);
232         if (filename) {
233                 fd = fopen(filename, "r");
234                 if (!fd) {
235                         return exit_error(OTHER_PROBLEM,
236                                           "Cannot open %s for reading: %s",
237                                           filename, strerror(errno));
238                 }
239                 rfd = fd;
240         }
241
242         while (fgets(cmdline, sizeof(cmdline), rfd)) {
243                 restore_line++;
244                 c = cmdline;
245                 while (isspace(c[0]))
246                         c++;
247                 if (c[0] == '\0' || c[0] == '#')
248                         continue;
249                 else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) {
250                         ret = ipset_commit(session);
251                         if (ret < 0)
252                                 handle_error();
253                         continue;
254                 }
255                 /* Build faked argv, argc */
256                 build_argv(c);
257
258                 /* Execute line */
259                 ret = parse_commandline(newargc, newargv);
260                 if (ret < 0)
261                         handle_error();
262         }
263         /* implicit "COMMIT" at EOF */
264         ret = ipset_commit(session);
265         if (ret < 0)
266                 handle_error();
267
268         free(newargv[0]);
269         return ret;
270 }
271
272 static bool do_parse(const struct ipset_arg *arg, bool family)
273 {
274         return !((family == true) ^ (arg->opt == IPSET_OPT_FAMILY));
275 }
276
277 static int
278 call_parser(int *argc, char *argv[], const struct ipset_type *type,
279             enum ipset_adt cmd, bool family)
280 {
281         const struct ipset_arg *arg;
282         const char *optstr;
283         const struct ipset_type *t = type;
284         uint8_t revision = type->revision;
285         int ret = 0, i = 1, j;
286
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'",
290                                   argv[i]);
291
292         while (*argc > i) {
293                 ret = -1;
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)))
298                                 continue;
299
300                         optstr = argv[i];
301                         /* Matched option */
302                         D("match %s, argc %u, i %u, %s",
303                           arg->name[0], *argc, i + 1,
304                           do_parse(arg, family) ? "parse" : "skip");
305                         i++;
306                         ret = 0;
307                         switch (arg->has_arg) {
308                         case IPSET_MANDATORY_ARG:
309                                 if (*argc - i < 1)
310                                         return exit_error(PARAMETER_PROBLEM,
311                                                 "Missing mandatory argument "
312                                                 "of option `%s'",
313                                                 arg->name[0]);
314                                 /* Fall through */
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]);
320                                                 if (ret < 0)
321                                                         return ret;
322                                         }
323                                         i++;
324                                         break;
325                                 }
326                                 /* Fall through */
327                         default:
328                                 if (do_parse(arg, family)) {
329                                         ret = ipset_call_parser(
330                                                 session, arg, optstr);
331                                         if (ret < 0)
332                                                 return ret;
333                                 }
334                         }
335                         break;
336                 }
337                 if (ret < 0)
338                         goto err_unknown;
339         }
340         if (!family)
341                 *argc = 0;
342         return ret;
343
344 err_unknown:
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.",
355                                         argv[i], type->name,
356                                         type->revision, revision);
357                 }
358                 t = type;
359         }
360         return exit_error(PARAMETER_PROBLEM, "Unknown argument: `%s'", argv[i]);
361 }
362
363 static enum ipset_adt
364 cmd2cmd(int cmd)
365 {
366         switch (cmd) {
367         case IPSET_CMD_ADD:
368                 return IPSET_ADD;
369         case IPSET_CMD_DEL:
370                 return IPSET_DEL;
371         case IPSET_CMD_TEST:
372                 return IPSET_TEST;
373         case IPSET_CMD_CREATE:
374                 return IPSET_CREATE;
375         default:
376                 return 0;
377         }
378 }
379
380 static void
381 check_mandatory(const struct ipset_type *type, enum ipset_cmd command)
382 {
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;
387         int i;
388
389         /* Range can be expressed by ip/cidr */
390         if (flags & IPSET_FLAG(IPSET_OPT_CIDR))
391                 flags |= IPSET_FLAG(IPSET_OPT_IP_TO);
392
393         mandatory &= ~flags;
394         if (!mandatory)
395                 return;
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.");
401                 return;
402         }
403
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",
409                                    arg->name[0]);
410                         return;
411                 }
412         }
413 }
414
415 static const char *
416 cmd2name(enum ipset_cmd cmd)
417 {
418         const struct ipset_commands *c;
419
420         for (c = ipset_commands; c->cmd; c++)
421                 if (cmd == c->cmd)
422                         return c->name[0];
423         return "unknown command";
424 }
425
426 static const char *
427 session_family(void)
428 {
429         switch (ipset_data_family(ipset_session_data(session))) {
430         case NFPROTO_IPV4:
431                 return "inet";
432         case NFPROTO_IPV6:
433                 return "inet6";
434         default:
435                 return "unspec";
436         }
437 }
438
439 static void
440 check_allowed(const struct ipset_type *type, enum ipset_cmd command)
441 {
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;
448         enum ipset_opt i;
449         int j;
450
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);
454
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)))
459                         continue;
460                 /* Not allowed element-expressions */
461                 switch (i) {
462                 case IPSET_OPT_CIDR:
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,
467                                 session_family());
468                         return;
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,
474                                 session_family());
475                         return;
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,
481                                 session_family());
482                         return;
483                 default:
484                         break;
485                 }
486                 /* Other options */
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);
492                         return;
493                 }
494                 for (j = 0; type->cmd[cmd].args[j] != IPSET_ARG_NONE; j++) {
495                         arg = ipset_keyword(type->cmd[cmd].args[j]);
496                         if (arg->opt != i)
497                                 continue;
498                         exit_error(OTHER_PROBLEM,
499                                 "%s parameter is not allowed in command %s "
500                                 "with set type %s and family %s",
501                                 arg->name[0],
502                                 cmd2name(command), type->name,
503                                 session_family());
504                         return;
505                 }
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);
510                 return;
511         }
512 }
513
514 static const struct ipset_type *
515 type_find(const char *name)
516 {
517         const struct ipset_type *t = ipset_types();
518
519         while (t) {
520                 if (ipset_match_typename(name, t))
521                         return t;
522                 t = t->next;
523         }
524         return NULL;
525 }
526
527 static enum ipset_adt cmd_help_order[] = {
528         IPSET_CREATE,
529         IPSET_ADD,
530         IPSET_DEL,
531         IPSET_TEST,
532         IPSET_CADT_MAX,
533 };
534
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",
540 };
541
542 /* Workhorse */
543 int
544 parse_commandline(int argc, char *argv[])
545 {
546         int ret = 0;
547         enum ipset_cmd cmd = IPSET_CMD_NONE;
548         int i;
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;
553
554         /* Set session lineno to report parser errors correctly */
555         ipset_session_lineno(session, restore_line);
556
557         /* Commandline parsing, somewhat similar to that of 'ip' */
558
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)) {
563                                 i++;
564                                 continue;
565                         }
566                         /* Shift off matched option */
567                         ipset_shift_argv(&argc, argv, i);
568                         switch (opt->has_arg) {
569                         case IPSET_MANDATORY_ARG:
570                                 if (i + 1 > argc)
571                                         return exit_error(PARAMETER_PROBLEM,
572                                                 "Missing mandatory argument "
573                                                 "to option %s",
574                                                 opt->name[0]);
575                                 /* Fall through */
576                         case IPSET_OPTIONAL_ARG:
577                                 if (i + 1 <= argc) {
578                                         ret = opt->parse(session, opt->flag,
579                                                          argv[i]);
580                                         if (ret < 0)
581                                                 return handle_error();
582                                         ipset_shift_argv(&argc, argv, i);
583                                 }
584                                 break;
585                         case IPSET_NO_ARG:
586                                 ret = opt->parse(session, opt->flag,
587                                                  opt->name[0]);
588                                 if (ret < 0)
589                                         return handle_error();
590                                 break;
591                         default:
592                                 break;
593                         }
594                 }
595         }
596
597         /* Second: parse command */
598         for (command = ipset_commands;
599                  argc > 1 && command->cmd && cmd == IPSET_CMD_NONE;
600              command++) {
601                 if (!ipset_match_cmd(argv[1], command->name))
602                         continue;
603
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 "
610                                 "in restore mode.",
611                                 command->name[0]);
612                 if (interactive && command->cmd == IPSET_CMD_RESTORE) {
613                         printf("Restore command ignored "
614                                "in interactive mode\n");
615                         return 0;
616                 }
617
618                 /* Shift off matched command arg */
619                 ipset_shift_argv(&argc, argv, 1);
620                 cmd = command->cmd;
621                 switch (command->has_arg) {
622                 case IPSET_MANDATORY_ARG:
623                 case IPSET_MANDATORY_ARG2:
624                         if (argc < 2)
625                                 return exit_error(PARAMETER_PROBLEM,
626                                         "Missing mandatory argument "
627                                         "to command %s",
628                                         command->name[0]);
629                         /* Fall through */
630                 case IPSET_OPTIONAL_ARG:
631                         arg0 = argv[1];
632                         if (argc >= 2)
633                                 /* Shift off first arg */
634                                 ipset_shift_argv(&argc, argv, 1);
635                         break;
636                 default:
637                         break;
638                 }
639                 if (command->has_arg == IPSET_MANDATORY_ARG2) {
640                         if (argc < 2)
641                                 return exit_error(PARAMETER_PROBLEM,
642                                         "Missing second mandatory "
643                                         "argument to command %s",
644                                         command->name[0]);
645                         arg1 = argv[1];
646                         /* Shift off second arg */
647                         ipset_shift_argv(&argc, argv, 1);
648                 }
649                 break;
650         }
651
652         /* Third: catch interactive mode, handle help, version */
653         switch (cmd) {
654         case IPSET_CMD_NONE:
655                 if (interactive) {
656                         printf("No command specified\n");
657                         if (session)
658                                 ipset_envopt_parse(session, 0, "reset");
659                         return 0;
660                 }
661                 if (argc > 1 && STREQ(argv[1], "-")) {
662                         interactive = true;
663                         printf("%s> ", program_name);
664                         /* Initialize newargv/newargc */
665                         newargv[newargc++] = program_name;
666                         while (fgets(cmdline, sizeof(cmdline), stdin)) {
667                                 c = cmdline;
668                                 while (isspace(c[0]))
669                                         c++;
670                                 if (c[0] == '\0' || c[0] == '#') {
671                                         printf("%s> ", program_name);
672                                         continue;
673                                 }
674                                 /* Build fake argv, argc */
675                                 build_argv(c);
676                                 /* Execute line: ignore soft errors */
677                                 if (parse_commandline(newargc, newargv) < 0)
678                                         handle_error();
679                                 printf("%s> ", program_name);
680                         }
681                         return exit_error(NO_PROBLEM, NULL);
682                 }
683                 if (argc > 1)
684                         return exit_error(PARAMETER_PROBLEM,
685                                 "No command specified: unknown argument %s",
686                                 argv[1]);
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);
691                 if (interactive)
692                         return 0;
693                 return exit_error(NO_PROBLEM, NULL);
694         case IPSET_CMD_HELP:
695                 help();
696
697                 if (interactive ||
698                     !ipset_envopt_test(session, IPSET_ENV_QUIET)) {
699                         if (arg0) {
700                                 const struct ipset_arg *arg;
701                                 int k;
702
703                                 /* Type-specific help, without kernel checking */
704                                 type = type_find(arg0);
705                                 if (!type)
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];
711                                         printf("%s %s %s\n",
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')
716                                                         continue;
717                                                 printf("               %s\n", arg->help);
718                                         }
719                                 }
720                                 printf("\n%s\n", type->usage);
721                                 if (type->usagefn)
722                                         type->usagefn();
723                                 if (type->family == NFPROTO_UNSPEC)
724                                         printf("\nType %s is family neutral.\n",
725                                                type->name);
726                                 else if (type->family == NFPROTO_IPSET_IPV46)
727                                         printf("\nType %s supports inet "
728                                                "and inet6.\n",
729                                                type->name);
730                                 else
731                                         printf("\nType %s supports family "
732                                                "%s only.\n",
733                                                type->name,
734                                                type->family == NFPROTO_IPV4
735                                                 ? "inet" : "inet6");
736                         } else {
737                                 printf("\nSupported set types:\n");
738                                 type = ipset_types();
739                                 while (type) {
740                                         printf("    %s\t%s%u\t%s\n",
741                                                type->name,
742                                                strlen(type->name) < 12 ? "\t" : "",
743                                                type->revision,
744                                                type->description);
745                                         type = type->next;
746                                 }
747                         }
748                 }
749                 if (interactive)
750                         return 0;
751                 return exit_error(NO_PROBLEM, NULL);
752         case IPSET_CMD_QUIT:
753                 return exit_error(NO_PROBLEM, NULL);
754         default:
755                 break;
756         }
757
758         /* Forth: parse command args and issue the command */
759         switch (cmd) {
760         case IPSET_CMD_CREATE:
761                 /* Args: setname typename [type specific options] */
762                 ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
763                 if (ret < 0)
764                         return handle_error();
765
766                 ret = ipset_parse_typename(session, IPSET_OPT_TYPENAME, arg1);
767                 if (ret < 0)
768                         return handle_error();
769
770                 type = ipset_type_get(session, cmd);
771                 if (type == NULL)
772                         return handle_error();
773
774                 /* Parse create options: first check INET family */
775                 ret = call_parser(&argc, argv, type, IPSET_CREATE, true);
776                 if (ret < 0)
777                         return handle_error();
778                 else if (ret)
779                         return ret;
780
781                 /* Parse create options: then check all options */
782                 ret = call_parser(&argc, argv, type, IPSET_CREATE, false);
783                 if (ret < 0)
784                         return handle_error();
785                 else if (ret)
786                         return ret;
787
788                 /* Check mandatory, then allowed options */
789                 check_mandatory(type, cmd);
790                 check_allowed(type, cmd);
791
792                 break;
793         case IPSET_CMD_LIST:
794         case IPSET_CMD_SAVE:
795                 if (filename != NULL) {
796                         fd = fopen(filename, "w");
797                         if (!fd)
798                                 return exit_error(OTHER_PROBLEM,
799                                                   "Cannot open %s for writing: "
800                                                   "%s", filename,
801                                                   strerror(errno));
802                         ipset_session_outfn(session, ipset_print_file);
803                 }
804         case IPSET_CMD_DESTROY:
805         case IPSET_CMD_FLUSH:
806                 /* Args: [setname] */
807                 if (arg0) {
808                         ret = ipset_parse_setname(session,
809                                                   IPSET_SETNAME, arg0);
810                         if (ret < 0)
811                                 return handle_error();
812                 }
813                 break;
814
815         case IPSET_CMD_RENAME:
816         case IPSET_CMD_SWAP:
817                 /* Args: from-setname to-setname */
818                 ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
819                 if (ret < 0)
820                         return handle_error();
821                 ret = ipset_parse_setname(session, IPSET_OPT_SETNAME2, arg1);
822                 if (ret < 0)
823                         return handle_error();
824                 break;
825
826         case IPSET_CMD_RESTORE:
827                 /* Restore mode */
828                 if (argc > 1)
829                         return exit_error(PARAMETER_PROBLEM,
830                                 "Unknown argument %s", argv[1]);
831                 return restore(argv[0]);
832         case IPSET_CMD_ADD:
833         case IPSET_CMD_DEL:
834         case IPSET_CMD_TEST:
835                 D("ADT: setname %s", arg0);
836                 /* Args: setname ip [options] */
837                 ret = ipset_parse_setname(session, IPSET_SETNAME, arg0);
838                 if (ret < 0)
839                         return handle_error();
840
841                 type = ipset_type_get(session, cmd);
842                 if (type == NULL)
843                         return handle_error();
844
845                 ret = ipset_parse_elem(session, type->last_elem_optional, arg1);
846                 if (ret < 0)
847                         return handle_error();
848
849                 /* Parse additional ADT options */
850                 ret = call_parser(&argc, argv, type, cmd2cmd(cmd), false);
851                 if (ret < 0)
852                         return handle_error();
853                 else if (ret)
854                         return ret;
855
856                 /* Check mandatory, then allowed options */
857                 check_mandatory(type, cmd);
858                 check_allowed(type, cmd);
859
860                 break;
861         default:
862                 break;
863         }
864
865         if (argc > 1)
866                 return exit_error(PARAMETER_PROBLEM,
867                         "Unknown argument %s", argv[1]);
868         ret = ipset_cmd(session, cmd, restore_line);
869         D("ret %d", ret);
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);
875         }
876         if (ret < 0)
877                 handle_error();
878
879         return ret;
880 }
881
882 int
883 main(int argc, char *argv[])
884 {
885         int ret;
886
887         /* Load set types */
888         ipset_load_types();
889
890         /* Initialize session */
891         session = ipset_session_init(printf);
892         if (session == NULL)
893                 return exit_error(OTHER_PROBLEM,
894                         "Cannot initialize ipset session, aborting.");
895
896         ret = parse_commandline(argc, argv);
897
898         ipset_session_fini(session);
899         if (fd)
900                 fclose(fd);
901
902         return ret;
903 }