diff options
Diffstat (limited to 'net/ipv4/datagram.c')
| -rw-r--r-- | net/ipv4/datagram.c | 24 | 
1 files changed, 17 insertions, 7 deletions
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index b28e863fe0a..a3095fdefbe 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -53,11 +53,11 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr,  			      RT_CONN_FLAGS(sk), oif,  			      sk->sk_protocol, -			      inet->inet_sport, usin->sin_port, sk, true); +			      inet->inet_sport, usin->sin_port, sk);  	if (IS_ERR(rt)) {  		err = PTR_ERR(rt);  		if (err == -ENETUNREACH) -			IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); +			IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);  		goto out;  	} @@ -86,18 +86,26 @@ out:  }  EXPORT_SYMBOL(ip4_datagram_connect); +/* Because UDP xmit path can manipulate sk_dst_cache without holding + * socket lock, we need to use sk_dst_set() here, + * even if we own the socket lock. + */  void ip4_datagram_release_cb(struct sock *sk)  {  	const struct inet_sock *inet = inet_sk(sk);  	const struct ip_options_rcu *inet_opt;  	__be32 daddr = inet->inet_daddr; +	struct dst_entry *dst;  	struct flowi4 fl4;  	struct rtable *rt; -	if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0)) -		return; -  	rcu_read_lock(); + +	dst = __sk_dst_get(sk); +	if (!dst || !dst->obsolete || dst->ops->check(dst, 0)) { +		rcu_read_unlock(); +		return; +	}  	inet_opt = rcu_dereference(inet->inet_opt);  	if (inet_opt && inet_opt->opt.srr)  		daddr = inet_opt->opt.faddr; @@ -105,8 +113,10 @@ void ip4_datagram_release_cb(struct sock *sk)  				   inet->inet_saddr, inet->inet_dport,  				   inet->inet_sport, sk->sk_protocol,  				   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if); -	if (!IS_ERR(rt)) -		__sk_dst_set(sk, &rt->dst); + +	dst = !IS_ERR(rt) ? &rt->dst : NULL; +	sk_dst_set(sk, dst); +  	rcu_read_unlock();  }  EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);  | 
