70e9942f17a6 ("netfilter: nf_conntrack: make event callback registration per-netns") introduced a per-netns callback for events to workaround a crash when delivering conntrack events on a stale per-netns nfnetlink kernel socket. This patch adds a new flag to the nf_ct_iter_data object to skip event delivery from the netns cleanup path to address this issue. Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> --- compiled tested only. @Florian: Maybe this helps to remove the per-netns nf_conntrack_event_cb callback without having to update nfnetlink to deal with this corner case? include/net/netfilter/nf_conntrack.h | 8 +++++++- net/netfilter/nf_conntrack_core.c | 14 +++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 14dd6bbe360c..25687bb80a64 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -199,7 +199,12 @@ void nf_ct_netns_put(struct net *net, u8 nfproto); void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls); int nf_conntrack_hash_check_insert(struct nf_conn *ct); -bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report); + +bool __nf_ct_delete(struct nf_conn *ct, u32 portid, int report, bool skip_event); +static inline bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report) +{ + return __nf_ct_delete(ct, pid, report, false); +} bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, u_int16_t l3num, struct net *net, @@ -244,6 +249,7 @@ struct nf_ct_iter_data { void *data; u32 portid; int report; + bool skip_event; }; /* Iterate over all conntracks: if iter returns true, it's deleted. */ diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 93c30c16bade..51d248ee28ca 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -687,7 +687,7 @@ static void nf_ct_delete_from_lists(struct nf_conn *ct) local_bh_enable(); } -bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) +bool __nf_ct_delete(struct nf_conn *ct, u32 portid, int report, bool skip_event) { struct nf_conn_tstamp *tstamp; struct net *net; @@ -704,6 +704,9 @@ bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) tstamp->stop -= jiffies_to_nsecs(-timeout); } + if (skip_event) + goto out; + if (nf_conntrack_event_report(IPCT_DESTROY, ct, portid, report) < 0) { /* destroy event was not delivered. nf_ct_put will @@ -717,6 +720,8 @@ bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) net = nf_ct_net(ct); if (nf_conntrack_ecache_dwork_pending(net)) nf_conntrack_ecache_work(net, NFCT_ECACHE_DESTROY_SENT); + +out: nf_ct_delete_from_lists(ct); nf_ct_put(ct); return true; @@ -2383,7 +2388,8 @@ static void nf_ct_iterate_cleanup(int (*iter)(struct nf_conn *i, void *data), while ((ct = get_next_corpse(iter, iter_data, &bucket)) != NULL) { /* Time to push up daises... */ - nf_ct_delete(ct, iter_data->portid, iter_data->report); + __nf_ct_delete(ct, iter_data->portid, iter_data->report, + iter_data->skip_event); nf_ct_put(ct); cond_resched(); } @@ -2532,7 +2538,9 @@ void nf_conntrack_cleanup_net(struct net *net) void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list) { - struct nf_ct_iter_data iter_data = {}; + struct nf_ct_iter_data iter_data = { + .skip_event = true, + }; struct net *net; int busy; -- 2.30.2