Re: [PATCH nft] evaluate: allow to re-use existing metered set

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Florian,

LGTM, just a comestic comment, see below

On Wed, Jan 22, 2025 at 10:18:04AM +0100, Florian Westphal wrote:
> Blamed commit translates old meter syntax (which used to allocate an
> anonymous set) to dynamic sets.
> 
> A side effect of this is that re-adding a meter rule after chain was
> flushed results in an error, unlike anonymous sets named sets are not
> impacted by the flush.
> 
> Refine this: if a set of the same name exists and is compatible, then
> re-use it instead of returning an error.
> 
> Also pick up the reproducer kindly provided by the reporter and place it
> in the shell test directory.
> 
> Fixes: b8f8ddfff733 ("evaluate: translate meter into dynamic set")
> Reported-by: Yi Chen <yiche@xxxxxxxxxx>
> Signed-off-by: Florian Westphal <fw@xxxxxxxxx>
> ---
>  src/evaluate.c                                |  46 ++++++--
>  .../sets/dumps/meter_set_reuse.json-nft       | 105 ++++++++++++++++++
>  .../testcases/sets/dumps/meter_set_reuse.nft  |  11 ++
>  tests/shell/testcases/sets/meter_set_reuse    |  20 ++++
>  4 files changed, 173 insertions(+), 9 deletions(-)
>  create mode 100644 tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft
>  create mode 100644 tests/shell/testcases/sets/dumps/meter_set_reuse.nft
>  create mode 100755 tests/shell/testcases/sets/meter_set_reuse
> 
> diff --git a/src/evaluate.c b/src/evaluate.c
> index 919ef90707d9..50443df14df4 100644
> --- a/src/evaluate.c
> +++ b/src/evaluate.c
> @@ -3356,7 +3356,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
>  
>  static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
>  {
> -	struct expr *key, *set, *setref;
> +	struct expr *key, *setref;
>  	struct set *existing_set;
>  	struct table *table;
>  
> @@ -3367,7 +3367,9 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
>  		return table_not_found(ctx);
>  
>  	existing_set = set_cache_find(table, stmt->meter.name);
> -	if (existing_set)
> +	if (existing_set &&
> +	    (!set_is_meter_compat(existing_set->flags) ||
> +	     set_is_map(existing_set->flags)))
>  		return cmd_error(ctx, &stmt->location,
>  				 "%s; meter '%s' overlaps an existing %s '%s' in family %s",
>  				 strerror(EEXIST),
> @@ -3388,17 +3390,43 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
>  
>  	/* Declare an empty set */
>  	key = stmt->meter.key;
> -	set = set_expr_alloc(&key->location, NULL);
> -	set->set_flags |= NFT_SET_EVAL;
> -	if (key->timeout)
> -		set->set_flags |= NFT_SET_TIMEOUT;
> +	if (existing_set) {
> +		if ((existing_set->flags & NFT_SET_TIMEOUT) && !key->timeout)
> +			return expr_error(ctx->msgs, stmt->meter.key,
> +					  "existing set '%s' has timeout flag",
> +					  stmt->meter.name);
> +
> +		if ((existing_set->flags & NFT_SET_TIMEOUT) == 0 && key->timeout)
> +			return expr_error(ctx->msgs, stmt->meter.key,
> +					  "existing set '%s' lacks timeout flag",
> +					  stmt->meter.name);
> +
> +		if (stmt->meter.size > 0 && existing_set->desc.size != stmt->meter.size)
> +			return expr_error(ctx->msgs, stmt->meter.key,
> +					  "existing set '%s' has size %u, meter has %u",
> +					  stmt->meter.name, existing_set->desc.size,
> +					  stmt->meter.size);
> +	}
        ^
remove this..

> +
> +	if (existing_set) {
        ^^^^^^^^^^^^^^^^^^^
and remove this too, then:

> +		setref = set_ref_expr_alloc(&key->location, existing_set);

just stays on the existing branch.

no need to send v2, just amend and push if you are happy with this.

> +	} else {
> +		struct expr *set;
> +
> +		set = set_expr_alloc(&key->location, existing_set);
> +		if (key->timeout)
> +			set->set_flags |= NFT_SET_TIMEOUT;
> +
> +		set->set_flags |= NFT_SET_EVAL;
> +		setref = implicit_set_declaration(ctx, stmt->meter.name,
> +						  expr_get(key), NULL, set, 0);
> +		if (setref)
> +			setref->set->desc.size = stmt->meter.size;
> +	}
>  
> -	setref = implicit_set_declaration(ctx, stmt->meter.name,
> -					  expr_get(key), NULL, set, 0);
>  	if (!setref)
>  		return -1;
>  
> -	setref->set->desc.size = stmt->meter.size;
>  	stmt->meter.set = setref;
>  
>  	if (stmt_evaluate(ctx, stmt->meter.stmt) < 0)
> diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft
> new file mode 100644
> index 000000000000..ab4ac06184d0
> --- /dev/null
> +++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft
> @@ -0,0 +1,105 @@
> +{
> +  "nftables": [
> +    {
> +      "metainfo": {
> +        "version": "VERSION",
> +        "release_name": "RELEASE_NAME",
> +        "json_schema_version": 1
> +      }
> +    },
> +    {
> +      "table": {
> +        "family": "ip",
> +        "name": "filter",
> +        "handle": 0
> +      }
> +    },
> +    {
> +      "chain": {
> +        "family": "ip",
> +        "table": "filter",
> +        "name": "input",
> +        "handle": 0
> +      }
> +    },
> +    {
> +      "set": {
> +        "family": "ip",
> +        "name": "http1",
> +        "table": "filter",
> +        "type": [
> +          "inet_service",
> +          "ipv4_addr"
> +        ],
> +        "handle": 0,
> +        "size": 65535,
> +        "flags": [
> +          "dynamic"
> +        ]
> +      }
> +    },
> +    {
> +      "rule": {
> +        "family": "ip",
> +        "table": "filter",
> +        "chain": "input",
> +        "handle": 0,
> +        "expr": [
> +          {
> +            "match": {
> +              "op": "==",
> +              "left": {
> +                "payload": {
> +                  "protocol": "tcp",
> +                  "field": "dport"
> +                }
> +              },
> +              "right": 80
> +            }
> +          },
> +          {
> +            "set": {
> +              "op": "add",
> +              "elem": {
> +                "concat": [
> +                  {
> +                    "payload": {
> +                      "protocol": "tcp",
> +                      "field": "dport"
> +                    }
> +                  },
> +                  {
> +                    "payload": {
> +                      "protocol": "ip",
> +                      "field": "saddr"
> +                    }
> +                  }
> +                ]
> +              },
> +              "set": "@http1",
> +              "stmt": [
> +                {
> +                  "limit": {
> +                    "rate": 200,
> +                    "burst": 5,
> +                    "per": "second",
> +                    "inv": true
> +                  }
> +                }
> +              ]
> +            }
> +          },
> +          {
> +            "counter": {
> +              "packets": 0,
> +              "bytes": 0
> +            }
> +          },
> +          {
> +            "drop": null
> +          }
> +        ]
> +      }
> +    }
> +  ]
> +}
> diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft
> new file mode 100644
> index 000000000000..f911acaffb85
> --- /dev/null
> +++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft
> @@ -0,0 +1,11 @@
> +table ip filter {
> +	set http1 {
> +		type inet_service . ipv4_addr
> +		size 65535
> +		flags dynamic
> +	}
> +
> +	chain input {
> +		tcp dport 80 add @http1 { tcp dport . ip saddr limit rate over 200/second burst 5 packets } counter packets 0 bytes 0 drop
> +	}
> +}
> diff --git a/tests/shell/testcases/sets/meter_set_reuse b/tests/shell/testcases/sets/meter_set_reuse
> new file mode 100755
> index 000000000000..94eccc1a7b82
> --- /dev/null
> +++ b/tests/shell/testcases/sets/meter_set_reuse
> @@ -0,0 +1,20 @@
> +#!/bin/bash
> +
> +set -e
> +
> +addrule()
> +{
> +	$NFT add rule ip filter input tcp dport 80 meter http1 { tcp dport . ip saddr limit rate over 200/second } counter drop
> +}
> +
> +$NFT add table filter
> +$NFT add chain filter input
> +addrule
> +
> +$NFT list meters
> +
> +# This used to remove the anon set, but not anymore
> +$NFT flush chain filter input
> +
> +# This re-add should work.
> +addrule
> -- 
> 2.48.1
> 
> 




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux