All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH nft 0/6] nftables: add --optimize support
@ 2021-05-27 15:43 Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 1/6] src: add proto ctx options Florian Westphal
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

This adds a new option, -O/--optimize, to enable/disable ruleset
transformations.

First two supported optimizations are:
 1. Allow removal of implicit dependencies on 'list ruleset'.
 2. Allow automatic replacement of anonymous sets with only one
    element.

There is currently no exported libnftables function to provide
access to the internal settings.

If there is a use case it can be added later on.

Florian Westphal (6):
  src: add proto ctx options
  src: allow to turn off dependency removal
  main: add -O help to dump list of supported optimzation flags
  evaluate: optionally kill anon sets with one element
  tests: add test case for -O no-remove-dependencies
  tests: add test case for removal of anon sets with only a single
    element

 include/nftables.h                            |  12 +++
 include/proto.h                               |  10 +-
 include/rule.h                                |   6 ++
 src/evaluate.c                                |  25 ++++-
 src/libnftables.c                             |  10 ++
 src/main.c                                    | 100 ++++++++++++++++++
 src/netlink.c                                 |   2 +-
 src/netlink_delinearize.c                     |  16 ++-
 src/proto.c                                   |   4 +-
 .../optimizations/dumps/payload_meta_deps.nft |  10 ++
 .../dumps/payload_meta_deps.no-remove-deps    |  10 ++
 .../optimizations/dumps/single_anon_set.nft   |  12 +++
 .../single_anon_set.replace-single-anon-sets  |  12 +++
 .../testcases/optimizations/payload_meta_deps |  33 ++++++
 .../testcases/optimizations/single_anon_set   |  30 ++++++
 15 files changed, 282 insertions(+), 10 deletions(-)
 create mode 100644 tests/shell/testcases/optimizations/dumps/payload_meta_deps.nft
 create mode 100644 tests/shell/testcases/optimizations/dumps/payload_meta_deps.no-remove-deps
 create mode 100644 tests/shell/testcases/optimizations/dumps/single_anon_set.nft
 create mode 100644 tests/shell/testcases/optimizations/dumps/single_anon_set.replace-single-anon-sets
 create mode 100755 tests/shell/testcases/optimizations/payload_meta_deps
 create mode 100755 tests/shell/testcases/optimizations/single_anon_set

-- 
2.26.3


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH nft 1/6] src: add proto ctx options
  2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
