diff options
| author | Pablo Neira Ayuso <pablo@netfilter.org> | 2025-02-27 18:36:16 +0100 |
|---|---|---|
| committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2025-03-07 13:56:47 +0100 |
| commit | ee32bc90b80f329822b1dd3ee35f2eb98ae7f260 (patch) | |
| tree | 026e60bac7b91350d9705d5ea0072d8349de39a3 | |
| parent | de447e248cce3ca3fd7040c386ac798c35721415 (diff) | |
evaluate: support for bitfield payload statement with binary operation
Update bitfield payload statement support to allow for bitwise
and/or/xor updates. Adjust payload expression to fetch 16-bits for
mangling while leaving unmodified bits intact.
# nft --debug=netlink add rule x y ip dscp set ip dscp or 0x1
ip x y
[ payload load 2b @ network header + 0 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x0000fbff ) ^ 0x00000400 ]
[ payload write reg 1 => 2b @ network header + 0 csum_type 1 csum_off 10 csum_flags 0x0 ]
Skip expr_evaluate_bits() transformation since these are only useful
for payload matching and set lookups.
Listing still shows a raw expression:
# nft list ruleset
...
@nh,8,5 set 0x0
The follow up patch completes it:
("netlink_delinearize: support for bitfield payload statement with binary operation")
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1698
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
| -rw-r--r-- | src/evaluate.c | 65 |
1 files changed, 63 insertions, 2 deletions
diff --git a/src/evaluate.c b/src/evaluate.c index d7915ed1..c9c56588 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -569,6 +569,13 @@ static int expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp) uint8_t shift; mpz_t bitmask; + /* payload statement with relational expression as a value does not + * require the transformations that are needed for payload matching, + * skip this. + */ + if (ctx->stmt && ctx->stmt->ops->type == STMT_PAYLOAD) + return 0; + switch (expr->etype) { case EXPR_PAYLOAD: shift = expr_offset_shift(expr, expr->payload.offset, @@ -3281,10 +3288,10 @@ static int stmt_evaluate_exthdr(struct eval_ctx *ctx, struct stmt *stmt) static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt) { - struct expr *mask, *and, *xor, *payload_bytes; - unsigned int masklen, extra_len = 0; + struct expr *mask, *and, *xor, *expr, *payload_bytes; unsigned int payload_byte_size, payload_byte_offset; uint8_t shift_imm, data[NFT_REG_SIZE]; + unsigned int masklen, extra_len = 0; struct expr *payload; mpz_t bitmask, ff; bool need_csum; @@ -3350,6 +3357,60 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt) if (shift_imm) mpz_lshift_ui(stmt->payload.val->value, shift_imm); break; + case EXPR_BINOP: + expr = stmt->payload.val; + while (expr->left->etype == EXPR_BINOP) + expr = expr->left; + + if (expr->left->etype != EXPR_PAYLOAD) + break; + + if (!payload_expr_cmp(payload, expr->left)) + break; + + /* Adjust payload to fetch 16-bits. */ + expr->left->payload.offset = payload_byte_offset * BITS_PER_BYTE; + expr->left->len = payload_byte_size * BITS_PER_BYTE; + expr->left->payload.is_raw = 1; + + switch (expr->right->etype) { + case EXPR_VALUE: + if (shift_imm) + mpz_lshift_ui(expr->right->value, shift_imm); + + /* build bitmask to keep unmodified bits intact */ + if (expr->op == OP_AND) { + masklen = payload_byte_size * BITS_PER_BYTE; + mpz_init_bitmask(ff, masklen); + + mpz_init2(bitmask, masklen); + mpz_bitmask(bitmask, payload->len); + mpz_lshift_ui(bitmask, shift_imm); + + mpz_xor(bitmask, ff, bitmask); + mpz_clear(ff); + + mpz_ior(bitmask, expr->right->value, bitmask); + mpz_set(expr->right->value, bitmask); + + mpz_clear(bitmask); + } + break; + default: + return expr_error(ctx->msgs, expr->right, + "payload statement for this expression is not supported"); + } + + expr_free(stmt->payload.expr); + /* statement payload is the same in expr and value, update it. */ + stmt->payload.expr = expr_clone(expr->left); + payload = stmt->payload.expr; + ctx->stmt_len = stmt->payload.expr->len; + + if (expr_evaluate(ctx, &stmt->payload.val) < 0) + return -1; + + return 0; default: return expr_error(ctx->msgs, stmt->payload.val, "payload statement for this expression is not supported"); |
