netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Christoph Paasch <christoph.paasch@gmail.com>
To: Jason Baron <jbaron@akamai.com>
Cc: netdev@vger.kernel.org
Subject: Re: [RFC PATCH] tcp: accept RST for rcv_nxt - 1 after receiving a FIN
Date: Tue, 10 Jan 2017 21:17:08 -0800	[thread overview]
Message-ID: <20170111051708.GC23178@Chimay.local> (raw)
In-Reply-To: <1483652008-20255-1-git-send-email-jbaron@akamai.com>

Hello Jason,

(resending as Gmail sent out with HTML)

On 05/01/17 - 16:33:28, Jason Baron wrote:
> Using a Mac OSX box as a client connecting to a Linux server, we have found
> that when certain applications (such as 'ab'), are abruptly terminated
> (via ^C), a FIN is sent followed by a RST packet on tcp connections. The
> FIN is accepted by the Linux stack but the RST is sent with the same
> sequence number as the FIN, and Linux responds with a challenge ACK per
> RFC 5961. The OSX client then does not reply with any RST as would be
> expected on a closed socket.

do you see this behavior consistently, even in a controlled environment?

The problem seems rather to be that after the first RST, the NAT on the path
has dropped its mapping and is thus dropping all other traffic. So, Linux's
challenge-ack does not go through to the OSX-host to "re-synchronize" the
state (which would allow OSX to send a RST with the updated sequence numbers).

This is also documented in RFC5961:

9.3.  Middleboxes That Drop the Challenge ACK

   It also needs to be noted that, some middleboxes (Firewalls/NATs)
   that don't have the fix recommended in the document, may drop the
   challenge ACK.  This can happen because, the original RST segment
   that was in window had already cleared the flow state pertaining to
   the TCP connection in the middlebox.  In such cases, the end hosts
   that have implemented the RST mitigation described in this document,
   will have the TCP connection left open.  This is a corner case and
   can go away if the middlebox is conformant with the changes proposed
   in this document.



Cheers,
Christoph

> 
> This results in sockets accumulating on the Linux server left mostly in
> the CLOSE_WAIT state, although LAST_ACK and CLOSING are also possible.
> This sequence of events can tie up a lot of resources on the Linux server
> since there may be a lot of data in write buffers at the time of the RST.
> Accepting a RST equal to rcv_nxt - 1, after we have already successfully
> processed a FIN, has made a significant difference for us in practice, by
> freeing up unneeded resources in a more expedient fashion.
> 
> I also found a posting that the iOS client behaves in a similar manner here
> (it will send a FIN followed by a RST for rcv_nxt - 1):
> https://www.snellman.net/blog/archive/2016-02-01-tcp-rst/
> 
> A packetdrill test demonstrating the behavior.
> 
> // testing mac osx rst behavior
> 
> // Establish a connection
> 0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
> 0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
> 0.000 bind(3, ..., ...) = 0
> 0.000 listen(3, 1) = 0
> 
> 0.100 < S 0:0(0) win 32768 <mss 1460,nop,wscale 10>
> 0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 5>
> 0.200 < . 1:1(0) ack 1 win 32768
> 0.200 accept(3, ..., ...) = 4
> 
> // Client closes the connection
> 0.300 < F. 1:1(0) ack 1 win 32768
> 
> // now send rst with same sequence
> 0.300 < R. 1:1(0) ack 1 win 32768
> 
> // make sure we are in TCP_CLOSE
> 0.400 %{
> assert tcpi_state == 7
> }%
> 
> Signed-off-by: Jason Baron <jbaron@akamai.com>
> ---
>  net/ipv4/tcp_input.c | 23 ++++++++++++++++++++++-
>  1 file changed, 22 insertions(+), 1 deletion(-)
> 
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index ec6d84363024..373bea05c93b 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -5249,6 +5249,24 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen)
>  	return err;
>  }
>  
> +/* Accept RST for rcv_nxt - 1 after a FIN.
> + * When tcp connections are abruptly terminated from Mac OSX (via ^C), a
> + * FIN is sent followed by a RST packet. The RST is sent with the same
> + * sequence number as the FIN, and thus according to RFC 5961 a challenge
> + * ACK should be sent. However, Mac OSX does not reply to the challenge ACK
> + * with a RST on the closed socket, hence accept this class of RSTs.
> + */
> +static bool tcp_reset_check(struct sock *sk, struct sk_buff *skb)
> +{
> +	struct tcp_sock *tp = tcp_sk(sk);
> +
> +	return unlikely((TCP_SKB_CB(skb)->seq == (tp->rcv_nxt - 1)) &&
> +			(TCP_SKB_CB(skb)->end_seq == (tp->rcv_nxt - 1))	&&
> +			(sk->sk_state == TCP_CLOSE_WAIT ||
> +			 sk->sk_state == TCP_LAST_ACK ||
> +			 sk->sk_state == TCP_CLOSING));
> +}
> +
>  /* Does PAWS and seqno based validation of an incoming segment, flags will
>   * play significant role here.
>   */
> @@ -5287,6 +5305,8 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
>  						  LINUX_MIB_TCPACKSKIPPEDSEQ,
>  						  &tp->last_oow_ack_time))
>  				tcp_send_dupack(sk, skb);
> +		} else if (tcp_reset_check(sk, skb)) {
> +			tcp_reset(sk);
>  		}
>  		goto discard;
>  	}
> @@ -5300,7 +5320,8 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
>  		 * else
>  		 *     Send a challenge ACK
>  		 */
> -		if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
> +		if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt ||
> +		    tcp_reset_check(sk, skb)) {
>  			rst_seq_match = true;
>  		} else if (tcp_is_sack(tp) && tp->rx_opt.num_sacks > 0) {
>  			struct tcp_sack_block *sp = &tp->selective_acks[0];
> -- 
> 2.6.1
> 

  reply	other threads:[~2017-01-11  5:17 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-05 21:33 [RFC PATCH] tcp: accept RST for rcv_nxt - 1 after receiving a FIN Jason Baron
2017-01-11  5:17 ` Christoph Paasch [this message]
2017-01-11 15:14   ` Jason Baron
2017-01-11 15:48 ` Eric Dumazet
2017-01-13 17:28   ` Jason Baron
2017-01-13 18:17     ` Eric Dumazet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170111051708.GC23178@Chimay.local \
    --to=christoph.paasch@gmail.com \
    --cc=jbaron@akamai.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).