@ 2021-05-27 15:43 ` Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 2/6] src: allow to turn off dependency removal Florian Westphal
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Add new option flag member and the needed function signature changes
to initialise it.  No changes in behaviour.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/proto.h           | 6 ++++--
 src/evaluate.c            | 3 ++-
 src/netlink.c             | 2 +-
 src/netlink_delinearize.c | 2 +-
 src/proto.c               | 4 +++-
 5 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/include/proto.h b/include/proto.h
index b9217588f3e3..001b6f9436f7 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -171,7 +171,8 @@ extern const struct proto_desc *proto_dev_desc(uint16_t type);
 /**
  * struct proto_ctx - protocol context
  *
- * debug_mask:	display debugging information
+ * @debug_mask:	display debugging information
+ * @options	option flags to control proto decoding
  * @family:	hook family
  * @location:	location of the relational expression defining the context
  * @desc:	protocol description for this layer
@@ -183,6 +184,7 @@ extern const struct proto_desc *proto_dev_desc(uint16_t type);
  */
 struct proto_ctx {
 	unsigned int			debug_mask;
+	uint8_t				options;
 	uint8_t				family;
 	union {
 		struct {
@@ -202,7 +204,7 @@ struct proto_ctx {
 };
 
 extern void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
-			   unsigned int debug_mask);
+			   unsigned int debug_mask, unsigned int proto_options);
 extern void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
 			     const struct location *loc,
 			     const struct proto_desc *desc);
diff --git a/src/evaluate.c b/src/evaluate.c
index 384e2fa786e0..6bfc464e677a 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4090,7 +4090,8 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
 	struct stmt *stmt, *tstmt = NULL;
 	struct error_record *erec;
 
-	proto_ctx_init(&ctx->pctx, rule->handle.family, ctx->nft->debug_mask);
+	proto_ctx_init(&ctx->pctx, rule->handle.family, ctx->nft->debug_mask,
+		       0);
 	memset(&ctx->ectx, 0, sizeof(ctx->ectx));
 
 	ctx->rule = rule;
diff --git a/src/netlink.c b/src/netlink.c
index 6b6fe27762d5..a307eb32c136 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1868,7 +1868,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt,
 				 meta_expr_alloc(&netlink_location,
 						 NFT_META_OIF), octx);
 
-	proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0);
+	proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, 0);
 	ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc;
 	if ((ll_desc == &proto_inet || ll_desc  == &proto_netdev) &&
 	    nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index a71d06d7fe12..6e907e95dbf1 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -2820,7 +2820,7 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
 	struct stmt *stmt, *next;
 
 	memset(&rctx, 0, sizeof(rctx));
-	proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask);
+	proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask, 0);
 
 	list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
 		enum stmt_types type = stmt->ops->type;
diff --git a/src/proto.c b/src/proto.c
index 63727605a20a..302a51f242ed 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -169,7 +169,8 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
  * @debug_mask:	display debugging information
  */
 void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
-		    unsigned int debug_mask)
+		    unsigned int debug_mask,
+		    unsigned int proto_options)
 {
 	const struct hook_proto_desc *h = &hook_proto_desc[family];
 
@@ -177,6 +178,7 @@ void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
 	ctx->family = family;
 	ctx->protocol[h->base].desc = h->desc;
 	ctx->debug_mask = debug_mask;
+	ctx->options = proto_options;
 
 	proto_ctx_debug(ctx, h->base, debug_mask);
 }
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH nft 2/6] src: allow to turn off dependency removal
  2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 1/6] src: add proto ctx options Florian Westphal
@ 2021-05-27 15:43 ` Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 3/6] main: add -O help to dump list of supported optimzation flags Florian Westphal
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

This allows to list a table without dependency removal:

nft -O no-remove-dependencies list ruleset
table inet filter {
        chain ssh {
                type filter hook input priority filter; policy accept;
                meta l4proto tcp tcp dport 22 accept
        }
}

The dependency "meta l4proto tcp" is retained.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/nftables.h        |  9 +++++
 include/proto.h           |  4 +++
 src/libnftables.c         |  1 +
 src/main.c                | 69 +++++++++++++++++++++++++++++++++++++++
 src/netlink_delinearize.c | 16 +++++++--
 5 files changed, 96 insertions(+), 3 deletions(-)

