diff mbox series

[nft] parser: extend limit statement syntax.

Message ID 20211002152230.1568537-1-jeremy@azazel.net
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series [nft] parser: extend limit statement syntax. | expand

Commit Message

Jeremy Sowden Oct. 2, 2021, 3:22 p.m. UTC
The documentation describes the syntax of limit statements thus:

  limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]
  limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]

  TIME_UNIT := second | minute | hour | day
  BYTE_UNIT := bytes | kbytes | mbytes

This implies that one may specify a limit as either of the following:

  limit rate 1048576 / second
  limit rate 1048576 mbytes / second

However, the latter currently does not parse:

  $ sudo /usr/sbin/nft add filter input limit rate 1048576 mbytes / second
  Error: wrong rate format
  add filter input limit rate 1048576 mbytes / second
                   ^^^^^^^^^^^^^^^^^^^^^^^^^

Extend the parser to support it.

Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
---

I can't help thinking that it ought to be possible to fold the two

  limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]

rules into one.  However, my attempts to get the scanner to tokenize
"bytes/second" as "bytes" "/" "second" (for example) failed.

 src/parser_bison.y           | 58 +++++++++++++++++++++++++++++++-----
 tests/py/any/limit.t         |  5 ++++
 tests/py/any/limit.t.json    | 39 ++++++++++++++++++++++++
 tests/py/any/limit.t.payload | 13 ++++++++
 4 files changed, 108 insertions(+), 7 deletions(-)

Comments

Jeremy Sowden Oct. 3, 2021, 2:58 p.m. UTC | #1
On 2021-10-02, at 16:22:30 +0100, Jeremy Sowden wrote:
> The documentation describes the syntax of limit statements thus:
>
>   limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]
>   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
>
>   TIME_UNIT := second | minute | hour | day
>   BYTE_UNIT := bytes | kbytes | mbytes
>
> This implies that one may specify a limit as either of the following:
>
>   limit rate 1048576 / second
>   limit rate 1048576 mbytes / second
>
> However, the latter currently does not parse:
>
>   $ sudo /usr/sbin/nft add filter input limit rate 1048576 mbytes / second
>   Error: wrong rate format
>   add filter input limit rate 1048576 mbytes / second
>                    ^^^^^^^^^^^^^^^^^^^^^^^^^
>
> Extend the parser to support it.
>
> Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> ---
>
> I can't help thinking that it ought to be possible to fold the two
>
>   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
>
> rules into one.  However, my attempts to get the scanner to tokenize
> "bytes/second" as "bytes" "/" "second" (for example) failed.

Having reread the Flex manual, I've changed my mind.  While it would be
possible, it would be rather fiddly and require more effort than it
would be worth.

