]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
evaluate: add support to set IPv6 non-byte header fields
authorFlorian Westphal <fw@strlen.de>
Mon, 1 Aug 2016 15:11:41 +0000 (17:11 +0200)
committerFlorian Westphal <fw@strlen.de>
Mon, 1 Aug 2016 15:11:41 +0000 (17:11 +0200)
'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 protocol 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>
Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/evaluate.c

index 7962e9e428df82e05b3feef4e52908d0fcfcd086..f2b6dd1e205e1c6e493cabae73897b05c92db5f0 100644 (file)
@@ -1613,13 +1613,85 @@ 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[NFT_REG_SIZE];
+       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;
+
+       if (!payload_needs_adjustment(payload))
+               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)