summaryrefslogtreecommitdiffstats
path: root/src/netlink_linearize.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2013-09-17 14:29:29 +0200
committerFlorian Westphal <fw@strlen.de>2015-09-18 00:06:01 +0200
commitc3f0501b41e653e980e60d14eb88ac9dfc3afc61 (patch)
tree6fb7d14f5bca09229cf98bbf128f52f9fe01638d /src/netlink_linearize.c
parent61ae81a313023e0b790594491bc030c06a3c0eab (diff)
src: netlink_linearize: handle sub-byte lengths
Currently length is expr->len / BITS_PER_BYTE, i.e. expr->len has to be a multiple of 8. When core asks for e.g. '9 bits', we truncate this to 8. Round up to 16 and inject a 9-bit mask to zero out the parts we're not interested in. This will also need change to the delinarization step to remove the extra op when dumping rules from kernel. Signed-off-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'src/netlink_linearize.c')
-rw-r--r--src/netlink_linearize.c46
1 files changed, 44 insertions, 2 deletions
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 707df49c..63f77f65 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -99,6 +99,44 @@ static void netlink_gen_concat(struct netlink_linearize_ctx *ctx,
}
}
+static void netlink_gen_payload_mask(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct nft_data_linearize nld, zero = {};
+ struct nftnl_expr *nle;
+ unsigned int offset, len, masklen;
+ mpz_t mask;
+
+ offset = expr->payload.offset % BITS_PER_BYTE;
+ masklen = expr->len + offset;
+
+ if (masklen > 128)
+ BUG("expr mask length is %u (len %u, offset %u)\n",
+ masklen, expr->len, offset);
+
+ mpz_init2(mask, masklen);
+ mpz_bitmask(mask, expr->len);
+
+ if (offset)
+ mpz_lshift_ui(mask, offset);
+
+ nle = alloc_nft_expr("bitwise");
+
+ len = div_round_up(expr->len, BITS_PER_BYTE);
+
+ nftnl_expr_set_u32(nle, NFT_EXPR_BITWISE_SREG, dreg);
+ nftnl_expr_set_u32(nle, NFT_EXPR_BITWISE_DREG, dreg);
+ nftnl_expr_set_u32(nle, NFT_EXPR_BITWISE_LEN, len);
+
+ netlink_gen_raw_data(mask, expr->byteorder, len, &nld);
+ nftnl_expr_set(nle, NFT_EXPR_BITWISE_MASK, nld.value, nld.len);
+ nftnl_expr_set(nle, NFT_EXPR_BITWISE_XOR, &zero.value, nld.len);
+
+ mpz_clear(mask);
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
@@ -111,9 +149,13 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
expr->payload.base - 1);
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET,
expr->payload.offset / BITS_PER_BYTE);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN,
- expr->len / BITS_PER_BYTE);
+ nftnl_expr_set_u32(nle, NFT_EXPR_PAYLOAD_LEN,
+ div_round_up(expr->len, BITS_PER_BYTE));
+
nftnl_rule_add_expr(ctx->nlr, nle);
+
+ if (expr->len % BITS_PER_BYTE)
+ netlink_gen_payload_mask(ctx, expr, dreg);
}
static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,