>  src/parser_bison.y           | 58 +++++++++++++++++++++++++++++++-----
>  tests/py/any/limit.t         |  5 ++++
>  tests/py/any/limit.t.json    | 39 ++++++++++++++++++++++++
>  tests/py/any/limit.t.payload | 13 ++++++++
>  4 files changed, 108 insertions(+), 7 deletions(-)
>
> diff --git a/src/parser_bison.y b/src/parser_bison.y
> index c25af6ba114a..4a41e9e3a293 100644
> --- a/src/parser_bison.y
> +++ b/src/parser_bison.y
> @@ -689,7 +689,7 @@ int nft_lex(void *, void *, void *);
>  %type <val>			level_type log_flags log_flags_tcp log_flag_tcp
>  %type <stmt>			limit_stmt quota_stmt connlimit_stmt
>  %destructor { stmt_free($$); }	limit_stmt quota_stmt connlimit_stmt
> -%type <val>			limit_burst_pkts limit_burst_bytes limit_mode time_unit quota_mode
> +%type <val>			limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
>  %type <stmt>			reject_stmt reject_stmt_alloc
>  %destructor { stmt_free($$); }	reject_stmt reject_stmt_alloc
>  %type <stmt>			nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
> @@ -3184,6 +3184,21 @@ limit_stmt		:	LIMIT	RATE	limit_mode	NUM	SLASH	time_unit	limit_burst_pkts	close_s
>  				$$->limit.type	= NFT_LIMIT_PKT_BYTES;
>  				$$->limit.flags = $3;
>  			}
> +			|	LIMIT	RATE	limit_mode	limit_bytes SLASH time_unit	limit_burst_bytes	close_scope_limit
> +			{
> +				if ($7 == 0) {
> +					erec_queue(error(&@7, "limit burst must be > 0"),
> +						   state->msgs);
> +					YYERROR;
> +				}
> +
> +				$$ = limit_stmt_alloc(&@$);
> +				$$->limit.rate	= $4;
> +				$$->limit.unit	= $6;
> +				$$->limit.burst	= $7;
> +				$$->limit.type	= NFT_LIMIT_PKT_BYTES;
> +				$$->limit.flags = $3;
> +			}
>  			|	LIMIT	NAME	stmt_expr	close_scope_limit
>  			{
>  				$$ = objref_stmt_alloc(&@$);
> @@ -3251,19 +3266,22 @@ limit_burst_pkts	:	/* empty */			{ $$ = 5; }
>  			;
>
>  limit_burst_bytes	:	/* empty */			{ $$ = 5; }
> -			|	BURST	NUM	BYTES		{ $$ = $2; }
> -			|	BURST	NUM	STRING
> +			|	BURST	limit_bytes		{ $$ = $2; }
> +			;
> +
> +limit_bytes		:	NUM	BYTES		{ $$ = $1; }
> +			|	NUM	STRING
>  			{
>  				struct error_record *erec;
>  				uint64_t rate;
>
> -				erec = data_unit_parse(&@$, $3, &rate);
> -				xfree($3);
> +				erec = data_unit_parse(&@$, $2, &rate);
> +				xfree($2);
>  				if (erec != NULL) {
>  					erec_queue(erec, state->msgs);
>  					YYERROR;
>  				}
> -				$$ = $2 * rate;
> +				$$ = $1 * rate;
>  			}
>  			;
>
> @@ -4317,7 +4335,22 @@ set_elem_stmt		:	COUNTER	close_scope_counter
>  				$$->limit.burst = $6;
>  				$$->limit.type  = NFT_LIMIT_PKT_BYTES;
>  				$$->limit.flags = $3;
> -                        }
> +			}
> +			|	LIMIT	RATE	limit_mode	limit_bytes SLASH time_unit	limit_burst_bytes	close_scope_limit
> +			{
> +				if ($7 == 0) {
> +					erec_queue(error(&@7, "limit burst must be > 0"),
> +						   state->msgs);
> +					YYERROR;
> +				}
> +
> +				$$ = limit_stmt_alloc(&@$);
> +				$$->limit.rate	= $4;
> +				$$->limit.unit	= $6;
> +				$$->limit.burst	= $7;
> +				$$->limit.type	= NFT_LIMIT_PKT_BYTES;
> +				$$->limit.flags	= $3;
> +			}
>  			|	CT	COUNT	NUM	close_scope_ct
>  			{
>  				$$ = connlimit_stmt_alloc(&@$);
> @@ -4581,6 +4614,17 @@ limit_config		:	RATE	limit_mode	NUM	SLASH	time_unit	limit_burst_pkts
>  				limit->type	= NFT_LIMIT_PKT_BYTES;
>  				limit->flags	= $2;
>  			}
> +			|	RATE	limit_mode	limit_bytes SLASH time_unit	limit_burst_bytes
> +			{
> +				struct limit *limit;
> +
> +				limit = &$<obj>0->limit;
> +				limit->rate	= $3;
> +				limit->unit	= $5;
> +				limit->burst	= $6;
> +				limit->type	= NFT_LIMIT_PKT_BYTES;
> +				limit->flags	= $2;
> +			}
>  			;
>
>  limit_obj		:	/* empty */
> diff --git a/tests/py/any/limit.t b/tests/py/any/limit.t
> index ef7f93133297..b4b4e5296088 100644
> --- a/tests/py/any/limit.t
> +++ b/tests/py/any/limit.t
> @@ -24,6 +24,11 @@ limit rate 10230 mbytes/second;ok
>  limit rate 1023000 mbytes/second;ok
>  limit rate 512 kbytes/second burst 5 packets;fail
>
> +limit rate 1 bytes / second;ok;limit rate 1 bytes/second
> +limit rate 1 kbytes / second;ok;limit rate 1 kbytes/second
> +limit rate 1 mbytes / second;ok;limit rate 1 mbytes/second
> +limit rate 1 gbytes / second;fail
> +
>  limit rate 1025 bytes/second burst 512 bytes;ok
>  limit rate 1025 kbytes/second burst 1023 kbytes;ok
>  limit rate 1025 mbytes/second burst 1025 kbytes;ok
> diff --git a/tests/py/any/limit.t.json b/tests/py/any/limit.t.json
> index 8bab7e3d79b4..b41ae60a3bd6 100644
> --- a/tests/py/any/limit.t.json
> +++ b/tests/py/any/limit.t.json
> @@ -125,6 +125,45 @@
>      }
>  ]
>
> +# limit rate 1 bytes / second
> +[
> +    {
> +        "limit": {
> +            "burst": 5,
> +            "burst_unit": "bytes",
> +            "per": "second",
> +            "rate": 1,
> +            "rate_unit": "bytes"
> +        }
> +    }
> +]
> +
> +# limit rate 1 kbytes / second
> +[
> +    {
> +        "limit": {
> +            "burst": 5,
> +            "burst_unit": "bytes",
> +            "per": "second",
> +            "rate": 1,
> +            "rate_unit": "kbytes"
> +        }
> +    }
> +]
> +
> +# limit rate 1 mbytes / second
> +[
> +    {
> +        "limit": {
> +            "burst": 5,
> +            "burst_unit": "bytes",
> +            "per": "second",
> +            "rate": 1,
> +            "rate_unit": "mbytes"
> +        }
> +    }
> +]
> +
>  # limit rate 1025 bytes/second burst 512 bytes
>  [
>      {
> diff --git a/tests/py/any/limit.t.payload b/tests/py/any/limit.t.payload
> index dc6cea9b2846..3bd85f4ebf45 100644
> --- a/tests/py/any/limit.t.payload
> +++ b/tests/py/any/limit.t.payload
> @@ -46,6 +46,19 @@ ip test-ip4 output
>  ip test-ip4 output
>    [ limit rate 1072693248000/second burst 5 type bytes flags 0x0 ]
>
> +# limit rate 1 bytes / second
> +ip
> +  [ limit rate 1/second burst 5 type bytes flags 0x0 ]
> +
> +# limit rate 1 kbytes / second
> +ip
> +  [ limit rate 1024/second burst 5 type bytes flags 0x0 ]
> +
> +# limit rate 1 mbytes / second
> +ip
> +  [ limit rate 1048576/second burst 5 type bytes flags 0x0 ]
> +
> +
>  # limit rate 1025 bytes/second burst 512 bytes
>  ip test-ip4 output
>    [ limit rate 1025/second burst 512 type bytes flags 0x0 ]
> --
> 2.33.0
>
>
Pablo Neira Ayuso Oct. 27, 2021, 9:21 a.m. UTC | #2
On Sun, Oct 03, 2021 at 03:58:32PM +0100, Jeremy Sowden wrote:
> On 2021-10-02, at 16:22:30 +0100, Jeremy Sowden wrote:
> > The documentation describes the syntax of limit statements thus:
> >
> >   limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]
> >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> >
> >   TIME_UNIT := second | minute | hour | day
> >   BYTE_UNIT := bytes | kbytes | mbytes
> >
> > This implies that one may specify a limit as either of the following:
> >
> >   limit rate 1048576 / second
> >   limit rate 1048576 mbytes / second
> >
> > However, the latter currently does not parse:
> >
> >   $ sudo /usr/sbin/nft add filter input limit rate 1048576 mbytes / second
> >   Error: wrong rate format
> >   add filter input limit rate 1048576 mbytes / second
> >                    ^^^^^^^^^^^^^^^^^^^^^^^^^
> >
> > Extend the parser to support it.
> >
> > Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> > ---
> >
> > I can't help thinking that it ought to be possible to fold the two
> >
> >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> >
> > rules into one.  However, my attempts to get the scanner to tokenize
> > "bytes/second" as "bytes" "/" "second" (for example) failed.
> 
> Having reread the Flex manual, I've changed my mind.  While it would be
> possible, it would be rather fiddly and require more effort than it
> would be worth.

I can apply this workaround meanwhile we have a better solution for
this if this is an issue on your side.

Did you get any bug report regarding this?

Thanks.
Pablo Neira Ayuso Oct. 27, 2021, 9:31 a.m. UTC | #3
On Wed, Oct 27, 2021 at 11:21:42AM +0200, Pablo Neira Ayuso wrote:
> On Sun, Oct 03, 2021 at 03:58:32PM +0100, Jeremy Sowden wrote:
> > On 2021-10-02, at 16:22:30 +0100, Jeremy Sowden wrote:
> > > The documentation describes the syntax of limit statements thus:
> > >
> > >   limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]
> > >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> > >
> > >   TIME_UNIT := second | minute | hour | day
> > >   BYTE_UNIT := bytes | kbytes | mbytes
> > >
> > > This implies that one may specify a limit as either of the following:
> > >
> > >   limit rate 1048576 / second
> > >   limit rate 1048576 mbytes / second
> > >
> > > However, the latter currently does not parse:
> > >
> > >   $ sudo /usr/sbin/nft add filter input limit rate 1048576 mbytes / second
> > >   Error: wrong rate format
> > >   add filter input limit rate 1048576 mbytes / second
> > >                    ^^^^^^^^^^^^^^^^^^^^^^^^^
> > >
> > > Extend the parser to support it.
> > >
> > > Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> > > ---
> > >
> > > I can't help thinking that it ought to be possible to fold the two
> > >
> > >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> > >
> > > rules into one.  However, my attempts to get the scanner to tokenize
> > > "bytes/second" as "bytes" "/" "second" (for example) failed.
> > 
> > Having reread the Flex manual, I've changed my mind.  While it would be
> > possible, it would be rather fiddly and require more effort than it
> > would be worth.
> 
> I can apply this workaround meanwhile we have a better solution for
> this if this is an issue on your side.
> 
> Did you get any bug report regarding this?

