Use the existing gc worker to sync flow stats with ct stats. It is not safe to access flow->ct from datapath since the GC worker drops the ct refcount on teardown while datapath could still be accessing the ct object to update the stats, due to ct typesafe rcu semantics. Fixes: 53c2b2899af7 ("netfilter: flowtable: add counter support") Signed-off-by: Pablo Neira Ayuso --- There is also flow_offload_fixup_ct(), which is also accessing flow->ct to adjust state and timeout to hand over the ct back to classic path. include/net/netfilter/nf_flow_table.h | 3 +++ net/netfilter/nf_flow_table_core.c | 21 ++++++++++++++++++++- net/netfilter/nf_flow_table_ip.c | 6 ++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index f2e2771f188f..6060202133c7 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -171,6 +171,9 @@ struct flow_offload_tuple { u32 iifidx; } tc; }; + + atomic64_t packets; + atomic64_t bytes; }; struct flow_offload_tuple_rhash { diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 03241d4bfd5e..a1f37ca956b8 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -13,6 +13,7 @@ #include #include #include +#include static DEFINE_MUTEX(flowtable_lock); static LIST_HEAD(flowtables); @@ -565,11 +566,29 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) nf_ct_put(ct); } +static void __nf_flow_sync_ct_stats(struct flow_offload *flow, int dir) +{ + u64 pkts, bytes; + + pkts = atomic64_xchg(&flow->tuplehash[dir].tuple.packets, 0); + bytes = atomic64_xchg(&flow->tuplehash[dir].tuple.bytes, 0); + nf_ct_acct_add(flow->ct, dir, pkts, bytes); +} + +static void nf_flow_sync_ct_stats(struct flow_offload *flow) +{ + __nf_flow_sync_ct_stats(flow, FLOW_OFFLOAD_DIR_ORIGINAL); + __nf_flow_sync_ct_stats(flow, FLOW_OFFLOAD_DIR_REPLY); +} + static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table, struct flow_offload *flow, void *data) { - bool teardown = test_bit(NF_FLOW_TEARDOWN, &flow->flags); + bool teardown; + + nf_flow_sync_ct_stats(flow); + teardown = test_bit(NF_FLOW_TEARDOWN, &flow->flags); if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) || !nf_flow_dst_check(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) || diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index c8c29a9a1684..66be815ac2fe 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -509,8 +509,10 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx, ip_decrease_ttl(iph); skb_clear_tstamp(skb); - if (flow_table->flags & NF_FLOWTABLE_COUNTER) - nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); + if (flow_table->flags & NF_FLOWTABLE_COUNTER) { + atomic64_add(1, &tuplehash->tuple.packets); + atomic64_add(skb->len, &tuplehash->tuple.bytes); + } return 1; } -- 2.47.3