diff --git a/include/nftables.h b/include/nftables.h
index f239fcf0e1f4..bbf287e68a4c 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -108,6 +108,7 @@ struct nft_ctx {
 	unsigned int		num_include_paths;
 	unsigned int		parser_max_errors;
 	unsigned int		debug_mask;
+	unsigned int		optimization_flags;
 	struct output_ctx	output;
 	bool			check;
 	struct nft_cache	cache;
@@ -213,4 +214,12 @@ int nft_gmp_print(struct output_ctx *octx, const char *fmt, ...)
 
 #define __NFT_OUTPUT_NOTSUPP	UINT_MAX
 
+/* XXX: exposing this in libnftables limits public API
+ * to 32 optimization flags.
+ */
+enum nft_optimization_flags {
+	NFT_OPTIMIZATION_F_REMOVE_DEPS = 1 << 0,
+};
+#define DEFAULT_OPTIMIZATION_FLAGS NFT_OPTIMIZATION_F_REMOVE_DEPS
+
 #endif /* NFTABLES_NFTABLES_H */
diff --git a/include/proto.h b/include/proto.h
index 001b6f9436f7..019e12e1899d 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -168,6 +168,10 @@ extern const struct proto_desc *proto_dev_desc(uint16_t type);
 
 #define PROTO_CTX_NUM_PROTOS	16
 
+enum proto_ctx_flags {
+	PROTO_F_REMOVE_DEPS = 1 << 0,
+};
+
 /**
  * struct proto_ctx - protocol context
  *
diff --git a/src/libnftables.c b/src/libnftables.c
index e080eb032770..a1f822cbe2e6 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -166,6 +166,7 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
 
 	ctx->state = xzalloc(sizeof(struct parser_state));
 	nft_ctx_add_include_path(ctx, DEFAULT_INCLUDE_PATH);
+	ctx->optimization_flags = DEFAULT_OPTIMIZATION_FLAGS;
 	ctx->parser_max_errors	= 10;
 	cache_init(&ctx->cache.table_cache);
 	ctx->top_scope = scope_alloc();
diff --git a/src/main.c b/src/main.c
index 8c47064459ec..6e14a7f26b0f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 
 #include <nftables/libnftables.h>
+#include <nftables.h>
 #include <utils.h>
 #include <cli.h>
 
@@ -34,6 +35,7 @@ enum opt_indices {
 #define IDX_RULESET_INPUT_START	IDX_FILE
 	IDX_INTERACTIVE,
         IDX_INCLUDEPATH,
+	IDX_OPTIMIZE,
 	IDX_CHECK,
 #define IDX_RULESET_INPUT_END	IDX_CHECK
         /* Ruleset list formatting */
@@ -78,9 +80,15 @@ enum opt_vals {
 	OPT_NUMERIC_PROTO	= 'p',
 	OPT_NUMERIC_TIME	= 'T',
 	OPT_TERSE		= 't',
+	OPT_OPTIMIZE		= 'O',
 	OPT_INVALID		= '?',
 };
 
+enum optimization_feature {
+	OPTIMIZE_UNDEFINED,
+	OPTIMIZE_REMOVE_DEPENDENCIES,
+};
+
 struct nft_opt {
 	const char    *name;
 	enum opt_vals  val;
@@ -104,6 +112,8 @@ static const struct nft_opt nft_options[] = {
 				     "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_OPTIMIZE]	    = NFT_OPT("optimize",		OPT_OPTIMIZE,		"<name [,name..]>",
+				     "Specify optimization options"),
 	[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,
@@ -303,6 +313,18 @@ static const struct {
 	},
 };
 
+static const struct {
+	const char		*name;
+	enum optimization_feature level;
+	enum nft_optimization_flags flag;
+} optimization_param[] = {
+	{
+		.name	= "remove-dependencies",
+		.level	= OPTIMIZE_REMOVE_DEPENDENCIES,
+		.flag	= NFT_OPTIMIZATION_F_REMOVE_DEPS,
+	},
+};
+
 static void nft_options_error(int argc, char * const argv[], int pos)
 {
 	int i;
@@ -331,9 +353,11 @@ static bool nft_options_check(int argc, char * const argv[])
 				return false;
 			} else if (argv[i][1] == 'd' ||
 				   argv[i][1] == 'I' ||
+				   argv[i][1] == 'O' ||
 				   argv[i][1] == 'f' ||
 				   !strcmp(argv[i], "--debug") ||
 				   !strcmp(argv[i], "--includepath") ||
+				   !strcmp(argv[i], "--optimize") ||
 				   !strcmp(argv[i], "--file")) {
 				skip = true;
 				continue;
@@ -346,6 +370,48 @@ static bool nft_options_check(int argc, char * const argv[])
 	return true;
 }
 
+static void optimize_settings_set_custom(struct nft_ctx *ctx, char *options)
+{
+	unsigned int i;
+	char *end;
+
+	do {
+		enum optimization_feature level = OPTIMIZE_UNDEFINED;
+		unsigned int flag = 0;
+		bool enable = true;
+
+		end = strchr(options, ',');
+		if (end)
+			*end = '\0';
+
+		if (strncmp(options, "no-", 3) == 0) {
+			options += 3;
+			enable = false;
+		}
+
+		for (i = 0; i < array_size(optimization_param); i++) {
+			if (strcmp(optimization_param[i].name, options))
+				continue;
+
+			level = optimization_param[i].level;
+			flag = optimization_param[i].flag;
+			break;
+		}
+
+		switch (level) {
+		case OPTIMIZE_UNDEFINED:
+			fprintf(stderr, "invalid optimization option `%s'\n", options);
+			exit(EXIT_FAILURE);
+		case OPTIMIZE_REMOVE_DEPENDENCIES:
+			break;
+		}
+		if (enable)
+			ctx->optimization_flags |= flag;
+		else
+			ctx->optimization_flags &= ~flag;
+	} while (end != NULL);
+}
+
 int main(int argc, char * const *argv)
 {
 	const struct option *options = get_options();
@@ -465,6 +531,9 @@ int main(int argc, char * const *argv)
 		case OPT_TERSE:
 			output_flags |= NFT_CTX_OUTPUT_TERSE;
 			break;
+		case OPT_OPTIMIZE:
+			optimize_settings_set_custom(nft, optarg);
+			break;
 		case OPT_INVALID:
 			exit(EXIT_FAILURE);
 		}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 6e907e95dbf1..9cd582211e78 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1847,6 +1847,9 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
 		assert(left->payload.base);
 		assert(base == left->payload.base);
 
+		if ((ctx->pctx.options & PROTO_F_REMOVE_DEPS) == 0)
+			continue;
+
 		stacked = payload_is_stacked(ctx->pctx.protocol[base].desc, nexpr);
 
 		/* Remember the first payload protocol expression to
@@ -1985,6 +1988,9 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
 
 		relational_expr_pctx_update(&ctx->pctx, expr);
 
+		if ((ctx->pctx.options & PROTO_F_REMOVE_DEPS) == 0)
+			break;
+
 		if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
 		    left->flags & EXPR_F_PROTOCOL) {
 			payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
@@ -2814,13 +2820,14 @@ rule_maybe_reset_payload_deps(struct payload_dep_ctx *pdctx, enum stmt_types t)
 	payload_dependency_reset(pdctx);
 }
 
-static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
+static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule,
+				   unsigned int options)
 {
 	struct rule_pp_ctx rctx;
 	struct stmt *stmt, *next;
 
 	memset(&rctx, 0, sizeof(rctx));
-	proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask, 0);
+	proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask, options);
 
 	list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
 		enum stmt_types type = stmt->ops->type;
@@ -2947,11 +2954,14 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
 				      struct nftnl_rule *nlr)
 {
 	struct netlink_parse_ctx _ctx, *pctx = &_ctx;
+	unsigned int proto_options = 0;
 	struct handle h;
 
 	memset(&_ctx, 0, sizeof(_ctx));
 	_ctx.msgs = ctx->msgs;
 	_ctx.debug_mask = ctx->nft->debug_mask;
+	if (ctx->nft->optimization_flags & NFT_OPTIMIZATION_F_REMOVE_DEPS)
+		proto_options |= PROTO_F_REMOVE_DEPS;
 
 	memset(&h, 0, sizeof(h));
 	h.family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
@@ -2971,7 +2981,7 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
 
 	nftnl_expr_foreach(nlr, netlink_parse_rule_expr, pctx);
 
-	rule_parse_postprocess(pctx, pctx->rule);
+	rule_parse_postprocess(pctx, pctx->rule, proto_options);
 	netlink_release_registers(pctx);
 	return pctx->rule;
 }
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH nft 3/6] main: add -O help to dump list of supported optimzation flags
  2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 1/6] src: add proto ctx options Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 2/6] src: allow to turn off dependency removal Florian Westphal
@ 2021-05-27 15:43 ` Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 4/6] evaluate: optionally kill anon sets with one element Florian Westphal
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Print list of supported options.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 src/main.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/src/main.c b/src/main.c
index 6e14a7f26b0f..cf00f27f06de 100644
--- a/src/main.c
+++ b/src/main.c
@@ -86,6 +86,7 @@ enum opt_vals {
 
 enum optimization_feature {
 	OPTIMIZE_UNDEFINED,
+	OPTIMIZE_HELP,
 	OPTIMIZE_REMOVE_DEPENDENCIES,
 };
 
@@ -315,11 +316,18 @@ static const struct {
 
 static const struct {
 	const char		*name;
+	const char		*help;
 	enum optimization_feature level;
 	enum nft_optimization_flags flag;
 } optimization_param[] = {
+	{
+		.name	= "help",
+		.help	= "",
+		.level	= OPTIMIZE_HELP,
+	},
 	{
 		.name	= "remove-dependencies",
+		.help	= "ruleset listing with redundant expressions removed",
 		.level	= OPTIMIZE_REMOVE_DEPENDENCIES,
 		.flag	= NFT_OPTIMIZATION_F_REMOVE_DEPS,
 	},
@@ -372,6 +380,7 @@ static bool nft_options_check(int argc, char * const argv[])
 
 static void optimize_settings_set_custom(struct nft_ctx *ctx, char *options)
 {
+	unsigned int default_flags = ctx->optimization_flags;
 	unsigned int i;
 	char *end;
 
@@ -401,7 +410,21 @@ static void optimize_settings_set_custom(struct nft_ctx *ctx, char *options)
 		switch (level) {
 		case OPTIMIZE_UNDEFINED:
 			fprintf(stderr, "invalid optimization option `%s'\n", options);
+			fprintf(stderr, "Try \"-O help\" for list of options.\n");
 			exit(EXIT_FAILURE);
+		case OPTIMIZE_HELP:
+			for (i = 0; i < array_size(optimization_param); i++) {
+				if (level == optimization_param[i].level)
+					continue;
+
+				flag = optimization_param[i].flag;
+				printf("%s: %s (default %s)\n",
+					optimization_param[i].name, optimization_param[i].help,
+					default_flags & flag ? "on" : "off");
+			}
+
+			printf("\nPrepend \"no-\" to disable options that are enabled by default.\n");
+			exit(EXIT_SUCCESS);
 		case OPTIMIZE_REMOVE_DEPENDENCIES:
 			break;
 		}
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH nft 4/6] evaluate: optionally kill anon sets with one element
  2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
                   ` (2 preceding siblings ...)
  2021-05-27 15:43 ` [PATCH nft 3/6] main: add -O help to dump list of supported optimzation flags Florian Westphal
@ 2021-05-27 15:43 ` Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 5/6] tests: add test case for -O no-remove-dependencies Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 6/6] tests: add test case for removal of anon sets with only a single element Florian Westphal
  5 siblings, 0 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Add a new optimization option, disabled by default, that auto-replaces
lookups in single-element anon sets with a standard compare.

'add rule foo bar meta iif { "lo" }' gets replaced with
'add rule foo bar meta iif "lo"'.

The former is a set lookup, the latter is a comparision.
Comparisions is slightly faster in this special case.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/nftables.h |  3 +++
 include/rule.h     |  6 ++++++
 src/evaluate.c     | 22 ++++++++++++++++++++--
 src/libnftables.c  |  9 +++++++++
 src/main.c         |  8 ++++++++
 5 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/include/nftables.h b/include/nftables.h
index bbf287e68a4c..23b7fea53f42 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -216,9 +216,12 @@ int nft_gmp_print(struct output_ctx *octx, const char *fmt, ...)
 
 /* XXX: exposing this in libnftables limits public API
  * to 32 optimization flags.
+ *
+ * For now this is internal to nft.
  */
 enum nft_optimization_flags {
 	NFT_OPTIMIZATION_F_REMOVE_DEPS = 1 << 0,
+	NFT_OPTIMIZATION_F_REPLACE_SINGLE_ELEM_ANON_SETS = 1 << 1,
 };
 #define DEFAULT_OPTIMIZATION_FLAGS NFT_OPTIMIZATION_F_REMOVE_DEPS
 
diff --git a/include/rule.h b/include/rule.h
index f469db55bf60..ae23467187ff 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -735,6 +735,10 @@ void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
 #include <payload.h>
 #include <expression.h>
 
+enum eval_ctx_flags {
+	EVAL_F_REPLACE_SINGLE_ELEM_ANON_SETS = 1 << 0,
+};
+
 /**
  * struct eval_ctx - evaluation context
  *
@@ -749,6 +753,7 @@ void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
  * @debug_mask: debugging bitmask
  * @ectx:	expression context
  * @pctx:	payload context
+ * @eval_flags: enum eval_ctx_flags
  */
 struct eval_ctx {
 	struct nft_ctx		*nft;
@@ -760,6 +765,7 @@ struct eval_ctx {
 	struct stmt		*stmt;
 	struct expr_ctx		ectx;
 	struct proto_ctx	pctx;
+	unsigned int		eval_flags;
 };
 
 extern int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd);
diff --git a/src/evaluate.c b/src/evaluate.c
index 6bfc464e677a..088742bfe6b4 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1455,8 +1455,26 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
 		}
 	}
 
