diff options
Diffstat (limited to 'net/sctp/protocol.c')
| -rw-r--r-- | net/sctp/protocol.c | 48 | 
1 files changed, 34 insertions, 14 deletions
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 59e80356672b..b7143337e4fa 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -487,23 +487,43 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,  	 */  	rcu_read_lock();  	list_for_each_entry_rcu(laddr, &bp->address_list, list) { +		struct net_device *odev; +  		if (!laddr->valid)  			continue; -		if ((laddr->state == SCTP_ADDR_SRC) && -		    (AF_INET == laddr->a.sa.sa_family)) { -			fl4->fl4_sport = laddr->a.v4.sin_port; -			flowi4_update_output(fl4, -					     asoc->base.sk->sk_bound_dev_if, -					     RT_CONN_FLAGS(asoc->base.sk), -					     daddr->v4.sin_addr.s_addr, -					     laddr->a.v4.sin_addr.s_addr); - -			rt = ip_route_output_key(sock_net(sk), fl4); -			if (!IS_ERR(rt)) { -				dst = &rt->dst; -				goto out_unlock; -			} +		if (laddr->state != SCTP_ADDR_SRC || +		    AF_INET != laddr->a.sa.sa_family) +			continue; + +		fl4->fl4_sport = laddr->a.v4.sin_port; +		flowi4_update_output(fl4, +				     asoc->base.sk->sk_bound_dev_if, +				     RT_CONN_FLAGS(asoc->base.sk), +				     daddr->v4.sin_addr.s_addr, +				     laddr->a.v4.sin_addr.s_addr); + +		rt = ip_route_output_key(sock_net(sk), fl4); +		if (IS_ERR(rt)) +			continue; + +		if (!dst) +			dst = &rt->dst; + +		/* Ensure the src address belongs to the output +		 * interface. +		 */ +		odev = __ip_dev_find(sock_net(sk), laddr->a.v4.sin_addr.s_addr, +				     false); +		if (!odev || odev->ifindex != fl4->flowi4_oif) { +			if (&rt->dst != dst) +				dst_release(&rt->dst); +			continue;  		} + +		if (dst != &rt->dst) +			dst_release(dst); +		dst = &rt->dst; +		break;  	}  out_unlock:  |