summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-03-16 06:00:34 +0400
committerDavid S. Miller <davem@davemloft.net>2012-03-17 12:47:08 +0400
commita16a1647fa6b6783c2e91623e72e86f0c2adac5e (patch)
tree949b73345b860aec586d67243eab4a9944a158ad
parentc577923756b7fe9071f28a76b66b83b306d1d001 (diff)
downloadlinux-a16a1647fa6b6783c2e91623e72e86f0c2adac5e.tar.xz
netfilter: ctnetlink: fix race between delete and timeout expiration
Kerin Millar reported hardlockups while running `conntrackd -c' in a busy firewall. That system (with several processors) was acting as backup in a primary-backup setup. After several tries, I found a race condition between the deletion operation of ctnetlink and timeout expiration. This patch fixes this problem. Tested-by: Kerin Millar <kerframil@gmail.com> Reported-by: Kerin Millar <kerframil@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/netfilter/nf_conntrack_netlink.c23
1 files changed, 12 insertions, 11 deletions
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 10687692831e..b49da6c925b3 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -943,20 +943,21 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
}
}
- if (nf_conntrack_event_report(IPCT_DESTROY, ct,
- NETLINK_CB(skb).pid,
- nlmsg_report(nlh)) < 0) {
+ if (del_timer(&ct->timeout)) {
+ if (nf_conntrack_event_report(IPCT_DESTROY, ct,
+ NETLINK_CB(skb).pid,
+ nlmsg_report(nlh)) < 0) {
+ nf_ct_delete_from_lists(ct);
+ /* we failed to report the event, try later */
+ nf_ct_insert_dying_list(ct);
+ nf_ct_put(ct);
+ return 0;
+ }
+ /* death_by_timeout would report the event again */
+ set_bit(IPS_DYING_BIT, &ct->status);
nf_ct_delete_from_lists(ct);
- /* we failed to report the event, try later */
- nf_ct_insert_dying_list(ct);
nf_ct_put(ct);
- return 0;
}
-
- /* death_by_timeout would report the event again */
- set_bit(IPS_DYING_BIT, &ct->status);
-
- nf_ct_kill(ct);
nf_ct_put(ct);
return 0;