Skip to content

Commit 46292d7

Browse files
rlubosdkalowsk
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]> (cherry picked from commit 1781505)
1 parent 51e1f38 commit 46292d7

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
@@ -2638,7 +2638,7 @@ static void tcp_queue_recv_data(struct tcp *conn, struct net_pkt *pkt,
26382638
}
26392639

26402640
static enum net_verdict tcp_data_received(struct tcp *conn, struct net_pkt *pkt,
2641-
size_t *len, bool psh)
2641+
size_t *len, bool psh, bool fin)
26422642
{
26432643
enum net_verdict ret;
26442644

@@ -2651,6 +2651,13 @@ static enum net_verdict tcp_data_received(struct tcp *conn, struct net_pkt *pkt,
26512651
net_stats_update_tcp_seg_recv(conn->iface);
26522652
conn_ack(conn, *len);
26532653

2654+
/* In case FIN was received, don't send ACK just yet, FIN,ACK will be
2655+
* sent instead.
2656+
*/
2657+
if (fin) {
2658+
return ret;
2659+
}
2660+
26542661
/* Delay ACK response in case of small window or missing PSH,
26552662
* as described in RFC 813.
26562663
*/
@@ -3061,37 +3068,8 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
30613068
}
30623069

30633070
break;
3064-
case TCP_ESTABLISHED:
3065-
/* full-close */
3066-
if (FL(&fl, &, FIN, th_seq(th) == conn->ack)) {
3067-
if (len) {
3068-
verdict = tcp_data_get(conn, pkt, &len);
3069-
if (verdict == NET_OK) {
3070-
/* net_pkt owned by the recv fifo now */
3071-
pkt = NULL;
3072-
}
3073-
} else {
3074-
verdict = NET_OK;
3075-
}
3076-
3077-
conn_ack(conn, + len + 1);
3078-
keep_alive_timer_stop(conn);
3079-
3080-
if (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) {
3081-
uint32_t len_acked = th_ack(th) - conn->seq;
3082-
3083-
conn_seq(conn, + len_acked);
3084-
}
3085-
3086-
tcp_out(conn, FIN | ACK);
3087-
conn_seq(conn, + 1);
3088-
tcp_setup_retransmission(conn);
3089-
3090-
tcp_setup_last_ack_timer(conn);
3091-
next = TCP_LAST_ACK;
3092-
3093-
break;
3094-
}
3071+
case TCP_ESTABLISHED: {
3072+
bool fin = FL(&fl, &, FIN, th_seq(th) == conn->ack);
30953073

30963074
/* Whatever we've received, we know that peer is alive, so reset
30973075
* the keepalive timer.
@@ -3197,11 +3175,21 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
31973175

31983176
/* We are closing the connection, send a FIN to peer */
31993177
if (conn->in_close && conn->send_data_total == 0) {
3200-
next = TCP_FIN_WAIT_1;
3201-
3202-
k_work_reschedule_for_queue(&tcp_work_q,
3203-
&conn->fin_timer,
3204-
FIN_TIMEOUT);
3178+
if (fin) {
3179+
/* If FIN was also present in the processed
3180+
* packet, acknowledge that and jump directly
3181+
* to TCP_LAST_ACK.
3182+
*/
3183+
conn_ack(conn, + 1);
3184+
next = TCP_LAST_ACK;
3185+
tcp_setup_last_ack_timer(conn);
3186+
} else {
3187+
/* Otherwise, wait for FIN in TCP_FIN_WAIT_1 */
3188+
next = TCP_FIN_WAIT_1;
3189+
k_work_reschedule_for_queue(&tcp_work_q,
3190+
&conn->fin_timer,
3191+
FIN_TIMEOUT);
3192+
}
32053193

32063194
tcp_out(conn, FIN | ACK);
32073195
conn_seq(conn, + 1);
@@ -3229,7 +3217,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
32293217
if (len > 0) {
32303218
bool psh = FL(&fl, &, PSH);
32313219

3232-
verdict = tcp_data_received(conn, pkt, &len, psh);
3220+
verdict = tcp_data_received(conn, pkt, &len, psh, fin);
32333221
if (verdict == NET_OK) {
32343222
/* net_pkt owned by the recv fifo now */
32353223
pkt = NULL;
@@ -3272,7 +3260,19 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
32723260
k_sem_give(&conn->tx_sem);
32733261
}
32743262

3263+
/* Finally, after all Data/ACK processing, check for FIN flag. */
3264+
if (fin) {
3265+
keep_alive_timer_stop(conn);
3266+
conn_ack(conn, + 1);
3267+
tcp_out(conn, FIN | ACK);
3268+
conn_seq(conn, + 1);
3269+
tcp_setup_retransmission(conn);
3270+
tcp_setup_last_ack_timer(conn);
3271+
next = TCP_LAST_ACK;
3272+
}
3273+
32753274
break;
3275+
}
32763276
case TCP_CLOSE_WAIT:
32773277
/* Half-close is not supported, so do nothing here */
32783278
break;

0 commit comments

Comments
 (0)