Skip to content

Commit ed21d39

Browse files
jukkarcvinayak
authored andcommitted
[nrf fromtree] net: tcp: Install a last ack timer in passive close
If we are in a passive close state, then it is possible that the ack we are waiting is lost or we do not accept the one peer sent to us because of some earlier out of memory issue. So install a timer (using by default the FIN timer value) to close the connection if the last ack is not received on time. Signed-off-by: Jukka Rissanen <[email protected]> (cherry picked from commit 40215e0) Signed-off-by: Robert Lubos <[email protected]>
1 parent d5ed38a commit ed21d39

File tree

1 file changed

+47
-4
lines changed

1 file changed

+47
-4
lines changed

subsys/net/ip/tcp.c

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ LOG_MODULE_REGISTER(net_tcp, CONFIG_NET_TCP_LOG_LEVEL);
2828

2929
#define ACK_TIMEOUT_MS CONFIG_NET_TCP_ACK_TIMEOUT
3030
#define ACK_TIMEOUT K_MSEC(ACK_TIMEOUT_MS)
31+
#define LAST_ACK_TIMEOUT_MS tcp_fin_timeout_ms
32+
#define LAST_ACK_TIMEOUT K_MSEC(LAST_ACK_TIMEOUT_MS)
3133
#define FIN_TIMEOUT K_MSEC(tcp_fin_timeout_ms)
3234
#define ACK_DELAY K_MSEC(100)
3335
#define ZWP_MAX_DELAY_MS 120000
@@ -1593,9 +1595,9 @@ static void tcp_resend_data(struct k_work *work)
15931595
conn->send_data_retries++;
15941596
if (ret == 0) {
15951597
if (conn->in_close && conn->send_data_total == 0) {
1596-
NET_DBG("TCP connection in active close, "
1598+
NET_DBG("TCP connection in %s close, "
15971599
"not disposing yet (waiting %dms)",
1598-
tcp_fin_timeout_ms);
1600+
"active", tcp_fin_timeout_ms);
15991601
k_work_reschedule_for_queue(&tcp_work_q,
16001602
&conn->fin_timer,
16011603
FIN_TIMEOUT);
@@ -1675,6 +1677,40 @@ static void tcp_fin_timeout(struct k_work *work)
16751677
(void)tcp_conn_close(conn, -ETIMEDOUT);
16761678
}
16771679

1680+
static void tcp_last_ack_timeout(struct k_work *work)
1681+
{
1682+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
1683+
struct tcp *conn = CONTAINER_OF(dwork, struct tcp, fin_timer);
1684+
1685+
NET_DBG("Did not receive %s in %dms", "last ACK", LAST_ACK_TIMEOUT_MS);
1686+
NET_DBG("conn: %p %s", conn, tcp_conn_state(conn, NULL));
1687+
1688+
(void)tcp_conn_close(conn, -ETIMEDOUT);
1689+
}
1690+
1691+
static void tcp_setup_last_ack_timer(struct tcp *conn)
1692+
{
1693+
/* Just in case the last ack is lost, install a timer that will
1694+
* close the connection in that case. Use the fin_timer for that
1695+
* as the fin handling cannot be done in this passive close state.
1696+
* Instead of default tcp_fin_timeout() function, have a separate
1697+
* function to catch this last ack case.
1698+
*/
1699+
k_work_init_delayable(&conn->fin_timer, tcp_last_ack_timeout);
1700+
1701+
NET_DBG("TCP connection in %s close, "
1702+
"not disposing yet (waiting %dms)",
1703+
"passive", LAST_ACK_TIMEOUT_MS);
1704+
k_work_reschedule_for_queue(&tcp_work_q,
1705+
&conn->fin_timer,
1706+
LAST_ACK_TIMEOUT);
1707+
}
1708+
1709+
static void tcp_cancel_last_ack_timer(struct tcp *conn)
1710+
{
1711+
k_work_cancel_delayable(&conn->fin_timer);
1712+
}
1713+
16781714
static void tcp_send_zwp(struct k_work *work)
16791715
{
16801716
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
@@ -2694,6 +2730,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
26942730
tcp_out(conn, FIN | ACK);
26952731
next = TCP_LAST_ACK;
26962732
verdict = NET_OK;
2733+
tcp_setup_last_ack_timer(conn);
26972734
break;
26982735
} else if (th && FL(&fl, ==, FIN, th_seq(th) == conn->ack)) {
26992736
conn_ack(conn, + 1);
@@ -2716,6 +2753,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
27162753
conn_ack(conn, + len + 1);
27172754
tcp_out(conn, FIN | ACK);
27182755
next = TCP_LAST_ACK;
2756+
tcp_setup_last_ack_timer(conn);
27192757
break;
27202758
}
27212759

@@ -2890,13 +2928,17 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
28902928
case TCP_CLOSE_WAIT:
28912929
tcp_out(conn, FIN);
28922930
next = TCP_LAST_ACK;
2931+
tcp_setup_last_ack_timer(conn);
28932932
break;
28942933
case TCP_LAST_ACK:
28952934
if (th && FL(&fl, ==, ACK, th_seq(th) == conn->ack)) {
28962935
tcp_send_timer_cancel(conn);
28972936
do_close = true;
28982937
verdict = NET_OK;
28992938
close_status = 0;
2939+
2940+
/* Remove the last ack timer if we received it in time */
2941+
tcp_cancel_last_ack_timer(conn);
29002942
}
29012943
break;
29022944
case TCP_CLOSED:
@@ -3222,8 +3264,9 @@ int net_tcp_put(struct net_context *context)
32223264
} else {
32233265
int ret;
32243266

3225-
NET_DBG("TCP connection in active close, not "
3226-
"disposing yet (waiting %dms)", tcp_fin_timeout_ms);
3267+
NET_DBG("TCP connection in %s close, "
3268+
"not disposing yet (waiting %dms)",
3269+
"active", tcp_fin_timeout_ms);
32273270
k_work_reschedule_for_queue(&tcp_work_q,
32283271
&conn->fin_timer,
32293272
FIN_TIMEOUT);

0 commit comments

Comments
 (0)