diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 435 |
1 files changed, 293 insertions, 142 deletions
@@ -8,12 +8,12 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ -#include <stdlib.h> +#include <nft.h> + #include <stddef.h> #include <unistd.h> #include <stdio.h> #include <errno.h> -#include <string.h> #include <getopt.h> #include <fcntl.h> #include <sys/types.h> @@ -24,11 +24,48 @@ static struct nft_ctx *nft; +enum opt_indices { + /* General options */ + IDX_HELP, + IDX_VERSION, + IDX_VERSION_LONG, + /* Ruleset input handling */ + IDX_FILE, +#define IDX_RULESET_INPUT_START IDX_FILE + IDX_DEFINE, + IDX_INTERACTIVE, + IDX_INCLUDEPATH, + IDX_CHECK, + IDX_OPTIMIZE, +#define IDX_RULESET_INPUT_END IDX_OPTIMIZE + /* Ruleset list formatting */ + IDX_HANDLE, +#define IDX_RULESET_LIST_START IDX_HANDLE + IDX_STATELESS, + IDX_TERSE, + IDX_SERVICE, + IDX_REVERSEDNS, + IDX_GUID, + IDX_NUMERIC, + IDX_NUMERIC_PRIO, + IDX_NUMERIC_PROTO, + IDX_NUMERIC_TIME, +#define IDX_RULESET_LIST_END IDX_NUMERIC_TIME + /* Command output formatting */ + IDX_ECHO, +#define IDX_CMD_OUTPUT_START IDX_ECHO + IDX_JSON, + IDX_DEBUG, +#define IDX_CMD_OUTPUT_END IDX_DEBUG +}; + enum opt_vals { OPT_HELP = 'h', OPT_VERSION = 'v', + OPT_VERSION_LONG = 'V', OPT_CHECK = 'c', OPT_FILE = 'f', + OPT_DEFINE = 'D', OPT_INTERACTIVE = 'i', OPT_INCLUDEPATH = 'I', OPT_JSON = 'j', @@ -44,124 +81,196 @@ enum opt_vals { OPT_NUMERIC_PROTO = 'p', OPT_NUMERIC_TIME = 'T', OPT_TERSE = 't', + OPT_OPTIMIZE = 'o', OPT_INVALID = '?', }; -#define OPTSTRING "+hvd:cf:iI:jvnsNaeSupypTt" -static const struct option options[] = { - { - .name = "help", - .val = OPT_HELP, - }, - { - .name = "version", - .val = OPT_VERSION, - }, - { - .name = "check", - .val = OPT_CHECK, - }, - { - .name = "file", - .val = OPT_FILE, - .has_arg = 1, - }, - { - .name = "interactive", - .val = OPT_INTERACTIVE, - }, - { - .name = "numeric", - .val = OPT_NUMERIC, - }, - { - .name = "stateless", - .val = OPT_STATELESS, - }, - { - .name = "reversedns", - .val = OPT_IP2NAME, - }, - { - .name = "service", - .val = OPT_SERVICE, - }, - { - .name = "includepath", - .val = OPT_INCLUDEPATH, - .has_arg = 1, - }, - { - .name = "debug", - .val = OPT_DEBUG, - .has_arg = 1, - }, - { - .name = "handle", - .val = OPT_HANDLE_OUTPUT, - }, - { - .name = "echo", - .val = OPT_ECHO, - }, - { - .name = "json", - .val = OPT_JSON, - }, - { - .name = "guid", - .val = OPT_GUID, - }, - { - .name = "numeric-priority", - .val = OPT_NUMERIC_PRIO, - }, - { - .name = "numeric-protocol", - .val = OPT_NUMERIC_PROTO, - }, - { - .name = "numeric-time", - .val = OPT_NUMERIC_TIME, - }, - { - .name = "terse", - .val = OPT_TERSE, - }, - { - .name = NULL - } +struct nft_opt { + const char *name; + enum opt_vals val; + const char *arg; + const char *help; }; +#define NFT_OPT(n, v, a, h) \ + (struct nft_opt) { .name = n, .val = v, .arg = a, .help = h } + +static const struct nft_opt nft_options[] = { + [IDX_HELP] = NFT_OPT("help", OPT_HELP, NULL, + "Show this help"), + [IDX_VERSION] = NFT_OPT("version", OPT_VERSION, NULL, + "Show version information"), + [IDX_VERSION_LONG] = NFT_OPT(NULL, OPT_VERSION_LONG, NULL, + "Show extended version information"), + [IDX_FILE] = NFT_OPT("file", OPT_FILE, "<filename>", + "Read input from <filename>"), + [IDX_DEFINE] = NFT_OPT("define", OPT_DEFINE, "<name=value>", + "Define variable, e.g. --define foo=1.2.3.4"), + [IDX_INTERACTIVE] = NFT_OPT("interactive", OPT_INTERACTIVE, NULL, + "Read input from interactive CLI"), + [IDX_INCLUDEPATH] = NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>", + "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH), + [IDX_CHECK] = NFT_OPT("check", OPT_CHECK, NULL, + "Check commands validity without actually applying the changes."), + [IDX_HANDLE] = NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL, + "Output rule handle."), + [IDX_STATELESS] = NFT_OPT("stateless", OPT_STATELESS, NULL, + "Omit stateful information of ruleset."), + [IDX_TERSE] = NFT_OPT("terse", OPT_TERSE, NULL, + "Omit contents of sets."), + [IDX_SERVICE] = NFT_OPT("service", OPT_SERVICE, NULL, + "Translate ports to service names as described in /etc/services."), + [IDX_REVERSEDNS] = NFT_OPT("reversedns", OPT_IP2NAME, NULL, + "Translate IP addresses to names."), + [IDX_GUID] = NFT_OPT("guid", OPT_GUID, NULL, + "Print UID/GID as defined in /etc/passwd and /etc/group."), + [IDX_NUMERIC] = NFT_OPT("numeric", OPT_NUMERIC, NULL, + "Print fully numerical output."), + [IDX_NUMERIC_PRIO] = NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL, + "Print chain priority numerically."), + [IDX_NUMERIC_PROTO] = NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL, + "Print layer 4 protocols numerically."), + [IDX_NUMERIC_TIME] = NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL, + "Print time values numerically."), + [IDX_ECHO] = NFT_OPT("echo", OPT_ECHO, NULL, + "Echo what has been added, inserted or replaced."), + [IDX_JSON] = NFT_OPT("json", OPT_JSON, NULL, + "Format output in JSON"), + [IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>", + "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"), + [IDX_OPTIMIZE] = NFT_OPT("optimize", OPT_OPTIMIZE, NULL, + "Optimize ruleset"), +}; + +#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0])) + +static const char *get_optstring(void) +{ + static char optstring[2 * NR_NFT_OPTIONS + 2]; + + if (!optstring[0]) { + size_t i, j; + + optstring[0] = '+'; + for (i = 0, j = 1; i < NR_NFT_OPTIONS && j < sizeof(optstring); i++) + j += snprintf(optstring + j, sizeof(optstring) - j, "%c%s", + nft_options[i].val, + nft_options[i].arg ? ":" : ""); + + assert(j < sizeof(optstring)); + } + return optstring; +} + +static const struct option *get_options(void) +{ + static struct option options[NR_NFT_OPTIONS + 1]; + + if (!options[0].name) { + size_t i, j; + + for (i = 0, j = 0; i < NR_NFT_OPTIONS; ++i) { + if (nft_options[i].name) { + options[j].name = nft_options[i].name; + options[j].val = nft_options[i].val; + options[j].has_arg = nft_options[i].arg != NULL; + j++; + } + } + } + return options; +} + +static void print_option(const struct nft_opt *opt) +{ + char optbuf[35] = ""; + int i; + + i = snprintf(optbuf, sizeof(optbuf), " -%c", opt->val); + if (opt->name) + i += snprintf(optbuf + i, sizeof(optbuf) - i, ", --%s", + opt->name); + if (opt->arg) + i += snprintf(optbuf + i, sizeof(optbuf) - i, " %s", opt->arg); + + printf("%-34s%s\n", optbuf, opt->help); +} + static void show_help(const char *name) { - printf( -"Usage: %s [ options ] [ cmds... ]\n" -"\n" -"Options:\n" -" -h, --help Show this help\n" -" -v, --version Show version information\n" -"\n" -" -c, --check Check commands validity without actually applying the changes.\n" -" -f, --file <filename> Read input from <filename>\n" -" -i, --interactive Read input from interactive CLI\n" -"\n" -" -j, --json Format output in JSON\n" -" -n, --numeric Print fully numerical output.\n" -" -s, --stateless Omit stateful information of ruleset.\n" -" -t, --terse Omit contents of sets.\n" -" -u, --guid Print UID/GID as defined in /etc/passwd and /etc/group.\n" -" -N Translate IP addresses to names.\n" -" -S, --service Translate ports to service names as described in /etc/services.\n" -" -p, --numeric-protocol Print layer 4 protocols numerically.\n" -" -y, --numeric-priority Print chain priority numerically.\n" -" -T, --numeric-time Print time values numerically.\n" -" -a, --handle Output rule handle.\n" -" -e, --echo Echo what has been added, inserted or replaced.\n" -" -I, --includepath <directory> Add <directory> to the paths searched for include files. Default is: %s\n" -" --debug <level [,level...]> Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n" -"\n", - name, DEFAULT_INCLUDE_PATH); + int i; + + printf("Usage: %s [ options ] [ cmds... ]\n" + "\n" + "Options (general):\n", name); + + print_option(&nft_options[IDX_HELP]); + print_option(&nft_options[IDX_VERSION]); + print_option(&nft_options[IDX_VERSION_LONG]); + + printf("\n" + "Options (ruleset input handling):" + "\n"); + + for (i = IDX_RULESET_INPUT_START; i <= IDX_RULESET_INPUT_END; i++) + print_option(&nft_options[i]); + + printf("\n" + "Options (ruleset list formatting):" + "\n"); + + for (i = IDX_RULESET_LIST_START; i <= IDX_RULESET_LIST_END; i++) + print_option(&nft_options[i]); + + printf("\n" + "Options (command output formatting):" + "\n"); + + for (i = IDX_CMD_OUTPUT_START; i <= IDX_CMD_OUTPUT_END; i++) + print_option(&nft_options[i]); + + fputs("\n", stdout); +} + +static void show_version(void) +{ + const char *cli, *minigmp, *json, *xt; + +#if defined(HAVE_LIBREADLINE) + cli = "readline"; +#elif defined(HAVE_LIBEDIT) + cli = "editline"; +#elif defined(HAVE_LIBLINENOISE) + cli = "linenoise"; +#else + cli = "no"; +#endif + +#if defined(HAVE_MINIGMP) + minigmp = "yes"; +#else + minigmp = "no"; +#endif + +#if defined(HAVE_JSON) + json = "yes"; +#else + json = "no"; +#endif + +#if defined(HAVE_XTABLES) + xt = "yes"; +#else + xt = "no"; +#endif + + printf("%s v%s (%s)\n" + " cli: %s\n" + " json: %s\n" + " minigmp: %s\n" + " libxtables: %s\n", + PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME, + cli, json, minigmp, xt); } static const struct { @@ -216,6 +325,7 @@ static bool nft_options_check(int argc, char * const argv[]) { bool skip = false, nonoption = false; int pos = 0, i; + size_t j; for (i = 1; i < argc; i++) { pos += strlen(argv[i - 1]) + 1; @@ -224,21 +334,22 @@ static bool nft_options_check(int argc, char * const argv[]) } else if (skip) { skip = false; continue; - } else if (argv[i][0] == '-') { - if (nonoption) { - nft_options_error(argc, argv, pos); - return false; - } else if (argv[i][1] == 'd' || - argv[i][1] == 'I' || - argv[i][1] == 'f' || - !strcmp(argv[i], "--debug") || - !strcmp(argv[i], "--includepath") || - !strcmp(argv[i], "--file")) { - skip = true; - continue; - } } else if (argv[i][0] != '-') { nonoption = true; + continue; + } + if (nonoption) { + nft_options_error(argc, argv, pos); + return false; + } + for (j = 0; j < NR_NFT_OPTIONS; j++) { + if (nft_options[j].arg && + (argv[i][1] == (char)nft_options[j].val || + (argv[i][1] == '-' && + !strcmp(argv[i] + 2, nft_options[j].name)))) { + skip = true; + break; + } } } @@ -247,12 +358,18 @@ static bool nft_options_check(int argc, char * const argv[]) int main(int argc, char * const *argv) { - char *buf = NULL, *filename = NULL; + const struct option *options = get_options(); + bool interactive = false, define = false; + const char *optstring = get_optstring(); unsigned int output_flags = 0; - bool interactive = false; + int i, val, rc = EXIT_SUCCESS; unsigned int debug_mask; + char *filename = NULL; unsigned int len; - int i, val, rc; + + /* nftables cannot be used with setuid in a safe way. */ + if (getuid() != geteuid()) + _exit(111); if (!nft_options_check(argc, argv)) exit(EXIT_FAILURE); @@ -260,25 +377,47 @@ int main(int argc, char * const *argv) nft = nft_ctx_new(NFT_CTX_DEFAULT); while (1) { - val = getopt_long(argc, argv, OPTSTRING, options, NULL); + val = getopt_long(argc, argv, optstring, options, NULL); if (val == -1) break; switch (val) { case OPT_HELP: show_help(argv[0]); - exit(EXIT_SUCCESS); + goto out; case OPT_VERSION: printf("%s v%s (%s)\n", PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME); - exit(EXIT_SUCCESS); + goto out; + case OPT_VERSION_LONG: + show_version(); + goto out; + case OPT_DEFINE: + if (nft_ctx_add_var(nft, optarg)) { + fprintf(stderr, + "Failed to define variable '%s'\n", + optarg); + goto out_fail; + } + define = true; + break; case OPT_CHECK: nft_ctx_set_dry_run(nft, true); break; case OPT_FILE: + if (interactive) { + fprintf(stderr, + "Error: -i/--interactive and -f/--file options cannot be combined\n"); + goto out_fail; + } filename = optarg; break; case OPT_INTERACTIVE: + if (filename) { + fprintf(stderr, + "Error: -i/--interactive and -f/--file options cannot be combined\n"); + goto out_fail; + } interactive = true; break; case OPT_INCLUDEPATH: @@ -286,7 +425,7 @@ int main(int argc, char * const *argv) fprintf(stderr, "Failed to add include path '%s'\n", optarg); - exit(EXIT_FAILURE); + goto out_fail; } break; case OPT_NUMERIC: @@ -321,7 +460,7 @@ int main(int argc, char * const *argv) if (i == array_size(debug_param)) { fprintf(stderr, "invalid debug parameter `%s'\n", optarg); - exit(EXIT_FAILURE); + goto out_fail; } if (end == NULL) @@ -341,7 +480,7 @@ int main(int argc, char * const *argv) output_flags |= NFT_CTX_OUTPUT_JSON; #else fprintf(stderr, "JSON support not compiled-in\n"); - exit(EXIT_FAILURE); + goto out_fail; #endif break; case OPT_GUID: @@ -359,14 +498,24 @@ int main(int argc, char * const *argv) case OPT_TERSE: output_flags |= NFT_CTX_OUTPUT_TERSE; break; + case OPT_OPTIMIZE: + nft_ctx_set_optimize(nft, 0x1); + break; case OPT_INVALID: - exit(EXIT_FAILURE); + goto out_fail; } } + if (!filename && define) { + fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n"); + goto out_fail; + } + nft_ctx_output_set_flags(nft, output_flags); if (optind != argc) { + char *buf; + for (len = 0, i = optind; i < argc; i++) len += strlen(argv[i]) + strlen(" "); @@ -374,7 +523,7 @@ int main(int argc, char * const *argv) if (buf == NULL) { fprintf(stderr, "%s:%u: Memory allocation failure\n", __FILE__, __LINE__); - exit(EXIT_FAILURE); + goto out_fail; } for (i = optind; i < argc; i++) { strcat(buf, argv[i]); @@ -382,22 +531,24 @@ int main(int argc, char * const *argv) strcat(buf, " "); } rc = !!nft_run_cmd_from_buffer(nft, buf); + free(buf); } else if (filename != NULL) { rc = !!nft_run_cmd_from_filename(nft, filename); } else if (interactive) { if (cli_init(nft) < 0) { fprintf(stderr, "%s: interactive CLI not supported in this build\n", argv[0]); - exit(EXIT_FAILURE); + goto out_fail; } - return EXIT_SUCCESS; } else { fprintf(stderr, "%s: no command specified\n", argv[0]); - exit(EXIT_FAILURE); + goto out_fail; } - free(buf); +out: nft_ctx_free(nft); - return rc; +out_fail: + nft_ctx_free(nft); + return EXIT_FAILURE; } |