tcp: Always allow ACKs when pending, fixes for no_snd_wnd and closing states

We won't necessarily have another choice to ACK in a timely fashion
if we skip ACKs from a number of states (including ESTABLISHED) when
there's enough window left. Check for ACKed bytes as soon as it makes
sense.

If the sending window is not reported by the kernel, ACK as soon as
we queue onto the socket, given that we're forced to use a rather
small window.

In FIN_WAIT_1_SOCK_FIN, we also have to account for the FIN flag sent
by the peer in the sequence.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
Stefano Brivio 2021-08-04 01:29:59 +02:00
parent c62490ffa8
commit 0017bc3c3e

37
tcp.c
View file

@ -1081,15 +1081,17 @@ static int tcp_send_to_tap(struct ctx *c, struct tcp_tap_conn *conn,
uint32_t ack_offset = conn->seq_from_tap - conn->seq_ack_to_tap;
char buf[USHRT_MAX] = { 0 }, *data;
struct tcp_info info = { 0 };
int ws = 0, err, ack_pending;
socklen_t sl = sizeof(info);
struct tcphdr *th;
int ws = 0, err;
if (ack_offset < conn->tcpi_snd_wnd / 10 && !flags) {
if (!ack_offset && !flags) {
err = 0;
info.tcpi_bytes_acked = conn->tcpi_acked_last;
info.tcpi_snd_wnd = conn->tcpi_snd_wnd;
info.tcpi_snd_wscale = conn->ws;
} else if (conn->no_snd_wnd && !(flags & SYN)) {
err = 0;
} else {
err = getsockopt(conn->sock, SOL_TCP, TCP_INFO, &info, &sl);
if (err && !(flags & RST)) {
@ -1135,21 +1137,32 @@ static int tcp_send_to_tap(struct ctx *c, struct tcp_tap_conn *conn,
conn->seq_to_tap += len;
}
if (!err && ((info.tcpi_bytes_acked > conn->tcpi_acked_last) ||
(flags & ACK) || len)) {
if (conn->no_snd_wnd) {
ack_pending = (conn->seq_from_tap - conn->seq_ack_to_tap) <
MAX_WINDOW;
} else {
ack_pending = info.tcpi_bytes_acked > conn->tcpi_acked_last;
}
if (!err && (ack_pending || (flags & ACK) || len)) {
th->ack = 1;
conn->seq_ack_to_tap = info.tcpi_bytes_acked +
conn->seq_init_from_tap;
if (conn->state == LAST_ACK) {
conn->seq_ack_to_tap = conn->seq_from_tap + 1;
th->seq = htonl(ntohl(th->seq) + 1);
if (conn->no_snd_wnd) {
conn->seq_ack_to_tap = conn->seq_from_tap;
} else {
conn->seq_ack_to_tap = info.tcpi_bytes_acked +
conn->seq_init_from_tap;
conn->tcpi_acked_last = info.tcpi_bytes_acked;
}
th->ack_seq = htonl(conn->seq_ack_to_tap);
if (conn->state == LAST_ACK ||
conn->state == FIN_WAIT_1_SOCK_FIN)
conn->seq_ack_to_tap = conn->seq_from_tap + 1;
conn->tcpi_acked_last = info.tcpi_bytes_acked;
if (conn->state == LAST_ACK)
th->seq = htonl(ntohl(th->seq) + 1);
th->ack_seq = htonl(conn->seq_ack_to_tap);
} else {
if (!len && !flags)
return 0;