summaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c435
1 files changed, 293 insertions, 142 deletions
diff --git a/src/main.c b/src/main.c
index 6ab1b89f..d3491cda 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
}