-	if (ctx->set && (ctx->set->flags & NFT_SET_CONCAT))
-		set->set_flags |= NFT_SET_CONCAT;
+	if (ctx->set) {
+		if (ctx->set->flags & NFT_SET_CONCAT)
+			set->set_flags |= NFT_SET_CONCAT;
+	} else if ((ctx->eval_flags & EVAL_F_REPLACE_SINGLE_ELEM_ANON_SETS) &&
+		   set->size == 1) {
+		i = list_first_entry(&set->expressions, struct expr, list);
+		if (i->etype == EXPR_SET_ELEM) {
+			switch (i->key->etype) {
+			case EXPR_PREFIX:
+			case EXPR_RANGE:
+			case EXPR_VALUE:
+				*expr = i->key;
+				i->key = NULL;
+				expr_free(set);
+				return 0;
+			default:
+				break;
+			}
+		}
+	}
 
 	set->set_flags |= NFT_SET_CONSTANT;
 
diff --git a/src/libnftables.c b/src/libnftables.c
index a1f822cbe2e6..0c17007bac4e 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -412,6 +412,12 @@ static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
 	return 0;
 }
 
+static void eval_ctx_set_flags(struct eval_ctx *ectx, const struct nft_ctx *nft)
+{
+	if (nft->optimization_flags & NFT_OPTIMIZATION_F_REPLACE_SINGLE_ELEM_ANON_SETS)
+		ectx->eval_flags |= EVAL_F_REPLACE_SINGLE_ELEM_ANON_SETS;
+}
+
 static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
 			struct list_head *cmds)
 {
@@ -427,6 +433,9 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
 			.nft	= nft,
 			.msgs	= msgs,
 		};