Another possibility is to remove the existing call to rate_parse().

the bison parser accepts both:

add filter input limit rate 1048576 mbytes / second
add filter input limit rate 1048576 mbytes/second

after your update, right? I'm missing a few deletions in this previous
(the existing rules). I think there's a way to consolidate this bison
rule.
Jeremy Sowden Oct. 27, 2021, 8:02 p.m. UTC | #4
On 2021-10-27, at 11:21:38 +0200, Pablo Neira Ayuso wrote:
> On Sun, Oct 03, 2021 at 03:58:32PM +0100, Jeremy Sowden wrote:
> > On 2021-10-02, at 16:22:30 +0100, Jeremy Sowden wrote:
> > > The documentation describes the syntax of limit statements thus:
> > >
> > >   limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]
> > >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> > >
> > >   TIME_UNIT := second | minute | hour | day
> > >   BYTE_UNIT := bytes | kbytes | mbytes
> > >
> > > This implies that one may specify a limit as either of the following:
> > >
> > >   limit rate 1048576 / second
> > >   limit rate 1048576 mbytes / second
> > >
> > > However, the latter currently does not parse:
> > >
> > >   $ sudo /usr/sbin/nft add filter input limit rate 1048576 mbytes / second
> > >   Error: wrong rate format
> > >   add filter input limit rate 1048576 mbytes / second
> > >                    ^^^^^^^^^^^^^^^^^^^^^^^^^
> > >
> > > Extend the parser to support it.
> > >
> > > Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> > > ---
> > >
> > > I can't help thinking that it ought to be possible to fold the two
> > >
> > >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> > >
> > > rules into one.  However, my attempts to get the scanner to
> > > tokenize "bytes/second" as "bytes" "/" "second" (for example)
> > > failed.
> >
> > Having reread the Flex manual, I've changed my mind.  While it would be
> > possible, it would be rather fiddly and require more effort than it
> > would be worth.
>
> I can apply this workaround meanwhile we have a better solution for
> this if this is an issue on your side.
>
> Did you get any bug report regarding this?

