diff mbox series

[nftables] evaluate: add support for variables in map expressions

Message ID 20240324145908.2643098-1-jeremy@azazel.net
State New
Headers show
Series [nftables] evaluate: add support for variables in map expressions | expand

Commit Message

Jeremy Sowden March 24, 2024, 2:59 p.m. UTC
It is possible to use a variable to initialize a map, which is then used in a
map statement:

  define m = { ::1234 : 5678 }

  table ip6 nat {
    map m {
      typeof ip6 daddr : tcp dport;
      elements = $m
    }
    chain prerouting {
      ip6 nexthdr tcp redirect to ip6 daddr map @m
    }
  }

However, if one tries to use the variable directly in the statement:

  define m = { ::1234 : 5678 }

  table ip6 nat {
    chain prerouting {
      ip6 nexthdr tcp redirect to ip6 daddr map $m
    }
  }

nft rejects it:

  /space/azazel/tmp/ruleset.1067161.nft:5:47-48: Error: invalid mapping expression variable
      ip6 nexthdr tcp redirect to ip6 daddr map $m
                                  ~~~~~~~~~     ^^

Extend `expr_evaluate_map` to allow it.

Add a test-case.

Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1067161
Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
---
 src/evaluate.c                                |  1 +
 .../shell/testcases/maps/anonymous_snat_map_1 | 16 +++++
 .../maps/dumps/anonymous_snat_map_1.json-nft  | 58 +++++++++++++++++++
 .../maps/dumps/anonymous_snat_map_1.nft       |  5 ++
 4 files changed, 80 insertions(+)
 create mode 100755 tests/shell/testcases/maps/anonymous_snat_map_1
 create mode 100644 tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
 create mode 100644 tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft

Comments

Pablo Neira Ayuso April 2, 2024, 10:42 p.m. UTC | #1
On Sun, Mar 24, 2024 at 02:59:07PM +0000, Jeremy Sowden wrote:
> It is possible to use a variable to initialize a map, which is then used in a
> map statement:
> 
>   define m = { ::1234 : 5678 }
> 
>   table ip6 nat {
>     map m {
>       typeof ip6 daddr : tcp dport;
>       elements = $m
>     }
>     chain prerouting {
>       ip6 nexthdr tcp redirect to ip6 daddr map @m
>     }
>   }
> 
> However, if one tries to use the variable directly in the statement:
> 
>   define m = { ::1234 : 5678 }
> 
>   table ip6 nat {
>     chain prerouting {
>       ip6 nexthdr tcp redirect to ip6 daddr map $m
>     }
>   }
> 
> nft rejects it:
> 
>   /space/azazel/tmp/ruleset.1067161.nft:5:47-48: Error: invalid mapping expression variable
>       ip6 nexthdr tcp redirect to ip6 daddr map $m
>                                   ~~~~~~~~~     ^^
> 
> Extend `expr_evaluate_map` to allow it.
> 
> Add a test-case.

Thanks for your patch.

> Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1067161
> Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> ---
>  src/evaluate.c                                |  1 +
>  .../shell/testcases/maps/anonymous_snat_map_1 | 16 +++++
>  .../maps/dumps/anonymous_snat_map_1.json-nft  | 58 +++++++++++++++++++
>  .../maps/dumps/anonymous_snat_map_1.nft       |  5 ++
>  4 files changed, 80 insertions(+)
>  create mode 100755 tests/shell/testcases/maps/anonymous_snat_map_1
>  create mode 100644 tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
>  create mode 100644 tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft
> 
> diff --git a/src/evaluate.c b/src/evaluate.c
> index 1682ba58989e..d49213f8d6bd 100644
> --- a/src/evaluate.c
> +++ b/src/evaluate.c
> @@ -2061,6 +2061,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)

expr_evaluate_objmap() also needs a similar fix.