+
+		eval_ctx_set_flags(&ectx, nft);
+
 		if (cmd_evaluate(&ectx, cmd) < 0 &&
 		    ++nft->state->nerrs == nft->parser_max_errors)
 			return -1;
diff --git a/src/main.c b/src/main.c
index cf00f27f06de..858218c79989 100644
--- a/src/main.c
+++ b/src/main.c
@@ -88,6 +88,7 @@ enum optimization_feature {
 	OPTIMIZE_UNDEFINED,
 	OPTIMIZE_HELP,
 	OPTIMIZE_REMOVE_DEPENDENCIES,
+	OPTIMIZE_REMOVE_SINGLE_ELEM_ANON_SET,
 };
 
 struct nft_opt {
@@ -331,6 +332,12 @@ static const struct {
 		.level	= OPTIMIZE_REMOVE_DEPENDENCIES,
 		.flag	= NFT_OPTIMIZATION_F_REMOVE_DEPS,
 	},
+	{
+		.name	= "replace-single-anon-sets",
+		.help	= "replace anonymous sets with one element with single compare",
+		.level	= OPTIMIZE_REMOVE_SINGLE_ELEM_ANON_SET,
+		.flag	= NFT_OPTIMIZATION_F_REPLACE_SINGLE_ELEM_ANON_SETS,
+	},
 };
 
 static void nft_options_error(int argc, char * const argv[], int pos)
