All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH nft] src: add --define key=value
@ 2021-07-20 10:52 Pablo Neira Ayuso
  0 siblings, 0 replies; only message in thread
From: Pablo Neira Ayuso @ 2021-07-20 10:52 UTC (permalink / raw)
  To: netfilter-devel

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                    |  4 ++
 include/nftables.h             | 11 ++++
 include/nftables/libnftables.h |  2 +
 src/libnftables.c              | 95 ++++++++++++++++++++++++++++++++++
 src/main.c                     | 22 +++++++-
 5 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/doc/nft.txt b/doc/nft.txt
index 46e8dc5366c2..13fe8b1f6671 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -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,
diff --git a/include/nftables.h b/include/nftables.h
index f239fcf0e1f4..7b6339053b54 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -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;
diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
index 765b20dd71ee..aaf7388e6db2 100644
--- a/include/nftables/libnftables.h
+++ b/include/nftables/libnftables.h
@@ -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);
 
diff --git a/src/libnftables.c b/src/libnftables.c
index e3b6ff0ae8d3..2b453864281d 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -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 descriptor 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) &&
diff --git a/src/main.c b/src/main.c
index 8c47064459ec..21096fc7398b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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) {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2021-07-20 10:55 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-20 10:52 [PATCH nft] src: add --define key=value Pablo Neira Ayuso

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.