Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+19 −11
Original line number Original line Diff line number Diff line
@@ -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)
		}
		}
	}
	}
	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,
		/* 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,
			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,
			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,
			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)
	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)
	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)
		/* 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)
	}
	}


drop:
drop:
	if (dropped)
		kfree_skb(skb);
		kfree_skb(skb);
	else
		consume_skb(skb);
	return 0;
	return 0;
}
}