@@ -426,6 +433,7 @@ static void optimize_settings_set_custom(struct nft_ctx *ctx, char *options)
 			printf("\nPrepend \"no-\" to disable options that are enabled by default.\n");
 			exit(EXIT_SUCCESS);
 		case OPTIMIZE_REMOVE_DEPENDENCIES:
+		case OPTIMIZE_REMOVE_SINGLE_ELEM_ANON_SET:
 			break;
 		}
 		if (enable)
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH nft 5/6] tests: add test case for -O no-remove-dependencies
  2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
                   ` (3 preceding siblings ...)
  2021-05-27 15:43 ` [PATCH nft 4/6] evaluate: optionally kill anon sets with one element Florian Westphal
@ 2021-05-27 15:43 ` Florian Westphal
  2021-05-27 15:43 ` [PATCH nft 6/6] tests: add test case for removal of anon sets with only a single element Florian Westphal
  5 siblings, 0 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Contains two different text files that contain the same rules, one
is with the implicit deps, the other one is without them.

Check they are the same and check that '-O no-remove-dependencies'
keeps the redundant meta/payload expressions.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 .../optimizations/dumps/payload_meta_deps.nft | 10 ++++++
 .../dumps/payload_meta_deps.no-remove-deps    | 10 ++++++
 .../testcases/optimizations/payload_meta_deps | 33 +++++++++++++++++++
 3 files changed, 53 insertions(+)
 create mode 100644 tests/shell/testcases/optimizations/dumps/payload_meta_deps.nft
 create mode 100644 tests/shell/testcases/optimizations/dumps/payload_meta_deps.no-remove-deps
 create mode 100755 tests/shell/testcases/optimizations/payload_meta_deps