Can't quite remember how I found it.  I may have been doing some testing
in relation to this:

  https://lore.kernel.org/netfilter-devel/20211007201222.2613750-1-jeremy@azazel.net/#r

I happened to notice that nft would accept all these:

  limit rate 1048576/second
  limit rate 1048576 / second
  limit rate 1048576 mbytes/second

but not this:

  limit rate 1048576 mbytes / second

The problem is that the scanner defines a string as:

  ({letter}|[_.])({letter}|{digit}|[/\-_\.])*

This means that:

  1048576/second

cannot be tokenized as a string, but:

 mbytes/second

can.  Thus the scanner will tokenize both

 1048576/second

and:

  1048576 / second

as:

  numberstring slash string

allowing this parser rule:

  LIMIT RATE limit_mode NUM SLASH time_unit limit_burst_pkts close_scope_limit

to match both.  On the other hand, the scanner will tokenize:

  mbytes/second

as:

  string

which is matched by the existing parser rule:

  LIMIT RATE limit_mode NUM STRING limit_burst_bytes close_scope_limit

but:

  mbytes / second

as:

  string slash string

and so I added a new parser rule to match the latter.

I did consider removing '/' from the scanner's definition of "string",
and it didn't seem to cause any unit test failures, but I dare say
somebody has used it somewhere weird that would break. :)

