diff options
author | Patrick McHardy <kaber@trash.net> | 2007-02-12 14:13:14 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-02-12 14:13:14 -0500 |
commit | abbaccda4c364815b8b1a82c45a94f60760e13e1 (patch) | |
tree | 58e28cc25db59456092fc760b7a29e6d223610c5 | |
parent | 923f4902fefdf4e89b0fb32c4e069d4f57d704f5 (diff) |
[NETFILTER]: ip_conntrack: fix invalid conntrack statistics RCU assumption
CONNTRACK_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 ip_conntrack_lock.
Add CONNTRACK_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/linux/netfilter_ipv4/ip_conntrack.h | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_core.c | 14 |
2 files changed, 13 insertions, 7 deletions
diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h index 33581c13d947..da9274e6bf12 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack.h +++ b/include/linux/netfilter_ipv4/ip_conntrack.h | |||
@@ -301,6 +301,12 @@ extern unsigned int ip_conntrack_htable_size; | |||
301 | extern int ip_conntrack_checksum; | 301 | extern int ip_conntrack_checksum; |
302 | 302 | ||
303 | #define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++) | 303 | #define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++) |
304 | #define CONNTRACK_STAT_INC_ATOMIC(count) \ | ||
305 | do { \ | ||
306 | local_bh_disable(); \ | ||
307 | __get_cpu_var(ip_conntrack_stat).count++; \ | ||
308 | local_bh_enable(); \ | ||
309 | } while (0) | ||
304 | 310 | ||
305 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | 311 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS |
306 | #include <linux/notifier.h> | 312 | #include <linux/notifier.h> |
diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index e7de6d31b853..a7e34d007ab0 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c | |||
@@ -538,7 +538,7 @@ static int early_drop(struct list_head *chain) | |||
538 | if (del_timer(&ct->timeout)) { | 538 | if (del_timer(&ct->timeout)) { |
539 | death_by_timeout((unsigned long)ct); | 539 | death_by_timeout((unsigned long)ct); |
540 | dropped = 1; | 540 | dropped = 1; |
541 | CONNTRACK_STAT_INC(early_drop); | 541 | CONNTRACK_STAT_INC_ATOMIC(early_drop); |
542 | } | 542 | } |
543 | ip_conntrack_put(ct); | 543 | ip_conntrack_put(ct); |
544 | return dropped; | 544 | return dropped; |
@@ -804,7 +804,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum, | |||
804 | 804 | ||
805 | /* Previously seen (loopback or untracked)? Ignore. */ | 805 | /* Previously seen (loopback or untracked)? Ignore. */ |
806 | if ((*pskb)->nfct) { | 806 | if ((*pskb)->nfct) { |
807 | CONNTRACK_STAT_INC(ignore); | 807 | CONNTRACK_STAT_INC_ATOMIC(ignore); |
808 | return NF_ACCEPT; | 808 | return NF_ACCEPT; |
809 | } | 809 | } |
810 | 810 | ||
@@ -840,20 +840,20 @@ unsigned int ip_conntrack_in(unsigned int hooknum, | |||
840 | * core what to do with the packet. */ | 840 | * core what to do with the packet. */ |
841 | if (proto->error != NULL | 841 | if (proto->error != NULL |
842 | && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) { | 842 | && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) { |
843 | CONNTRACK_STAT_INC(error); | 843 | CONNTRACK_STAT_INC_ATOMIC(error); |
844 | CONNTRACK_STAT_INC(invalid); | 844 | CONNTRACK_STAT_INC_ATOMIC(invalid); |
845 | return -ret; | 845 | return -ret; |
846 | } | 846 | } |
847 | 847 | ||
848 | if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) { | 848 | if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) { |
849 | /* Not valid part of a connection */ | 849 | /* Not valid part of a connection */ |
850 | CONNTRACK_STAT_INC(invalid); | 850 | CONNTRACK_STAT_INC_ATOMIC(invalid); |
851 | return NF_ACCEPT; | 851 | return NF_ACCEPT; |
852 | } | 852 | } |
853 | 853 | ||
854 | if (IS_ERR(ct)) { | 854 | if (IS_ERR(ct)) { |
855 | /* Too stressed to deal. */ | 855 | /* Too stressed to deal. */ |
856 | CONNTRACK_STAT_INC(drop); | 856 | CONNTRACK_STAT_INC_ATOMIC(drop); |
857 | return NF_DROP; | 857 | return NF_DROP; |
858 | } | 858 | } |
859 | 859 | ||
@@ -865,7 +865,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum, | |||
865 | * the netfilter core what to do*/ | 865 | * the netfilter core what to do*/ |
866 | nf_conntrack_put((*pskb)->nfct); | 866 | nf_conntrack_put((*pskb)->nfct); |
867 | (*pskb)->nfct = NULL; | 867 | (*pskb)->nfct = NULL; |
868 | CONNTRACK_STAT_INC(invalid); | 868 | CONNTRACK_STAT_INC_ATOMIC(invalid); |
869 | return -ret; | 869 | return -ret; |
870 | } | 870 | } |
871 | 871 | ||