Skip to content

Commit 1ec9e9c

Browse files
committed
[nrf fromtree] 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 b33e0dc commit 1ec9e9c

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
@@ -2719,7 +2719,7 @@ static void tcp_queue_recv_data(struct tcp *conn, struct net_pkt *pkt,
27192719
}
27202720

27212721
static enum net_verdict tcp_data_received(struct tcp *conn, struct net_pkt *pkt,
2722-
size_t *len, bool psh)
2722+
size_t *len, bool psh, bool fin)
27232723
{
27242724
enum net_verdict ret;
27252725

@@ -2732,6 +2732,13 @@ static enum net_verdict tcp_data_received(struct tcp *conn, struct net_pkt *pkt,
27322732
net_stats_update_tcp_seg_recv(conn->iface);
27332733
conn_ack(conn, *len);
27342734

2735+
/* In case FIN was received, don't send ACK just yet, FIN,ACK will be
2736+
* sent instead.
2737+
*/
2738+
if (fin) {
2739+
return ret;
2740+
}
2741+
27352742
/* Delay ACK response in case of small window or missing PSH,
27362743
* as described in RFC 813.
27372744
*/
@@ -3143,37 +3150,8 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
31433150
}
31443151

31453152
break;
3146-
case TCP_ESTABLISHED:
3147-
/* full-close */
3148-
if (FL(&fl, &, FIN, th_seq(th) == conn->ack)) {
3149-
if (len) {
3150-
verdict = tcp_data_get(conn, pkt, &len);
3151-
if (verdict == NET_OK) {
3152-
/* net_pkt owned by the recv fifo now */
3153-
pkt = NULL;
3154-
}
3155-
} else {
3156-
verdict = NET_OK;
3157-
}
3158-
3159-
conn_ack(conn, + len + 1);
3160-
keep_alive_timer_stop(conn);
3161-
3162-
if (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) {
3163-
uint32_t len_acked = th_ack(th) - conn->seq;
3164-
3165-
conn_seq(conn, + len_acked);
3166-
}
3167-
3168-
tcp_out(conn, FIN | ACK);
3169-
conn_seq(conn, + 1);
3170-
tcp_setup_retransmission(conn);
3171-
3172-
tcp_setup_last_ack_timer(conn);
3173-
next = TCP_LAST_ACK;
3174-
3175-
break;
3176-
}
3153+
case TCP_ESTABLISHED: {
3154+
bool fin = FL(&fl, &, FIN, th_seq(th) == conn->ack);
31773155

31783156
/* Whatever we've received, we know that peer is alive, so reset
31793157
* the keepalive timer.
@@ -3279,11 +3257,21 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
32793257

32803258
/* We are closing the connection, send a FIN to peer */
32813259
if (conn->in_close && conn->send_data_total == 0) {
3282-
next = TCP_FIN_WAIT_1;
3283-
3284-
k_work_reschedule_for_queue(&tcp_work_q,
3285-
&conn->fin_timer,
3286-
FIN_TIMEOUT);
3260+
if (fin) {
3261+
/* If FIN was also present in the processed
3262+
* packet, acknowledge that and jump directly
3263+
* to TCP_LAST_ACK.
3264+
*/
3265+
conn_ack(conn, + 1);
3266+
next = TCP_LAST_ACK;
3267+
tcp_setup_last_ack_timer(conn);
3268+
} else {
3269+
/* Otherwise, wait for FIN in TCP_FIN_WAIT_1 */
3270+
next = TCP_FIN_WAIT_1;
3271+
k_work_reschedule_for_queue(&tcp_work_q,
3272+
&conn->fin_timer,
3273+
FIN_TIMEOUT);
3274+
}
32873275

32883276
tcp_out(conn, FIN | ACK);
32893277
conn_seq(conn, + 1);
@@ -3314,7 +3302,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
33143302
data_recv:
33153303
psh = FL(&fl, &, PSH);
33163304

3317-
verdict = tcp_data_received(conn, pkt, &len, psh);
3305+
verdict = tcp_data_received(conn, pkt, &len, psh, fin);
33183306
if (verdict == NET_OK) {
33193307
/* net_pkt owned by the recv fifo now */
33203308
pkt = NULL;
@@ -3358,7 +3346,19 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
33583346
k_sem_give(&conn->tx_sem);
33593347
}
33603348

3349+
/* Finally, after all Data/ACK processing, check for FIN flag. */
3350+
if (fin) {
3351+
keep_alive_timer_stop(conn);
3352+
conn_ack(conn, + 1);
3353+
tcp_out(conn, FIN | ACK);
3354+
conn_seq(conn, + 1);
3355+
tcp_setup_retransmission(conn);
3356+
tcp_setup_last_ack_timer(conn);
3357+
next = TCP_LAST_ACK;
3358+
}
3359+
33613360
break;
3361+
}
33623362
case TCP_CLOSE_WAIT:
33633363
/* Half-close is not supported, so do nothing here */
33643364
break;

0 commit comments

Comments
 (0)