Skip to content

Commit 6edacfa

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 4b5ef27 commit 6edacfa

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
@@ -1493,9 +1495,9 @@ static void tcp_resend_data(struct k_work *work)
14931495
conn->send_data_retries++;
14941496
if (ret == 0) {
14951497
if (conn->in_close && conn->send_data_total == 0) {
1496-
NET_DBG("TCP connection in active close, "
1498+
NET_DBG("TCP connection in %s close, "
14971499
"not disposing yet (waiting %dms)",
1498-
tcp_fin_timeout_ms);
1500+
"active", tcp_fin_timeout_ms);
14991501
k_work_reschedule_for_queue(&tcp_work_q,
15001502
&conn->fin_timer,
15011503
FIN_TIMEOUT);
@@ -1572,6 +1574,40 @@ static void tcp_fin_timeout(struct k_work *work)
15721574
(void)tcp_conn_close(conn, -ETIMEDOUT);
15731575
}
15741576

1577+
static void tcp_last_ack_timeout(struct k_work *work)
1578+
{
1579+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
1580+
struct tcp *conn = CONTAINER_OF(dwork, struct tcp, fin_timer);
1581+
1582+
NET_DBG("Did not receive %s in %dms", "last ACK", LAST_ACK_TIMEOUT_MS);
1583+
NET_DBG("conn: %p %s", conn, tcp_conn_state(conn, NULL));
1584+
1585+
(void)tcp_conn_close(conn, -ETIMEDOUT);
1586+
}
1587+
1588+
static void tcp_setup_last_ack_timer(struct tcp *conn)
1589+
{
1590+
/* Just in case the last ack is lost, install a timer that will
1591+
* close the connection in that case. Use the fin_timer for that
1592+
* as the fin handling cannot be done in this passive close state.
1593+
* Instead of default tcp_fin_timeout() function, have a separate
1594+
* function to catch this last ack case.
1595+
*/
1596+
k_work_init_delayable(&conn->fin_timer, tcp_last_ack_timeout);
1597+
1598+
NET_DBG("TCP connection in %s close, "
1599+
"not disposing yet (waiting %dms)",
1600+
"passive", LAST_ACK_TIMEOUT_MS);
1601+
k_work_reschedule_for_queue(&tcp_work_q,
1602+
&conn->fin_timer,
1603+
LAST_ACK_TIMEOUT);
1604+
}
1605+
1606+
static void tcp_cancel_last_ack_timer(struct tcp *conn)
1607+
{
1608+
k_work_cancel_delayable(&conn->fin_timer);
1609+
}
1610+
15751611
static void tcp_send_zwp(struct k_work *work)
15761612
{
15771613
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
@@ -2520,6 +2556,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
25202556
tcp_out(conn, FIN | ACK);
25212557
next = TCP_LAST_ACK;
25222558
verdict = NET_OK;
2559+
tcp_setup_last_ack_timer(conn);
25232560
break;
25242561
} else if (th && FL(&fl, ==, FIN, th_seq(th) == conn->ack)) {
25252562
conn_ack(conn, + 1);
@@ -2542,6 +2579,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
25422579
conn_ack(conn, + len + 1);
25432580
tcp_out(conn, FIN | ACK);
25442581
next = TCP_LAST_ACK;
2582+
tcp_setup_last_ack_timer(conn);
25452583
break;
25462584
}
25472585

@@ -2716,13 +2754,17 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
27162754
case TCP_CLOSE_WAIT:
27172755
tcp_out(conn, FIN);
27182756
next = TCP_LAST_ACK;
2757+
tcp_setup_last_ack_timer(conn);
27192758
break;
27202759
case TCP_LAST_ACK:
27212760
if (th && FL(&fl, ==, ACK, th_seq(th) == conn->ack)) {
27222761
tcp_send_timer_cancel(conn);
27232762
do_close = true;
27242763
verdict = NET_OK;
27252764
close_status = 0;
2765+
2766+
/* Remove the last ack timer if we received it in time */
2767+
tcp_cancel_last_ack_timer(conn);
27262768
}
27272769
break;
27282770
case TCP_CLOSED:
@@ -3048,8 +3090,9 @@ int net_tcp_put(struct net_context *context)
30483090
} else {
30493091
int ret;
30503092

3051-
NET_DBG("TCP connection in active close, not "
3052-
"disposing yet (waiting %dms)", tcp_fin_timeout_ms);
3093+
NET_DBG("TCP connection in %s close, "
3094+
"not disposing yet (waiting %dms)",
3095+
"active", tcp_fin_timeout_ms);
30533096
k_work_reschedule_for_queue(&tcp_work_q,
30543097
&conn->fin_timer,
30553098
FIN_TIMEOUT);

0 commit comments

Comments
 (0)