The other possibility I tried was to have a separate definition of
"string" active in the "SCANSTATE_LIMIT" start-condition, but that
turned out to require a lot more than just the one extra rule and to be
a much more fiddly and invasive change than just adding the parser rule.

J.
Jeremy Sowden Oct. 28, 2021, 8:06 a.m. UTC | #5
On 2021-10-27, at 11:31:19 +0200, Pablo Neira Ayuso wrote:
> On Wed, Oct 27, 2021 at 11:21:42AM +0200, Pablo Neira Ayuso wrote:
> > On Sun, Oct 03, 2021 at 03:58:32PM +0100, Jeremy Sowden wrote:
> > > On 2021-10-02, at 16:22:30 +0100, Jeremy Sowden wrote:
> > > > The documentation describes the syntax of limit statements thus:
> > > >
> > > >   limit rate [over] packet_number / TIME_UNIT [burst packet_number packets]
> > > >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> > > >
> > > >   TIME_UNIT := second | minute | hour | day
> > > >   BYTE_UNIT := bytes | kbytes | mbytes
> > > >
> > > > This implies that one may specify a limit as either of the following:
> > > >
> > > >   limit rate 1048576 / second
> > > >   limit rate 1048576 mbytes / second
> > > >
> > > > However, the latter currently does not parse:
> > > >
> > > >   $ sudo /usr/sbin/nft add filter input limit rate 1048576 mbytes / second
> > > >   Error: wrong rate format
> > > >   add filter input limit rate 1048576 mbytes / second
> > > >                    ^^^^^^^^^^^^^^^^^^^^^^^^^
> > > >
> > > > Extend the parser to support it.
> > > >
> > > > Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
> > > > ---
> > > >
> > > > I can't help thinking that it ought to be possible to fold the two
> > > >
> > > >   limit rate [over] byte_number BYTE_UNIT / TIME_UNIT [burst byte_number BYTE_UNIT]
> > > >
> > > > rules into one.  However, my attempts to get the scanner to tokenize
> > > > "bytes/second" as "bytes" "/" "second" (for example) failed.
> > >
> > > Having reread the Flex manual, I've changed my mind.  While it would be
> > > possible, it would be rather fiddly and require more effort than it
> > > would be worth.
> >
> > I can apply this workaround meanwhile we have a better solution for
> > this if this is an issue on your side.
> >
> > Did you get any bug report regarding this?
>
> Another possibility is to remove the existing call to rate_parse().
>
> the bison parser accepts both:
>
> add filter input limit rate 1048576 mbytes / second
> add filter input limit rate 1048576 mbytes/second
>
> after your update, right? I'm missing a few deletions in this previous
> (the existing rules). I think there's a way to consolidate this bison
> rule.