>  	mappings->set_flags |= NFT_SET_MAP;
>  
>  	switch (map->mappings->etype) {
> +	case EXPR_VARIABLE:
>  	case EXPR_SET:
>  		if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
>  			key = expr_clone(ctx->ectx.key);
Jeremy Sowden April 3, 2024, 7:38 a.m. UTC | #2
On 2024-04-03, at 00:42:59 +0200, Pablo Neira Ayuso wrote:
> On Sun, Mar 24, 2024 at 02:59:07PM +0000, Jeremy Sowden wrote:
> > It is possible to use a variable to initialize a map, which is then used in a
> > map statement:
> > 
> >   define m = { ::1234 : 5678 }
> > 
> >   table ip6 nat {
> >     map m {
> >       typeof ip6 daddr : tcp dport;
> >       elements = $m
> >     }
> >     chain prerouting {
> >       ip6 nexthdr tcp redirect to ip6 daddr map @m
> >     }
> >   }
> > 
> > However, if one tries to use the variable directly in the statement:
> > 
> >   define m = { ::1234 : 5678 }
> > 
> >   table ip6 nat {
> >     chain prerouting {
> >       ip6 nexthdr tcp redirect to ip6 daddr map $m
> >     }
> >   }
> > 
> > nft rejects it:
> > 
> >   /space/azazel/tmp/ruleset.1067161.nft:5:47-48: Error: invalid mapping expression variable
> >       ip6 nexthdr tcp redirect to ip6 daddr map $m
> >                                   ~~~~~~~~~     ^^
> > 
> > Extend `expr_evaluate_map` to allow it.
> > 
> > Add a test-case.
> 
> Thanks for your patch.
> 
> > Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1067161
> > Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> > ---
> >  src/evaluate.c                                |  1 +
> >  .../shell/testcases/maps/anonymous_snat_map_1 | 16 +++++
> >  .../maps/dumps/anonymous_snat_map_1.json-nft  | 58 +++++++++++++++++++
> >  .../maps/dumps/anonymous_snat_map_1.nft       |  5 ++
> >  4 files changed, 80 insertions(+)
> >  create mode 100755 tests/shell/testcases/maps/anonymous_snat_map_1
> >  create mode 100644 tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
> >  create mode 100644 tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft
> > 
> > diff --git a/src/evaluate.c b/src/evaluate.c
> > index 1682ba58989e..d49213f8d6bd 100644
> > --- a/src/evaluate.c
> > +++ b/src/evaluate.c
> > @@ -2061,6 +2061,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
> 
> expr_evaluate_objmap() also needs a similar fix.

Cool.  Will update and resend.

J.

> >  	mappings->set_flags |= NFT_SET_MAP;
> >  
> >  	switch (map->mappings->etype) {
> > +	case EXPR_VARIABLE:
> >  	case EXPR_SET:
> >  		if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
> >  			key = expr_clone(ctx->ectx.key);
diff mbox series

Patch

diff --git a/src/evaluate.c b/src/evaluate.c
index 1682ba58989e..d49213f8d6bd 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2061,6 +2061,7 @@  static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
 	mappings->set_flags |= NFT_SET_MAP;
 
 	switch (map->mappings->etype) {
+	case EXPR_VARIABLE:
 	case EXPR_SET:
 		if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
 			key = expr_clone(ctx->ectx.key);
diff --git a/tests/shell/testcases/maps/anonymous_snat_map_1 b/tests/shell/testcases/maps/anonymous_snat_map_1
new file mode 100755
index 000000000000..031de0c1a83f
--- /dev/null
+++ b/tests/shell/testcases/maps/anonymous_snat_map_1
@@ -0,0 +1,16 @@ 
+#!/bin/bash
+
+# Variable containing anonymous map can be added to a snat rule
+
+set -e
+
+RULESET='
+define m = {1.1.1.1 : 2.2.2.2}
+table nat {
+  chain postrouting {
+    snat ip saddr map $m
+  }
+}
+'
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
new file mode 100644
index 000000000000..f4c55706787c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
@@ -0,0 +1,58 @@ 
+{
+  "nftables": [
+    {
+      "metainfo": {
+        "version": "VERSION",
+        "release_name": "RELEASE_NAME",
+        "json_schema_version": 1
+      }
+    },
+    {
+      "table": {
+        "family": "ip",
+        "name": "nat",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "ip",
+        "table": "nat",
+        "name": "postrouting",
+        "handle": 0
+      }
+    },
+    {
+      "rule": {
+        "family": "ip",
+        "table": "nat",
+        "chain": "postrouting",
+        "handle": 0,
+        "expr": [
+          {
+            "snat": {
+              "addr": {
+                "map": {
+                  "key": {
+                    "payload": {
+                      "protocol": "ip",
+                      "field": "saddr"
+                    }
+                  },
+                  "data": {
+                    "set": [
+                      [
+                        "1.1.1.1",
+                        "2.2.2.2"
+                      ]
+                    ]
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  ]
+}
diff --git a/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft
new file mode 100644
index 000000000000..5009560c9d69
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft
@@ -0,0 +1,5 @@ 
+table ip nat {
+	chain postrouting {
+		snat to ip saddr map { 1.1.1.1 : 2.2.2.2 }
+	}
+}