diff options
Diffstat (limited to 'net/sctp/ipv6.c')
| -rw-r--r-- | net/sctp/ipv6.c | 49 | 
1 files changed, 32 insertions, 17 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 961ee59f696a..f5b45b8b8b16 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -240,12 +240,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,  	struct sctp_bind_addr *bp;  	struct ipv6_pinfo *np = inet6_sk(sk);  	struct sctp_sockaddr_entry *laddr; -	union sctp_addr *baddr = NULL;  	union sctp_addr *daddr = &t->ipaddr;  	union sctp_addr dst_saddr;  	struct in6_addr *final_p, final;  	__u8 matchlen = 0; -	__u8 bmatchlen;  	sctp_scope_t scope;  	memset(fl6, 0, sizeof(struct flowi6)); @@ -312,23 +310,37 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,  	 */  	rcu_read_lock();  	list_for_each_entry_rcu(laddr, &bp->address_list, list) { -		if (!laddr->valid) +		struct dst_entry *bdst; +		__u8 bmatchlen; + +		if (!laddr->valid || +		    laddr->state != SCTP_ADDR_SRC || +		    laddr->a.sa.sa_family != AF_INET6 || +		    scope > sctp_scope(&laddr->a))  			continue; -		if ((laddr->state == SCTP_ADDR_SRC) && -		    (laddr->a.sa.sa_family == AF_INET6) && -		    (scope <= sctp_scope(&laddr->a))) { -			bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); -			if (!baddr || (matchlen < bmatchlen)) { -				baddr = &laddr->a; -				matchlen = bmatchlen; -			} -		} -	} -	if (baddr) { -		fl6->saddr = baddr->v6.sin6_addr; -		fl6->fl6_sport = baddr->v6.sin6_port; + +		fl6->saddr = laddr->a.v6.sin6_addr; +		fl6->fl6_sport = laddr->a.v6.sin6_port;  		final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); -		dst = ip6_dst_lookup_flow(sk, fl6, final_p); +		bdst = ip6_dst_lookup_flow(sk, fl6, final_p); + +		if (!IS_ERR(bdst) && +		    ipv6_chk_addr(dev_net(bdst->dev), +				  &laddr->a.v6.sin6_addr, bdst->dev, 1)) { +			if (!IS_ERR_OR_NULL(dst)) +				dst_release(dst); +			dst = bdst; +			break; +		} + +		bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); +		if (matchlen > bmatchlen) +			continue; + +		if (!IS_ERR_OR_NULL(dst)) +			dst_release(dst); +		dst = bdst; +		matchlen = bmatchlen;  	}  	rcu_read_unlock(); @@ -665,6 +677,9 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,  	newnp = inet6_sk(newsk);  	memcpy(newnp, np, sizeof(struct ipv6_pinfo)); +	newnp->ipv6_mc_list = NULL; +	newnp->ipv6_ac_list = NULL; +	newnp->ipv6_fl_list = NULL;  	rcu_read_lock();  	opt = rcu_dereference(np->opt);  |