diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2023-03-17 10:16:45 +0100 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2023-03-28 10:26:34 +0200 |
commit | f60ef911f1e05a88fae650c487965e7b85d17d2a (patch) | |
tree | ae36f84adaa75146ac3f834c49955eb4ec012a46 /src/evaluate.c | |
parent | 3c64ea7995cbbc4f1d9d7707f907667325eb62b9 (diff) |
evaluate: honor statement length in bitwise evaluation
Get length from statement, instead infering it from the expression that
is used to set the value. In the particular case of {ct|meta} mark, this
is 32 bits.
Otherwise, bytecode generation is not correct:
# nft -c --debug=netlink 'add rule ip6 x y ct mark set ip6 dscp << 2 | 0x10'
[ 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, 1) ]
[ bitwise reg 1 = ( reg 1 << 0x00000002 ) ]
[ bitwise reg 1 = ( reg 1 & 0x00000fef ) ^ 0x00000010 ] <--- incorrect!
[ ct set mark with reg 1 ]
the previous bitwise shift already upgraded to 32-bits (not visible from
the netlink debug output above).
After this patch, the last | 0x10 uses 32-bits:
[ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
note that mask 0xffffffef is used instead of 0x00000fef.
Patch ("evaluate: support shifts larger than the width of the left operand")
provides the statement length through eval context. Use it to evaluate the
bitwise expression accordingly, otherwise bytecode is incorrect:
# nft --debug=netlink add rule ip x y 'ct mark set ip dscp & 0x0f << 1 | 0xff000000'
ip x y
[ payload load 1b @ network header + 1 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
[ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
[ bitwise reg 1 = ( reg 1 & 0x1e000000 ) ^ 0x000000ff ] <-- incorrect byteorder for OR
[ byteorder reg 1 = ntoh(reg 1, 4, 4) ] <-- no needed for single ip dscp byte
[ ct set mark with reg 1 ]
Correct bytecode:
# nft --debug=netlink add rule ip x y 'ct mark set ip dscp & 0x0f << 1 | 0xff000000
ip x y
[ payload load 1b @ network header + 1 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
[ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
[ bitwise reg 1 = ( reg 1 & 0x0000001e ) ^ 0xff000000 ]
[ ct set mark with reg 1 ]
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/evaluate.c')
-rw-r--r-- | src/evaluate.c | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/src/evaluate.c b/src/evaluate.c index 4178be4e..c4ddb007 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1326,13 +1326,32 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr) static int expr_evaluate_bitwise(struct eval_ctx *ctx, struct expr **expr) { struct expr *op = *expr, *left = op->left; + const struct datatype *dtype; + unsigned int max_len; + int byteorder; + + if (ctx->stmt_len > left->len) { + max_len = ctx->stmt_len; + byteorder = BYTEORDER_HOST_ENDIAN; + dtype = &integer_type; + + /* Both sides need to be in host byte order */ + if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0) + return -1; + + left = op->left; + } else { + max_len = left->len; + byteorder = left->byteorder; + dtype = left->dtype; + } - if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0) + if (byteorder_conversion(ctx, &op->right, byteorder) < 0) return -1; - op->dtype = left->dtype; - op->byteorder = left->byteorder; - op->len = left->len; + datatype_set(op, dtype); + op->byteorder = byteorder; + op->len = max_len; if (expr_is_constant(left)) return constant_binop_simplify(ctx, expr); |