/* * Copyright (c) 2008 Patrick McHardy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Development of this code funded by Astaro AG (http://www.astaro.com/) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned int max_errors = 10; unsigned int numeric_output; unsigned int ip2name_output; unsigned int handle_output; #ifdef DEBUG unsigned int debug_level; #endif const char *include_paths[INCLUDE_PATHS_MAX] = { DEFAULT_INCLUDE_PATH }; static unsigned int num_include_paths = 1; enum opt_vals { OPT_HELP = 'h', OPT_VERSION = 'v', OPT_FILE = 'f', OPT_INTERACTIVE = 'i', OPT_INCLUDEPATH = 'I', OPT_NUMERIC = 'n', OPT_IP2NAME = 'N', OPT_DEBUG = 'd', OPT_HANDLE_OUTPUT = 'a', OPT_INVALID = '?', }; #define OPTSTRING "hvf:iI:vnNa" static const struct option options[] = { { .name = "help", .val = OPT_HELP, }, { .name = "version", .val = OPT_VERSION, }, { .name = "file", .val = OPT_FILE, .has_arg = 1, }, { .name = "interactive", .val = OPT_INTERACTIVE, }, { .name = "numeric", .val = OPT_NUMERIC, }, { .name = "reversedns", .val = OPT_IP2NAME, }, { .name = "includepath", .val = OPT_INCLUDEPATH, .has_arg = 1, }, #ifdef DEBUG { .name = "debug", .val = OPT_DEBUG, .has_arg = 1, }, #endif { .name = "handle", .val = OPT_HANDLE_OUTPUT, }, { .name = NULL } }; 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" " -f/--file Read input from \n" " -i/--interactive Read input from interactive CLI\n" "\n" " -n/--numeric When specified once, show network addresses numerically (default behaviour).\n" " When specified twice, show Internet services,\n" " user IDs and group IDs numerically.\n" " When specified thrice, also show protocols numerically.\n" " -N Translate IP addresses to names.\n" " -a/--handle Output rule handle.\n" " -I/--includepath Add to the paths searched for include files.\n" #ifdef DEBUG " --debug Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n" #endif "\n", name); } #ifdef DEBUG static const struct { const char *name; enum debug_level level; } debug_param[] = { { .name = "scanner", .level = DEBUG_SCANNER, }, { .name = "parser", .level = DEBUG_PARSER, }, { .name = "eval", .level = DEBUG_EVALUATION, }, { .name = "netlink", .level = DEBUG_NETLINK, }, { .name = "mnl", .level = DEBUG_MNL, }, { .name = "proto-ctx", .level = DEBUG_PROTO_CTX, }, { .name = "segtree", .level = DEBUG_SEGTREE, }, { .name = "all", .level = ~0, }, }; #endif static const struct input_descriptor indesc_cmdline = { .type = INDESC_BUFFER, .name = "", }; static int nft_netlink(struct parser_state *state, struct list_head *msgs) { struct netlink_ctx ctx; struct cmd *cmd; struct mnl_err *err, *tmp; LIST_HEAD(err_list); uint32_t batch_seqnum; bool batch_supported = netlink_batch_supported(); int ret = 0; netlink_genid_get(); mnl_batch_init(); batch_seqnum = mnl_batch_begin(); list_for_each_entry(cmd, &state->cmds, list) { memset(&ctx, 0, sizeof(ctx)); ctx.msgs = msgs; ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(); ctx.batch_supported = batch_supported; init_list_head(&ctx.list); ret = do_command(&ctx, cmd); if (ret < 0) goto out; } mnl_batch_end(); if (!mnl_batch_ready()) goto out; ret = netlink_batch_send(&err_list); list_for_each_entry_safe(err, tmp, &err_list, head) { list_for_each_entry(cmd, &state->cmds, list) { if (err->seqnum == cmd->seqnum || err->seqnum == batch_seqnum) { netlink_io_error(&ctx, &cmd->location, "Could not process rule: %s", strerror(err->err)); ret = -1; errno = err->err; if (err->seqnum == cmd->seqnum) { mnl_err_list_free(err); break; } } } } out: mnl_batch_reset(); return ret; } int nft_run(void *scanner, struct parser_state *state, struct list_head *msgs) { struct cmd *cmd, *next; int ret; ret = nft_parse(scanner, state); if (ret != 0 || state->nerrs > 0) return -1; retry: ret = nft_netlink(state, msgs); if (ret < 0 && errno == EINTR) { netlink_restart(); goto retry; } list_for_each_entry_safe(cmd, next, &state->cmds, list) { list_del(&cmd->list); cmd_free(cmd); } return ret; } int main(int argc, char * const *argv) { struct parser_state state; void *scanner; LIST_HEAD(msgs); char *buf = NULL, *filename = NULL; unsigned int len; bool interactive = false; int i, val, rc = NFT_EXIT_SUCCESS; while (1) { val = getopt_long(argc, argv, OPTSTRING, options, NULL); if (val == -1) break; switch (val) { case OPT_HELP: show_help(argv[0]); exit(NFT_EXIT_SUCCESS); case OPT_VERSION: printf("%s v%s (%s)\n", PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME); exit(NFT_EXIT_SUCCESS); case OPT_FILE: filename = optarg; break; case OPT_INTERACTIVE: interactive = true; break; case OPT_INCLUDEPATH: if (num_include_paths >= INCLUDE_PATHS_MAX) { fprintf(stderr, "Too many include paths " "specified, max. %u\n", INCLUDE_PATHS_MAX - 1); exit(NFT_EXIT_FAILURE); } include_paths[num_include_paths++] = optarg; break; case OPT_NUMERIC: numeric_output++; break; case OPT_IP2NAME: ip2name_output++; break; #ifdef DEBUG case OPT_DEBUG: for (;;) { unsigned int i; char *end; end = strchr(optarg, ','); if (end) *end = '\0'; for (i = 0; i < array_size(debug_param); i++) { if (strcmp(debug_param[i].name, optarg)) continue; debug_level |= debug_param[i].level; break; } if (i == array_size(debug_param)) { fprintf(stderr, "invalid debug parameter `%s'\n", optarg); exit(NFT_EXIT_FAILURE); } if (end == NULL) break; optarg = end + 1; } break; #endif case OPT_HANDLE_OUTPUT: handle_output++; break; case OPT_INVALID: exit(NFT_EXIT_FAILURE); } } if (optind != argc) { for (len = 0, i = optind; i < argc; i++) len += strlen(argv[i]) + strlen(" "); buf = xzalloc(len + 1); for (i = optind; i < argc; i++) { strcat(buf, argv[i]); if (i + 1 < argc) strcat(buf, " "); } parser_init(&state, &msgs); scanner = scanner_init(&state); scanner_push_buffer(scanner, &indesc_cmdline, buf); } else if (filename != NULL) { parser_init(&state, &msgs); scanner = scanner_init(&state); if (scanner_read_file(scanner, filename, &internal_location) < 0) goto out; } else if (interactive) { if (cli_init(&state) < 0) { fprintf(stderr, "%s: interactive CLI not supported in this build\n", argv[0]); exit(NFT_EXIT_FAILURE); } return 0; } else { fprintf(stderr, "%s: no command specified\n", argv[0]); exit(NFT_EXIT_FAILURE); } if (nft_run(scanner, &state, &msgs) != 0) rc = NFT_EXIT_FAILURE; out: scanner_destroy(scanner); erec_print_list(stderr, &msgs); xfree(buf); return rc; }