diff options
author | Eric Dumazet <edumazet@google.com> | 2012-09-06 16:37:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-09-07 14:17:10 -0400 |
commit | d679c5324d9a87c6295f56c2dea52d5f68834f41 (patch) | |
tree | 694dc71d7cdf3075237dbee0128d77897244a5e9 /net/ipv4 | |
parent | e966c8ec0cd2ff97ff1d87c381e27da3c086ee35 (diff) |
igmp: avoid drop_monitor false positives
igmp should call consume_skb() for all correctly processed packets,
to avoid false dropwatch/drop_monitor false positives.
Reported-by: Shawn Bohrer <sbohrer@rgmadvisors.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/igmp.c | 30 |
1 files changed, 19 insertions, 11 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 0b5580c69f2d..3479b98a00a7 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -815,14 +815,15 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) | |||
815 | return 1; | 815 | return 1; |
816 | } | 816 | } |
817 | 817 | ||
818 | static void igmp_heard_report(struct in_device *in_dev, __be32 group) | 818 | /* return true if packet was dropped */ |
819 | static bool igmp_heard_report(struct in_device *in_dev, __be32 group) | ||
819 | { | 820 | { |
820 | struct ip_mc_list *im; | 821 | struct ip_mc_list *im; |
821 | 822 | ||
822 | /* Timers are only set for non-local groups */ | 823 | /* Timers are only set for non-local groups */ |
823 | 824 | ||
824 | if (group == IGMP_ALL_HOSTS) | 825 | if (group == IGMP_ALL_HOSTS) |
825 | return; | 826 | return false; |
826 | 827 | ||
827 | rcu_read_lock(); | 828 | rcu_read_lock(); |
828 | for_each_pmc_rcu(in_dev, im) { | 829 | for_each_pmc_rcu(in_dev, im) { |
@@ -832,9 +833,11 @@ static void igmp_heard_report(struct in_device *in_dev, __be32 group) | |||
832 | } | 833 | } |
833 | } | 834 | } |
834 | rcu_read_unlock(); | 835 | rcu_read_unlock(); |
836 | return false; | ||
835 | } | 837 | } |
836 | 838 | ||
837 | static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | 839 | /* return true if packet was dropped */ |
840 | static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | ||
838 | int len) | 841 | int len) |
839 | { | 842 | { |
840 | struct igmphdr *ih = igmp_hdr(skb); | 843 | struct igmphdr *ih = igmp_hdr(skb); |
@@ -866,7 +869,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
866 | /* clear deleted report items */ | 869 | /* clear deleted report items */ |
867 | igmpv3_clear_delrec(in_dev); | 870 | igmpv3_clear_delrec(in_dev); |
868 | } else if (len < 12) { | 871 | } else if (len < 12) { |
869 | return; /* ignore bogus packet; freed by caller */ | 872 | return true; /* ignore bogus packet; freed by caller */ |
870 | } else if (IGMP_V1_SEEN(in_dev)) { | 873 | } else if (IGMP_V1_SEEN(in_dev)) { |
871 | /* This is a v3 query with v1 queriers present */ | 874 | /* This is a v3 query with v1 queriers present */ |
872 | max_delay = IGMP_Query_Response_Interval; | 875 | max_delay = IGMP_Query_Response_Interval; |
@@ -883,13 +886,13 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
883 | max_delay = 1; /* can't mod w/ 0 */ | 886 | max_delay = 1; /* can't mod w/ 0 */ |
884 | } else { /* v3 */ | 887 | } else { /* v3 */ |
885 | if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) | 888 | if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) |
886 | return; | 889 | return true; |
887 | 890 | ||
888 | ih3 = igmpv3_query_hdr(skb); | 891 | ih3 = igmpv3_query_hdr(skb); |
889 | if (ih3->nsrcs) { | 892 | if (ih3->nsrcs) { |
890 | if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) | 893 | if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) |
891 | + ntohs(ih3->nsrcs)*sizeof(__be32))) | 894 | + ntohs(ih3->nsrcs)*sizeof(__be32))) |
892 | return; | 895 | return true; |
893 | ih3 = igmpv3_query_hdr(skb); | 896 | ih3 = igmpv3_query_hdr(skb); |
894 | } | 897 | } |
895 | 898 | ||
@@ -901,9 +904,9 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
901 | in_dev->mr_qrv = ih3->qrv; | 904 | in_dev->mr_qrv = ih3->qrv; |
902 | if (!group) { /* general query */ | 905 | if (!group) { /* general query */ |
903 | if (ih3->nsrcs) | 906 | if (ih3->nsrcs) |
904 | return; /* no sources allowed */ | 907 | return false; /* no sources allowed */ |
905 | igmp_gq_start_timer(in_dev); | 908 | igmp_gq_start_timer(in_dev); |
906 | return; | 909 | return false; |
907 | } | 910 | } |
908 | /* mark sources to include, if group & source-specific */ | 911 | /* mark sources to include, if group & source-specific */ |
909 | mark = ih3->nsrcs != 0; | 912 | mark = ih3->nsrcs != 0; |
@@ -939,6 +942,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, | |||
939 | igmp_mod_timer(im, max_delay); | 942 | igmp_mod_timer(im, max_delay); |
940 | } | 943 | } |
941 | rcu_read_unlock(); | 944 | rcu_read_unlock(); |
945 | return false; | ||
942 | } | 946 | } |
943 | 947 | ||
944 | /* called in rcu_read_lock() section */ | 948 | /* called in rcu_read_lock() section */ |
@@ -948,6 +952,7 @@ int igmp_rcv(struct sk_buff *skb) | |||
948 | struct igmphdr *ih; | 952 | struct igmphdr *ih; |
949 | struct in_device *in_dev = __in_dev_get_rcu(skb->dev); | 953 | struct in_device *in_dev = __in_dev_get_rcu(skb->dev); |
950 | int len = skb->len; | 954 | int len = skb->len; |
955 | bool dropped = true; | ||
951 | 956 | ||
952 | if (in_dev == NULL) | 957 | if (in_dev == NULL) |
953 | goto drop; | 958 | goto drop; |
@@ -969,7 +974,7 @@ int igmp_rcv(struct sk_buff *skb) | |||
969 | ih = igmp_hdr(skb); | 974 | ih = igmp_hdr(skb); |
970 | switch (ih->type) { | 975 | switch (ih->type) { |
971 | case IGMP_HOST_MEMBERSHIP_QUERY: | 976 | case IGMP_HOST_MEMBERSHIP_QUERY: |
972 | igmp_heard_query(in_dev, skb, len); | 977 | dropped = igmp_heard_query(in_dev, skb, len); |
973 | break; | 978 | break; |
974 | case IGMP_HOST_MEMBERSHIP_REPORT: | 979 | case IGMP_HOST_MEMBERSHIP_REPORT: |
975 | case IGMPV2_HOST_MEMBERSHIP_REPORT: | 980 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
@@ -979,7 +984,7 @@ int igmp_rcv(struct sk_buff *skb) | |||
979 | /* don't rely on MC router hearing unicast reports */ | 984 | /* don't rely on MC router hearing unicast reports */ |
980 | if (skb->pkt_type == PACKET_MULTICAST || | 985 | if (skb->pkt_type == PACKET_MULTICAST || |
981 | skb->pkt_type == PACKET_BROADCAST) | 986 | skb->pkt_type == PACKET_BROADCAST) |
982 | igmp_heard_report(in_dev, ih->group); | 987 | dropped = igmp_heard_report(in_dev, ih->group); |
983 | break; | 988 | break; |
984 | case IGMP_PIM: | 989 | case IGMP_PIM: |
985 | #ifdef CONFIG_IP_PIMSM_V1 | 990 | #ifdef CONFIG_IP_PIMSM_V1 |
@@ -997,7 +1002,10 @@ int igmp_rcv(struct sk_buff *skb) | |||
997 | } | 1002 | } |
998 | 1003 | ||
999 | drop: | 1004 | drop: |
1000 | kfree_skb(skb); | 1005 | if (dropped) |
1006 | kfree_skb(skb); | ||
1007 | else | ||
1008 | consume_skb(skb); | ||
1001 | return 0; | 1009 | return 0; |
1002 | } | 1010 | } |
1003 | 1011 | ||