diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ct.c | 71 | ||||
-rw-r--r-- | src/evaluate.c | 92 | ||||
-rw-r--r-- | src/netlink_delinearize.c | 52 | ||||
-rw-r--r-- | src/parser_bison.y | 42 | ||||
-rw-r--r-- | src/rt.c | 15 |
5 files changed, 200 insertions, 72 deletions
@@ -16,6 +16,8 @@ #include <inttypes.h> #include <string.h> +#include <netinet/ip.h> +#include <linux/netfilter.h> #include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_conntrack_common.h> #include <linux/netfilter/nf_conntrack_tuple_common.h> @@ -269,9 +271,11 @@ static const struct ct_template ct_templates[] = { BYTEORDER_HOST_ENDIAN, 32), }; -static void ct_print(enum nft_ct_keys key, int8_t dir, struct output_ctx *octx) +static void ct_print(enum nft_ct_keys key, int8_t dir, uint8_t nfproto, + struct output_ctx *octx) { const struct symbolic_constant *s; + const struct proto_desc *desc; nft_print(octx, "ct "); if (dir < 0) @@ -283,13 +287,25 @@ static void ct_print(enum nft_ct_keys key, int8_t dir, struct output_ctx *octx) break; } } + + switch (key) { + case NFT_CT_SRC: /* fallthrough */ + case NFT_CT_DST: + desc = proto_find_upper(&proto_inet, nfproto); + if (desc) + printf("%s ", desc->name); + break; + default: + break; + } + done: nft_print(octx, "%s", ct_templates[key].token); } static void ct_expr_print(const struct expr *expr, struct output_ctx *octx) { - ct_print(expr->ct.key, expr->ct.direction, octx); + ct_print(expr->ct.key, expr->ct.direction, expr->ct.nfproto, octx); } static bool ct_expr_cmp(const struct expr *e1, const struct expr *e2) @@ -308,21 +324,21 @@ static void ct_expr_clone(struct expr *new, const struct expr *expr) static void ct_expr_pctx_update(struct proto_ctx *ctx, const struct expr *expr) { const struct expr *left = expr->left, *right = expr->right; - const struct proto_desc *base, *desc; + const struct proto_desc *base = NULL, *desc; + uint32_t nhproto; assert(expr->op == OP_EQ); - switch (left->ct.key) { - case NFT_CT_PROTOCOL: - base = ctx->protocol[PROTO_BASE_NETWORK_HDR].desc; - desc = proto_find_upper(base, mpz_get_uint32(right->value)); + nhproto = mpz_get_uint32(right->value); - proto_ctx_update(ctx, PROTO_BASE_TRANSPORT_HDR, - &expr->location, desc); - break; - default: - break; - } + base = ctx->protocol[left->ct.base].desc; + if (!base) + return; + desc = proto_find_upper(base, nhproto); + if (!desc) + return; + + proto_ctx_update(ctx, left->ct.base + 1, &expr->location, desc); } static const struct expr_ops ct_expr_ops = { @@ -335,7 +351,7 @@ static const struct expr_ops ct_expr_ops = { }; struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key, - int8_t direction) + int8_t direction, uint8_t nfproto) { const struct ct_template *tmpl = &ct_templates[key]; struct expr *expr; @@ -344,10 +360,24 @@ struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key, tmpl->byteorder, tmpl->len); expr->ct.key = key; expr->ct.direction = direction; + expr->ct.nfproto = nfproto; switch (key) { + case NFT_CT_SRC: + case NFT_CT_DST: + expr->ct.base = PROTO_BASE_NETWORK_HDR; + break; + case NFT_CT_PROTO_SRC: + case NFT_CT_PROTO_DST: + expr->ct.base = PROTO_BASE_TRANSPORT_HDR; + break; case NFT_CT_PROTOCOL: expr->flags = EXPR_F_PROTOCOL; + expr->ct.base = PROTO_BASE_NETWORK_HDR; + break; + case NFT_CT_L3PROTOCOL: + expr->flags = EXPR_F_PROTOCOL; + expr->ct.base = PROTO_BASE_LL_HDR; break; default: break; @@ -360,20 +390,23 @@ void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr) { const struct proto_desc *desc; + desc = ctx->protocol[expr->ct.base].desc; + switch (expr->ct.key) { case NFT_CT_SRC: case NFT_CT_DST: - desc = ctx->protocol[PROTO_BASE_NETWORK_HDR].desc; - if (desc == &proto_ip) + if (desc == &proto_ip) { expr->dtype = &ipaddr_type; - else if (desc == &proto_ip6) + expr->ct.nfproto = NFPROTO_IPV4; + } else if (desc == &proto_ip6) { expr->dtype = &ip6addr_type; + expr->ct.nfproto = NFPROTO_IPV6; + } expr->len = expr->dtype->size; break; case NFT_CT_PROTO_SRC: case NFT_CT_PROTO_DST: - desc = ctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc; if (desc == NULL) break; expr->dtype = &inet_service_type; @@ -385,7 +418,7 @@ void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr) static void ct_stmt_print(const struct stmt *stmt, struct output_ctx *octx) { - ct_print(stmt->ct.key, stmt->ct.direction, octx); + ct_print(stmt->ct.key, stmt->ct.direction, 0, octx); nft_print(octx, " set "); expr_print(stmt->ct.expr, octx); } diff --git a/src/evaluate.c b/src/evaluate.c index c796c3c3..ca9180b7 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -662,32 +662,28 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp) return 0; } -static int expr_error_base(struct list_head *msgs, const struct expr *e) -{ - return expr_error(msgs, e, - "meta nfproto ipv4 or ipv6 must be specified " - "before %s expression", e->ops->name); -} - /* * RT expression: validate protocol dependencies. */ static int expr_evaluate_rt(struct eval_ctx *ctx, struct expr **expr) { - const struct proto_desc *base; + static const char emsg[] = "cannot determine ip protocol version, use \"ip nexthop\" or \"ip6 nexthop\" instead"; struct expr *rt = *expr; rt_expr_update_type(&ctx->pctx, rt); - base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc; switch (rt->rt.key) { case NFT_RT_NEXTHOP4: - if (base != &proto_ip) - return expr_error_base(ctx->msgs, rt); + if (rt->dtype != &ipaddr_type) + return expr_error(ctx->msgs, rt, "%s", emsg); + if (ctx->pctx.family == NFPROTO_IPV6) + return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip"); break; case NFT_RT_NEXTHOP6: - if (base != &proto_ip6) - return expr_error_base(ctx->msgs, rt); + if (rt->dtype != &ip6addr_type) + return expr_error(ctx->msgs, rt, "%s", emsg); + if (ctx->pctx.family == NFPROTO_IPV4) + return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip6"); break; default: break; @@ -696,27 +692,85 @@ static int expr_evaluate_rt(struct eval_ctx *ctx, struct expr **expr) return expr_evaluate_primary(ctx, expr); } +static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct) +{ + const struct proto_desc *base, *base_now; + struct expr *left, *right, *dep; + struct stmt *nstmt = NULL; + + base_now = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc; + + switch (ct->ct.nfproto) { + case NFPROTO_IPV4: + base = &proto_ip; + break; + case NFPROTO_IPV6: + base = &proto_ip6; + break; + default: + base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc; + if (base == &proto_ip) + ct->ct.nfproto = NFPROTO_IPV4; + else if (base == &proto_ip) + ct->ct.nfproto = NFPROTO_IPV6; + + if (base) + break; + + return expr_error(ctx->msgs, ct, + "cannot determine ip protocol version, use \"ip %1$caddr\" or \"ip6 %1$caddr\" instead", + ct->ct.key == NFT_CT_SRC ? 's' : 'd'); + } + + /* no additional dependency needed? */ + if (base == base_now) + return 0; + + if (base_now && base_now != base) + return expr_error(ctx->msgs, ct, + "conflicting dependencies: %s vs. %s\n", + base->name, + ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name); + switch (ctx->pctx.family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + return 0; + } + + left = ct_expr_alloc(&ct->location, NFT_CT_L3PROTOCOL, ct->ct.direction, ct->ct.nfproto); + + right = constant_expr_alloc(&ct->location, left->dtype, + left->dtype->byteorder, left->len, + constant_data_ptr(ct->ct.nfproto, left->len)); + dep = relational_expr_alloc(&ct->location, OP_EQ, left, right); + + left->ops->pctx_update(&ctx->pctx, dep); + + nstmt = expr_stmt_alloc(&dep->location, dep); + + list_add_tail(&nstmt->list, &ctx->stmt->list); + return 0; +} + /* * CT expression: update the protocol dependant types bases on the protocol * context. */ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr) { - const struct proto_desc *base; struct expr *ct = *expr; - ct_expr_update_type(&ctx->pctx, ct); - - base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc; switch (ct->ct.key) { case NFT_CT_SRC: case NFT_CT_DST: - if (base != &proto_ip && base != &proto_ip6) - return expr_error_base(ctx->msgs, ct); + ct_gen_nh_dependency(ctx, ct); + break; default: break; } + ct_expr_update_type(&ctx->pctx, ct); + return expr_evaluate_primary(ctx, expr); } diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 42206ebc..44328879 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -716,7 +716,7 @@ static void netlink_parse_ct_expr(struct netlink_parse_ctx *ctx, dir = nftnl_expr_get_u8(nle, NFTNL_EXPR_CT_DIR); key = nftnl_expr_get_u32(nle, NFTNL_EXPR_CT_KEY); - expr = ct_expr_alloc(loc, key, dir); + expr = ct_expr_alloc(loc, key, dir, NFPROTO_UNSPEC); dreg = netlink_parse_register(nle, NFTNL_EXPR_CT_DREG); netlink_set_register(ctx, dreg, expr); @@ -1385,12 +1385,29 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx, } } -static void ct_meta_common_postprocess(const struct expr *expr) +static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, + const struct expr *expr, + enum proto_bases base) { const struct expr *left = expr->left; struct expr *right = expr->right; switch (expr->op) { + case OP_EQ: + if (expr->right->ops->type == EXPR_RANGE) + break; + + expr->left->ops->pctx_update(&ctx->pctx, expr); + + if (ctx->pdctx.pbase == PROTO_BASE_INVALID && + left->flags & EXPR_F_PROTOCOL) { + payload_dependency_store(&ctx->pdctx, ctx->stmt, base); + } else if (ctx->pdctx.pbase < PROTO_BASE_TRANSPORT_HDR) { + __payload_dependency_kill(&ctx->pdctx, base); + if (left->flags & EXPR_F_PROTOCOL) + 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; @@ -1406,40 +1423,17 @@ static void ct_meta_common_postprocess(const struct expr *expr) static void meta_match_postprocess(struct rule_pp_ctx *ctx, const struct expr *expr) { - struct expr *left = expr->left; - - switch (expr->op) { - case OP_EQ: - if (expr->right->ops->type == EXPR_RANGE) - break; - - expr->left->ops->pctx_update(&ctx->pctx, expr); + const struct expr *left = expr->left; - if (ctx->pdctx.pbase == PROTO_BASE_INVALID && - left->flags & EXPR_F_PROTOCOL) - payload_dependency_store(&ctx->pdctx, ctx->stmt, - left->meta.base); - break; - default: - ct_meta_common_postprocess(expr); - break; - } + ct_meta_common_postprocess(ctx, expr, left->meta.base); } static void ct_match_postprocess(struct rule_pp_ctx *ctx, const struct expr *expr) { - switch (expr->op) { - case OP_EQ: - if (expr->right->ops->type == EXPR_RANGE) - break; + const struct expr *left = expr->left; - expr->left->ops->pctx_update(&ctx->pctx, expr); - break; - default: - ct_meta_common_postprocess(expr); - break; - } + ct_meta_common_postprocess(ctx, expr, left->ct.base); } /* Convert a bitmask to a prefix length */ diff --git a/src/parser_bison.y b/src/parser_bison.y index 75a77358..f996d9d9 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -665,11 +665,11 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type <expr> rt_expr %destructor { expr_free($$); } rt_expr -%type <val> rt_key +%type <val> rt_key_proto rt_key %type <expr> ct_expr %destructor { expr_free($$); } ct_expr -%type <val> ct_key ct_dir ct_key_dir_optional ct_key_dir +%type <val> ct_key ct_dir ct_key_dir_optional ct_key_dir ct_key_proto ct_key_proto_field %type <expr> fib_expr %destructor { expr_free($$); } fib_expr @@ -3246,10 +3246,32 @@ hash_expr : JHASH expr MOD NUM SEED NUM offset_opt } ; +rt_key_proto : IP { $$ = NFPROTO_IPV4; } + | IP6 { $$ = NFPROTO_IPV6; } + ; + rt_expr : RT rt_key { $$ = rt_expr_alloc(&@$, $2, true); } + | RT rt_key_proto rt_key + { + enum nft_rt_keys rtk = $3; + + switch ($2) { + case NFPROTO_IPV4: + break; + case NFPROTO_IPV6: + if ($3 == NFT_RT_NEXTHOP4) + rtk = NFT_RT_NEXTHOP6; + break; + default: + YYERROR; + break; + } + + $$ = rt_expr_alloc(&@$, rtk, false); + } ; rt_key : CLASSID { $$ = NFT_RT_CLASSID; } @@ -3259,11 +3281,15 @@ rt_key : CLASSID { $$ = NFT_RT_CLASSID; } ct_expr : CT ct_key { - $$ = ct_expr_alloc(&@$, $2, -1); + $$ = ct_expr_alloc(&@$, $2, -1, NFPROTO_UNSPEC); } | CT ct_dir ct_key_dir { - $$ = ct_expr_alloc(&@$, $3, $2); + $$ = ct_expr_alloc(&@$, $3, $2, NFPROTO_UNSPEC); + } + | CT ct_dir ct_key_proto ct_key_proto_field + { + $$ = ct_expr_alloc(&@$, $4, $2, $3); } ; @@ -3297,6 +3323,14 @@ ct_key_dir : SADDR { $$ = NFT_CT_SRC; } | ct_key_dir_optional ; +ct_key_proto : IP { $$ = NFPROTO_IPV4; } + | IP6 { $$ = NFPROTO_IPV6; } + ; + +ct_key_proto_field : SADDR { $$ = NFT_CT_SRC; } + | DADDR { $$ = NFT_CT_DST; } + ; + ct_key_dir_optional : BYTES { $$ = NFT_CT_BYTES; } | PACKETS { $$ = NFT_CT_PKTS; } | AVGPKT { $$ = NFT_CT_AVGPKT; } @@ -82,7 +82,20 @@ static const struct rt_template rt_templates[] = { static void rt_expr_print(const struct expr *expr, struct output_ctx *octx) { - nft_print(octx, "rt %s", rt_templates[expr->rt.key].token); + const char *ip = ""; + + switch (expr->rt.key) { + case NFT_RT_NEXTHOP4: + ip = "ip "; + break; + case NFT_RT_NEXTHOP6: + ip = "ip6 "; + break; + default: + break; + } + + nft_print(octx, "rt %s%s", ip, rt_templates[expr->rt.key].token); } static bool rt_expr_cmp(const struct expr *e1, const struct expr *e2) |