]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
evaluate: place byteorder conversion before rshift in payload statement
authorPablo Neira Ayuso <pablo@netfilter.org>
Fri, 7 Jul 2023 21:40:19 +0000 (23:40 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 22 Jan 2025 23:05:48 +0000 (00:05 +0100)
commit 668c18f672038dffa72b67d834445e0fe5ae286d upstream.

For bitfield that spans more than one byte, such as ip6 dscp, byteorder
conversion needs to be done before rshift. Add unary expression for this
conversion only in the case of meta and ct statements.

Before this patch:

 # nft --debug=netlink add rule ip6 x y 'meta mark set ip6 dscp'
 ip6 x y
  [ payload load 2b @ network header + 0 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
  [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
  [ byteorder reg 1 = ntoh(reg 1, 2, 2) ] <--------- incorrect
  [ meta set mark with reg 1 ]

After this patch:

 # nft --debug=netlink add rule ip6 x y 'meta mark set ip6 dscp'
 ip6 x y
  [ payload load 2b @ network header + 0 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
  [ byteorder reg 1 = ntoh(reg 1, 2, 2) ] <-------- correct
  [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
  [ meta set mark with reg 1 ]

For the matching case, binary transfer already deals with the rshift to
adjust left and right hand side of the expression, the unary conversion
is not needed in such case.

Fixes: 8221d86e616b ("tests: py: add test-cases for ct and packet mark payload expressions")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/evaluate.c

index 92421efe8c23f50882ee9c8a3aa176c1eabd04d3..dc50fb0ad8e76aed725d2caa78892dd7c5003e45 100644 (file)
@@ -490,6 +490,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
 {
        struct expr *expr = *exprp, *and, *mask, *rshift, *off;
        unsigned masklen, len = expr->len, extra_len = 0;
+       enum byteorder byteorder;
        uint8_t shift;
        mpz_t bitmask;
 
@@ -524,6 +525,15 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
        and->len        = masklen;
 
        if (shift) {
+               if (ctx->stmt_len > 0 && div_round_up(masklen, BITS_PER_BYTE) > 1) {
+                       int op = byteorder_conversion_op(expr, BYTEORDER_HOST_ENDIAN);
+                       and = unary_expr_alloc(&expr->location, op, and);
+                       and->len = masklen;
+                       byteorder = BYTEORDER_HOST_ENDIAN;
+               } else {
+                       byteorder = expr->byteorder;
+               }
+
                off = constant_expr_alloc(&expr->location,
                                          expr_basetype(expr),
                                          BYTEORDER_HOST_ENDIAN,
@@ -531,7 +541,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
 
                rshift = binop_expr_alloc(&expr->location, OP_RSHIFT, and, off);
                rshift->dtype           = expr->dtype;
-               rshift->byteorder       = expr->byteorder;
+               rshift->byteorder       = byteorder;
                rshift->len             = masklen;
 
                *exprp = rshift;