diff --git a/tests/shell/testcases/optimizations/dumps/payload_meta_deps.nft b/tests/shell/testcases/optimizations/dumps/payload_meta_deps.nft
new file mode 100644
index 000000000000..5f26f0d317d9
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/payload_meta_deps.nft
@@ -0,0 +1,10 @@
+table inet test {
+	chain test {
+		ip saddr 1.2.3.4
+		ip6 saddr dead::1
+		tcp dport 22
+		ip saddr 1.2.3.5 tcp dport 22 accept
+		ip6 nexthdr udp
+		ip ttl < 5 drop
+	}
+}
diff --git a/tests/shell/testcases/optimizations/dumps/payload_meta_deps.no-remove-deps b/tests/shell/testcases/optimizations/dumps/payload_meta_deps.no-remove-deps
new file mode 100644
index 000000000000..5e458e88eb29
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/payload_meta_deps.no-remove-deps
@@ -0,0 +1,10 @@
+table inet test {
+	chain test {
+		meta nfproto ipv4 ip saddr 1.2.3.4
+		meta nfproto ipv6 ip6 saddr dead::1
+		meta l4proto tcp tcp dport 22
+		meta nfproto ipv4 ip saddr 1.2.3.5 meta l4proto tcp tcp dport 22 accept
+		meta nfproto ipv6 ip6 nexthdr udp
+		meta nfproto ipv4 ip ttl < 5 drop
+	}
+}
diff --git a/tests/shell/testcases/optimizations/payload_meta_deps b/tests/shell/testcases/optimizations/payload_meta_deps
new file mode 100755
index 000000000000..1ec3bf0776e8
--- /dev/null
+++ b/tests/shell/testcases/optimizations/payload_meta_deps
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+
+# Input files are identical.  Second version includes the
+# (redundant) payload/meta dependencies, i.e. the files
+# are textually different but logically the rule sets are
+# the same.
+dumpfile_depon=$(dirname $0)/dumps/$(basename $0).nft
+dumpfile_depoff=$(dirname $0)/dumps/$(basename $0).no-remove-deps
+
+$NFT -f "$dumpfile_depon"
+A=$(mktemp)
+$NFT -O no-remove-dependencies list ruleset > "$A"
+
+# This checks that -O no-remove... kept all auto-generated
+# dependencies, even though the imported file doesn't
+# mention them.
+
+diff -u $dumpfile_depoff "$A"
+ret=$?
+
+rm -f "$A"
+
+$NFT delete table inet test
+
+# This makes calling test script check that the
+# no-remove-deps dump file logically matches the .nft version.
+# This detects future mismatches between the two representations.
+
+$NFT -f "$dumpfile_depoff"
+
+exit $ret
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH nft 6/6] tests: add test case for removal of anon sets with only a single element
  2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
                   ` (4 preceding siblings ...)
  2021-05-27 15:43 ` [PATCH nft 5/6] tests: add test case for -O no-remove-dependencies Florian Westphal
