diff options
Diffstat (limited to 'net/ipv4/tcp_input.c')
| -rw-r--r-- | net/ipv4/tcp_input.c | 46 | 
1 files changed, 29 insertions, 17 deletions
| diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ab5f0ea166f1..bc2ea12221f9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2513,6 +2513,21 @@ static inline bool tcp_may_undo(const struct tcp_sock *tp)  	return tp->undo_marker && (!tp->undo_retrans || tcp_packet_delayed(tp));  } +static bool tcp_is_non_sack_preventing_reopen(struct sock *sk) +{ +	struct tcp_sock *tp = tcp_sk(sk); + +	if (tp->snd_una == tp->high_seq && tcp_is_reno(tp)) { +		/* Hold old state until something *above* high_seq +		 * is ACKed. For Reno it is MUST to prevent false +		 * fast retransmits (RFC2582). SACK TCP is safe. */ +		if (!tcp_any_retrans_done(sk)) +			tp->retrans_stamp = 0; +		return true; +	} +	return false; +} +  /* People celebrate: "We love our President!" */  static bool tcp_try_undo_recovery(struct sock *sk)  { @@ -2535,14 +2550,8 @@ static bool tcp_try_undo_recovery(struct sock *sk)  	} else if (tp->rack.reo_wnd_persist) {  		tp->rack.reo_wnd_persist--;  	} -	if (tp->snd_una == tp->high_seq && tcp_is_reno(tp)) { -		/* Hold old state until something *above* high_seq -		 * is ACKed. For Reno it is MUST to prevent false -		 * fast retransmits (RFC2582). SACK TCP is safe. */ -		if (!tcp_any_retrans_done(sk)) -			tp->retrans_stamp = 0; +	if (tcp_is_non_sack_preventing_reopen(sk))  		return true; -	}  	tcp_set_ca_state(sk, TCP_CA_Open);  	tp->is_sack_reneg = 0;  	return false; @@ -2578,6 +2587,8 @@ static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo)  			NET_INC_STATS(sock_net(sk),  					LINUX_MIB_TCPSPURIOUSRTOS);  		inet_csk(sk)->icsk_retransmits = 0; +		if (tcp_is_non_sack_preventing_reopen(sk)) +			return true;  		if (frto_undo || tcp_is_sack(tp)) {  			tcp_set_ca_state(sk, TCP_CA_Open);  			tp->is_sack_reneg = 0; @@ -3614,12 +3625,9 @@ bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,  /* RFC 5961 7 [ACK Throttling] */  static void tcp_send_challenge_ack(struct sock *sk)  { -	/* unprotected vars, we dont care of overwrites */ -	static u32 challenge_timestamp; -	static unsigned int challenge_count;  	struct tcp_sock *tp = tcp_sk(sk);  	struct net *net = sock_net(sk); -	u32 count, now; +	u32 count, now, ack_limit;  	/* First check our per-socket dupack rate limit. */  	if (__tcp_oow_rate_limited(net, @@ -3627,18 +3635,22 @@ static void tcp_send_challenge_ack(struct sock *sk)  				   &tp->last_oow_ack_time))  		return; +	ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit); +	if (ack_limit == INT_MAX) +		goto send_ack; +  	/* Then check host-wide RFC 5961 rate limit. */  	now = jiffies / HZ; -	if (now != challenge_timestamp) { -		u32 ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit); +	if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) {  		u32 half = (ack_limit + 1) >> 1; -		challenge_timestamp = now; -		WRITE_ONCE(challenge_count, half + prandom_u32_max(ack_limit)); +		WRITE_ONCE(net->ipv4.tcp_challenge_timestamp, now); +		WRITE_ONCE(net->ipv4.tcp_challenge_count, half + prandom_u32_max(ack_limit));  	} -	count = READ_ONCE(challenge_count); +	count = READ_ONCE(net->ipv4.tcp_challenge_count);  	if (count > 0) { -		WRITE_ONCE(challenge_count, count - 1); +		WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1); +send_ack:  		NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);  		tcp_send_ack(sk);  	} | 
