@@ -1608,13 +1608,86 @@ static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct expr *binop, *mask, *and, *payload_bytes;
+ unsigned int masklen, extra_len = 0;
+ unsigned int payload_byte_size;
+ uint8_t shift_imm, data[16];
+ struct expr *payload;
+ mpz_t bitmask, ff;
+
if (__expr_evaluate_payload(ctx, stmt->payload.expr) < 0)
return -1;
- return stmt_evaluate_arg(ctx, stmt,
- stmt->payload.expr->dtype,
- stmt->payload.expr->len,
- &stmt->payload.val);
+ payload = stmt->payload.expr;
+ if (stmt_evaluate_arg(ctx, stmt, payload->dtype, payload->len,
+ &stmt->payload.val) < 0)
+ return -1;
+
+ /* Normal case: byte sized and byte aligned */
+ if (payload->payload.offset % BITS_PER_BYTE == 0 &&
+ payload->len % BITS_PER_BYTE == 0)
+ return 0;
+
+ shift_imm = expr_offset_shift(payload, payload->payload.offset, &extra_len);
+ if (shift_imm) {
+ struct expr *off;
+
+ off = constant_expr_alloc(&payload->location,
+ expr_basetype(payload),
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(shift_imm), &shift_imm);
+
+ binop = binop_expr_alloc(&payload->location, OP_LSHIFT,
+ stmt->payload.val, off);
+ binop->dtype = payload->dtype;
+ binop->byteorder = payload->byteorder;
+
+ stmt->payload.val = binop;
+ }
+
+ payload_byte_size = round_up(payload->len, BITS_PER_BYTE) / BITS_PER_BYTE;
+ payload_byte_size += (extra_len / BITS_PER_BYTE);
+ 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);
+
+ assert(sizeof(data) * BITS_PER_BYTE >= masklen);
+ mpz_export_data(data, bitmask, BYTEORDER_HOST_ENDIAN, masklen);
+ mask = constant_expr_alloc(&payload->location, expr_basetype(payload),
+ BYTEORDER_HOST_ENDIAN, masklen, data);
+
+ payload_bytes = payload_expr_alloc(&payload->location, NULL, 0);
+ payload_init_raw(payload_bytes, payload->payload.base,
+ (payload->payload.offset / BITS_PER_BYTE) * BITS_PER_BYTE,
+ payload_byte_size * BITS_PER_BYTE);
+
+ payload_bytes->payload.desc = payload->payload.desc;
+ payload_bytes->dtype = &integer_type;
+ payload_bytes->byteorder = payload->byteorder;
+
+ payload->len = payload_bytes->len;
+ payload->payload.offset = payload_bytes->payload.offset;
+
+ and = binop_expr_alloc(&payload->location, OP_AND, payload_bytes, mask);
+
+ and->dtype = payload_bytes->dtype;
+ and->byteorder = payload_bytes->byteorder;
+ and->len = payload_bytes->len;
+
+ binop = binop_expr_alloc(&payload->location, OP_XOR, and,
+ stmt->payload.val);
+ binop->dtype = payload->dtype;
+ binop->byteorder = payload->byteorder;
+ binop->len = mask->len;
+ stmt->payload.val = binop;
+
+ return expr_evaluate(ctx, &stmt->payload.val);
}
static int stmt_evaluate_flow(struct eval_ctx *ctx, struct stmt *stmt)
'ip6 ecn set 1' will generate a zero-sized write operation. Just like when matching on bit-sized header fields we need to round up to a byte-sized quantity and add a mask to retain those bits outside of the header bits that we want to change. Example: ip6 ecn set ce [ payload load 1b @ network header + 1 => reg 1 ] [ bitwise reg 1 = (reg=1 & 0x000000cf ) ^ 0x00000030 ] [ payload write reg 1 => 1b @ network header + 1 csum_type 0 csum_off 0 ] 1. Load the full byte containing the ecn bits 2 .Mask out everything *BUT* the ecn bits 3 .Set the CE mark This patch only works if the protcol doesn't need a checksum fixup. Will address this in a followup patch. This also doesn't yet include the needed reverse translation. Signed-off-by: Florian Westphal <fw@strlen.de> --- src/evaluate.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 4 deletions(-)