summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/netlink_delinearize.c72
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);