diff options
| -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 0b5580c69f2..3479b98a00a 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)  	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;  	/* Timers are only set for non-local groups */  	if (group == IGMP_ALL_HOSTS) -		return; +		return false;  	rcu_read_lock();  	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(); +	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)  {  	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 */  		igmpv3_clear_delrec(in_dev);  	} 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)) {  		/* This is a v3 query with v1 queriers present */  		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 */  	} else { /* v3 */  		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) -			return; +			return true;  		ih3 = igmpv3_query_hdr(skb);  		if (ih3->nsrcs) {  			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)  					   + ntohs(ih3->nsrcs)*sizeof(__be32))) -				return; +				return true;  			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;  		if (!group) { /* general query */  			if (ih3->nsrcs) -				return;	/* no sources allowed */ +				return false;	/* no sources allowed */  			igmp_gq_start_timer(in_dev); -			return; +			return false;  		}  		/* mark sources to include, if group & source-specific */  		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);  	}  	rcu_read_unlock(); +	return false;  }  /* called in rcu_read_lock() section */ @@ -948,6 +952,7 @@ int igmp_rcv(struct sk_buff *skb)  	struct igmphdr *ih;  	struct in_device *in_dev = __in_dev_get_rcu(skb->dev);  	int len = skb->len; +	bool dropped = true;  	if (in_dev == NULL)  		goto drop; @@ -969,7 +974,7 @@ int igmp_rcv(struct sk_buff *skb)  	ih = igmp_hdr(skb);  	switch (ih->type) {  	case IGMP_HOST_MEMBERSHIP_QUERY: -		igmp_heard_query(in_dev, skb, len); +		dropped = igmp_heard_query(in_dev, skb, len);  		break;  	case IGMP_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 */  		if (skb->pkt_type == PACKET_MULTICAST ||  		    skb->pkt_type == PACKET_BROADCAST) -			igmp_heard_report(in_dev, ih->group); +			dropped = igmp_heard_report(in_dev, ih->group);  		break;  	case IGMP_PIM:  #ifdef CONFIG_IP_PIMSM_V1 @@ -997,7 +1002,10 @@ int igmp_rcv(struct sk_buff *skb)  	}  drop: -	kfree_skb(skb); +	if (dropped) +		kfree_skb(skb); +	else +		consume_skb(skb);  	return 0;  }  |