From 6979625686ec8d915f5ad5fdc28f24f55b6be3f7 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Fri, 16 Mar 2018 00:03:19 +0100 Subject: relational: Eliminate meta OPs With a bit of code reorganization, relational meta OPs OP_RANGE, OP_FLAGCMP and OP_LOOKUP become unused and can be removed. The only meta OP left is OP_IMPLICIT which is usually treated as alias to OP_EQ. Though it needs to stay in place for one reason: When matching against a bitmask (e.g. TCP flags or conntrack states), it has a different meaning: | nft --debug=netlink add rule ip t c tcp flags syn | ip t c | [ meta load l4proto => reg 1 ] | [ cmp eq reg 1 0x00000006 ] | [ payload load 1b @ transport header + 13 => reg 1 ] | [ bitwise reg 1 = (reg=1 & 0x00000002 ) ^ 0x00000000 ] | [ cmp neq reg 1 0x00000000 ] | nft --debug=netlink add rule ip t c tcp flags == syn | ip t c | [ meta load l4proto => reg 1 ] | [ cmp eq reg 1 0x00000006 ] | [ payload load 1b @ transport header + 13 => reg 1 ] | [ cmp eq reg 1 0x00000002 ] OP_IMPLICIT creates a match which just checks the given flag is present, while OP_EQ creates a match which ensures the given flag and no other is present. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- src/evaluate.c | 114 +++++++--------------------------------------- src/expression.c | 8 +--- src/netlink_delinearize.c | 21 ++++----- src/netlink_linearize.c | 20 +++++--- src/parser_bison.y | 2 +- src/rule.c | 13 +++++- 6 files changed, 53 insertions(+), 125 deletions(-) (limited to 'src') diff --git a/src/evaluate.c b/src/evaluate.c index b71b67b9..114c831f 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1565,28 +1565,6 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) return -1; right = rel->right; - if (rel->op == OP_IMPLICIT) { - switch (right->ops->type) { - case EXPR_RANGE: - rel->op = OP_RANGE; - break; - case EXPR_SET: - case EXPR_SET_REF: - rel->op = OP_LOOKUP; - break; - case EXPR_LIST: - rel->op = OP_FLAGCMP; - break; - default: - if (right->dtype->basetype != NULL && - right->dtype->basetype->type == TYPE_BITMASK) - rel->op = OP_FLAGCMP; - else - rel->op = OP_EQ; - break; - } - } - if (!expr_is_constant(right)) return expr_binary_error(ctx->msgs, right, rel, "Right hand side of relational " @@ -1598,56 +1576,34 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) "constant value", expr_op_symbols[rel->op]); + if (!datatype_equal(left->dtype, right->dtype)) + return expr_binary_error(ctx->msgs, right, left, + "datatype mismatch, expected %s, " + "expression has type %s", + left->dtype->desc, + right->dtype->desc); + switch (rel->op) { - case OP_LOOKUP: - /* A literal set expression implicitly declares the set */ - if (right->ops->type == EXPR_SET) - right = rel->right = - implicit_set_declaration(ctx, "__set%d", - left, right); - else if (!datatype_equal(left->dtype, right->dtype)) - return expr_binary_error(ctx->msgs, right, left, - "datatype mismatch, expected %s, " - "set has type %s", - left->dtype->desc, - right->dtype->desc); - - /* Data for range lookups needs to be in big endian order */ - if (right->set->flags & NFT_SET_INTERVAL && - byteorder_conversion(ctx, &rel->left, - BYTEORDER_BIG_ENDIAN) < 0) - return -1; - left = rel->left; - break; case OP_EQ: - if (!datatype_equal(left->dtype, right->dtype)) - return expr_binary_error(ctx->msgs, right, left, - "datatype mismatch, expected %s, " - "expression has type %s", - left->dtype->desc, - right->dtype->desc); + case OP_IMPLICIT: /* * Update protocol context for payload and meta iiftype * equality expressions. */ - relational_expr_pctx_update(&ctx->pctx, rel); - - if (left->ops->type == EXPR_CONCAT) - return 0; + if (expr_is_singleton(right)) + relational_expr_pctx_update(&ctx->pctx, rel); /* fall through */ case OP_NEQ: - case OP_FLAGCMP: - if (!datatype_equal(left->dtype, right->dtype)) - return expr_binary_error(ctx->msgs, right, left, - "datatype mismatch, expected %s, " - "expression has type %s", - left->dtype->desc, - right->dtype->desc); - switch (right->ops->type) { case EXPR_RANGE: - goto range; + if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + if (byteorder_conversion(ctx, &right->left, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + if (byteorder_conversion(ctx, &right->right, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + break; case EXPR_PREFIX: if (byteorder_conversion(ctx, &right->prefix, left->byteorder) < 0) return -1; @@ -1657,12 +1613,10 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) return -1; break; case EXPR_SET: - assert(rel->op == OP_NEQ); right = rel->right = implicit_set_declaration(ctx, "__set%d", left, right); /* fall through */ case EXPR_SET_REF: - assert(rel->op == OP_NEQ); /* Data for range lookups needs to be in big endian order */ if (right->set->flags & NFT_SET_INTERVAL && byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0) @@ -1676,13 +1630,6 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) case OP_GT: case OP_LTE: case OP_GTE: - if (!datatype_equal(left->dtype, right->dtype)) - return expr_binary_error(ctx->msgs, right, left, - "datatype mismatch, expected %s, " - "expression has type %s", - left->dtype->desc, - right->dtype->desc); - switch (left->ops->type) { case EXPR_CONCAT: return expr_binary_error(ctx->msgs, left, rel, @@ -1706,33 +1653,6 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) if (byteorder_conversion(ctx, &rel->right, BYTEORDER_BIG_ENDIAN) < 0) return -1; break; - case OP_RANGE: - if (!datatype_equal(left->dtype, right->dtype)) - return expr_binary_error(ctx->msgs, right, left, - "datatype mismatch, expected %s, " - "expression has type %s", - left->dtype->desc, - right->dtype->desc); - -range: - switch (left->ops->type) { - case EXPR_CONCAT: - return expr_binary_error(ctx->msgs, left, rel, - "Relational expression (%s) is undefined" - "for %s expressions", - expr_op_symbols[rel->op], - left->ops->name); - default: - break; - } - - if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0) - return -1; - if (byteorder_conversion(ctx, &right->left, BYTEORDER_BIG_ENDIAN) < 0) - return -1; - if (byteorder_conversion(ctx, &right->right, BYTEORDER_BIG_ENDIAN) < 0) - return -1; - break; default: BUG("invalid relational operation %u\n", rel->op); } diff --git a/src/expression.c b/src/expression.c index d7f54ad7..5f023d2a 100644 --- a/src/expression.c +++ b/src/expression.c @@ -496,8 +496,6 @@ const char *expr_op_symbols[] = { [OP_GT] = ">", [OP_LTE] = "<=", [OP_GTE] = ">=", - [OP_RANGE] = "within range", - [OP_LOOKUP] = NULL, }; static void unary_expr_print(const struct expr *expr, struct output_ctx *octx) @@ -562,10 +560,6 @@ static void binop_arg_print(const struct expr *op, const struct expr *arg, static bool must_print_eq_op(const struct expr *expr) { - if (expr->right->dtype->basetype != NULL && - expr->right->dtype->basetype->type == TYPE_BITMASK) - return true; - return expr->left->ops->type == EXPR_BINOP; } @@ -645,7 +639,7 @@ void relational_expr_pctx_update(struct proto_ctx *ctx, const struct expr *left = expr->left; assert(expr->ops->type == EXPR_RELATIONAL); - assert(expr->op == OP_EQ); + assert(expr->op == OP_EQ || expr->op == OP_IMPLICIT); if (left->ops->pctx_update && (left->flags & EXPR_F_PROTOCOL)) diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index d225b3e4..997fb53b 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -323,7 +323,7 @@ static void netlink_parse_lookup(struct netlink_parse_ctx *ctx, if (dreg != NFT_REG_VERDICT) return netlink_set_register(ctx, dreg, expr); } else { - expr = relational_expr_alloc(loc, OP_LOOKUP, left, right); + expr = relational_expr_alloc(loc, OP_EQ, left, right); } if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOOKUP_FLAGS)) { @@ -1524,9 +1524,14 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, const struct expr *left = expr->left; struct expr *right = expr->right; + if (right->ops->type == EXPR_SET || right->ops->type == EXPR_SET_REF) + expr_set_type(right, left->dtype, left->byteorder); + switch (expr->op) { case OP_EQ: - if (expr->right->ops->type == EXPR_RANGE) + if (expr->right->ops->type == EXPR_RANGE || + expr->right->ops->type == EXPR_SET || + expr->right->ops->type == EXPR_SET_REF) break; relational_expr_pctx_update(&ctx->pctx, expr); @@ -1543,14 +1548,6 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, payload_dependency_store(&ctx->pdctx, ctx->stmt, base); } break; - case OP_NEQ: - if (right->ops->type != EXPR_SET && right->ops->type != EXPR_SET_REF) - break; - /* fall through */ - case OP_LOOKUP: - expr_set_type(right, left->dtype, left->byteorder); - break; - default: break; } @@ -1729,13 +1726,13 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e /* Flag comparison: data & flags != 0 * * Split the flags into a list of flag values and convert the - * op to OP_FLAGCMP. + * op to OP_EQ. */ expr_free(value); expr->left = expr_get(binop->left); expr->right = binop_tree_to_list(NULL, binop->right); - expr->op = OP_FLAGCMP; + expr->op = OP_EQ; expr_free(binop); } else if (binop->left->dtype->flags & DTYPE_F_PREFIX && diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index be1c750c..dce8f5ce 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -297,6 +297,7 @@ static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op) { switch (op) { case OP_EQ: + case OP_IMPLICIT: return NFT_CMP_EQ; case OP_NEQ: return NFT_CMP_NEQ; @@ -316,6 +317,9 @@ static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op) static void netlink_gen_range(struct netlink_linearize_ctx *ctx, const struct expr *expr, enum nft_registers dreg); +static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg); static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx, const struct expr *expr, @@ -362,6 +366,8 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx, case EXPR_SET: case EXPR_SET_REF: return netlink_gen_lookup(ctx, expr, dreg); + case EXPR_LIST: + return netlink_gen_flagcmp(ctx, expr, dreg); case EXPR_PREFIX: sreg = get_register(ctx, expr->left); if (expr_basetype(expr->left)->type != TYPE_STRING) { @@ -376,6 +382,11 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx, } break; default: + if (expr->op == OP_IMPLICIT && + expr->right->dtype->basetype != NULL && + expr->right->dtype->basetype->type == TYPE_BITMASK) + return netlink_gen_flagcmp(ctx, expr, dreg); + sreg = get_register(ctx, expr->left); len = div_round_up(expr->right->len, BITS_PER_BYTE); right = expr->right; @@ -421,8 +432,8 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx, nld.value, nld.len); nftnl_rule_add_expr(ctx->nlr, nle); break; - case OP_RANGE: case OP_EQ: + case OP_IMPLICIT: nle = alloc_nft_expr("cmp"); netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, @@ -491,6 +502,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, enum nft_registers dreg) { switch (expr->op) { + case OP_IMPLICIT: case OP_EQ: case OP_NEQ: case OP_LT: @@ -498,12 +510,6 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, case OP_LTE: case OP_GTE: return netlink_gen_cmp(ctx, expr, dreg); - case OP_RANGE: - return netlink_gen_range(ctx, expr, dreg); - case OP_FLAGCMP: - return netlink_gen_flagcmp(ctx, expr, dreg); - case OP_LOOKUP: - return netlink_gen_lookup(ctx, expr, dreg); default: BUG("invalid relational operation %u\n", expr->op); } diff --git a/src/parser_bison.y b/src/parser_bison.y index 6fba7e59..bdf2fb49 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -3185,7 +3185,7 @@ relational_expr : expr /* implicit */ rhs_expr } | expr /* implicit */ list_rhs_expr { - $$ = relational_expr_alloc(&@$, OP_FLAGCMP, $1, $2); + $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2); } | expr relational_op rhs_expr { diff --git a/src/rule.c b/src/rule.c index c5bf6593..4334efac 100644 --- a/src/rule.c +++ b/src/rule.c @@ -2130,6 +2130,16 @@ static int payload_match_stmt_cmp(const void *p1, const void *p2) return e1->left->payload.offset - e2->left->payload.offset; } +static bool relational_ops_match(const struct expr *e1, const struct expr *e2) +{ + enum ops op1, op2; + + op1 = e1->op == OP_IMPLICIT ? OP_EQ : e1->op; + op2 = e2->op == OP_IMPLICIT ? OP_EQ : e2->op; + + return op1 == op2; +} + static void payload_do_merge(struct stmt *sa[], unsigned int n) { struct expr *last, *this, *expr1, *expr2; @@ -2144,7 +2154,7 @@ static void payload_do_merge(struct stmt *sa[], unsigned int n) this = stmt->expr; if (!payload_can_merge(last->left, this->left) || - last->op != this->op) { + !relational_ops_match(last, this)) { last = this; j = i; continue; @@ -2227,6 +2237,7 @@ static void payload_try_merge(const struct rule *rule) continue; switch (stmt->expr->op) { case OP_EQ: + case OP_IMPLICIT: case OP_NEQ: break; default: -- cgit v1.2.3