[nft] support of dynamic map addition and update of elements
diff mbox series

Message ID 20180315082321.ivg3df2hodchhccz@nevthink
State Accepted
Delegated to: Pablo Neira
Headers show
Series
  • [nft] support of dynamic map addition and update of elements
Related show

Commit Message

Laura Garcia March 15, 2018, 8:23 a.m. UTC
The support of dynamic adds and updates are only available for sets
and meters. This patch gives such abilities to maps as well.

This patch is useful in cases where dynamic population of maps are
required, for example, to maintain a persistence during some period
of time.

Example:

table ip nftlb {
    map persistencia {
        type ipv4_addr : mark
        timeout 1h
        elements = { 192.168.1.132 expires 59m55s : 0x00000064,
                     192.168.56.101 expires 59m24s : 0x00000065 }
    }

    chain pre {
        type nat hook prerouting priority 0; policy accept;
        map update \
            { @nh,96,32 : numgen inc mod 2 offset 100 } @persistencia
    }
}

An example of the netlink generated sequence:

 nft --debug=netlink add rule ip nftlb pre map add \
    { ip saddr : numgen inc mod 2 offset 100 } @persistencia
ip nftlb pre
  [ payload load 4b @ network header + 12 => reg 1 ]
  [ numgen reg 2 = inc mod 2 offset 100 ]
  [ dynset add reg_key 1 set persistencia sreg_data 2 ]

Signed-off-by: Laura Garcia Liebana <nevola@gmail.com>
---
 include/statement.h       | 11 +++++++++++
 src/evaluate.c            | 10 ++++++++++
 src/netlink_delinearize.c | 12 ++++++++++--
 src/netlink_linearize.c   | 29 +++++++++++++++++++++++++++++
 src/parser_bison.y        | 12 ++++++++++++
 src/statement.c           | 28 ++++++++++++++++++++++++++++
 6 files changed, 100 insertions(+), 2 deletions(-)

Comments

Pablo Neira Ayuso March 15, 2018, 6:24 p.m. UTC | #1
On Thu, Mar 15, 2018 at 09:23:21AM +0100, Laura Garcia Liebana wrote:
> The support of dynamic adds and updates are only available for sets
> and meters. This patch gives such abilities to maps as well.
> 
> This patch is useful in cases where dynamic population of maps are
> required, for example, to maintain a persistence during some period
> of time.
> 
> Example:
> 
> table ip nftlb {
>     map persistencia {
>         type ipv4_addr : mark
>         timeout 1h
>         elements = { 192.168.1.132 expires 59m55s : 0x00000064,
>                      192.168.56.101 expires 59m24s : 0x00000065 }
>     }
> 
>     chain pre {
>         type nat hook prerouting priority 0; policy accept;
>         map update \
>             { @nh,96,32 : numgen inc mod 2 offset 100 } @persistencia

Not related to this patch, but this reminds me I think it would be
good if we place curly braces around the key in set updates:

         set update { ip saddr } @x

>     }
> }
> 
> An example of the netlink generated sequence:
> 
>  nft --debug=netlink add rule ip nftlb pre map add \
>     { ip saddr : numgen inc mod 2 offset 100 } @persistencia
> ip nftlb pre
>   [ payload load 4b @ network header + 12 => reg 1 ]
>   [ numgen reg 2 = inc mod 2 offset 100 ]
>   [ dynset add reg_key 1 set persistencia sreg_data 2 ]

Applied, thanks Laura.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pablo Neira Ayuso March 15, 2018, 6:35 p.m. UTC | #2
On Thu, Mar 15, 2018 at 07:24:01PM +0100, Pablo Neira Ayuso wrote:
> On Thu, Mar 15, 2018 at 09:23:21AM +0100, Laura Garcia Liebana wrote:
> > The support of dynamic adds and updates are only available for sets
> > and meters. This patch gives such abilities to maps as well.
> > 
> > This patch is useful in cases where dynamic population of maps are
> > required, for example, to maintain a persistence during some period
> > of time.
> > 
> > Example:
> > 
> > table ip nftlb {
> >     map persistencia {
> >         type ipv4_addr : mark
> >         timeout 1h
> >         elements = { 192.168.1.132 expires 59m55s : 0x00000064,
> >                      192.168.56.101 expires 59m24s : 0x00000065 }
> >     }
> > 
> >     chain pre {
> >         type nat hook prerouting priority 0; policy accept;
> >         map update \
> >             { @nh,96,32 : numgen inc mod 2 offset 100 } @persistencia
> 
> Not related to this patch, but this reminds me I think it would be
> good if we place curly braces around the key in set updates:
> 
>          set update { ip saddr } @x
> 
> >     }
> > }
> > 
> > An example of the netlink generated sequence:
> > 
> >  nft --debug=netlink add rule ip nftlb pre map add \
> >     { ip saddr : numgen inc mod 2 offset 100 } @persistencia
> > ip nftlb pre
> >   [ payload load 4b @ network header + 12 => reg 1 ]
> >   [ numgen reg 2 = inc mod 2 offset 100 ]
> >   [ dynset add reg_key 1 set persistencia sreg_data 2 ]

