]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: queue: allow use of arbitrary queue expressions
authorFlorian Westphal <fw@strlen.de>
Tue, 15 Jun 2021 12:57:08 +0000 (14:57 +0200)
committerFlorian Westphal <fw@strlen.de>
Mon, 21 Jun 2021 12:44:58 +0000 (14:44 +0200)
back in 2016 Liping Zhang added support to kernel and libnftnl to
specify a source register containing the queue number to use.

This was never added to nft itself, so allow this.
On linearization side, check if attached expression is a range.
If its not, allocate a new register and set NFTNL_EXPR_QUEUE_SREG_QNUM
attribute after generating the lowlevel expressions for the kernel.

On delinarization we need to check for presence of
NFTNL_EXPR_QUEUE_SREG_QNUM and decode the expression(s) when present.

Also need to do postprocessing for STMT_QUEUE so that the protocol
context is set correctly, without this only raw payload expressions
will be shown (@nh,32,...) instead of 'ip ...'.

Next patch adds test cases.

Signed-off-by: Florian Westphal <fw@strlen.de>
doc/statements.txt
src/evaluate.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/statement.c

index 602a5b2011a71fd8baa341977ba6e1c86f3df651..c2a616594fce99fc72b98ff170380fe6b6398469 100644 (file)
@@ -591,11 +591,15 @@ for details.
 ____
 *queue* [*flags* 'QUEUE_FLAGS'] [*num* 'queue_number']
 *queue* [*flags* 'QUEUE_FLAGS'] [*num* 'queue_number_from' - 'queue_number_to']
+*queue* [*flags* 'QUEUE_FLAGS'] [*to* 'QUEUE_EXPRESSION' ]
 
 'QUEUE_FLAGS' := 'QUEUE_FLAG' [*,* 'QUEUE_FLAGS']
 'QUEUE_FLAG'  := *bypass* | *fanout*
+'QUEUE_EXPRESSION' := *numgen* | *hash* | *symhash*
 ____
 
+QUEUE_EXPRESSION can be used to compute a queue number
+at run-time with the hash or numgen expressions.
 
 .queue statement values
 [options="header"]
index c24fee4c8acc314f62757031570faa86b60fe556..4f19dc43c7972448d42eb0ad2e4f75fd1fd634f6 100644 (file)
@@ -1019,7 +1019,6 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
        if (mpz_cmp(left->value, right->value) >= 0)
                return expr_error(ctx->msgs, range,
                                  "Range has zero or negative size");
-
        datatype_set(range, left->dtype);
        range->flags |= EXPR_F_CONSTANT;
        return 0;
@@ -3446,14 +3445,16 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
                                      BYTEORDER_HOST_ENDIAN,
                                      &stmt->queue.queue) < 0)
                        return -1;
-               if (!expr_is_constant(stmt->queue.queue))
-                       return expr_error(ctx->msgs, stmt->queue.queue,
-                                         "queue number is not constant");
-               if (stmt->queue.queue->etype != EXPR_RANGE &&
-                   (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT))
+
+               if ((stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) &&
+                   stmt->queue.queue->etype != EXPR_RANGE)
                        return expr_error(ctx->msgs, stmt->queue.queue,
                                          "fanout requires a range to be "
                                          "specified");
+
+               if (ctx->ectx.maxval > USHRT_MAX)
+                       return expr_error(ctx->msgs, stmt->queue.queue,
+                                         "queue expression max value exceeds %u", USHRT_MAX);
        }
        stmt->flags |= STMT_F_TERMINAL;
        return 0;
index 5849780996a6e250cae7255fe6fa11e82613893c..6a6f7747699421eb9d9dbb93e8227c83b765e3d7 100644 (file)
@@ -1467,19 +1467,32 @@ static void netlink_parse_queue(struct netlink_parse_ctx *ctx,
                              const struct location *loc,
                              const struct nftnl_expr *nle)
 {
-       uint16_t num, total, flags;
-       struct expr *expr, *high;
+       struct expr *expr;
+       uint16_t flags;
 
-       num   = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_NUM);
-       total = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_TOTAL);
+       if (nftnl_expr_is_set(nle, NFTNL_EXPR_QUEUE_SREG_QNUM)) {
+               enum nft_registers reg = netlink_parse_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM);
 
-       expr = constant_expr_alloc(loc, &integer_type,
-                                  BYTEORDER_HOST_ENDIAN, 16, &num);
-       if (total > 1) {
-               total += num - 1;
-               high = constant_expr_alloc(loc, &integer_type,
+               expr = netlink_get_register(ctx, loc, reg);
+               if (!expr) {
+                       netlink_error(ctx, loc, "queue statement has no sreg expression");
+                       return;
+               }
+       } else {
+               uint16_t total = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_TOTAL);
+               uint16_t num = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_NUM);
+
+               expr = constant_expr_alloc(loc, &integer_type,
+                                          BYTEORDER_HOST_ENDIAN, 16, &num);
+
+               if (total > 1) {
+                       struct expr *high;
+
+                       total += num - 1;
+                       high = constant_expr_alloc(loc, &integer_type,
                                           BYTEORDER_HOST_ENDIAN, 16, &total);
-               expr = range_expr_alloc(loc, expr, high);
+                       expr = range_expr_alloc(loc, expr, high);
+               }
        }
 
        flags = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_FLAGS);
