Skip to content

Commit 1781505

Browse files
rluboshenrikbrixandersen
authored andcommitted
net: tcp: Fix ACK processing when FIN packet is received
In case FIN packed also acknowledged most recently sent data, not all ack-related TCP context variables were updated, resulting in invalid SEQ number values sent in consecutive packets. Fix this by refactoring the FIN handling in TCP_ESTABLISHED state. Instead of having a separate block strictly for FIN packet processing, let the packet be processed by common code responsible for regular data/ack processing. This should be less error-prone for any future modifications or not-yet-discovered issues. Only after the common processing of data/ack is done, we check whether FIN flag was present in the packet, and mark the connection for closing. Signed-off-by: Robert Lubos <[email protected]>
1 parent 99b4357 commit 1781505

File tree

1 file changed

+38
-38
lines changed

1 file changed

+38
-38
lines changed

subsys/net/ip/tcp.c

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,7 +2783,7 @@ static void tcp_queue_recv_data(struct tcp *conn, struct net_pkt *pkt,
27832783
}
27842784

27852785
static enum net_verdict tcp_data_received(struct tcp *conn, struct net_pkt *pkt,
2786-
size_t *len, bool psh)
2786+
size_t *len, bool psh, bool fin)
27872787
{
27882788
enum net_verdict ret;
27892789

@@ -2796,6 +2796,13 @@ static enum net_verdict tcp_data_received(struct tcp *conn, struct net_pkt *pkt,
27962796
net_stats_update_tcp_seg_recv(conn->iface);
27972797
conn_ack(conn, *len);
27982798

2799+
/* In case FIN was received, don't send ACK just yet, FIN,ACK will be
2800+
* sent instead.
2801+
*/
2802+
if (fin) {
2803+
return ret;
2804+
}
2805+
27992806
/* Delay ACK response in case of small window or missing PSH,
28002807
* as described in RFC 813.
28012808
*/
@@ -3212,37 +3219,8 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
32123219
}
32133220

32143221
break;
3215-
case TCP_ESTABLISHED:
3216-
/* full-close */
3217-
if (FL(&fl, &, FIN, th_seq(th) == conn->ack)) {
3218-
if (len) {
3219-
verdict = tcp_data_get(conn, pkt, &len);
3220-
if (verdict == NET_OK) {
3221-
/* net_pkt owned by the recv fifo now */
3222-
pkt = NULL;
3223-
}
3224-
} else {
3225-
verdict = NET_OK;
3226-
}
3227-
3228-
conn_ack(conn, + len + 1);
3229-
keep_alive_timer_stop(conn);
3230-
3231-
if (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) {
3232-
uint32_t len_acked = th_ack(th) - conn->seq;
3233-
3234-
conn_seq(conn, + len_acked);
3235-
}
3236-
3237-
tcp_out(conn, FIN | ACK);
3238-
conn_seq(conn, + 1);
3239-
tcp_setup_retransmission(conn);
3240-
3241-
tcp_setup_last_ack_timer(conn);
3242-
next = TCP_LAST_ACK;
3243-
3244-
break;
3245-
}
3222+
case TCP_ESTABLISHED: {
3223+
bool fin = FL(&fl, &, FIN, th_seq(th) == conn->ack);
32463224

32473225
/* Whatever we've received, we know that peer is alive, so reset
32483226
* the keepalive timer.
@@ -3348,11 +3326,21 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
33483326

33493327
/* We are closing the connection, send a FIN to peer */
33503328
if (conn->in_close && conn->send_data_total == 0) {
3351-
next = TCP_FIN_WAIT_1;
3352-
3353-
k_work_reschedule_for_queue(&tcp_work_q,
3354-
&conn->fin_timer,
3355-
FIN_TIMEOUT);
3329+
if (fin) {
3330+
/* If FIN was also present in the processed
3331+
* packet, acknowledge that and jump directly
3332+
* to TCP_LAST_ACK.
3333+
*/
3334+
conn_ack(conn, + 1);
3335+
next = TCP_LAST_ACK;
3336+
tcp_setup_last_ack_timer(conn);
3337+
} else {
3338+
/* Otherwise, wait for FIN in TCP_FIN_WAIT_1 */
3339+
next = TCP_FIN_WAIT_1;
3340+
k_work_reschedule_for_queue(&tcp_work_q,
3341+
&conn->fin_timer,
3342+
FIN_TIMEOUT);
3343+
}
33563344

33573345
tcp_out(conn, FIN | ACK);
33583346
conn_seq(conn, + 1);
@@ -3383,7 +3371,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
33833371
data_recv:
33843372
psh = FL(&fl, &, PSH);
33853373

3386-
verdict = tcp_data_received(conn, pkt, &len, psh);
3374+
verdict = tcp_data_received(conn, pkt, &len, psh, fin);
33873375
if (verdict == NET_OK) {
33883376
/* net_pkt owned by the recv fifo now */
33893377
pkt = NULL;
@@ -3427,7 +3415,19 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
34273415
k_sem_give(&conn->tx_sem);
34283416
}
34293417

3418+
/* Finally, after all Data/ACK processing, check for FIN flag. */
3419+
if (fin) {
3420+
keep_alive_timer_stop(conn);
3421+
conn_ack(conn, + 1);
3422+
tcp_out(conn, FIN | ACK);
3423+
conn_seq(conn, + 1);
3424+
tcp_setup_retransmission(conn);
3425+
tcp_setup_last_ack_timer(conn);
3426+
next = TCP_LAST_ACK;
3427+
}
3428+
34303429
break;
3430+
}
34313431
case TCP_CLOSE_WAIT:
34323432
/* Half-close is not supported, so do nothing here */
34333433
break;

0 commit comments

Comments
 (0)