diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 21 | 
1 files changed, 18 insertions, 3 deletions
| diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 578ab6305c3f..0ce52d46e4f8 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -348,11 +348,20 @@ failure:  static void tcp_v6_mtu_reduced(struct sock *sk)  {  	struct dst_entry *dst; +	u32 mtu;  	if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))  		return; -	dst = inet6_csk_update_pmtu(sk, tcp_sk(sk)->mtu_info); +	mtu = READ_ONCE(tcp_sk(sk)->mtu_info); + +	/* Drop requests trying to increase our current mss. +	 * Check done in __ip6_rt_update_pmtu() is too late. +	 */ +	if (tcp_mtu_to_mss(sk, mtu) >= tcp_sk(sk)->mss_cache) +		return; + +	dst = inet6_csk_update_pmtu(sk, mtu);  	if (!dst)  		return; @@ -433,6 +442,8 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  	}  	if (type == ICMPV6_PKT_TOOBIG) { +		u32 mtu = ntohl(info); +  		/* We are not interested in TCP_LISTEN and open_requests  		 * (SYN-ACKs send out by Linux are always <576bytes so  		 * they should go through unfragmented). @@ -443,7 +454,11 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,  		if (!ip6_sk_accept_pmtu(sk))  			goto out; -		tp->mtu_info = ntohl(info); +		if (mtu < IPV6_MIN_MTU) +			goto out; + +		WRITE_ONCE(tp->mtu_info, mtu); +  		if (!sock_owned_by_user(sk))  			tcp_v6_mtu_reduced(sk);  		else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, @@ -540,7 +555,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,  		opt = ireq->ipv6_opt;  		if (!opt)  			opt = rcu_dereference(np->opt); -		err = ip6_xmit(sk, skb, fl6, sk->sk_mark, opt, +		err = ip6_xmit(sk, skb, fl6, skb->mark ? : sk->sk_mark, opt,  			       tclass, sk->sk_priority);  		rcu_read_unlock();  		err = net_xmit_eval(err); |