]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
netfilter: nft_ct: enable labels for get case too
authorFlorian Westphal <fw@strlen.de>
Wed, 22 Oct 2025 15:18:10 +0000 (17:18 +0200)
committerFlorian Westphal <fw@strlen.de>
Wed, 29 Oct 2025 13:47:59 +0000 (14:47 +0100)
conntrack labels can only be set when the conntrack has been created
with the "ctlabel" extension.

For older iptables (connlabel match), adding an "-m connlabel" rule
turns on the ctlabel extension allocation for all future conntrack
entries.

For nftables, its only enabled for 'ct label set foo', but not for
'ct label foo' (i.e. check).
But users could have a ruleset that only checks for presence, and rely
on userspace to set a label bit via ctnetlink infrastructure.

This doesn't work without adding a dummy 'ct label set' rule.
We could also enable extension infra for the first (failing) ctnetlink
request, but unlike ruleset we would not be able to disable the
extension again.

Therefore turn on ctlabel extension allocation if an nftables ruleset
checks for a connlabel too.

Fixes: 1ad8f48df6f6 ("netfilter: nftables: add connlabel set support")
Reported-by: Antonio Ojea <aojea@google.com>
Closes: https://lore.kernel.org/netfilter-devel/aPi_VdZpVjWujZ29@strlen.de/
Signed-off-by: Florian Westphal <fw@strlen.de>
net/netfilter/nft_ct.c

index d526e69a2a2b80a51b7f6b4854a2821964d874ae..a418eb3d612b8da68528da07ba04914bb8dec12b 100644 (file)
@@ -379,6 +379,14 @@ static bool nft_ct_tmpl_alloc_pcpu(void)
 }
 #endif
 
+static void __nft_ct_get_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
+{
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+       if (priv->key == NFT_CT_LABELS)
+               nf_connlabels_put(ctx->net);
+#endif
+}
+
 static int nft_ct_get_init(const struct nft_ctx *ctx,
                           const struct nft_expr *expr,
                           const struct nlattr * const tb[])
@@ -413,6 +421,10 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
                if (tb[NFTA_CT_DIRECTION] != NULL)
                        return -EINVAL;
                len = NF_CT_LABELS_MAX_SIZE;
+
+               err = nf_connlabels_get(ctx->net, (len * BITS_PER_BYTE) - 1);
+               if (err)
+                       return err;
                break;
 #endif
        case NFT_CT_HELPER:
@@ -494,7 +506,8 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
                case IP_CT_DIR_REPLY:
                        break;
                default:
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto err;
                }
        }
 
@@ -502,11 +515,11 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
        err = nft_parse_register_store(ctx, tb[NFTA_CT_DREG], &priv->dreg, NULL,
                                       NFT_DATA_VALUE, len);
        if (err < 0)
-               return err;
+               goto err;
 
        err = nf_ct_netns_get(ctx->net, ctx->family);
        if (err < 0)
-               return err;
+               goto err;
 
        if (priv->key == NFT_CT_BYTES ||
            priv->key == NFT_CT_PKTS  ||
@@ -514,6 +527,9 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
                nf_ct_set_acct(ctx->net, true);
 
        return 0;
+err:
+       __nft_ct_get_destroy(ctx, priv);
+       return err;
 }
 
 static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
@@ -626,6 +642,9 @@ err1:
 static void nft_ct_get_destroy(const struct nft_ctx *ctx,
                               const struct nft_expr *expr)
 {
+       struct nft_ct *priv = nft_expr_priv(expr);
+
+       __nft_ct_get_destroy(ctx, priv);
        nf_ct_netns_put(ctx->net, ctx->family);
 }