Commit d679c532 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarShawn Bohrer <sbohrer@rgmadvisors.com>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e966c8ec
...@@ -815,14 +815,15 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) ...@@ -815,14 +815,15 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
return 1; return 1;
} }
static void igmp_heard_report(struct in_device *in_dev, __be32 group) /* return true if packet was dropped */
static bool igmp_heard_report(struct in_device *in_dev, __be32 group)
{ {
struct ip_mc_list *im; struct ip_mc_list *im;
/* Timers are only set for non-local groups */ /* Timers are only set for non-local groups */
if (group == IGMP_ALL_HOSTS) if (group == IGMP_ALL_HOSTS)
return; return false;
rcu_read_lock(); rcu_read_lock();
for_each_pmc_rcu(in_dev, im) { for_each_pmc_rcu(in_dev, im) {
...@@ -832,9 +833,11 @@ static void igmp_heard_report(struct in_device *in_dev, __be32 group) ...@@ -832,9 +833,11 @@ static void igmp_heard_report(struct in_device *in_dev, __be32 group)
} }
} }
rcu_read_unlock(); rcu_read_unlock();
return false;
} }
static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, /* return true if packet was dropped */
static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
int len) int len)
{ {
struct igmphdr *ih = igmp_hdr(skb); 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,7 +869,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
/* clear deleted report items */ /* clear deleted report items */
igmpv3_clear_delrec(in_dev); igmpv3_clear_delrec(in_dev);
} else if (len < 12) { } else if (len < 12) {
return; /* ignore bogus packet; freed by caller */ return true; /* ignore bogus packet; freed by caller */
} else if (IGMP_V1_SEEN(in_dev)) { } else if (IGMP_V1_SEEN(in_dev)) {
/* This is a v3 query with v1 queriers present */ /* This is a v3 query with v1 queriers present */
max_delay = IGMP_Query_Response_Interval; 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,13 +886,13 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
max_delay = 1; /* can't mod w/ 0 */ max_delay = 1; /* can't mod w/ 0 */
} else { /* v3 */ } else { /* v3 */
if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
return; return true;
ih3 = igmpv3_query_hdr(skb); ih3 = igmpv3_query_hdr(skb);
if (ih3->nsrcs) { if (ih3->nsrcs) {
if (!pskb_may_pull(skb, sizeof(struct igmpv3_query) if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)
+ ntohs(ih3->nsrcs)*sizeof(__be32))) + ntohs(ih3->nsrcs)*sizeof(__be32)))
return; return true;
ih3 = igmpv3_query_hdr(skb); ih3 = igmpv3_query_hdr(skb);
} }
...@@ -901,9 +904,9 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, ...@@ -901,9 +904,9 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
in_dev->mr_qrv = ih3->qrv; in_dev->mr_qrv = ih3->qrv;
if (!group) { /* general query */ if (!group) { /* general query */
if (ih3->nsrcs) if (ih3->nsrcs)
return; /* no sources allowed */ return false; /* no sources allowed */
igmp_gq_start_timer(in_dev); igmp_gq_start_timer(in_dev);
return; return false;
} }
/* mark sources to include, if group & source-specific */ /* mark sources to include, if group & source-specific */
mark = ih3->nsrcs != 0; mark = ih3->nsrcs != 0;
...@@ -939,6 +942,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, ...@@ -939,6 +942,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
igmp_mod_timer(im, max_delay); igmp_mod_timer(im, max_delay);
} }
rcu_read_unlock(); rcu_read_unlock();
return false;
} }
/* called in rcu_read_lock() section */ /* called in rcu_read_lock() section */
...@@ -948,6 +952,7 @@ int igmp_rcv(struct sk_buff *skb) ...@@ -948,6 +952,7 @@ int igmp_rcv(struct sk_buff *skb)
struct igmphdr *ih; struct igmphdr *ih;
struct in_device *in_dev = __in_dev_get_rcu(skb->dev); struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
int len = skb->len; int len = skb->len;
bool dropped = true;
if (in_dev == NULL) if (in_dev == NULL)
goto drop; goto drop;
...@@ -969,7 +974,7 @@ int igmp_rcv(struct sk_buff *skb) ...@@ -969,7 +974,7 @@ int igmp_rcv(struct sk_buff *skb)
ih = igmp_hdr(skb); ih = igmp_hdr(skb);
switch (ih->type) { switch (ih->type) {
case IGMP_HOST_MEMBERSHIP_QUERY: case IGMP_HOST_MEMBERSHIP_QUERY:
igmp_heard_query(in_dev, skb, len); dropped = igmp_heard_query(in_dev, skb, len);
break; break;
case IGMP_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_MEMBERSHIP_REPORT:
case IGMPV2_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT:
...@@ -979,7 +984,7 @@ int igmp_rcv(struct sk_buff *skb) ...@@ -979,7 +984,7 @@ int igmp_rcv(struct sk_buff *skb)
/* don't rely on MC router hearing unicast reports */ /* don't rely on MC router hearing unicast reports */
if (skb->pkt_type == PACKET_MULTICAST || if (skb->pkt_type == PACKET_MULTICAST ||
skb->pkt_type == PACKET_BROADCAST) skb->pkt_type == PACKET_BROADCAST)
igmp_heard_report(in_dev, ih->group); dropped = igmp_heard_report(in_dev, ih->group);
break; break;
case IGMP_PIM: case IGMP_PIM:
#ifdef CONFIG_IP_PIMSM_V1 #ifdef CONFIG_IP_PIMSM_V1
...@@ -997,7 +1002,10 @@ int igmp_rcv(struct sk_buff *skb) ...@@ -997,7 +1002,10 @@ int igmp_rcv(struct sk_buff *skb)
} }
drop: drop:
kfree_skb(skb); if (dropped)
kfree_skb(skb);
else
consume_skb(skb);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment