]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
nft: support listing expressions that use non-byte header fields
authorFlorian Westphal <fw@strlen.de>
Fri, 7 Aug 2015 10:09:08 +0000 (12:09 +0200)
committerFlorian Westphal <fw@strlen.de>
Thu, 17 Sep 2015 22:06:49 +0000 (00:06 +0200)
This allows to list rules that check fields that are not aligned on byte
boundary.

Signed-off-by: Florian Westphal <fw@strlen.de>
include/payload.h
src/evaluate.c
src/netlink_delinearize.c
src/payload.c

index 95364af1410d478f399118b8417e71ee9d801755..ca9b013865168c4451a16352466d41c1d784167d 100644 (file)
@@ -19,7 +19,10 @@ extern bool payload_is_adjacent(const struct expr *e1, const struct expr *e2);
 extern struct expr *payload_expr_join(const struct expr *e1,
                                      const struct expr *e2);
 
+bool payload_expr_trim(struct expr *expr, struct expr *mask,
+                      const struct proto_ctx *ctx);
 extern void payload_expr_expand(struct list_head *list, struct expr *expr,
+                               struct expr *mask,
                                const struct proto_ctx *ctx);
 extern void payload_expr_complete(struct expr *expr,
                                  const struct proto_ctx *ctx);
index 9546918057daa25665b0f027847f5b1167d5436d..581f364166a03b1fc1784c0b029efe5f3bb9b932 100644 (file)
@@ -310,8 +310,13 @@ static bool resolve_protocol_conflict(struct eval_ctx *ctx,
                const struct proto_desc *next = ctx->pctx.protocol[base + 1].desc;
 
                if (payload->payload.desc == next) {
+                       ctx->pctx.protocol[base + 1].desc = NULL;
+                       ctx->pctx.protocol[base].desc = next;
+                       ctx->pctx.protocol[base].offset += desc->length;
                        payload->payload.offset += desc->length;
                        return true;
+               } else if (next) {
+                       return false;
                }
        }
 
@@ -321,6 +326,7 @@ static bool resolve_protocol_conflict(struct eval_ctx *ctx,
 
        payload->payload.offset += ctx->pctx.protocol[base].offset;
        list_add_tail(&nstmt->list, &ctx->stmt->list);
+       ctx->pctx.protocol[base + 1].desc = NULL;
 
        return true;
 }
index af972032d8d98291e912a7e310d2343915c1aae5..16e3aaaf9d358d7d0bab0f7b925232d7edf9b90e 100644 (file)
@@ -918,16 +918,20 @@ static void integer_type_postprocess(struct expr *expr)
        }
 }
 
-static void payload_match_expand(struct rule_pp_ctx *ctx, struct expr *expr)
+static void payload_match_expand(struct rule_pp_ctx *ctx,
+                                struct expr *expr,
+                                struct expr *payload,
+                                struct expr *mask)
 {
-       struct expr *left = expr->left, *right = expr->right, *tmp;
+       struct expr *left = payload, *right = expr->right, *tmp;
        struct list_head list = LIST_HEAD_INIT(list);
        struct stmt *nstmt;
        struct expr *nexpr = NULL;
        enum proto_bases base = left->payload.base;
        const struct expr_ops *payload_ops = left->ops;
 
-       payload_expr_expand(&list, left, &ctx->pctx);
+       payload_expr_expand(&list, left, mask, &ctx->pctx);
+
        list_for_each_entry(left, &list, list) {
                tmp = constant_expr_splice(right, left->len);
                expr_set_type(tmp, left->dtype, left->byteorder);
@@ -980,10 +984,11 @@ static void payload_match_expand(struct rule_pp_ctx *ctx, struct expr *expr)
 }
 
 static void payload_match_postprocess(struct rule_pp_ctx *ctx,
-                                     struct expr *expr)
+                                     struct expr *expr,
+                                     struct expr *payload,
+                                     struct expr *mask)
 {
-       enum proto_bases base = expr->left->payload.base;
-       struct expr *payload = expr->left;
+       enum proto_bases base = payload->payload.base;
 
        assert(payload->payload.offset >= ctx->pctx.protocol[base].offset);
        payload->payload.offset -= ctx->pctx.protocol[base].offset;
@@ -992,7 +997,7 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
        case OP_EQ:
        case OP_NEQ:
                if (expr->right->ops->type == EXPR_VALUE) {
-                       payload_match_expand(ctx, expr);
+                       payload_match_expand(ctx, expr, payload, mask);
                        break;
                }
                /* Fall through */
@@ -1073,7 +1078,7 @@ static struct expr *binop_tree_to_list(struct expr *list, struct expr *expr)
        return list;
 }
 
-static void relational_binop_postprocess(struct expr *expr)
+static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
 {
        struct expr *binop = expr->left, *value = expr->right;
 
@@ -1101,6 +1106,61 @@ static void relational_binop_postprocess(struct expr *expr)
                                                expr_mask_to_prefix(binop->right));
                expr_free(value);
                expr_free(binop);