There's a shift/reduce conflict in bison with this syntax, problem is
'map_expr'.

We could use this syntax instead?

        update map { @nh,96,32 : numgen inc mod 2 offset 100 } @persistencia

(see patch below)

We should get this in sync with existing dynset syntax:

        update set { 192.168.2.1 } @x

BTW, Florian wanted to have upper ceiling for all sets/maps that are
populated from the packet path, so we may need a follow up patch on top
of this one to make sure map definition includes 'size X' option,
otherwise, display an error.

diff --git a/src/parser_bison.y b/src/parser_bison.y
index 39b99ae8c288..6fba7e59555c 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2719,10 +2719,10 @@ set_stmt_op             :       ADD     { $$ = NFT_DYNSET_OP_ADD; }
                        |       UPDATE  { $$ = NFT_DYNSET_OP_UPDATE; }
                        ;
 
-map_stmt               :       MAP     set_stmt_op     '{'     set_elem_expr_stmt      COLON   set_elem_expr_stmt      '}'     symbol_expr
+map_stmt               :       set_stmt_op     MAP '{' set_elem_expr_stmt      COLON   set_elem_expr_stmt      '}'     symbol_expr
                        {
                                $$ = map_stmt_alloc(&@$);
-                               $$->map.op  = $2;
+                               $$->map.op  = $1;
                                $$->map.map = map_expr_alloc(&@$, $4, $6);
                                $$->map.set = $8;
                        }
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox series

