diff options
author | Patrick McHardy <kaber@trash.net> | 2007-02-12 14:13:43 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-02-12 14:13:43 -0500 |
commit | c0e912d7ed8999f87fa7f084928aac1266e251f3 (patch) | |
tree | ec48a83001871b4e0db78ee9eab520a1fbb02e14 | |
parent | abbaccda4c364815b8b1a82c45a94f60760e13e1 (diff) |
[NETFILTER]: nf_conntrack: fix invalid conntrack statistics RCU assumption
NF_CT_STAT_INC assumes rcu_read_lock in nf_hook_slow disables
preemption as well, making it legal to use __get_cpu_var without
disabling preemption manually. The assumption is not correct anymore
with preemptable RCU, additionally we need to protect against softirqs
when not holding nf_conntrack_lock.
Add NF_CT_STAT_INC_ATOMIC macro, which disables local softirqs,
and use where necessary.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/netfilter/nf_conntrack.h | 6 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 4 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 14 |
3 files changed, 15 insertions, 9 deletions
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 68ec27490c20..0e690e34c00b 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h | |||
@@ -257,6 +257,12 @@ extern int nf_conntrack_max; | |||
257 | 257 | ||
258 | DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat); | 258 | DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat); |
259 | #define NF_CT_STAT_INC(count) (__get_cpu_var(nf_conntrack_stat).count++) | 259 | #define NF_CT_STAT_INC(count) (__get_cpu_var(nf_conntrack_stat).count++) |
260 | #define NF_CT_STAT_INC_ATOMIC(count) \ | ||
261 | do { \ | ||
262 | local_bh_disable(); \ | ||
263 | __get_cpu_var(nf_conntrack_stat).count++; \ | ||
264 | local_bh_enable(); \ | ||
265 | } while (0) | ||
260 | 266 | ||
261 | /* no helper, no nat */ | 267 | /* no helper, no nat */ |
262 | #define NF_CT_F_BASIC 0 | 268 | #define NF_CT_F_BASIC 0 |
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index a2353edf4ebc..4b7be4bb4d03 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -154,8 +154,8 @@ ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff, | |||
154 | */ | 154 | */ |
155 | if ((protoff < 0) || (protoff > (*pskb)->len)) { | 155 | if ((protoff < 0) || (protoff > (*pskb)->len)) { |
156 | DEBUGP("ip6_conntrack_core: can't find proto in pkt\n"); | 156 | DEBUGP("ip6_conntrack_core: can't find proto in pkt\n"); |
157 | NF_CT_STAT_INC(error); | 157 | NF_CT_STAT_INC_ATOMIC(error); |
158 | NF_CT_STAT_INC(invalid); | 158 | NF_CT_STAT_INC_ATOMIC(invalid); |
159 | return -NF_ACCEPT; | 159 | return -NF_ACCEPT; |
160 | } | 160 | } |
161 | 161 | ||
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 3deeb900263b..d59640e2377b 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -563,7 +563,7 @@ static int early_drop(struct list_head *chain) | |||
563 | if (del_timer(&ct->timeout)) { | 563 | if (del_timer(&ct->timeout)) { |
564 | death_by_timeout((unsigned long)ct); | 564 | death_by_timeout((unsigned long)ct); |
565 | dropped = 1; | 565 | dropped = 1; |
566 | NF_CT_STAT_INC(early_drop); | 566 | NF_CT_STAT_INC_ATOMIC(early_drop); |
567 | } | 567 | } |
568 | nf_ct_put(ct); | 568 | nf_ct_put(ct); |
569 | return dropped; | 569 | return dropped; |
@@ -821,7 +821,7 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff **pskb) | |||
821 | 821 | ||
822 | /* Previously seen (loopback or untracked)? Ignore. */ | 822 | /* Previously seen (loopback or untracked)? Ignore. */ |
823 | if ((*pskb)->nfct) { | 823 | if ((*pskb)->nfct) { |
824 | NF_CT_STAT_INC(ignore); | 824 | NF_CT_STAT_INC_ATOMIC(ignore); |
825 | return NF_ACCEPT; | 825 | return NF_ACCEPT; |
826 | } | 826 | } |
827 | 827 | ||
@@ -840,8 +840,8 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff **pskb) | |||
840 | * core what to do with the packet. */ | 840 | * core what to do with the packet. */ |
841 | if (l4proto->error != NULL && | 841 | if (l4proto->error != NULL && |
842 | (ret = l4proto->error(*pskb, dataoff, &ctinfo, pf, hooknum)) <= 0) { | 842 | (ret = l4proto->error(*pskb, dataoff, &ctinfo, pf, hooknum)) <= 0) { |
843 | NF_CT_STAT_INC(error); | 843 | NF_CT_STAT_INC_ATOMIC(error); |
844 | NF_CT_STAT_INC(invalid); | 844 | NF_CT_STAT_INC_ATOMIC(invalid); |
845 | return -ret; | 845 | return -ret; |
846 | } | 846 | } |
847 | 847 | ||
@@ -849,13 +849,13 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff **pskb) | |||
849 | &set_reply, &ctinfo); | 849 | &set_reply, &ctinfo); |
850 | if (!ct) { | 850 | if (!ct) { |
851 | /* Not valid part of a connection */ | 851 | /* Not valid part of a connection */ |
852 | NF_CT_STAT_INC(invalid); | 852 | NF_CT_STAT_INC_ATOMIC(invalid); |
853 | return NF_ACCEPT; | 853 | return NF_ACCEPT; |
854 | } | 854 | } |
855 | 855 | ||
856 | if (IS_ERR(ct)) { | 856 | if (IS_ERR(ct)) { |
857 | /* Too stressed to deal. */ | 857 | /* Too stressed to deal. */ |
858 | NF_CT_STAT_INC(drop); | 858 | NF_CT_STAT_INC_ATOMIC(drop); |
859 | return NF_DROP; | 859 | return NF_DROP; |
860 | } | 860 | } |
861 | 861 | ||
@@ -868,7 +868,7 @@ nf_conntrack_in(int pf, unsigned int hooknum, struct sk_buff **pskb) | |||
868 | DEBUGP("nf_conntrack_in: Can't track with proto module\n"); | 868 | DEBUGP("nf_conntrack_in: Can't track with proto module\n"); |
869 | nf_conntrack_put((*pskb)->nfct); | 869 | nf_conntrack_put((*pskb)->nfct); |
870 | (*pskb)->nfct = NULL; | 870 | (*pskb)->nfct = NULL; |
871 | NF_CT_STAT_INC(invalid); | 871 | NF_CT_STAT_INC_ATOMIC(invalid); |
872 | return -ret; | 872 | return -ret; |
873 | } | 873 | } |
874 | 874 | ||