diff options
-rw-r--r-- | include/expression.h | 11 | ||||
-rw-r--r-- | include/json.h | 2 | ||||
-rw-r--r-- | src/evaluate.c | 17 | ||||
-rw-r--r-- | src/expression.c | 51 | ||||
-rw-r--r-- | src/json.c | 14 | ||||
-rw-r--r-- | src/netlink_delinearize.c | 64 | ||||
-rw-r--r-- | src/parser_bison.y | 16 | ||||
-rw-r--r-- | tests/py/inet/tcp.t | 2 | ||||
-rw-r--r-- | tests/py/inet/tcp.t.payload | 2 |
9 files changed, 155 insertions, 24 deletions
diff --git a/include/expression.h b/include/expression.h index be703d75..742fcdd7 100644 --- a/include/expression.h +++ b/include/expression.h @@ -72,6 +72,7 @@ enum expr_types { EXPR_FIB, EXPR_XFRM, EXPR_SET_ELEM_CATCHALL, + EXPR_FLAGCMP, }; #define EXPR_MAX EXPR_XFRM @@ -370,6 +371,12 @@ struct expr { uint8_t ttl; uint32_t flags; } osf; + struct { + /* EXPR_FLAGCMP */ + struct expr *expr; + struct expr *mask; + struct expr *value; + } flagcmp; }; }; @@ -500,6 +507,10 @@ extern struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *set_elem_catchall_expr_alloc(const struct location *loc); +struct expr *flagcmp_expr_alloc(const struct location *loc, enum ops op, + struct expr *expr, struct expr *mask, + struct expr *value); + extern void range_expr_value_low(mpz_t rop, const struct expr *expr); extern void range_expr_value_high(mpz_t rop, const struct expr *expr); diff --git a/include/json.h b/include/json.h index 41142208..dd594bd0 100644 --- a/include/json.h +++ b/include/json.h @@ -28,6 +28,7 @@ struct list_head; json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx); json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx); +json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx); json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx); json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx); json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx); @@ -127,6 +128,7 @@ static inline json_t *name##_json(arg1_t arg1, arg2_t arg2) { return NULL; } JSON_PRINT_STUB(name##_stmt, const struct stmt *, struct output_ctx *) EXPR_PRINT_STUB(binop_expr) +EXPR_PRINT_STUB(flagcmp_expr) EXPR_PRINT_STUB(relational_expr) EXPR_PRINT_STUB(range_expr) EXPR_PRINT_STUB(meta_expr) diff --git a/src/evaluate.c b/src/evaluate.c index 2e31ed10..a3a1d1c0 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -2134,6 +2134,21 @@ static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp) return expr_evaluate_primary(ctx, exprp); } +static int expr_evaluate_flagcmp(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp, *binop, *rel; + + binop = binop_expr_alloc(&expr->location, OP_AND, + expr_get(expr->flagcmp.expr), + expr_get(expr->flagcmp.mask)); + rel = relational_expr_alloc(&expr->location, expr->op, binop, + expr_get(expr->flagcmp.value)); + expr_free(expr); + *exprp = rel; + + return expr_evaluate(ctx, exprp); +} + static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) { if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { @@ -2203,6 +2218,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) return expr_evaluate_xfrm(ctx, expr); case EXPR_SET_ELEM_CATCHALL: return 0; + case EXPR_FLAGCMP: + return expr_evaluate_flagcmp(ctx, expr); default: BUG("unknown expression type %s\n", expr_name(*expr)); } diff --git a/src/expression.c b/src/expression.c index b3400751..7ae075d2 100644 --- a/src/expression.c +++ b/src/expression.c @@ -1351,6 +1351,56 @@ struct expr *set_elem_catchall_expr_alloc(const struct location *loc) return expr; } +static void flagcmp_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + expr_print(expr->flagcmp.expr, octx); + nft_print(octx, " "); + expr_print(expr->flagcmp.value, octx); + nft_print(octx, " / "); + expr_print(expr->flagcmp.mask, octx); +} + +static void flagcmp_expr_clone(struct expr *new, const struct expr *expr) +{ + new->flagcmp.expr = expr_clone(expr->flagcmp.expr); + new->flagcmp.mask = expr_clone(expr->flagcmp.mask); + new->flagcmp.value = expr_clone(expr->flagcmp.value); +} + +static void flagcmp_expr_destroy(struct expr *expr) +{ + expr_free(expr->flagcmp.expr); + expr_free(expr->flagcmp.mask); + expr_free(expr->flagcmp.value); +} + +static const struct expr_ops flagcmp_expr_ops = { + .type = EXPR_FLAGCMP, + .name = "flags comparison", + .print = flagcmp_expr_print, + .json = flagcmp_expr_json, + .clone = flagcmp_expr_clone, + .destroy = flagcmp_expr_destroy, +}; + +struct expr *flagcmp_expr_alloc(const struct location *loc, enum ops op, + struct expr *match, struct expr *mask, + struct expr *value) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_FLAGCMP, match->dtype, match->byteorder, + match->len); + expr->op = op; + expr->flagcmp.expr = match; + expr->flagcmp.mask = mask; + /* json output needs this operation for compatibility */ + expr->flagcmp.mask->op = OP_OR; + expr->flagcmp.value = value; + + return expr; +} + void range_expr_value_low(mpz_t rop, const struct expr *expr) { switch (expr->etype) { @@ -1427,6 +1477,7 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype) case EXPR_FIB: return &fib_expr_ops; case EXPR_XFRM: return &xfrm_expr_ops; case EXPR_SET_ELEM_CATCHALL: return &set_elem_catchall_expr_ops; + case EXPR_FLAGCMP: return &flagcmp_expr_ops; } BUG("Unknown expression type %d\n", etype); @@ -479,6 +479,20 @@ static json_t *table_print_json(const struct table *table) return json_pack("{s:o}", "table", root); } +json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *left; + + left = json_pack("{s:[o, o]}", expr_op_symbols[OP_AND], + expr_print_json(expr->flagcmp.expr, octx), + expr_print_json(expr->flagcmp.mask, octx)); + + return json_pack("{s:{s:s, s:o, s:o}}", "match", + "op", expr_op_symbols[expr->op] ? : "in", + "left", left, + "right", expr_print_json(expr->flagcmp.value, octx)); +} + json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx) { return json_pack("{s:[o, o]}", expr_op_symbols[expr->op], diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 81fe4c16..75869d33 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -2166,35 +2166,55 @@ static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr) binop_postprocess(ctx, expr); } -static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr) +static void relational_binop_postprocess(struct rule_pp_ctx *ctx, + struct expr **exprp) { - struct expr *binop = expr->left, *value = expr->right; + struct expr *expr = *exprp, *binop = expr->left, *value = expr->right; if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) && value->dtype->basetype && - value->dtype->basetype->type == TYPE_BITMASK && - value->etype == EXPR_VALUE && - !mpz_cmp_ui(value->value, 0)) { - /* Flag comparison: data & flags != 0 - * - * Split the flags into a list of flag values and convert the - * op to OP_EQ. - */ - expr_free(value); - - expr->left = expr_get(binop->left); - expr->right = binop_tree_to_list(NULL, binop->right); - switch (expr->op) { - case OP_NEQ: - expr->op = OP_IMPLICIT; + value->dtype->basetype->type == TYPE_BITMASK) { + switch (value->etype) { + case EXPR_VALUE: + if (!mpz_cmp_ui(value->value, 0)) { + /* Flag comparison: data & flags != 0 + * + * Split the flags into a list of flag values and convert the + * op to OP_EQ. + */ + expr_free(value); + + expr->left = expr_get(binop->left); + expr->right = binop_tree_to_list(NULL, binop->right); + switch (expr->op) { + case OP_NEQ: + expr->op = OP_IMPLICIT; + break; + case OP_EQ: + expr->op = OP_NEG; + break; + default: + BUG("unknown operation type %d\n", expr->op); + } + expr_free(binop); + } else { + *exprp = flagcmp_expr_alloc(&expr->location, expr->op, + expr_get(binop->left), + binop_tree_to_list(NULL, binop->right), + expr_get(value)); + expr_free(expr); + } break; - case OP_EQ: - expr->op = OP_NEG; + case EXPR_BINOP: + *exprp = flagcmp_expr_alloc(&expr->location, expr->op, + expr_get(binop->left), + binop_tree_to_list(NULL, binop->right), + binop_tree_to_list(NULL, value)); + expr_free(expr); break; default: - BUG("unknown operation type %d\n", expr->op); + break; } - expr_free(binop); } else if (binop->left->dtype->flags & DTYPE_F_PREFIX && binop->op == OP_AND && expr->right->etype == EXPR_VALUE && expr_mask_is_prefix(binop->right)) { @@ -2403,7 +2423,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(ctx, expr); + relational_binop_postprocess(ctx, exprp); break; default: break; diff --git a/src/parser_bison.y b/src/parser_bison.y index 000eb40a..cdb04fa8 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -4463,6 +4463,22 @@ relational_expr : expr /* implicit */ rhs_expr { $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2); } + | expr /* implicit */ basic_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2); + } + | expr /* implicit */ list_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2); + } + | expr relational_op basic_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3); + } + | expr relational_op list_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3); + } | expr relational_op rhs_expr { $$ = relational_expr_alloc(&@2, $2, $1, $3); diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t index 5f2caea9..a8d46831 100644 --- a/tests/py/inet/tcp.t +++ b/tests/py/inet/tcp.t @@ -77,7 +77,7 @@ tcp flags != { fin, urg, ecn, cwr} drop;ok tcp flags cwr;ok tcp flags != cwr;ok tcp flags == syn;ok -tcp flags & (syn|fin) == (syn|fin);ok;tcp flags & (fin | syn) == fin | syn +tcp flags fin,syn / fin,syn;ok tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr;ok;tcp flags == 0xff tcp flags { syn, syn | ack };ok tcp flags & (fin | syn | rst | psh | ack | urg) == { fin, ack, psh | ack, fin | psh | ack };ok diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload index da932b6d..07ac151c 100644 --- a/tests/py/inet/tcp.t.payload +++ b/tests/py/inet/tcp.t.payload @@ -434,7 +434,7 @@ inet test-inet input [ payload load 1b @ transport header + 13 => reg 1 ] [ cmp eq reg 1 0x00000002 ] -# tcp flags & (syn|fin) == (syn|fin) +# tcp flags fin,syn / fin,syn inet test-inet input [ meta load l4proto => reg 1 ] [ cmp eq reg 1 0x00000006 ] |