diff mbox series

[nft,4/4] src: integrate stateful expressions into sets and maps

Message ID 20180816000350.12226-4-pablo@netfilter.org
State Accepted
Delegated to: Pablo Neira
Headers show
Series [nft,1/4] statement: incorrect spacing in set reference | expand

Commit Message

Pablo Neira Ayuso Aug. 16, 2018, 12:03 a.m. UTC
The following example shows how to populate a set from the packet path
using the destination IP address, for each entry there is a counter. The
entry expires after the 1 hour timeout if no packets matching this entry
are seen.

 table ip x {
        set xyz {
                type ipv4_addr
                size 65535
                flags dynamic,timeout
                timeout 1h
        }

        chain y {
                type filter hook output priority filter; policy accept;
                update @xyz { ip daddr counter } counter
        }
 }

Similar example, that creates a mapping better IP address and mark,
where the mark is assigned using an incremental sequence generator from
0 to 1 inclusive.

 table ip x {
        map xyz {
                type ipv4_addr : mark
                size 65535
                flags dynamic,timeout
                timeout 1h
        }

        chain y {
                type filter hook input priority filter; policy accept;
                update @xyz { ip saddr counter : numgen inc mod 2 }
        }
 }

Supported stateful statements are: limit, quota, counter and connlimit.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/statement.h       |  2 ++
 src/evaluate.c            | 14 ++++++++++++++
 src/expression.c          |  7 +++----
 src/netlink_delinearize.c | 26 +++++++++++++++-----------
 src/netlink_linearize.c   |  8 ++++++++
 src/parser_bison.y        | 32 ++++++++++++++++++++++++++------
 src/rule.c                |  7 ++++---
 src/statement.c           | 12 ++++++++++++
 8 files changed, 84 insertions(+), 24 deletions(-)
diff mbox series

Patch

diff --git a/include/statement.h b/include/statement.h
index 6c583a918eb9..48ba6673553b 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -184,6 +184,7 @@  uint32_t fwd_stmt_type(const char *type);
 struct set_stmt {
 	struct expr		*set;
 	struct expr		*key;
+	struct stmt		*stmt;
 	enum nft_dynset_ops	op;
 };
 
@@ -195,6 +196,7 @@  struct map_stmt {
 	struct expr		*set;
 	struct expr		*key;
 	struct expr		*data;
+	struct stmt		*stmt;
 	enum nft_dynset_ops	op;
 };
 
diff --git a/src/evaluate.c b/src/evaluate.c
index 9bc67d8f71f1..647e16069ba4 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2708,6 +2708,13 @@  static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
 	if (stmt->set.key->comment != NULL)
 		return expr_error(ctx->msgs, stmt->set.key,
 				  "Key expression comments are not supported");
+	if (stmt->set.stmt) {
+		if (stmt_evaluate(ctx, stmt->set.stmt) < 0)
+			return -1;
+		if (!(stmt->set.stmt->flags & STMT_F_STATEFUL))
+			return stmt_binary_error(ctx, stmt->set.stmt, stmt,
+						 "meter statement must be stateful");
+	}
 
 	return 0;
 }
@@ -2739,6 +2746,13 @@  static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
 	if (stmt->map.data->comment != NULL)
 		return expr_error(ctx->msgs, stmt->map.data,
 				  "Data expression comments are not supported");
+	if (stmt->map.stmt) {
+		if (stmt_evaluate(ctx, stmt->map.stmt) < 0)
+			return -1;
+		if (!(stmt->map.stmt->flags & STMT_F_STATEFUL))
+			return stmt_binary_error(ctx, stmt->map.stmt, stmt,
+						 "meter statement must be stateful");
+	}
 
 	return 0;
 }
diff --git a/src/expression.c b/src/expression.c
index bea0f4c8d9bc..0bd5112287e7 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -1045,13 +1045,12 @@  static void set_elem_expr_print(const struct expr *expr,
 		nft_print(octx, " expires ");
 		time_print(expr->expiration, octx);
 	}
-	if (expr->comment)
-		nft_print(octx, " comment \"%s\"", expr->comment);
-
 	if (expr->stmt) {
-		nft_print(octx, " : ");
+		nft_print(octx, " ");
 		stmt_print(expr->stmt, octx);
 	}
+	if (expr->comment)
+		nft_print(octx, " comment \"%s\"", expr->comment);
 }
 
 static void set_elem_expr_destroy(struct expr *expr)
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 898c737f9b28..6c5188cd9b5f 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1312,23 +1312,27 @@  static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
 		expr_data = netlink_get_register(ctx, loc, sreg_data);
 	}
 
-	if (dstmt != NULL) {
-		stmt = meter_stmt_alloc(loc);
-		stmt->meter.set  = set_ref_expr_alloc(loc, set);
-		stmt->meter.key  = expr;
-		stmt->meter.stmt = dstmt;
-		stmt->meter.size = set->desc.size;
-	} else if (expr_data != NULL) {
+	if (expr_data != NULL) {
 		stmt = map_stmt_alloc(loc);
 		stmt->map.set	= set_ref_expr_alloc(loc, set);
 		stmt->map.key	= expr;
 		stmt->map.data	= expr_data;
+		stmt->map.stmt	= dstmt;
 		stmt->map.op	= nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
 	} else {
-		stmt = set_stmt_alloc(loc);
-		stmt->set.set   = set_ref_expr_alloc(loc, set);
-		stmt->set.op    = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
-		stmt->set.key   = expr;
+		if (dstmt != NULL && set->flags & NFT_SET_ANONYMOUS) {
+			stmt = meter_stmt_alloc(loc);
+			stmt->meter.set  = set_ref_expr_alloc(loc, set);
+			stmt->meter.key  = expr;
+			stmt->meter.stmt = dstmt;
+			stmt->meter.size = set->desc.size;
+		} else {
+			stmt = set_stmt_alloc(loc);
+			stmt->set.set   = set_ref_expr_alloc(loc, set);
+			stmt->set.op    = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP);
+			stmt->set.key   = expr;
+			stmt->set.stmt	= dstmt;
+		}
 	}
 
 	ctx->stmt = stmt;
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 821fcd0a6377..0bd946a1cf80 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1269,6 +1269,10 @@  static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
 	nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
 	nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
 	nftnl_rule_add_expr(ctx->nlr, nle);
