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 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