Skip to content

Commit 5b423f6

Browse files
committed
netfilter: nf_conntrack: fix racy timer handling with reliable events
Existing code assumes that del_timer returns true for alive conntrack entries. However, this is not true if reliable events are enabled. In that case, del_timer may return true for entries that were just inserted in the dying list. Note that packets / ctnetlink may hold references to conntrack entries that were just inserted to such list. This patch fixes the issue by adding an independent timer for event delivery. This increases the size of the ecache extension. Still we can revisit this later and use variable size extensions to allocate this area on demand. Tested-by: Oliver Smith <olipro@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 3f509c6 commit 5b423f6

File tree

2 files changed

+12
-5
lines changed

2 files changed

+12
-5
lines changed

include/net/netfilter/nf_conntrack_ecache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct nf_conntrack_ecache {
1818
u16 ctmask; /* bitmask of ct events to be delivered */
1919
u16 expmask; /* bitmask of expect events to be delivered */
2020
u32 pid; /* netlink pid of destroyer */
21+
struct timer_list timeout;
2122
};
2223

2324
static inline struct nf_conntrack_ecache *

net/netfilter/nf_conntrack_core.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,15 @@ static void death_by_event(unsigned long ul_conntrack)
249249
{
250250
struct nf_conn *ct = (void *)ul_conntrack;
251251
struct net *net = nf_ct_net(ct);
252+
struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
253+
254+
BUG_ON(ecache == NULL);
252255

253256
if (nf_conntrack_event(IPCT_DESTROY, ct) < 0) {
254257
/* bad luck, let's retry again */
255-
ct->timeout.expires = jiffies +
258+
ecache->timeout.expires = jiffies +
256259
(random32() % net->ct.sysctl_events_retry_timeout);
257-
add_timer(&ct->timeout);
260+
add_timer(&ecache->timeout);
258261
return;
259262
}
260263
/* we've got the event delivered, now it's dying */
@@ -268,17 +271,20 @@ static void death_by_event(unsigned long ul_conntrack)
268271
void nf_ct_insert_dying_list(struct nf_conn *ct)
269272
{
270273
struct net *net = nf_ct_net(ct);
274+
struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
275+
276+
BUG_ON(ecache == NULL);
271277

272278
/* add this conntrack to the dying list */
273279
spin_lock_bh(&nf_conntrack_lock);
274280
hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
275281
&net->ct.dying);
276282
spin_unlock_bh(&nf_conntrack_lock);
277283
/* set a new timer to retry event delivery */
278-
setup_timer(&ct->timeout, death_by_event, (unsigned long)ct);
279-
ct->timeout.expires = jiffies +
284+
setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct);
285+
ecache->timeout.expires = jiffies +
280286
(random32() % net->ct.sysctl_events_retry_timeout);
281-
add_timer(&ct->timeout);
287+
add_timer(&ecache->timeout);
282288
}
283289
EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list);
284290

0 commit comments

Comments
 (0)