+
+	if (stmt->set.stmt)
+		nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
+			       netlink_gen_stmt_stateful(ctx, stmt->set.stmt), 0);
 }
 
 static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
@@ -1296,6 +1300,10 @@  static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
 	nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
 	nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
 
+	if (stmt->map.stmt)
+		nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
+			       netlink_gen_stmt_stateful(ctx, stmt->map.stmt), 0);
+
 	nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 199ef13d8c1d..cc114717f579 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -561,8 +561,8 @@  int nft_lex(void *, void *, void *);
 %destructor { stmt_list_free($$); xfree($$); } stmt_list
 %type <stmt>			stmt match_stmt verdict_stmt
 %destructor { stmt_free($$); }	stmt match_stmt verdict_stmt
-%type <stmt>			counter_stmt counter_stmt_alloc
-%destructor { stmt_free($$); }	counter_stmt counter_stmt_alloc
+%type <stmt>			counter_stmt counter_stmt_alloc stateful_stmt
+%destructor { stmt_free($$); }	counter_stmt counter_stmt_alloc stateful_stmt
 %type <stmt>			payload_stmt
 %destructor { stmt_free($$); }	payload_stmt
 %type <stmt>			ct_stmt
@@ -2112,16 +2112,19 @@  stmt_list		:	stmt
 			}
 			;
 
+stateful_stmt		:	counter_stmt
+			|	limit_stmt
+			|	quota_stmt
+			|	connlimit_stmt
+			;
+
 stmt			:	verdict_stmt
 			|	match_stmt
 			|	meter_stmt
-			|	connlimit_stmt
-			|	counter_stmt
 			|	payload_stmt
+			|	stateful_stmt
 			|	meta_stmt
 			|	log_stmt
-			|	limit_stmt
-			|	quota_stmt
 			|	reject_stmt
 			|	nat_stmt
 			|	tproxy_stmt
@@ -2862,6 +2865,14 @@  set_stmt		:	SET	set_stmt_op	set_elem_expr_stmt	symbol_expr
 				$$->set.key = $4;
 				$$->set.set = $2;
 			}
+			|	set_stmt_op	symbol_expr '{'	set_elem_expr_stmt	stateful_stmt	'}'
+			{
+				$$ = set_stmt_alloc(&@$);
+				$$->set.op  = $1;
+				$$->set.key = $4;
+				$$->set.set = $2;
+				$$->set.stmt = $5;
+			}
 			;
 
 set_stmt_op		:	ADD	{ $$ = NFT_DYNSET_OP_ADD; }
@@ -2876,6 +2887,15 @@  map_stmt		:	set_stmt_op	symbol_expr '{'	set_elem_expr_stmt	COLON	set_elem_expr_s
 				$$->map.data = $6;
 				$$->map.set = $2;
 			}
+			|	set_stmt_op	symbol_expr '{'	set_elem_expr_stmt	stateful_stmt COLON	set_elem_expr_stmt	'}'
+			{
+				$$ = map_stmt_alloc(&@$);
+				$$->map.op  = $1;
+				$$->map.key = $4;
+				$$->map.data = $7;
+				$$->map.stmt = $5;
+				$$->map.set = $2;
+			}
 			;
 
 meter_stmt		:	flow_stmt_legacy_alloc		flow_stmt_opts	'{' meter_key_expr stmt '}'
diff --git a/src/rule.c b/src/rule.c
index fcfcf60cbc7c..8288483a6ad0 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -335,10 +335,11 @@  static void set_print_declaration(const struct set *set,
 	const char *type;
 	uint32_t flags;
 
-	if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
-		type = "map";
-	else if (set->flags & NFT_SET_EVAL)
+	if ((set->flags & (NFT_SET_EVAL | NFT_SET_ANONYMOUS)) ==
+				(NFT_SET_EVAL | NFT_SET_ANONYMOUS))
 		type = "meter";
+	else if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+		type = "map";
 	else
 		type = "set";
 
diff --git a/src/statement.c b/src/statement.c
index 039ca943e92c..98e568448486 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -630,6 +630,12 @@  static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 	expr_print(stmt->set.set, octx);
 	nft_print(octx, " { ");
 	expr_print(stmt->set.key, octx);
+	if (stmt->set.stmt) {
+		nft_print(octx, " ");
+		octx->stateless++;
+		stmt_print(stmt->set.stmt, octx);
+		octx->stateless--;
+	}
 	nft_print(octx, " }");
 }
 
@@ -658,6 +664,12 @@  static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 	expr_print(stmt->map.set, octx);
 	nft_print(octx, " { ");
 	expr_print(stmt->map.key, octx);
+	if (stmt->map.stmt) {
+		nft_print(octx, " ");
+		octx->stateless++;
+		stmt_print(stmt->map.stmt, octx);
+		octx->stateless--;
+	}
 	nft_print(octx, " : ");
 	expr_print(stmt->map.data, octx);
 	nft_print(octx, " }");