I'll take another look.

J.
diff mbox series

Patch

diff --git a/src/parser_bison.y b/src/parser_bison.y
index c25af6ba114a..4a41e9e3a293 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -689,7 +689,7 @@  int nft_lex(void *, void *, void *);
 %type <val>			level_type log_flags log_flags_tcp log_flag_tcp
 %type <stmt>			limit_stmt quota_stmt connlimit_stmt
 %destructor { stmt_free($$); }	limit_stmt quota_stmt connlimit_stmt
-%type <val>			limit_burst_pkts limit_burst_bytes limit_mode time_unit quota_mode
+%type <val>			limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
 %type <stmt>			reject_stmt reject_stmt_alloc
 %destructor { stmt_free($$); }	reject_stmt reject_stmt_alloc
 %type <stmt>			nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
@@ -3184,6 +3184,21 @@  limit_stmt		:	LIMIT	RATE	limit_mode	NUM	SLASH	time_unit	limit_burst_pkts	close_s
 				$$->limit.type	= NFT_LIMIT_PKT_BYTES;
 				$$->limit.flags = $3;
 			}
+			|	LIMIT	RATE	limit_mode	limit_bytes SLASH time_unit	limit_burst_bytes	close_scope_limit
+			{
+				if ($7 == 0) {
+					erec_queue(error(&@7, "limit burst must be > 0"),
+						   state->msgs);
+					YYERROR;
+				}
+
+				$$ = limit_stmt_alloc(&@$);
+				$$->limit.rate	= $4;
+				$$->limit.unit	= $6;
+				$$->limit.burst	= $7;
+				$$->limit.type	= NFT_LIMIT_PKT_BYTES;
+				$$->limit.flags = $3;
+			}
 			|	LIMIT	NAME	stmt_expr	close_scope_limit
 			{
 				$$ = objref_stmt_alloc(&@$);
@@ -3251,19 +3266,22 @@  limit_burst_pkts	:	/* empty */			{ $$ = 5; }
 			;
 
 limit_burst_bytes	:	/* empty */			{ $$ = 5; }
-			|	BURST	NUM	BYTES		{ $$ = $2; }
-			|	BURST	NUM	STRING
+			|	BURST	limit_bytes		{ $$ = $2; }
+			;
+
+limit_bytes		:	NUM	BYTES		{ $$ = $1; }
+			|	NUM	STRING
 			{
 				struct error_record *erec;
 				uint64_t rate;
 
-				erec = data_unit_parse(&@$, $3, &rate);
-				xfree($3);
+				erec = data_unit_parse(&@$, $2, &rate);
+				xfree($2);
 				if (erec != NULL) {
 					erec_queue(erec, state->msgs);
 					YYERROR;
 				}
-				$$ = $2 * rate;
+				$$ = $1 * rate;
 			}
 			;
 
@@ -4317,7 +4335,22 @@  set_elem_stmt		:	COUNTER	close_scope_counter
 				$$->limit.burst = $6;
 				$$->limit.type  = NFT_LIMIT_PKT_BYTES;
 				$$->limit.flags = $3;
-                        }
+			}
+			|	LIMIT	RATE	limit_mode	limit_bytes SLASH time_unit	limit_burst_bytes	close_scope_limit
+			{
+				if ($7 == 0) {
+					erec_queue(error(&@7, "limit burst must be > 0"),
+						   state->msgs);
+					YYERROR;
+				}
+
+				$$ = limit_stmt_alloc(&@$);
+				$$->limit.rate	= $4;
+				$$->limit.unit	= $6;
+				$$->limit.burst	= $7;
+				$$->limit.type	= NFT_LIMIT_PKT_BYTES;
+				$$->limit.flags	= $3;
+			}
 			|	CT	COUNT	NUM	close_scope_ct
 			{
 				$$ = connlimit_stmt_alloc(&@$);
@@ -4581,6 +4614,17 @@  limit_config		:	RATE	limit_mode	NUM	SLASH	time_unit	limit_burst_pkts
 				limit->type	= NFT_LIMIT_PKT_BYTES;
 				limit->flags	= $2;
 			}
