A flow offloaded in the original direction alone carries a reply tuple
that conntrack has never seen a packet for. Leave that direction on the
classic path, so conntrack tracks it, and offload it as well once the
connection is assured.
act_ct promotes its unidirectional UDP flows on the same condition.
Signed-off-by: Julius Bairaktaris <redacted>
Assisted-by: Claude:claude-opus-5
---
net/netfilter/nf_flow_table_ip.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index c8c29a9a1684..f2cd5c2c5365 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -465,6 +465,26 @@ nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx,
return flow_offload_lookup(flow_table, &tuple);
}
+/* The reply direction of a flow offloaded in one direction only stays on the
+ * classic path so that conntrack sees it. Once conntrack has, the flow is
+ * offloaded in both directions.
+ */
+static bool nf_flow_reply_unoffloaded(struct nf_flowtable *flow_table,
+ struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir)
+{
+ if (dir != FLOW_OFFLOAD_DIR_REPLY ||
+ test_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags))
+ return false;
+
+ if (test_bit(IPS_ASSURED_BIT, &flow->ct->status)) {
+ set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
+ flow_offload_refresh(flow_table, flow, true);
+ }
+
+ return true;
+}
+
static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
struct nf_flowtable *flow_table,
struct flow_offload_tuple_rhash *tuplehash,@@ -478,6 +498,9 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+ if (nf_flow_reply_unoffloaded(flow_table, flow, dir))
+ return 0;
+
mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset;
if (flow->tuplehash[!dir].tuple.tun_num)
mtu -= sizeof(*iph);
@@ -1074,6 +1097,9 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+ if (nf_flow_reply_unoffloaded(flow_table, flow, dir))
+ return 0;
+
mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset;
if (flow->tuplehash[!dir].tuple.tun_num)
mtu -= sizeof(*ip6h);
--
2.53.0