Skip to content

Commit

Permalink
net/sched: act_ct: Fix promotion of offloaded unreplied tuple
Browse files Browse the repository at this point in the history
[ Upstream commit 41f2c7c ]

Currently UNREPLIED and UNASSURED connections are added to the nf flow
table. This causes the following connection packets to be processed
by the flow table which then skips conntrack_in(), and thus such the
connections will remain UNREPLIED and UNASSURED even if reply traffic
is then seen. Even still, the unoffloaded reply packets are the ones
triggering hardware update from new to established state, and if
there aren't any to triger an update and/or previous update was
missed, hardware can get out of sync with sw and still mark
packets as new.

Fix the above by:
1) Not skipping conntrack_in() for UNASSURED packets, but still
   refresh for hardware, as before the cited patch.
2) Try and force a refresh by reply-direction packets that update
   the hardware rules from new to established state.
3) Remove any bidirectional flows that didn't failed to update in
   hardware for re-insertion as bidrectional once any new packet
   arrives.

Fixes: 6a9bad0 ("net/sched: act_ct: offload UDP NEW connections")
Co-developed-by: Vlad Buslov <vladbu@nvidia.com>
Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
Signed-off-by: Paul Blakey <paulb@nvidia.com>
Reviewed-by: Florian Westphal <fw@strlen.de>
Link: https://lore.kernel.org/r/1686313379-117663-1-git-send-email-paulb@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Stable-dep-of: 125f1c7 ("net/sched: act_ct: Take per-cb reference to tcf_ct_flow_table")
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Paul Blakey authored and gregkh committed Jan 10, 2024
1 parent 87466a3 commit df01de0
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 7 deletions.
2 changes: 1 addition & 1 deletion include/net/netfilter/nf_flow_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ int flow_offload_route_init(struct flow_offload *flow,

int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow);
void flow_offload_refresh(struct nf_flowtable *flow_table,
struct flow_offload *flow);
struct flow_offload *flow, bool force);

struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table,
struct flow_offload_tuple *tuple);
Expand Down
13 changes: 10 additions & 3 deletions net/netfilter/nf_flow_table_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,12 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
EXPORT_SYMBOL_GPL(flow_offload_add);

void flow_offload_refresh(struct nf_flowtable *flow_table,
struct flow_offload *flow)
struct flow_offload *flow, bool force)
{
u32 timeout;

timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
if (timeout - READ_ONCE(flow->timeout) > HZ)
if (force || timeout - READ_ONCE(flow->timeout) > HZ)
WRITE_ONCE(flow->timeout, timeout);
else
return;
Expand All @@ -331,6 +331,12 @@ void flow_offload_refresh(struct nf_flowtable *flow_table,
}
EXPORT_SYMBOL_GPL(flow_offload_refresh);

static bool nf_flow_is_outdated(const struct flow_offload *flow)
{
return test_bit(IPS_SEEN_REPLY_BIT, &flow->ct->status) &&
!test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags);
}

static inline bool nf_flow_has_expired(const struct flow_offload *flow)
{
return nf_flow_timeout_delta(flow->timeout) <= 0;
Expand Down Expand Up @@ -420,7 +426,8 @@ static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
struct flow_offload *flow, void *data)
{
if (nf_flow_has_expired(flow) ||
nf_ct_is_dying(flow->ct))
nf_ct_is_dying(flow->ct) ||
nf_flow_is_outdated(flow))
flow_offload_teardown(flow);

if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
Expand Down
4 changes: 2 additions & 2 deletions net/netfilter/nf_flow_table_ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
if (skb_try_make_writable(skb, thoff + hdrsize))
return NF_DROP;

flow_offload_refresh(flow_table, flow);
flow_offload_refresh(flow_table, flow, false);

nf_flow_encap_pop(skb, tuplehash);
thoff -= offset;
Expand Down Expand Up @@ -646,7 +646,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
if (skb_try_make_writable(skb, thoff + hdrsize))
return NF_DROP;

flow_offload_refresh(flow_table, flow);
flow_offload_refresh(flow_table, flow, false);

nf_flow_encap_pop(skb, tuplehash);

Expand Down
9 changes: 8 additions & 1 deletion net/sched/act_ct.c
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
struct flow_offload_tuple tuple = {};
enum ip_conntrack_info ctinfo;
struct tcphdr *tcph = NULL;
bool force_refresh = false;
struct flow_offload *flow;
struct nf_conn *ct;
u8 dir;
Expand Down Expand Up @@ -643,6 +644,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
* established state, then don't refresh.
*/
return false;
force_refresh = true;
}

if (tcph && (unlikely(tcph->fin || tcph->rst))) {
Expand All @@ -656,7 +658,12 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
else
ctinfo = IP_CT_ESTABLISHED_REPLY;

flow_offload_refresh(nf_ft, flow);
flow_offload_refresh(nf_ft, flow, force_refresh);
if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
/* Process this flow in SW to allow promoting to ASSURED */
return false;
}

nf_conntrack_get(&ct->ct_general);
nf_ct_set(skb, ct, ctinfo);
if (nf_ft->flags & NF_FLOWTABLE_COUNTER)
Expand Down

0 comments on commit df01de0

Please sign in to comment.