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>
This commit is contained in:
		
					parent
					
						
							
								e966c8ec0c
							
						
					
				
			
			
				commit
				
					
						d679c5324d
					
				
			
		
					 1 changed files with 19 additions and 11 deletions
				
			
		|  | @ -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: | ||||||
| 	kfree_skb(skb); | 	if (dropped) | ||||||
|  | 		kfree_skb(skb); | ||||||
|  | 	else | ||||||
|  | 		consume_skb(skb); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Dumazet
				Eric Dumazet