]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
netlink_delinearize: handle extension header templates with odd sizes
authorFlorian Westphal <fw@strlen.de>
Wed, 2 Mar 2016 12:56:43 +0000 (13:56 +0100)
committerFlorian Westphal <fw@strlen.de>
Wed, 2 Mar 2016 12:56:43 +0000 (13:56 +0100)
This enables nft to display
frag frag-off 33

... by considering a mask during binop postprocess in case
the initial template lookup done when the exthdr expression was
created did not yield a match.

In the above example, kernel netlink data specifies 16bits,
but the frag field is only 13bits wide.

We use the implicit binop mask to re-do the template lookup with
corrected offset and size information.

Signed-off-by: Florian Westphal <fw@strlen.de>
Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/exthdr.h
src/exthdr.c
src/netlink_delinearize.c

index 87c428576bbb1a5fb8a4f5abccb3b97222e152cb..d17841bc46159da1c10505caa8322834e5396ada 100644 (file)
@@ -23,6 +23,8 @@ extern struct expr *exthdr_expr_alloc(const struct location *loc,
 extern void exthdr_init_raw(struct expr *expr, uint8_t type,
                            unsigned int offset, unsigned int len);
 
+extern bool exthdr_find_template(struct expr *expr, const struct expr *mask,
+                                unsigned int *shift);
 
 enum hbh_hdr_fields {
        HBHHDR_INVALID,
index 512de0aedd9a41c0c44fc15dfa70868ea4d88139..f392cffe35ca8b0071242da7ba53e98d187dfe0c 100644 (file)
@@ -102,6 +102,37 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
        }
 }
 
+static unsigned int mask_length(const struct expr *mask)
+{
+       unsigned long off = mpz_scan1(mask->value, 0);
+
+       return mpz_scan0(mask->value, off + 1);
+}
+
+bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift)
+{
+       unsigned int off, mask_offset, mask_len;
+
+       if (expr->exthdr.tmpl != &exthdr_unknown_template)
+               return false;
+
+       mask_offset = mpz_scan1(mask->value, 0);
+       mask_len = mask_length(mask);
+
+       off = expr->exthdr.offset;
+       off += round_up(mask->len, BITS_PER_BYTE) - mask_len;
+
+       exthdr_init_raw(expr, expr->exthdr.desc->type,
+                       off, mask_len - mask_offset);
+
+       /* still failed to find a template... Bug. */
+       if (expr->exthdr.tmpl == &exthdr_unknown_template)
+               return false;
+
+       *shift = mask_offset;
+       return true;
+}
+
 #define HDR_TEMPLATE(__name, __dtype, __type, __member)                        \
        PROTO_HDR_TEMPLATE(__name, __dtype,                             \
                           BYTEORDER_BIG_ENDIAN,                        \
index 30c5f62e366275babb17492b28232bab5c88c14f..fae6e33d157f61880372a6c897d0a21a5349ef6c 100644 (file)
@@ -1218,7 +1218,9 @@ static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
        unsigned int shift;
 
        if ((left->ops->type == EXPR_PAYLOAD &&
-           payload_expr_trim(left, mask, &ctx->pctx, &shift))) {
+           payload_expr_trim(left, mask, &ctx->pctx, &shift)) ||
+           (left->ops->type == EXPR_EXTHDR &&
+            exthdr_find_template(left, mask, &shift))) {
                /* mask is implicit, binop needs to be removed.
                 *
                 * Fix all values of the expression according to the mask
@@ -1226,7 +1228,7 @@ static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
                 * sizes and offsets we're interested in.
                 *
                 * Finally, convert the expression to 1) by replacing
-                * the binop with the binop payload expr.
+                * the binop with the binop payload/exthdr expression.
                 */
                if (value->ops->type == EXPR_VALUE) {
                        assert(value->len >= expr->left->right->len);
@@ -1238,8 +1240,10 @@ static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
                assert(binop->left == left);
                expr->left = expr_get(left);
                expr_free(binop);
-
-               payload_match_postprocess(ctx, expr, left);
+               if (left->ops->type == EXPR_PAYLOAD)
+                       payload_match_postprocess(ctx, expr, left);
+               else if (left->ops->type == EXPR_EXTHDR)
+                       expr_set_type(expr->right, left->dtype, left->byteorder);
        }
 }