]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add --define key=value
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 20 Jul 2021 10:17:33 +0000 (12:17 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 20 Jul 2021 15:47:55 +0000 (17:47 +0200)
This patch adds a new option to define variables from the command line.

 # cat test.nft
 table netdev x {
        chain y {
                type filter hook ingress devices = $dev priority 0;
                counter accept
        }
 }
 # nft --define dev="{ eth0, eth1 }" -f test.nft

You can only combine it with -f/--filename.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
doc/nft.txt
include/nftables.h
include/nftables/libnftables.h
src/libnftables.c
src/main.c
tests/shell/testcases/nft-f/0028variable_cmdline_0 [new file with mode: 0755]
tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft [new file with mode: 0644]

index 46e8dc5366c26368485fc8128cbc93f9cc7e4e2e..13fe8b1f6671635d6a754bf66a6bc5cefad0151e 100644 (file)
@@ -44,6 +44,10 @@ understanding of their meaning. You can get information about options by running
 *--file 'filename'*::
        Read input from 'filename'. If 'filename' is -, read from stdin.
 
+*-D*::
+*--define 'name=value'*::
+       Define a variable. You can only combine this option with '-f'.
+
 *-i*::
 *--interactive*::
        Read input from an interactive readline CLI. You can use quit to exit, or use the EOF marker,
index f239fcf0e1f47aaf8150d0a01ca3153a8e14ac15..7b6339053b54f7486f4088ba7441c8ac8e0eebdb 100644 (file)
@@ -100,12 +100,23 @@ struct mnl_socket;
 struct parser_state;
 struct scope;
 
+struct nft_vars {
+       const char      *key;
+       const char      *value;
+};
+
 #define MAX_INCLUDE_DEPTH      16
 
 struct nft_ctx {
        struct mnl_socket       *nf_sock;
        char                    **include_paths;
        unsigned int            num_include_paths;
+       struct nft_vars         *vars;
+       struct {
+               const char      *buf;
+               struct list_head indesc_list;
+       } vars_ctx;
+       unsigned int            num_vars;
        unsigned int            parser_max_errors;
        unsigned int            debug_mask;
        struct output_ctx       output;
index 765b20dd71ee19d44543989eb0f3b222b3241afd..aaf7388e6db2bb12cbca70a36af21294c4ea04b3 100644 (file)
@@ -78,6 +78,8 @@ const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx);
 int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path);
 void nft_ctx_clear_include_paths(struct nft_ctx *ctx);
 
+int nft_ctx_add_var(struct nft_ctx *ctx, const char *var);
+
 int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf);
 int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename);
 
index e3b6ff0ae8d3658583bdef514e5d5f541567ea82..de6dc7cdae6c84e85ae1ca7011b386f8a8cba863 100644 (file)
@@ -119,6 +119,43 @@ static void nft_exit(struct nft_ctx *ctx)
        mark_table_exit(ctx);
 }
 
+EXPORT_SYMBOL(nft_ctx_add_var);
+int nft_ctx_add_var(struct nft_ctx *ctx, const char *var)
+{
+       char *separator = strchr(var, '=');
+       int pcount = ctx->num_vars;
+       struct nft_vars *tmp;
+       const char *value;
+
+       if (!separator)
+               return -1;
+
+       tmp = realloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
+       if (!tmp)
+               return -1;
+
+       *separator = '\0';
+       value = separator + 1;
+
+       ctx->vars = tmp;
+       ctx->vars[pcount].key = xstrdup(var);
+       ctx->vars[pcount].value = xstrdup(value);
+       ctx->num_vars++;
+
+       return 0;
+}
+
+static void nft_ctx_clear_vars(struct nft_ctx *ctx)
+{
+       unsigned int i;
+
+       for (i = 0; i < ctx->num_vars; i++) {
+               xfree(ctx->vars[i].key);
+               xfree(ctx->vars[i].value);
+       }
+       xfree(ctx->vars);
+}
+
 EXPORT_SYMBOL(nft_ctx_add_include_path);
 int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
 {
@@ -178,6 +215,7 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
        ctx->flags = flags;
        ctx->output.output_fp = stdout;
        ctx->output.error_fp = stderr;
+       init_list_head(&ctx->vars_ctx.indesc_list);
 
        if (flags == NFT_CTX_DEFAULT)
                nft_ctx_netlink_init(ctx);
@@ -311,6 +349,7 @@ void nft_ctx_free(struct nft_ctx *ctx)
        exit_cookie(&ctx->output.error_cookie);
        iface_cache_release();
        nft_cache_release(&ctx->cache);
+       nft_ctx_clear_vars(ctx);
        nft_ctx_clear_include_paths(ctx);
        scope_free(ctx->top_scope);
        xfree(ctx->state);
@@ -507,6 +546,47 @@ err:
        return rc;
 }
 
