--- /dev/null
+From 4914109a8e1e494c6aa9852f9e84ec77a5fc643f Mon Sep 17 00:00:00 2001
+From: Xin Long <lucien.xin@gmail.com>
+Date: Sun, 16 Jul 2023 17:09:17 -0400
+Subject: netfilter: allow exp not to be removed in nf_ct_find_expectation
+
+From: Xin Long <lucien.xin@gmail.com>
+
+commit 4914109a8e1e494c6aa9852f9e84ec77a5fc643f upstream.
+
+Currently nf_conntrack_in() calling nf_ct_find_expectation() will
+remove the exp from the hash table. However, in some scenario, we
+expect the exp not to be removed when the created ct will not be
+confirmed, like in OVS and TC conntrack in the following patches.
+
+This patch allows exp not to be removed by setting IPS_CONFIRMED
+in the status of the tmpl.
+
+Signed-off-by: Xin Long <lucien.xin@gmail.com>
+Acked-by: Aaron Conole <aconole@redhat.com>
+Acked-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/netfilter/nf_conntrack_expect.h | 2 +-
+ net/netfilter/nf_conntrack_core.c | 2 +-
+ net/netfilter/nf_conntrack_expect.c | 4 ++--
+ net/netfilter/nft_ct.c | 2 ++
+ 4 files changed, 6 insertions(+), 4 deletions(-)
+
+--- a/include/net/netfilter/nf_conntrack_expect.h
++++ b/include/net/netfilter/nf_conntrack_expect.h
+@@ -100,7 +100,7 @@ nf_ct_expect_find_get(struct net *net,
+ struct nf_conntrack_expect *
+ nf_ct_find_expectation(struct net *net,
+ const struct nf_conntrack_zone *zone,
+- const struct nf_conntrack_tuple *tuple);
++ const struct nf_conntrack_tuple *tuple, bool unlink);
+
+ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
+ u32 portid, int report);
+--- a/net/netfilter/nf_conntrack_core.c
++++ b/net/netfilter/nf_conntrack_core.c
+@@ -1770,7 +1770,7 @@ init_conntrack(struct net *net, struct n
+ cnet = nf_ct_pernet(net);
+ if (cnet->expect_count) {
+ spin_lock_bh(&nf_conntrack_expect_lock);
+- exp = nf_ct_find_expectation(net, zone, tuple);
++ exp = nf_ct_find_expectation(net, zone, tuple, !tmpl || nf_ct_is_confirmed(tmpl));
+ if (exp) {
+ pr_debug("expectation arrives ct=%p exp=%p\n",
+ ct, exp);
+--- a/net/netfilter/nf_conntrack_expect.c
++++ b/net/netfilter/nf_conntrack_expect.c
+@@ -171,7 +171,7 @@ EXPORT_SYMBOL_GPL(nf_ct_expect_find_get)
+ struct nf_conntrack_expect *
+ nf_ct_find_expectation(struct net *net,
+ const struct nf_conntrack_zone *zone,
+- const struct nf_conntrack_tuple *tuple)
++ const struct nf_conntrack_tuple *tuple, bool unlink)
+ {
+ struct nf_conntrack_net *cnet = nf_ct_pernet(net);
+ struct nf_conntrack_expect *i, *exp = NULL;
+@@ -211,7 +211,7 @@ nf_ct_find_expectation(struct net *net,
+ !refcount_inc_not_zero(&exp->master->ct_general.use)))
+ return NULL;
+
+- if (exp->flags & NF_CT_EXPECT_PERMANENT) {
++ if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) {
+ refcount_inc(&exp->use);
+ return exp;
+ } else if (del_timer(&exp->timeout)) {
+--- a/net/netfilter/nft_ct.c
++++ b/net/netfilter/nft_ct.c
+@@ -272,6 +272,7 @@ static void nft_ct_set_zone_eval(const s
+ regs->verdict.code = NF_DROP;
+ return;
+ }
++ __set_bit(IPS_CONFIRMED_BIT, &ct->status);
+ }
+
+ nf_ct_set(skb, ct, IP_CT_NEW);
+@@ -378,6 +379,7 @@ static bool nft_ct_tmpl_alloc_pcpu(void)
+ return false;
+ }
+
++ __set_bit(IPS_CONFIRMED_BIT, &tmp->status);
+ per_cpu(nft_ct_pcpu_template, cpu) = tmp;
+ }
+