From 39f15c243912a20f3014c0efba6b8dbc80caf0e9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 7 Aug 2015 12:09:08 +0200 Subject: nft: support listing expressions that use non-byte header fields This allows to list rules that check fields that are not aligned on byte boundary. Signed-off-by: Florian Westphal --- src/evaluate.c | 6 ++++ src/netlink_delinearize.c | 80 +++++++++++++++++++++++++++++++++++++++------ src/payload.c | 83 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 156 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/evaluate.c b/src/evaluate.c index 95469180..581f3641 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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; } diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index af972032..16e3aaaf 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -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; diff --git a/src/payload.c b/src/payload.c index 880b5d0d..23afa2f2 100644 --- a/src/payload.c +++ b/src/payload.c @@ -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; -- cgit v1.2.3