diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/netlink_delinearize.c | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index b9f195b0..82eefa61 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -1389,6 +1389,74 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx, } } +/* We have seen a protocol key expression that restricts matching at the network + * base, leave it in place since this is meaninful in bridge, inet and netdev + * families. Exceptions are ICMP and ICMPv6 where this code assumes that can + * only happen with IPv4 and IPv6. + */ +static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx, + unsigned int family, + const struct expr *expr) +{ + struct expr *dep = ctx->pdep->expr; + + if (ctx->pbase != PROTO_BASE_NETWORK_HDR) + return true; + + switch (family) { + case NFPROTO_INET: + switch (dep->left->ops->type) { + case EXPR_META: + if (dep->left->meta.key == NFT_META_NFPROTO && + (mpz_get_uint16(dep->right->value) == NFPROTO_IPV4 || + mpz_get_uint16(dep->right->value) == NFPROTO_IPV6) && + expr->left->meta.key == NFT_META_L4PROTO && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMP && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMPV6) + return false; + break; + case EXPR_PAYLOAD: + if (dep->left->payload.base == PROTO_BASE_LL_HDR && + (mpz_get_uint16(dep->right->value) == ETH_P_IP || + mpz_get_uint16(dep->right->value) == ETH_P_IPV6) && + expr->left->meta.key == NFT_META_L4PROTO && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMP && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMPV6) + return false; + break; + default: + break; + } + break; + case NFPROTO_NETDEV: + case NFPROTO_BRIDGE: + switch (dep->left->ops->type) { + case EXPR_META: + if (dep->left->meta.key == NFT_META_PROTOCOL && + (mpz_get_uint16(dep->right->value) == ETH_P_IP || + mpz_get_uint16(dep->right->value) == ETH_P_IPV6) && + expr->left->meta.key == NFT_META_L4PROTO && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMP && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMPV6) + return false; + break; + case EXPR_PAYLOAD: + if (dep->left->payload.base == PROTO_BASE_LL_HDR && + (mpz_get_uint16(dep->right->value) == ETH_P_IP || + mpz_get_uint16(dep->right->value) == ETH_P_IPV6) && + expr->left->meta.key == NFT_META_L4PROTO && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMP && + mpz_get_uint8(expr->right->value) != IPPROTO_ICMPV6) + return false; + break; + default: + break; + } + break; + } + return true; +} + static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, const struct expr *expr, enum proto_bases base) @@ -1407,7 +1475,9 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, left->flags & EXPR_F_PROTOCOL) { payload_dependency_store(&ctx->pdctx, ctx->stmt, base); } else if (ctx->pdctx.pbase < PROTO_BASE_TRANSPORT_HDR) { - if (payload_dependency_exists(&ctx->pdctx, base)) + if (payload_dependency_exists(&ctx->pdctx, base) && + meta_may_dependency_kill(&ctx->pdctx, + ctx->pctx.family, expr)) payload_dependency_release(&ctx->pdctx); if (left->flags & EXPR_F_PROTOCOL) payload_dependency_store(&ctx->pdctx, ctx->stmt, base); |