Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 22 Sep 2025 17:34:40 +0800
From:      Tilnel <deng1991816@gmail.com>
To:        Vadim Goncharov <vadimnuclight@gmail.com>
Cc:        freebsd-net@freebsd.org
Subject:   Re: Two different places between TCP socket behavior and RFC documents
Message-ID:  <CADvKEf_WWFwNzqAB4dqSZrQAWdYsa_91iO0dvtMRLV%2BhOqmScg@mail.gmail.com>
In-Reply-To: <20250919002647.367809e7@nuclight.lan>
References:  <CADvKEf-vkJ-eKpwe_-x-z0pUTyx2sZRE3v7%2BZRV7cP_pq7h__w@mail.gmail.com> <20250919002647.367809e7@nuclight.lan>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, Sep 19, 2025 at 5:27=E2=80=AFAM Vadim Goncharov <vadimnuclight@gmai=
l.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 s=
ocket
> > 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 d=
ata 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 s=
tack?
> I.e. does the connection still hold indefinitely for A after B's close() =
?

No. The connection will not be maintained indefinitely, because FreeBSD see=
ms
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 cor=
rect"
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" cl=
ose
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 capitali=
zed).
> > 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 implement=
ation
> > 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 syst=
ems
> check the liveness of host (not being spoofed SYN) by sending out-of-wind=
ow
> 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 atta=
ckers
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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CADvKEf_WWFwNzqAB4dqSZrQAWdYsa_91iO0dvtMRLV%2BhOqmScg>