On 25/05/2025 08:12, Alexander Bluhm wrote:
> On Mon, May 19, 2025 at 12:24:59PM +0000, Hu Ruinan wrote:
>>> Description:
>> When a TCP connection is in the ESTABLISHED state, OpenBSD's TCP
>> implementation incorrectly accepts a packet with an acknowledgment
>> number (ACK) smaller than expected and containing the FIN+ACK flags.
>> In my test, I sent such a FIN+ACK packet with a smaller ACK value and
>> OpenBSD accepted it, responded with FIN+ACK.
> 
> I can confirm this behavior.
> 
>> According to RFC 9293, if the ACK is a duplicate, it can be ignored. If the 
>> ACK acks something not yet sent, then send an ACK, drop the segment, and 
>> return.
> 
> I guess you refer to this section of the RFC.  Qouting the relevant
> path and conditions.
> 
> 3.  Functional Specification
> 3.10. Event Processing
> 3.10.7.  SEGMENT ARRIVES
> 3.10.7.4.  Other States
>       Fifth, check the ACK field:
>       -  if the ACK bit is on,
> 
>          o  RFC 5961 [9], Section 5 describes a potential blind data
>             injection attack, and mitigation that implementations MAY
>             choose to include (MAY-12).  TCP stacks that implement RFC
>             5961 MUST add an input check that the ACK value is
>             acceptable only if it is in the range of ((SND.UNA -
>             MAX.SND.WND) =< SEG.ACK =< SND.NXT).  All incoming segments
>             whose ACK value doesn't satisfy the above condition MUST be
>             discarded and an ACK sent back.  The new state variable
>             MAX.SND.WND is defined as the largest window that the local
>             sender has ever received from its peer (subject to window
>             scaling) or may be hard-coded to a maximum permissible
>             window value.  When the ACK value is acceptable, the per-
>             state processing below applies:
> 
>          o  ESTABLISHED STATE
> 
>             +  If SND.UNA < SEG.ACK =< SND.NXT, then set SND.UNA <-
>                SEG.ACK.  Any segments on the retransmission queue that
>                are thereby entirely acknowledged are removed.  Users
>                should receive positive acknowledgments for buffers that
>                have been SENT and fully acknowledged (i.e., SEND buffer
>                should be returned with "ok" response).  If the ACK is a
>                duplicate (SEG.ACK =< SND.UNA), it can be ignored.  If
>                the ACK acks something not yet sent (SEG.ACK > SND.NXT),
>                then send an ACK, drop the segment, and return.
> 
>       Eighth, check the FIN bit:
> 
> The question is, whether our behavior is illegal.
> 
> Before we do "Eighth, check the FIN bit" we must comply to "Fifth,
> check the ACK field".
> 
> There the hard requirement to drop the ACK packet is ((SND.UNA -
> MAX.SND.WND) =< SEG.ACK =< SND.NXT).  In your script you use
> ack=ack_ack-5.  As 5 is less than MAX.SND.WND the condition is not
> met.
> 
> The part (SEG.ACK > SND.NXT) does not apply, as your ack number is
> too small.  The text "not yet sent" refers to some potential data
> in the future, not never existing data in the past.
> 
> So the case you refer to is (SEG.ACK =< SND.UNA).  It says "it can
> be ignored".  Note that "can" is not "MUST".  And what does "it"
> mean?  The acknowledgement or the whole packet that carries the
> additional FIN?
> 
> So while our behavior is questionable, I think it is not illegal.
> Usually I am very carefull here, as every change has a high risk.
> Interoperability may suffer or even DoS is possible when sending
> too many ACK packets.  And just changing some line in tcp_input()
> can have unexpected consequences for other corner cases.
> 
> Do you see a clear violation of the RFC?
> Do you see a potential DoS by not being strict enough?
> What would be the benefit if we would ignore your ACK packet?
> 
> Thanks for testing our TCP stack and searching for corner cases.
> 
> bluhm

Thank you for your detailed explanation. I've conducted further testing
about the threshold of this behavior.

When I send `ack=ack_ack-67035`, I receive the FIN+ACK from OpenBSD.

But when I send `ack=ack_ack-67036`, OpenBSD send nothing.

I also modified the window size in my script, and the behavior remains
the same.

Is the -67035 also larger than SND.UNA-MAX.SND.WND? And there is no ACK
packets send back when the ack number is too small, is this behavior as
expected?

Trace of `ack=ack_ack-67035`:

10:52:02.851556 enp45s0 Out IP 192.168.31.251.49825 >
192.168.31.131.12347: Flags [S], seq 1505569531, win 64224, options [mss
1460,nop,eol], length 0
10:52:02.851819 enp45s0 In  IP 192.168.31.131.12347 >
192.168.31.251.49825: Flags [S.], seq 2019486834, ack 1505569532, win
16384, options [mss 1460], length 0
10:52:02.878657 enp45s0 Out IP 192.168.31.251.49825 >
192.168.31.131.12347: Flags [.], ack 1, win 502, length 0
10:52:02.897896 enp45s0 Out IP 192.168.31.251.49825 >
192.168.31.131.12347: Flags [F.], seq 1, ack 4294900262, win 502, length 0
10:52:02.898131 enp45s0 In  IP 192.168.31.131.12347 >
192.168.31.251.49825: Flags [.], ack 2, win 17520, length 0
10:52:02.898644 enp45s0 In  IP 192.168.31.131.12347 >
192.168.31.251.49825: Flags [F.], seq 1, ack 2, win 17520, length 0
10:52:03.225831 enp45s0 Out IP 192.168.31.251.49825 >
192.168.31.131.12347: Flags [.], ack 2, win 502, options [nop,nop,eol],
length 0

Trace of `ack=ack_ack-67036`:

10:52:20.772672 enp45s0 Out IP 192.168.31.251.47692 >
192.168.31.131.12347: Flags [S], seq 1033802970, win 64224, options [mss
1460,nop,eol], length 0
10:52:20.772934 enp45s0 In  IP 192.168.31.131.12347 >
192.168.31.251.47692: Flags [S.], seq 621784845, ack 1033802971, win
16384, options [mss 1460], length 0
10:52:20.790977 enp45s0 Out IP 192.168.31.251.47692 >
192.168.31.131.12347: Flags [.], ack 1, win 502, length 0
10:52:20.832912 enp45s0 Out IP 192.168.31.251.47692 >
192.168.31.131.12347: Flags [F.], seq 1, ack 4294900261, win 502, length 0
10:52:21.166072 enp45s0 Out IP 192.168.31.251.47692 >
192.168.31.131.12347: Flags [.], ack 2, win 502, options [nop,nop,eol],
length 0
10:52:21.166361 enp45s0 In  IP 192.168.31.131.12347 >
192.168.31.251.47692: Flags [.], ack 1, win 17520, length 0

Ruinan

Reply via email to