@ 2021-05-27 15:43 ` Florian Westphal
  5 siblings, 0 replies; 7+ messages in thread
From: Florian Westphal @ 2021-05-27 15:43 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 .../optimizations/dumps/single_anon_set.nft   | 12 ++++++++
 .../single_anon_set.replace-single-anon-sets  | 12 ++++++++
 .../testcases/optimizations/single_anon_set   | 30 +++++++++++++++++++
 3 files changed, 54 insertions(+)
 create mode 100644 tests/shell/testcases/optimizations/dumps/single_anon_set.nft
 create mode 100644 tests/shell/testcases/optimizations/dumps/single_anon_set.replace-single-anon-sets
 create mode 100755 tests/shell/testcases/optimizations/single_anon_set

diff --git a/tests/shell/testcases/optimizations/dumps/single_anon_set.nft b/tests/shell/testcases/optimizations/dumps/single_anon_set.nft
new file mode 100644
index 000000000000..5320bcfc360a
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/single_anon_set.nft
@@ -0,0 +1,12 @@
+table ip test {
+	chain test {
+		ip saddr { 127.0.0.1 } accept
+		ip saddr { 127.0.0.0/8 } accept
+		ip saddr { 127.0.0.1-192.168.7.3 } accept
+		ip daddr { 192.168.7.1, 192.168.7.5 } accept
+		tcp dport { 80, 443 } accept
+		tcp dport != { 22 } drop
+		tcp sport { 1-1023 } drop
+		iif { "lo" } accept
+	}
+}
diff --git a/tests/shell/testcases/optimizations/dumps/single_anon_set.replace-single-anon-sets b/tests/shell/testcases/optimizations/dumps/single_anon_set.replace-single-anon-sets
new file mode 100644
index 000000000000..b9afa245fc16
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/single_anon_set.replace-single-anon-sets
@@ -0,0 +1,12 @@
+table ip test {
+	chain test {
+		ip saddr 127.0.0.1 accept
+		ip saddr 127.0.0.0/8 accept
+		ip saddr 127.0.0.1-192.168.7.3 accept
+		ip daddr { 192.168.7.1, 192.168.7.5 } accept
+		tcp dport { 80, 443 } accept
+		tcp dport != 22 drop
+		tcp sport 1-1023 drop
+		iif "lo" accept
+	}
+}
diff --git a/tests/shell/testcases/optimizations/single_anon_set b/tests/shell/testcases/optimizations/single_anon_set
new file mode 100755
index 000000000000..81c8533b2a57
--- /dev/null
+++ b/tests/shell/testcases/optimizations/single_anon_set
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+
+# Input file contains rules with anon sets that contain
+# one element, plus extra rule with two elements (that should be
+# left alone).
+
+# Second file contains a postprocessed version with rules
+# translated to simple equality test rather than set lookup.
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+dumpfile_pp=$(dirname $0)/dumps/$(basename $0).replace-single-anon-sets
+
+$NFT -O replace-single-anon-sets -f "$dumpfile"
+A=$(mktemp)
+$NFT list ruleset > "$A"
+
+# This checks that -O no-remove... kept all auto-generated
+# dependencies, even though the imported file doesn't
+# mention them.
+
+diff -u "$A" "$dumpfile_pp"
+ret=$?
+
+rm -f "$A"
+
+$NFT delete table ip test
+$NFT -f "$dumpfile"
+
+exit $ret
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-05-27 15:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-27 15:43 [PATCH nft 0/6] nftables: add --optimize support Florian Westphal
2021-05-27 15:43 ` [PATCH nft 1/6] src: add proto ctx options Florian Westphal
2021-05-27 15:43 ` [PATCH nft 2/6] src: allow to turn off dependency removal Florian Westphal
2021-05-27 15:43 ` [PATCH nft 3/6] main: add -O help to dump list of supported optimzation flags Florian Westphal
2021-05-27 15:43 ` [PATCH nft 4/6] evaluate: optionally kill anon sets with one element Florian Westphal
2021-05-27 15:43 ` [PATCH nft 5/6] tests: add test case for -O no-remove-dependencies Florian Westphal
2021-05-27 15:43 ` [PATCH nft 6/6] tests: add test case for removal of anon sets with only a single element Florian Westphal

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.