net: tcp: Fix FIN with data handling

In case FIN packet included data bytes, Zephyr would ignore the
data. It wasn't passed to the application, and it wasn't considered
when bumping the ACK counter. This ended out in connection timing out,
instead of being properly terminated.

Fix this, by refactoring FIN processing in TCP_ESTABLISHED state.
Instead of handling FIN/FIN,ACK/FIN,ACK,PSH cases separately, have a
common handler when FIN flag is present, and when ACK flag is present
along with FIN. When FIN is present, take any potentially incoming data
into account.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2024-04-11 11:45:49 +02:00 committed by Alberto Escolar
parent 2fc42054a5
commit a0f0e55668

View file

@ -2998,30 +2998,9 @@ next_state:
break;
case TCP_ESTABLISHED:
/* full-close */
if (th && FL(&fl, ==, (FIN | ACK), th_seq(th) == conn->ack)) {
if (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) {
uint32_t len_acked = th_ack(th) - conn->seq;
if (th && FL(&fl, &, FIN, th_seq(th) == conn->ack)) {
bool acked = false;
conn_seq(conn, + len_acked);
}
conn_ack(conn, + 1);
tcp_out(conn, FIN | ACK);
conn_seq(conn, + 1);
next = TCP_LAST_ACK;
verdict = NET_OK;
keep_alive_timer_stop(conn);
tcp_setup_last_ack_timer(conn);
break;
} else if (th && FL(&fl, ==, FIN, th_seq(th) == conn->ack)) {
conn_ack(conn, + 1);
tcp_out(conn, ACK);
next = TCP_CLOSE_WAIT;
verdict = NET_OK;
keep_alive_timer_stop(conn);
break;
} else if (th && FL(&fl, ==, (FIN | ACK | PSH),
th_seq(th) == conn->ack)) {
if (len) {
verdict = tcp_data_get(conn, pkt, &len);
if (verdict == NET_OK) {
@ -3033,11 +3012,28 @@ next_state:
}
conn_ack(conn, + len + 1);
tcp_out(conn, FIN | ACK);
conn_seq(conn, + 1);
next = TCP_LAST_ACK;
keep_alive_timer_stop(conn);
tcp_setup_last_ack_timer(conn);
if (FL(&fl, &, ACK)) {
acked = true;
if (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) {
uint32_t len_acked = th_ack(th) - conn->seq;
conn_seq(conn, + len_acked);
}
}
if (acked) {
tcp_out(conn, FIN | ACK);
conn_seq(conn, + 1);
tcp_setup_last_ack_timer(conn);
next = TCP_LAST_ACK;
} else {
tcp_out(conn, ACK);
next = TCP_CLOSE_WAIT;
}
break;
}