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>
*--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,
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;
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);
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)
{
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);
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);
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)
{
LIST_HEAD(msgs);
LIST_HEAD(cmds);
+ rc = load_cmdline_vars(nft, &msgs);
+ if (rc < 0)
+ goto err;
+
if (!strcmp(filename, "-"))
filename = "/dev/stdin";
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) &&
/* Ruleset input handling */
IDX_FILE,
#define IDX_RULESET_INPUT_START IDX_FILE
+ IDX_DEFINE,
IDX_INTERACTIVE,
IDX_INCLUDEPATH,
IDX_CHECK,
OPT_VERSION_LONG = 'V',
OPT_CHECK = 'c',
OPT_FILE = 'f',
+ OPT_DEFINE = 'D',
OPT_INTERACTIVE = 'i',
OPT_INCLUDEPATH = 'I',
OPT_JSON = 'j',
"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>",
} 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;
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;
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;
}
}
+ 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) {
--- /dev/null
+#!/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"
--- /dev/null
+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 }
+ }
+}