diff options
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
| -rw-r--r-- | net/ipv4/tcp_ipv4.c | 76 | 
1 files changed, 48 insertions, 28 deletions
| diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2e62e0d6373a..13d868c43284 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -508,9 +508,12 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)  	if (sk->sk_state == TCP_CLOSE)  		goto out; -	if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) { -		__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); -		goto out; +	if (static_branch_unlikely(&ip4_min_ttl)) { +		/* min_ttl can be changed concurrently from do_ip_setsockopt() */ +		if (unlikely(iph->ttl < READ_ONCE(inet_sk(sk)->min_ttl))) { +			__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); +			goto out; +		}  	}  	tp = tcp_sk(sk); @@ -1037,6 +1040,20 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)  DEFINE_STATIC_KEY_FALSE(tcp_md5_needed);  EXPORT_SYMBOL(tcp_md5_needed); +static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *new) +{ +	if (!old) +		return true; + +	/* l3index always overrides non-l3index */ +	if (old->l3index && new->l3index == 0) +		return false; +	if (old->l3index == 0 && new->l3index) +		return true; + +	return old->prefixlen < new->prefixlen; +} +  /* Find the Key structure for an address.  */  struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,  					   const union tcp_md5_addr *addr, @@ -1059,7 +1076,7 @@ struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,  				 lockdep_sock_is_held(sk)) {  		if (key->family != family)  			continue; -		if (key->l3index && key->l3index != l3index) +		if (key->flags & TCP_MD5SIG_FLAG_IFINDEX && key->l3index != l3index)  			continue;  		if (family == AF_INET) {  			mask = inet_make_mask(key->prefixlen); @@ -1074,8 +1091,7 @@ struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,  			match = false;  		} -		if (match && (!best_match || -			      key->prefixlen > best_match->prefixlen)) +		if (match && better_md5_match(best_match, key))  			best_match = key;  	}  	return best_match; @@ -1085,7 +1101,7 @@ EXPORT_SYMBOL(__tcp_md5_do_lookup);  static struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk,  						      const union tcp_md5_addr *addr,  						      int family, u8 prefixlen, -						      int l3index) +						      int l3index, u8 flags)  {  	const struct tcp_sock *tp = tcp_sk(sk);  	struct tcp_md5sig_key *key; @@ -1105,7 +1121,9 @@ static struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk,  				 lockdep_sock_is_held(sk)) {  		if (key->family != family)  			continue; -		if (key->l3index && key->l3index != l3index) +		if ((key->flags & TCP_MD5SIG_FLAG_IFINDEX) != (flags & TCP_MD5SIG_FLAG_IFINDEX)) +			continue; +		if (key->l3index != l3index)  			continue;  		if (!memcmp(&key->addr, addr, size) &&  		    key->prefixlen == prefixlen) @@ -1129,7 +1147,7 @@ EXPORT_SYMBOL(tcp_v4_md5_lookup);  /* This can be called on a newly created socket, from other files */  int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, -		   int family, u8 prefixlen, int l3index, +		   int family, u8 prefixlen, int l3index, u8 flags,  		   const u8 *newkey, u8 newkeylen, gfp_t gfp)  {  	/* Add Key to the list */ @@ -1137,7 +1155,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,  	struct tcp_sock *tp = tcp_sk(sk);  	struct tcp_md5sig_info *md5sig; -	key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index); +	key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index, flags);  	if (key) {  		/* Pre-existing entry - just update that one.  		 * Note that the key might be used concurrently. @@ -1182,6 +1200,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,  	key->family = family;  	key->prefixlen = prefixlen;  	key->l3index = l3index; +	key->flags = flags;  	memcpy(&key->addr, addr,  	       (family == AF_INET6) ? sizeof(struct in6_addr) :  				      sizeof(struct in_addr)); @@ -1191,11 +1210,11 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,  EXPORT_SYMBOL(tcp_md5_do_add);  int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, -		   u8 prefixlen, int l3index) +		   u8 prefixlen, int l3index, u8 flags)  {  	struct tcp_md5sig_key *key; -	key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index); +	key = tcp_md5_do_lookup_exact(sk, addr, family, prefixlen, l3index, flags);  	if (!key)  		return -ENOENT;  	hlist_del_rcu(&key->node); @@ -1229,6 +1248,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,  	const union tcp_md5_addr *addr;  	u8 prefixlen = 32;  	int l3index = 0; +	u8 flags;  	if (optlen < sizeof(cmd))  		return -EINVAL; @@ -1239,6 +1259,8 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,  	if (sin->sin_family != AF_INET)  		return -EINVAL; +	flags = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; +  	if (optname == TCP_MD5SIG_EXT &&  	    cmd.tcpm_flags & TCP_MD5SIG_FLAG_PREFIX) {  		prefixlen = cmd.tcpm_prefixlen; @@ -1246,7 +1268,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,  			return -EINVAL;  	} -	if (optname == TCP_MD5SIG_EXT && +	if (optname == TCP_MD5SIG_EXT && cmd.tcpm_ifindex &&  	    cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) {  		struct net_device *dev; @@ -1267,12 +1289,12 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,  	addr = (union tcp_md5_addr *)&sin->sin_addr.s_addr;  	if (!cmd.tcpm_keylen) -		return tcp_md5_do_del(sk, addr, AF_INET, prefixlen, l3index); +		return tcp_md5_do_del(sk, addr, AF_INET, prefixlen, l3index, flags);  	if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)  		return -EINVAL; -	return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, +	return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,  			      cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);  } @@ -1596,7 +1618,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,  		 * memory, then we end up not copying the key  		 * across. Shucks.  		 */ -		tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, +		tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags,  			       key->key, key->keylen, GFP_ATOMIC);  		sk_nocaps_add(newsk, NETIF_F_GSO_MASK);  	} @@ -1684,7 +1706,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)  		sock_rps_save_rxhash(sk, skb);  		sk_mark_napi_id(sk, skb);  		if (dst) { -			if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || +			if (sk->sk_rx_dst_ifindex != skb->skb_iif ||  			    !INDIRECT_CALL_1(dst->ops->check, ipv4_dst_check,  					     dst, 0)) {  				dst_release(dst); @@ -1769,7 +1791,7 @@ int tcp_v4_early_demux(struct sk_buff *skb)  			if (dst)  				dst = dst_check(dst, 0);  			if (dst && -			    inet_sk(sk)->rx_dst_ifindex == skb->skb_iif) +			    sk->sk_rx_dst_ifindex == skb->skb_iif)  				skb_dst_set_noref(skb, dst);  		}  	} @@ -1941,7 +1963,6 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph,  int tcp_v4_rcv(struct sk_buff *skb)  {  	struct net *net = dev_net(skb->dev); -	struct sk_buff *skb_to_free;  	int sdif = inet_sdif(skb);  	int dif = inet_iif(skb);  	const struct iphdr *iph; @@ -2050,9 +2071,13 @@ process:  			return 0;  		}  	} -	if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) { -		__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); -		goto discard_and_relse; + +	if (static_branch_unlikely(&ip4_min_ttl)) { +		/* min_ttl can be changed concurrently from do_ip_setsockopt() */ +		if (unlikely(iph->ttl < READ_ONCE(inet_sk(sk)->min_ttl))) { +			__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); +			goto discard_and_relse; +		}  	}  	if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) @@ -2082,17 +2107,12 @@ process:  	tcp_segs_in(tcp_sk(sk), skb);  	ret = 0;  	if (!sock_owned_by_user(sk)) { -		skb_to_free = sk->sk_rx_skb_cache; -		sk->sk_rx_skb_cache = NULL;  		ret = tcp_v4_do_rcv(sk, skb);  	} else {  		if (tcp_add_backlog(sk, skb))  			goto discard_and_relse; -		skb_to_free = NULL;  	}  	bh_unlock_sock(sk); -	if (skb_to_free) -		__kfree_skb(skb_to_free);  put_and_return:  	if (refcounted) @@ -2182,7 +2202,7 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)  	if (dst && dst_hold_safe(dst)) {  		sk->sk_rx_dst = dst; -		inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; +		sk->sk_rx_dst_ifindex = skb->skb_iif;  	}  }  EXPORT_SYMBOL(inet_sk_rx_dst_set); |