+static int load_cmdline_vars(struct nft_ctx *ctx, struct list_head *msgs)
+{
+       unsigned int bufsize, ret, i, offset = 0;
+       LIST_HEAD(cmds);
+       char *buf;
+       int rc;
+
+       if (ctx->num_vars == 0)
+               return 0;
+
+       bufsize = 1024;
+       buf = xzalloc(bufsize + 1);
+       for (i = 0; i < ctx->num_vars; i++) {
+retry:
+               ret = snprintf(buf + offset, bufsize - offset,
+                              "define %s=%s; ",
+                              ctx->vars[i].key, ctx->vars[i].value);
+               if (ret >= bufsize - offset) {
+                       bufsize *= 2;
+                       buf = xrealloc(buf, bufsize + 1);
+                       goto retry;
+               }
+               offset += ret;
+       }
+       snprintf(buf + offset, bufsize - offset, "\n");
+
+       rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds);
+
+       assert(list_empty(&cmds));
+       /* Stash the buffer that contains the variable definitions and zap the
+        * list of input descriptors before releasing the scanner state,
+        * otherwise error reporting path walks over released objects.
+        */
+       ctx->vars_ctx.buf = buf;
+       list_splice_init(&ctx->state->indesc_list, &ctx->vars_ctx.indesc_list);
+       scanner_destroy(ctx);
+       ctx->scanner = NULL;
+
+       return rc;
+}
+
 EXPORT_SYMBOL(nft_run_cmd_from_filename);
 int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
 {
@@ -515,6 +595,10 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
        LIST_HEAD(msgs);
        LIST_HEAD(cmds);
 
+       rc = load_cmdline_vars(nft, &msgs);
+       if (rc < 0)
+               goto err;
+
        if (!strcmp(filename, "-"))
                filename = "/dev/stdin";
 
@@ -548,6 +632,17 @@ err:
                scanner_destroy(nft);
                nft->scanner = NULL;
        }
+       if (!list_empty(&nft->vars_ctx.indesc_list)) {
+               struct input_descriptor *indesc, *next;
+
+               list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) {
+                       if (indesc->name)
+                               xfree(indesc->name);
+
+                       xfree(indesc);
+               }
+       }
+       xfree(nft->vars_ctx.buf);
 
        if (!rc &&
            nft_output_json(&nft->output) &&
index 8c47064459ecb40ed5c17d4fdc63b0c4d0397717..21096fc7398b19e886ee2ee7d95192d58b01be2d 100644 (file)
@@ -32,6 +32,7 @@ enum opt_indices {
         /* Ruleset input handling */
        IDX_FILE,
 #define IDX_RULESET_INPUT_START        IDX_FILE
+       IDX_DEFINE,
        IDX_INTERACTIVE,
         IDX_INCLUDEPATH,
        IDX_CHECK,
@@ -63,6 +64,7 @@ enum opt_vals {
        OPT_VERSION_LONG        = 'V',
        OPT_CHECK               = 'c',
        OPT_FILE                = 'f',
+       OPT_DEFINE              = 'D',
        OPT_INTERACTIVE         = 'i',
        OPT_INCLUDEPATH         = 'I',
        OPT_JSON                = 'j',
@@ -100,6 +102,8 @@ static const struct nft_opt nft_options[] = {
                                     "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>",
@@ -332,8 +336,10 @@ static bool nft_options_check(int argc, char * const argv[])
                        } else if (argv[i][1] == 'd' ||
                                   argv[i][1] == 'I' ||
                                   argv[i][1] == 'f' ||
+                                  argv[i][1] == 'D' ||
                                   !strcmp(argv[i], "--debug") ||
                                   !strcmp(argv[i], "--includepath") ||
+                                  !strcmp(argv[i], "--define") ||
                                   !strcmp(argv[i], "--file")) {
                                skip = true;
                                continue;
@@ -349,10 +355,10 @@ static bool nft_options_check(int argc, char * const argv[])
 int main(int argc, char * const *argv)
 {
        const struct option *options = get_options();
+       bool interactive = false, define = false;
        const char *optstring = get_optstring();
        char *buf = NULL, *filename = NULL;
        unsigned int output_flags = 0;
-       bool interactive = false;
        unsigned int debug_mask;
        unsigned int len;
        int i, val, rc;
@@ -378,6 +384,15 @@ int main(int argc, char * const *argv)
                case OPT_VERSION_LONG:
                        show_version();
                        exit(EXIT_SUCCESS);
+               case OPT_DEFINE:
+                       if (nft_ctx_add_var(nft, optarg)) {
+                               fprintf(stderr,
+                                       "Failed to define variable '%s'\n",
+                                       optarg);
+                               exit(EXIT_FAILURE);
+                       }
+                       define = true;
+                       break;
                case OPT_CHECK:
                        nft_ctx_set_dry_run(nft, true);
                        break;
@@ -470,6 +485,11 @@ int main(int argc, char * const *argv)
                }
        }
 
+       if (!filename && define) {
+               fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n");
+               exit(EXIT_FAILURE);
+       }
+
        nft_ctx_output_set_flags(nft, output_flags);
 
        if (optind != argc) {
diff --git a/tests/shell/testcases/nft-f/0028variable_cmdline_0 b/tests/shell/testcases/nft-f/0028variable_cmdline_0
new file mode 100755 (executable)
index 0000000..a2bbd5d
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+
+RULESET="table inet filter {
+       set whitelist_v4 { type ipv4_addr; }
+}
+add element inet filter whitelist_v4 \$whitelist_v4
+"
+
+# this is intentional: exercise error path
+$NFT --define whitelist_v4="{ wrong }" -f - <<< "$RULESET"
+$NFT --define whitelist_v4="{ 1.1.1.1, \$wrong }" -f - <<< "$RULESET"
+
+set -e
+
+$NFT --define whitelist_v4="{ 1.1.1.1, 2.2.2.2 }" -f - <<< "$RULESET"
+$NFT --define x={5.5.5.5} --define whitelist_v4="{ 3.3.3.3, 4.4.4.4, \$x }" -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft b/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft
new file mode 100644 (file)
index 0000000..aa08112
--- /dev/null
@@ -0,0 +1,8 @@
+table inet filter {
+       set whitelist_v4 {
+               type ipv4_addr
+               elements = { 1.1.1.1, 2.2.2.2,
+                            3.3.3.3, 4.4.4.4,
+                            5.5.5.5 }
+       }
+}