+			|	RATE	limit_mode	limit_bytes SLASH time_unit	limit_burst_bytes
+			{
+				struct limit *limit;
+
+				limit = &$<obj>0->limit;
+				limit->rate	= $3;
+				limit->unit	= $5;
+				limit->burst	= $6;
+				limit->type	= NFT_LIMIT_PKT_BYTES;
+				limit->flags	= $2;
+			}
 			;
 
 limit_obj		:	/* empty */
diff --git a/tests/py/any/limit.t b/tests/py/any/limit.t
index ef7f93133297..b4b4e5296088 100644
--- a/tests/py/any/limit.t
+++ b/tests/py/any/limit.t
@@ -24,6 +24,11 @@  limit rate 10230 mbytes/second;ok
 limit rate 1023000 mbytes/second;ok
 limit rate 512 kbytes/second burst 5 packets;fail
 
+limit rate 1 bytes / second;ok;limit rate 1 bytes/second
+limit rate 1 kbytes / second;ok;limit rate 1 kbytes/second
+limit rate 1 mbytes / second;ok;limit rate 1 mbytes/second
+limit rate 1 gbytes / second;fail
+
 limit rate 1025 bytes/second burst 512 bytes;ok
 limit rate 1025 kbytes/second burst 1023 kbytes;ok
 limit rate 1025 mbytes/second burst 1025 kbytes;ok
diff --git a/tests/py/any/limit.t.json b/tests/py/any/limit.t.json
index 8bab7e3d79b4..b41ae60a3bd6 100644
--- a/tests/py/any/limit.t.json
+++ b/tests/py/any/limit.t.json
@@ -125,6 +125,45 @@ 
     }
 ]
 
+# limit rate 1 bytes / second
+[
+    {
+        "limit": {
+            "burst": 5,
+            "burst_unit": "bytes",
+            "per": "second",
+            "rate": 1,
+            "rate_unit": "bytes"
+        }
+    }
+]
+
+# limit rate 1 kbytes / second
+[
+    {
+        "limit": {
+            "burst": 5,
+            "burst_unit": "bytes",
+            "per": "second",
+            "rate": 1,
+            "rate_unit": "kbytes"
+        }
+    }
+]
+
+# limit rate 1 mbytes / second
+[
+    {
+        "limit": {
+            "burst": 5,
+            "burst_unit": "bytes",
+            "per": "second",
+            "rate": 1,
+            "rate_unit": "mbytes"
+        }
+    }
+]
+
 # limit rate 1025 bytes/second burst 512 bytes
 [
     {
diff --git a/tests/py/any/limit.t.payload b/tests/py/any/limit.t.payload
index dc6cea9b2846..3bd85f4ebf45 100644
--- a/tests/py/any/limit.t.payload
+++ b/tests/py/any/limit.t.payload
@@ -46,6 +46,19 @@  ip test-ip4 output
 ip test-ip4 output
   [ limit rate 1072693248000/second burst 5 type bytes flags 0x0 ]
 
+# limit rate 1 bytes / second
+ip
+  [ limit rate 1/second burst 5 type bytes flags 0x0 ]
+
+# limit rate 1 kbytes / second
+ip
+  [ limit rate 1024/second burst 5 type bytes flags 0x0 ]
+
+# limit rate 1 mbytes / second
+ip
+  [ limit rate 1048576/second burst 5 type bytes flags 0x0 ]
+
+
 # limit rate 1025 bytes/second burst 512 bytes
 ip test-ip4 output
   [ limit rate 1025/second burst 512 type bytes flags 0x0 ]