diff --git a/include/statement.h b/include/statement.h
index 27c7356..bb4af9d 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -171,6 +171,14 @@  struct set_stmt {
 
 extern struct stmt *set_stmt_alloc(const struct location *loc);
 
+struct map_stmt {
+	struct expr		*set;
+	struct expr		*map;
+	enum nft_dynset_ops	op;
+};
+
+extern struct stmt *map_stmt_alloc(const struct location *loc);
+
 struct meter_stmt {
 	struct expr		*set;
 	struct expr		*key;
@@ -238,6 +246,7 @@  extern struct stmt *xt_stmt_alloc(const struct location *loc);
  * @STMT_OBJREF:	stateful object reference statement
  * @STMT_EXTHDR:	extension header statement
  * @STMT_FLOW_OFFLOAD:	flow offload statement
+ * @STMT_MAP:		map statement
  */
 enum stmt_types {
 	STMT_INVALID,
@@ -264,6 +273,7 @@  enum stmt_types {
 	STMT_OBJREF,
 	STMT_EXTHDR,
 	STMT_FLOW_OFFLOAD,
+	STMT_MAP,
 };
 
 /**
@@ -325,6 +335,7 @@  struct stmt {
 		struct xt_stmt		xt;
 		struct objref_stmt	objref;
 		struct flow_stmt	flow;
+		struct map_stmt		map;
 	};
 };
 
diff --git a/src/evaluate.c b/src/evaluate.c
index a2c1c72..b71b67b 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2681,6 +2681,14 @@  static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
 	return 0;
 }
 
+static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
+{
+	if (expr_evaluate(ctx, &stmt->map.map->map) < 0)
+		return -1;
+
+	return 0;
+}
+
 static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
 {
 	struct expr *map = stmt->objref.expr;
@@ -2822,6 +2830,8 @@  int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 		return stmt_evaluate_set(ctx, stmt);
 	case STMT_OBJREF:
 		return stmt_evaluate_objref(ctx, stmt);
+	case STMT_MAP:
+		return stmt_evaluate_map(ctx, stmt);
 	default:
 		BUG("unknown statement type %s\n", stmt->ops->name);
 	}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index d65aacf..bed491a 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1151,10 +1151,10 @@  static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
 				 const struct nftnl_expr *nle)
 {
 	const struct nftnl_expr *dnle;
-	struct expr *expr;
+	struct expr *expr, *expr_data;
 	struct stmt *stmt, *dstmt;
 	struct set *set;
-	enum nft_registers sreg;
+	enum nft_registers sreg, sreg_data;
 	const char *name;
 
 	name = nftnl_expr_get_str(nle, NFTNL_EXPR_DYNSET_SET_NAME);
@@ -1191,11 +1191,19 @@  static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
 		dstmt = ctx->stmt;
 	}
 
+	sreg_data = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA);
+	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;
+	} else if (expr_data != NULL) {
+		stmt = map_stmt_alloc(loc);
+		stmt->map.set	= set_ref_expr_alloc(loc, set);
+		stmt->map.map	= map_expr_alloc(loc, expr, expr_data);
+		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);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 5edb2d3..be1c750 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1234,6 +1234,33 @@  static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
 	nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
+static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
+				 const struct stmt *stmt)
+{
+	struct nftnl_expr *nle;
+	enum nft_registers sreg_key;
+	enum nft_registers sreg_data;
+
+	sreg_key = get_register(ctx, stmt->map.map->map->key);
+	netlink_gen_expr(ctx, stmt->map.map->map->key, sreg_key);
+
+	sreg_data = get_register(ctx, stmt->map.map->mappings);
+	netlink_gen_expr(ctx, stmt->map.map->mappings, sreg_data);
+
+	release_register(ctx, stmt->map.map->map->key);
+	release_register(ctx, stmt->map.map->mappings);
+
+	nle = alloc_nft_expr("dynset");
+	netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY, sreg_key);
+	netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA, sreg_data);
+
+	nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->map.op);
+	nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, stmt->map.set->identifier);
+	nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, stmt->map.set->set->handle.set_id);
+
+	nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
 static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
 				   const struct stmt *stmt)
 {
@@ -1315,6 +1342,8 @@  static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
 		return netlink_gen_flow_offload_stmt(ctx, stmt);
 	case STMT_OBJREF:
 		return netlink_gen_objref_stmt(ctx, stmt);
+	case STMT_MAP:
+		return netlink_gen_map_stmt(ctx, stmt);
 	default:
 		BUG("unknown statement type %s\n", stmt->ops->name);
 	}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 5f84d79..39b99ae 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -577,6 +577,8 @@  int nft_lex(void *, void *, void *);
 %type <stmt>			set_stmt
 %destructor { stmt_free($$); }	set_stmt
 %type <val>			set_stmt_op
+%type <stmt>			map_stmt
+%destructor { stmt_free($$); }	map_stmt
 %type <stmt>			meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
 %destructor { stmt_free($$); }	meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
 
@@ -2046,6 +2048,7 @@  stmt			:	verdict_stmt
 			|	dup_stmt
 			|	fwd_stmt
 			|	set_stmt
+			|	map_stmt
 			;
 
 verdict_stmt		:	verdict_expr
@@ -2716,6 +2719,15 @@  set_stmt_op		:	ADD	{ $$ = NFT_DYNSET_OP_ADD; }
 			|	UPDATE	{ $$ = NFT_DYNSET_OP_UPDATE; }
 			;
 
+map_stmt		:	MAP	set_stmt_op	'{'	set_elem_expr_stmt	COLON	set_elem_expr_stmt	'}'	symbol_expr
+			{
+				$$ = map_stmt_alloc(&@$);
+				$$->map.op  = $2;
+				$$->map.map = map_expr_alloc(&@$, $4, $6);
+				$$->map.set = $8;
+			}
+			;
+
 meter_stmt		:	flow_stmt_legacy_alloc		flow_stmt_opts	'{' meter_key_expr stmt '}'
 			{
 				$1->meter.key  = $4;
diff --git a/src/statement.c b/src/statement.c
index 701337d..51b6a89 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -639,6 +639,34 @@  struct stmt *set_stmt_alloc(const struct location *loc)
 	return stmt_alloc(loc, &set_stmt_ops);
 }
 
+static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+	nft_print(octx, "map %s { ", set_stmt_op_names[stmt->map.op]);
+	expr_print(stmt->map.map->map->key, octx);
+	nft_print(octx, " : ");
+	expr_print(stmt->map.map->mappings, octx);
+	nft_print(octx, " } ");
+	expr_print(stmt->map.set, octx);
+}
+
+static void map_stmt_destroy(struct stmt *stmt)
+{
+	expr_free(stmt->map.map);
+	expr_free(stmt->map.set);
+}
+
+static const struct stmt_ops map_stmt_ops = {
+	.type		= STMT_MAP,
+	.name		= "map",
+	.print		= map_stmt_print,
+	.destroy	= map_stmt_destroy,
+};
+
+struct stmt *map_stmt_alloc(const struct location *loc)
+{
+	return stmt_alloc(loc, &map_stmt_ops);
+}
+
 static void dup_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 {
 	nft_print(octx, "dup");