@@ -2852,6 +2865,18 @@ static void stmt_payload_postprocess(struct rule_pp_ctx *ctx)
        expr_postprocess(ctx, &stmt->payload.val);
 }
 
+static void stmt_queue_postprocess(struct rule_pp_ctx *ctx)
+{
+       struct stmt *stmt = ctx->stmt;
+       struct expr *e = stmt->queue.queue;
+
+       if (e == NULL || e->etype == EXPR_VALUE ||
+           e->etype == EXPR_RANGE)
+               return;
+
+       expr_postprocess(ctx, &stmt->queue.queue);
+}
+
 /*
  * We can only remove payload dependencies if they occur without
  * a statement with side effects in between.
@@ -2956,6 +2981,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
                case STMT_OBJREF:
                        expr_postprocess(&rctx, &stmt->objref.expr);
                        break;
+               case STMT_QUEUE:
+                       stmt_queue_postprocess(&rctx);
+                       break;
                default:
                        break;
                }
index 7b35aae1f913a57df61443fcb449b8e642677807..b1f3feeeb4b76400d961432b64d2d70e906fc6dc 100644 (file)
@@ -1334,21 +1334,39 @@ static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
 static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
                                 const struct stmt *stmt)
 {
+       enum nft_registers sreg = 0;
        struct nftnl_expr *nle;
        uint16_t total_queues;
+       struct expr *expr;
        mpz_t low, high;
 
        mpz_init2(low, 16);
        mpz_init2(high, 16);
-       if (stmt->queue.queue != NULL) {
-               range_expr_value_low(low, stmt->queue.queue);
-               range_expr_value_high(high, stmt->queue.queue);
+
+       expr = stmt->queue.queue;
+
+       if (expr) {
+               if (expr->etype == EXPR_RANGE || expr->etype == EXPR_VALUE) {
+                       range_expr_value_low(low, stmt->queue.queue);
+                       range_expr_value_high(high, stmt->queue.queue);
+               } else {
+                       sreg = get_register(ctx, expr);
+                       netlink_gen_expr(ctx, expr, sreg);
+                       release_register(ctx, expr);
+               }
        }
+
        total_queues = mpz_get_uint16(high) - mpz_get_uint16(low) + 1;
 
        nle = alloc_nft_expr("queue");
-       nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low));
-       nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues);
+
+       if (sreg) {
+               netlink_put_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM, sreg);
+       } else {
+               nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low));
+               nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues);
+       }
+
        if (stmt->queue.flags)
                nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_FLAGS,
                                   stmt->queue.flags);
index 277918ef51c4fd41b2d62a08cd81a86f9f9ac1cc..cb3e80e3facfa3f5818a12a5ecceb89366192a2b 100644 (file)
@@ -705,8 +705,8 @@ int nft_lex(void *, void *, void *);
 
 %type <stmt>                   queue_stmt queue_stmt_alloc     queue_stmt_compat
 %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc     queue_stmt_compat
-%type <expr>                   queue_stmt_expr_simple
-%destructor { expr_free($$); } queue_stmt_expr_simple
+%type <expr>                   queue_stmt_expr_simple queue_stmt_expr
+%destructor { expr_free($$); } queue_stmt_expr_simple queue_stmt_expr
 %type <val>                    queue_stmt_flags queue_stmt_flag
 %type <stmt>                   dup_stmt
 %destructor { stmt_free($$); } dup_stmt
@@ -3739,6 +3739,14 @@ nf_nat_flag              :       RANDOM          { $$ = NF_NAT_RANGE_PROTO_RANDOM; }
                        ;
 
 queue_stmt             :       queue_stmt_compat       close_scope_queue
+                       |       QUEUE TO queue_stmt_expr        close_scope_queue
+                       {
+                               $$ = queue_stmt_alloc(&@$, $3, 0);
+                       }
+                       |       QUEUE FLAGS     queue_stmt_flags TO queue_stmt_expr close_scope_queue
+                       {
+                               $$ = queue_stmt_alloc(&@$, $5, $3);
+                       }
                        |       QUEUE   FLAGS   queue_stmt_flags QUEUENUM queue_stmt_expr_simple close_scope_queue
                        {
                                $$ = queue_stmt_alloc(&@$, $5, $3);
@@ -3777,6 +3785,10 @@ queue_stmt_expr_simple   :       integer_expr
                        |       range_rhs_expr
                        ;
 
+queue_stmt_expr                :       numgen_expr
+                       |       hash_expr
+                       ;
+
 queue_stmt_flags       :       queue_stmt_flag
                        |       queue_stmt_flags        COMMA   queue_stmt_flag
                        {
index 9eb49339555b1fa39e9c57f1b99f67d31178902c..dfd275104c59ed713cc7f59c6260b0ef501cd9a6 100644 (file)
@@ -507,8 +507,13 @@ static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
                nft_print(octx, "%sfanout", delim);
 
        if (e) {
-               nft_print(octx, " num ");
-               expr_print(stmt->queue.queue, octx);
+               if (e->etype == EXPR_VALUE || e->etype == EXPR_RANGE) {
+                       nft_print(octx, " num ");
+                       expr_print(stmt->queue.queue, octx);
+               } else {
+                       nft_print(octx, " to ");
+                       expr_print(stmt->queue.queue, octx);
+               }
        } else {
                nft_print(octx, " num 0");
        }