diff options
Diffstat (limited to 'net/ipv4/udp.c')
| -rw-r--r-- | net/ipv4/udp.c | 35 | 
1 files changed, 31 insertions, 4 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 83aa604f9273..c0a15e7f359f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1013,11 +1013,31 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)  	if (!rt) {  		struct net *net = sock_net(sk); +		__u8 flow_flags = inet_sk_flowi_flags(sk);  		fl4 = &fl4_stack; + +		/* unconnected socket. If output device is enslaved to a VRF +		 * device lookup source address from VRF table. This mimics +		 * behavior of ip_route_connect{_init}. +		 */ +		if (netif_index_is_vrf(net, ipc.oif)) { +			flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, +					   RT_SCOPE_UNIVERSE, sk->sk_protocol, +					   (flow_flags | FLOWI_FLAG_VRFSRC), +					   faddr, saddr, dport, +					   inet->inet_sport); + +			rt = ip_route_output_flow(net, fl4, sk); +			if (!IS_ERR(rt)) { +				saddr = fl4->saddr; +				ip_rt_put(rt); +			} +		} +  		flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,  				   RT_SCOPE_UNIVERSE, sk->sk_protocol, -				   inet_sk_flowi_flags(sk), +				   flow_flags,  				   faddr, saddr, dport, inet->inet_sport);  		security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); @@ -1995,12 +2015,19 @@ void udp_v4_early_demux(struct sk_buff *skb)  	skb->sk = sk;  	skb->destructor = sock_efree; -	dst = sk->sk_rx_dst; +	dst = READ_ONCE(sk->sk_rx_dst);  	if (dst)  		dst = dst_check(dst, 0); -	if (dst) -		skb_dst_set_noref(skb, dst); +	if (dst) { +		/* DST_NOCACHE can not be used without taking a reference */ +		if (dst->flags & DST_NOCACHE) { +			if (likely(atomic_inc_not_zero(&dst->__refcnt))) +				skb_dst_set(skb, dst); +		} else { +			skb_dst_set_noref(skb, dst); +		} +	}  }  int udp_rcv(struct sk_buff *skb)  |