Re: Two different places between TCP socket behavior and RFC documents
- In reply to: Vadim Goncharov : "Re: Two different places between TCP socket behavior and RFC documents"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 22 Sep 2025 09:34:40 UTC
On Fri, Sep 19, 2025 at 5:27 AM Vadim Goncharov <vadimnuclight@gmail.com> wrote: > > On Thu, 18 Sep 2025 16:50:46 +0800 > Tilnel <deng1991816@gmail.com> wrote: > > > Hi, > > > > I found two behaviors different with RFC recommendations in FreeBSD 14.3 TCP > > socket. > > > > 1. Failure to RST on close with data pending > > According to RFC2525 section 2.17, RST should be sent when close() on socket > > with pending data to read in receive buffer. > > According to RFC1122: A host MAY implement a "half-duplex" TCP close > > sequence, ... cannot continue to read data ... If such a host issues a CLOSE > > call while received data is still pending in TCP, or if new data is > > received after CLOSE is called, its TCP SHOULD send a RST to show > > that data was lost. > > It's not the case with FreeBSD TCP socket. Here is TCPDUMP output, > > showing close() > > on socket with pending data emit FIN instead of RST. > > A > B: Flags [S], seq 2636678338, win 65535, length 0 > > B > A: Flags [S.], seq 1969223298, ack 2636678339, win 65535, length 0 > > A > B: Flags [.], ack 1, win 1277, length 0 > > A > B: Flags [P.], seq 1:6, ack 1, win 1277, length 5 > > B > A: Flags [.], ack 6, win 1277, length 0 > > B > A: Flags [F.], seq 1, ack 6, win 1277, length 0 > > A > B: Flags [.], ack 2, win 1277, length 0 > > All close()/shutdown(SHUT_RDWR)/shutdown(SHUT_RD) and both SO_LINGER on or > > off give the same trace. While on Linux the same execution gives this: > > A > B: Flags [S], seq 2879877684, win 65495, length 0 > > B > A: Flags [S.], seq 1538598692, ack 2879877685, win 65483, length 0 > > A > B: Flags [.], ack 1, win 512, length 0 > > A > B: Flags [P.], seq 1:6, ack 1, win 512, length 5 > > B > A: Flags [.], ack 6, win 512, length 0 > > B > A: Flags [R.], seq 1, ack 6, win 512, length 0 > > Is the situation from RFC 2525 section 2/17 still applicable to our TCP stack? > I.e. does the connection still hold indefinitely for A after B's close() ? No. The connection will not be maintained indefinitely, because FreeBSD seems to have adopted another approach, similar to the trace on RFC 2525 Page 52: although it sends a FIN-ACK, it opens the receive window again. This allows the other side to continue sending data and eventually receive an RST, or send a FIN-ACK to close the connection normally. However, this approach is still considered "Better, but still not fully correct" in RFC 2525. I guess this may be because it causes unnecessary traffic and resource waste, and the act of opening the window to invite the other party to send data is fundamentally contrary to the purpose of this "half-duplex" close implementation. > > > 2. Sending RST to segment with old sequence SYN-RECEIVED instead of > > acknowledgement > > According to RFC793 page 69: If an incoming segment is not acceptable, an > > acknowledgement should be sent in reply. (here `should` is not capitalized). > > This should be applied to all states including and after SYN-RECEIVED. But > > it's not the case with FreeBSD TCP socket. I found this with manually > > constructed TCP segment: > > A > B: Flags [S], seq 1, win 8192, length 0 > > B > A: Flags [S.], seq 4054810353, ack 2, win 65535, length 0 > > A > B: Flags [.], ack 1, win 8192, length 0 > > B > A: Flags [R], seq 4054810354, win 0, length 0 > > Expected behavior is to send an empty ack: > > A > B: Flags [S], seq 1, win 8192, length 0 > > B > A: Flags [S.], seq 3620804602, ack 2, win 65495, length 0 > > A > B: Flags [.], ack 1, win 8192, length 0 > > B > A: Flags [.], ack 1, win 65495, length 0 > > Which is the case with Linux. > > > > Does anyone know why these two violations exist? Did FreeBSD choose not to > > comply with the RFC for a specific reason, or is it simply an implementation > > error? > > RFC 9293 still does not capitalize "should" here, therefore it is not a > normative requirement. In fact, I vaguely recall that some anti-DDoS systems > check the liveness of host (not being spoofed SYN) by sending out-of-window > packet and expecting RST while main connection is unaffected. > In the scenario above, A has no reason to send an out-of-window ACK to test if B's SYN is spoofed, as the initial SYN is sent by A itself, not B. Furthermore, if this were a legitimate detection mechanism, a large number of systems compliant with RFC recommendations would be falsely flagged as attackers just for replying with an ACK. I haven't found a system with such a defense strategy. Thanks for taking the time to answer my questions. Tilnel > -- > WBR, @nuclight