+       } else if (binop->op == OP_AND &&
+                  binop->left->ops->type == EXPR_PAYLOAD &&
+                  binop->right->ops->type == EXPR_VALUE) {
+               struct expr *payload = expr->left->left;
+               struct expr *mask = expr->left->right;
+
+               /*
+                * This *might* be a payload match testing header fields that
+                * have non byte divisible offsets and/or bit lengths.
+                *
+                * Thus we need to deal with two different cases.
+                *
+                * 1 the simple version:
+                *        relation
+                * payload        value|setlookup
+                *
+                * expr: relation, left: payload, right: value, e.g.  tcp dport == 22.
+                *
+                * 2. The '&' version (this is what we're looking at now).
+                *            relation
+                *     binop          value1|setlookup
+                * payload  value2
+                *
+                * expr: relation, left: binop, right: value, e.g.
+                * ip saddr 10.0.0.0/8
+                *
+                * payload_expr_trim will figure out if the mask is needed to match
+                * templates.
+                */
+               if (payload_expr_trim(payload, mask, &ctx->pctx)) {
+                       /* mask is implicit, binop needs to be removed.
+                        *
+                        * Fix all values of the expression according to the mask
+                        * and then process the payload instruction using the real
+                        * sizes and offsets we're interested in.
+                        *
+                        * Finally, convert the expression to 1) by replacing
+                        * the binop with the binop payload expr.
+                        */
+                       if (value->ops->type == EXPR_VALUE) {
+                               assert(value->len >= expr->left->right->len);
+                               value->len = mask->len;
+                       }
+
+                       payload->len = mask->len;
+                       payload->payload.offset += mpz_scan1(mask->value, 0);
+
+                       payload_match_postprocess(ctx, expr, payload, mask);
+
+                       assert(expr->left->ops->type == EXPR_BINOP);
+
+                       assert(binop->left == payload);
+                       expr->left = payload;
+                       expr_free(binop);
+               }
        }
 }
 
@@ -1159,7 +1219,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
        case EXPR_RELATIONAL:
                switch (expr->left->ops->type) {
                case EXPR_PAYLOAD:
-                       payload_match_postprocess(ctx, expr);
+                       payload_match_postprocess(ctx, expr, expr->left, NULL);
                        return;
                default:
                        expr_postprocess(ctx, &expr->left);
@@ -1174,7 +1234,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
                        meta_match_postprocess(ctx, expr);
                        break;
                case EXPR_BINOP:
-                       relational_binop_postprocess(expr);
+                       relational_binop_postprocess(ctx, expr);
                        break;
                default:
                        break;
index 880b5d0d0b9708eded6c0cb9073366e8f5b5d15a..23afa2f21709983a7c2f38508f46d5ce76125a39 100644 (file)
@@ -297,12 +297,87 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
        }
 }
 
+static unsigned int mask_to_offset(const struct expr *mask)
+{
+       return mask ? mpz_scan1(mask->value, 0) : 0;
+}
+
+static unsigned int mask_length(const struct expr *mask)
+{
+       unsigned long off;
+
+        off = mask_to_offset(mask);
+
+       return mpz_scan0(mask->value, off + 1);
+}
+
+/**
+ * payload_expr_trim - trim payload expression according to mask
+ *
+ * @expr:      the payload expression
+ * @mask:      mask to use when searching templates
+ * @ctx:       protocol context
+ *
+ * Walk the template list and determine if a match can be found without
+ * using the provided mask.
+ *
+ * If the mask has to be used, trim the mask length accordingly
+ * and return true to let the caller know that the mask is a dependency.
+ */
+bool payload_expr_trim(struct expr *expr, struct expr *mask,
+                      const struct proto_ctx *ctx)
+{
+       unsigned int payload_offset = expr->payload.offset + mask_to_offset(mask);
+       unsigned int mask_len = mask_length(mask);
+       const struct proto_hdr_template *tmpl;
+       unsigned int payload_len = expr->len;
+       const struct proto_desc *desc;
+       unsigned int i, matched_len = mask_to_offset(mask);
+
+       assert(expr->ops->type == EXPR_PAYLOAD);
+
+       desc = ctx->protocol[expr->payload.base].desc;
+       if (desc == NULL)
+               return false;
+
+       assert(desc->base == expr->payload.base);
+
+       if (ctx->protocol[expr->payload.base].offset) {
+               assert(payload_offset >= ctx->protocol[expr->payload.base].offset);
+               payload_offset -= ctx->protocol[expr->payload.base].offset;
+       }
+
+       for (i = 1; i < array_size(desc->templates); i++) {
+               tmpl = &desc->templates[i];
+               if (tmpl->offset != payload_offset)
+                       continue;
+
+               if (tmpl->len > payload_len)
+                       return false;
+
+               payload_len -= tmpl->len;
+               matched_len += tmpl->len;
+               payload_offset += tmpl->len;
+               if (payload_len == 0)
+                       return false;
+
+               if (matched_len == mask_len) {
+                       assert(mask->len >= mask_len);
+                       mask->len = mask_len;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 /**
  * payload_expr_expand - expand raw merged adjacent payload expressions into its
  *                      original components
  *
  * @list:      list to append expanded payload expressions to
  * @expr:      the payload expression to expand
+ * @mask:      optional/implicit mask to use when searching templates
  * @ctx:       protocol context
  *
  * Expand a merged adjacent payload expression into its original components
@@ -312,14 +387,16 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
  *      offset order.
  */
 void payload_expr_expand(struct list_head *list, struct expr *expr,
-                        const struct proto_ctx *ctx)
+                        struct expr *mask, const struct proto_ctx *ctx)
 {
-       const struct proto_desc *desc;
+       unsigned int off = mask_to_offset(mask);
        const struct proto_hdr_template *tmpl;
+       const struct proto_desc *desc;
        struct expr *new;
        unsigned int i;
 
        assert(expr->ops->type == EXPR_PAYLOAD);
+       assert(!mask || mask->len != 0);
 
        desc = ctx->protocol[expr->payload.base].desc;
        if (desc == NULL)
@@ -336,7 +413,7 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
                        list_add_tail(&new->list, list);
                        expr->len            -= tmpl->len;
                        expr->payload.offset += tmpl->len;
-                       if (expr->len == 0)
+                       if (expr->len == off)
                                return;
                } else
                        break;