diff options
Diffstat (limited to 'net/ipv6/mcast.c')
| -rw-r--r-- | net/ipv6/mcast.c | 91 | 
1 files changed, 55 insertions, 36 deletions
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index d18f9f903db..617f0958e16 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -999,7 +999,7 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,  static void mld_gq_start_timer(struct inet6_dev *idev)  { -	unsigned long tv = net_random() % idev->mc_maxdelay; +	unsigned long tv = prandom_u32() % idev->mc_maxdelay;  	idev->mc_gq_running = 1;  	if (!mod_timer(&idev->mc_gq_timer, jiffies+tv+2)) @@ -1015,7 +1015,7 @@ static void mld_gq_stop_timer(struct inet6_dev *idev)  static void mld_ifc_start_timer(struct inet6_dev *idev, unsigned long delay)  { -	unsigned long tv = net_random() % delay; +	unsigned long tv = prandom_u32() % delay;  	if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2))  		in6_dev_hold(idev); @@ -1030,7 +1030,7 @@ static void mld_ifc_stop_timer(struct inet6_dev *idev)  static void mld_dad_start_timer(struct inet6_dev *idev, unsigned long delay)  { -	unsigned long tv = net_random() % delay; +	unsigned long tv = prandom_u32() % delay;  	if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2))  		in6_dev_hold(idev); @@ -1061,7 +1061,7 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)  	}  	if (delay >= resptime) -		delay = net_random() % resptime; +		delay = prandom_u32() % resptime;  	ma->mca_timer.expires = jiffies + delay;  	if (!mod_timer(&ma->mca_timer, jiffies + delay)) @@ -1301,8 +1301,17 @@ int igmp6_event_query(struct sk_buff *skb)  	len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);  	len -= skb_network_header_len(skb); -	/* Drop queries with not link local source */ -	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) +	/* RFC3810 6.2 +	 * Upon reception of an MLD message that contains a Query, the node +	 * checks if the source address of the message is a valid link-local +	 * address, if the Hop Limit is set to 1, and if the Router Alert +	 * option is present in the Hop-By-Hop Options header of the IPv6 +	 * packet.  If any of these checks fails, the packet is dropped. +	 */ +	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL) || +	    ipv6_hdr(skb)->hop_limit != 1 || +	    !(IP6CB(skb)->flags & IP6SKB_ROUTERALERT) || +	    IP6CB(skb)->ra != htons(IPV6_OPT_ROUTERALERT_MLD))  		return -EINVAL;  	idev = __in6_dev_get(skb->dev); @@ -1620,11 +1629,12 @@ static void mld_sendpack(struct sk_buff *skb)  		      dst_output);  out:  	if (!err) { -		ICMP6MSGOUT_INC_STATS_BH(net, idev, ICMPV6_MLD2_REPORT); -		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS); -		IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_OUTMCAST, payload_len); -	} else -		IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS); +		ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); +		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); +		IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, payload_len); +	} else { +		IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); +	}  	rcu_read_unlock();  	return; @@ -1665,7 +1675,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,  	skb_tailroom(skb)) : 0)  static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, -	int type, int gdeleted, int sdeleted) +	int type, int gdeleted, int sdeleted, int crsend)  {  	struct inet6_dev *idev = pmc->idev;  	struct net_device *dev = idev->dev; @@ -1757,7 +1767,7 @@ empty_source:  		if (type == MLD2_ALLOW_NEW_SOURCES ||  		    type == MLD2_BLOCK_OLD_SOURCES)  			return skb; -		if (pmc->mca_crcount || isquery) { +		if (pmc->mca_crcount || isquery || crsend) {  			/* make sure we have room for group header */  			if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)) {  				mld_sendpack(skb); @@ -1789,7 +1799,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)  				type = MLD2_MODE_IS_EXCLUDE;  			else  				type = MLD2_MODE_IS_INCLUDE; -			skb = add_grec(skb, pmc, type, 0, 0); +			skb = add_grec(skb, pmc, type, 0, 0, 0);  			spin_unlock_bh(&pmc->mca_lock);  		}  	} else { @@ -1798,7 +1808,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)  			type = MLD2_MODE_IS_EXCLUDE;  		else  			type = MLD2_MODE_IS_INCLUDE; -		skb = add_grec(skb, pmc, type, 0, 0); +		skb = add_grec(skb, pmc, type, 0, 0, 0);  		spin_unlock_bh(&pmc->mca_lock);  	}  	read_unlock_bh(&idev->lock); @@ -1843,13 +1853,13 @@ static void mld_send_cr(struct inet6_dev *idev)  		if (pmc->mca_sfmode == MCAST_INCLUDE) {  			type = MLD2_BLOCK_OLD_SOURCES;  			dtype = MLD2_BLOCK_OLD_SOURCES; -			skb = add_grec(skb, pmc, type, 1, 0); -			skb = add_grec(skb, pmc, dtype, 1, 1); +			skb = add_grec(skb, pmc, type, 1, 0, 0); +			skb = add_grec(skb, pmc, dtype, 1, 1, 0);  		}  		if (pmc->mca_crcount) {  			if (pmc->mca_sfmode == MCAST_EXCLUDE) {  				type = MLD2_CHANGE_TO_INCLUDE; -				skb = add_grec(skb, pmc, type, 1, 0); +				skb = add_grec(skb, pmc, type, 1, 0, 0);  			}  			pmc->mca_crcount--;  			if (pmc->mca_crcount == 0) { @@ -1880,8 +1890,8 @@ static void mld_send_cr(struct inet6_dev *idev)  			type = MLD2_ALLOW_NEW_SOURCES;  			dtype = MLD2_BLOCK_OLD_SOURCES;  		} -		skb = add_grec(skb, pmc, type, 0, 0); -		skb = add_grec(skb, pmc, dtype, 0, 1);	/* deleted sources */ +		skb = add_grec(skb, pmc, type, 0, 0, 0); +		skb = add_grec(skb, pmc, dtype, 0, 1, 0);	/* deleted sources */  		/* filter mode changes */  		if (pmc->mca_crcount) { @@ -1889,7 +1899,7 @@ static void mld_send_cr(struct inet6_dev *idev)  				type = MLD2_CHANGE_TO_EXCLUDE;  			else  				type = MLD2_CHANGE_TO_INCLUDE; -			skb = add_grec(skb, pmc, type, 0, 0); +			skb = add_grec(skb, pmc, type, 0, 0, 0);  			pmc->mca_crcount--;  		}  		spin_unlock_bh(&pmc->mca_lock); @@ -1997,27 +2007,36 @@ err_out:  	goto out;  } -static void mld_resend_report(struct inet6_dev *idev) +static void mld_send_initial_cr(struct inet6_dev *idev)  { -	if (mld_in_v1_mode(idev)) { -		struct ifmcaddr6 *mcaddr; -		read_lock_bh(&idev->lock); -		for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) { -			if (!(mcaddr->mca_flags & MAF_NOREPORT)) -				igmp6_send(&mcaddr->mca_addr, idev->dev, -					   ICMPV6_MGM_REPORT); -		} -		read_unlock_bh(&idev->lock); -	} else { -		mld_send_report(idev, NULL); +	struct sk_buff *skb; +	struct ifmcaddr6 *pmc; +	int type; + +	if (mld_in_v1_mode(idev)) +		return; + +	skb = NULL; +	read_lock_bh(&idev->lock); +	for (pmc=idev->mc_list; pmc; pmc=pmc->next) { +		spin_lock_bh(&pmc->mca_lock); +		if (pmc->mca_sfcount[MCAST_EXCLUDE]) +			type = MLD2_CHANGE_TO_EXCLUDE; +		else +			type = MLD2_CHANGE_TO_INCLUDE; +		skb = add_grec(skb, pmc, type, 0, 0, 1); +		spin_unlock_bh(&pmc->mca_lock);  	} +	read_unlock_bh(&idev->lock); +	if (skb) +		mld_sendpack(skb);  }  void ipv6_mc_dad_complete(struct inet6_dev *idev)  {  	idev->mc_dad_count = idev->mc_qrv;  	if (idev->mc_dad_count) { -		mld_resend_report(idev); +		mld_send_initial_cr(idev);  		idev->mc_dad_count--;  		if (idev->mc_dad_count)  			mld_dad_start_timer(idev, idev->mc_maxdelay); @@ -2028,7 +2047,7 @@ static void mld_dad_timer_expire(unsigned long data)  {  	struct inet6_dev *idev = (struct inet6_dev *)data; -	mld_resend_report(idev); +	mld_send_initial_cr(idev);  	if (idev->mc_dad_count) {  		idev->mc_dad_count--;  		if (idev->mc_dad_count) @@ -2328,7 +2347,7 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)  	igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); -	delay = net_random() % unsolicited_report_interval(ma->idev); +	delay = prandom_u32() % unsolicited_report_interval(ma->idev);  	spin_lock_bh(&ma->mca_lock);  	if (del_timer(&ma->mca_timer)) {  | 
