tcp: Unconditionally force ACK for all !SYN, !RST packets

Currently we set ACK on flags packets only when the acknowledged byte
pointer has advanced, or we hadn't previously set a window.  This means
in particular that we can send a window update with no ACK flag, which
doesn't appear to be correct.  RFC 9293 requires a receiver to ignore such
a packet [0], and indeed it appears that every non-SYN, non-RST packet
should have the ACK flag.

The reason for the existing logic, rather than always forcing an ACK seems
to be to avoid having the packet mistaken as a duplicate ACK which might
trigger a fast retransmit.  However, earlier tests in the function mean we
won't reach here if we don't have either an advance in the ack pointer -
which will already set the ACK flag, or a window update - which shouldn't
trigger a fast retransmit.

[0] https://www.ietf.org/rfc/rfc9293.html#section-3.10.7.4-2.5.2.1

Link: https://github.com/containers/podman/issues/22146
Link: https://bugs.passt.top/show_bug.cgi?id=84
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2024-03-26 16:42:24 +11:00 committed by Stefano Brivio
parent 5894a245b9
commit 4988e2b406

6
tcp.c
View file

@ -1593,8 +1593,6 @@ static void tcp_update_seqack_from_tap(const struct ctx *c,
*/ */
static int tcp_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags) static int tcp_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags)
{ {
uint32_t prev_ack_to_tap = conn->seq_ack_to_tap;
uint32_t prev_wnd_to_tap = conn->wnd_to_tap;
struct tcp4_l2_flags_buf_t *b4 = NULL; struct tcp4_l2_flags_buf_t *b4 = NULL;
struct tcp6_l2_flags_buf_t *b6 = NULL; struct tcp6_l2_flags_buf_t *b6 = NULL;
struct tcp_info tinfo = { 0 }; struct tcp_info tinfo = { 0 };
@ -1675,9 +1673,7 @@ static int tcp_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags)
*data++ = OPT_WS_LEN; *data++ = OPT_WS_LEN;
*data++ = conn->ws_to_tap; *data++ = conn->ws_to_tap;
} else if (!(flags & RST)) { } else if (!(flags & RST)) {
if (conn->seq_ack_to_tap != prev_ack_to_tap || flags |= ACK;
!prev_wnd_to_tap)
flags |= ACK;
} }
th->doff = (sizeof(*th) + optlen) / 4; th->doff = (sizeof(*th) + optlen) / 4;