Skip to content

Commit ac7866c

Browse files
jukkarnashif
authored andcommitted
net: tcp2: Fix connection termination
We need to have timer that closes the connection for good if we do not get the FIN and ACK reponse from the peer. If there is any pending data when application does close(), send them before sending FIN. Signed-off-by: Jukka Rissanen <[email protected]>
1 parent 13a7baf commit ac7866c

File tree

2 files changed

+88
-10
lines changed

2 files changed

+88
-10
lines changed

subsys/net/ip/tcp2.c

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ LOG_MODULE_REGISTER(net_tcp, CONFIG_NET_TCP_LOG_LEVEL);
2222
#include "net_private.h"
2323
#include "tcp2_priv.h"
2424

25+
#define FIN_TIMEOUT_MS MSEC_PER_SEC
26+
#define FIN_TIMEOUT K_MSEC(FIN_TIMEOUT_MS)
27+
2528
static int tcp_rto = CONFIG_NET_TCP_INIT_RETRANSMISSION_TIMEOUT;
2629
static int tcp_retries = CONFIG_NET_TCP_RETRY_COUNT;
2730
static int tcp_window = NET_IPV6_MTU;
@@ -335,6 +338,7 @@ static int tcp_conn_unref(struct tcp *conn)
335338
tcp_pkt_unref(conn->send_data);
336339

337340
k_delayed_work_cancel(&conn->timewait_timer);
341+
k_delayed_work_cancel(&conn->fin_timer);
338342

339343
sys_slist_find_and_remove(&tcp_conns, &conn->next);
340344

@@ -420,8 +424,10 @@ static void tcp_send_timer_cancel(struct tcp *conn)
420424
{
421425
struct net_pkt *pkt = tcp_slist(&conn->send_queue, get,
422426
struct net_pkt, next);
423-
NET_DBG("%s", log_strdup(tcp_th(pkt)));
424-
tcp_pkt_unref(pkt);
427+
if (pkt) {
428+
NET_DBG("%s", log_strdup(tcp_th(pkt)));
429+
tcp_pkt_unref(pkt);
430+
}
425431
}
426432

427433
if (sys_slist_is_empty(&conn->send_queue)) {
@@ -875,6 +881,23 @@ static void tcp_resend_data(struct k_work *work)
875881
ret = tcp_send_data(conn);
876882
if (ret == 0) {
877883
conn->send_data_retries++;
884+
885+
if (conn->in_close && conn->send_data_total == 0) {
886+
NET_DBG("TCP connection in active close, "
887+
"not disposing yet (waiting %dms)",
888+
FIN_TIMEOUT_MS);
889+
k_delayed_work_submit(&conn->fin_timer, FIN_TIMEOUT);
890+
891+
conn_state(conn, TCP_FIN_WAIT_1);
892+
893+
ret = tcp_out_ext(conn, FIN | ACK, NULL,
894+
conn->seq + conn->unacked_len);
895+
if (ret == 0) {
896+
conn_seq(conn, + 1);
897+
}
898+
899+
goto out;
900+
}
878901
}
879902

880903
k_delayed_work_submit(&conn->send_data_timer, K_MSEC(tcp_rto));
@@ -891,7 +914,19 @@ static void tcp_timewait_timeout(struct k_work *work)
891914

892915
NET_DBG("conn: %p %s", conn, log_strdup(tcp_conn_state(conn, NULL)));
893916

894-
tcp_conn_unref(conn);
917+
/* Extra unref from net_tcp_put() */
918+
net_context_unref(conn->context);
919+
}
920+
921+
static void tcp_fin_timeout(struct k_work *work)
922+
{
923+
struct tcp *conn = CONTAINER_OF(work, struct tcp, fin_timer);
924+
925+
NET_DBG("Did not receive FIN in %dms", FIN_TIMEOUT_MS);
926+
NET_DBG("conn: %p %s", conn, log_strdup(tcp_conn_state(conn, NULL)));
927+
928+
/* Extra unref from net_tcp_put() */
929+
net_context_unref(conn->context);
895930
}
896931

897932
static void tcp_conn_ref(struct tcp *conn)
@@ -927,6 +962,7 @@ static struct tcp *tcp_conn_alloc(void)
927962
k_delayed_work_init(&conn->send_timer, tcp_send_process);
928963

929964
k_delayed_work_init(&conn->timewait_timer, tcp_timewait_timeout);
965+
k_delayed_work_init(&conn->fin_timer, tcp_fin_timeout);
930966

931967
conn->send_data = tcp_pkt_alloc(conn, 0);
932968
k_delayed_work_init(&conn->send_data_timer, tcp_resend_data);
@@ -1299,6 +1335,16 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
12991335
}
13001336
conn->data_mode = TCP_DATA_MODE_SEND;
13011337

1338+
/* We are closing the connection, send a FIN to peer */
1339+
if (conn->in_close && conn->send_data_total == 0) {
1340+
tcp_send_timer_cancel(conn);
1341+
next = TCP_FIN_WAIT_1;
1342+
1343+
tcp_out(conn, FIN | ACK);
1344+
conn_seq(conn, + 1);
1345+
break;
1346+
}
1347+
13021348
ret = tcp_send_queued_data(conn);
13031349
if (ret < 0 && ret != -ENOBUFS) {
13041350
tcp_out(conn, RST);
@@ -1349,7 +1395,11 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
13491395
}
13501396
break;
13511397
case TCP_FIN_WAIT_2:
1352-
if (th && FL(&fl, ==, FIN, th_seq(th) == conn->ack)) {
1398+
if (th && (FL(&fl, ==, FIN, th_seq(th) == conn->ack) ||
1399+
FL(&fl, ==, FIN | ACK, th_seq(th) == conn->ack))) {
1400+
/* Received FIN on FIN_WAIT_2, so cancel the timer */
1401+
k_delayed_work_cancel(&conn->fin_timer);
1402+
13531403
conn_ack(conn, + 1);
13541404
tcp_out(conn, ACK);
13551405
next = TCP_TIME_WAIT;
@@ -1386,22 +1436,48 @@ int net_tcp_put(struct net_context *context)
13861436
{
13871437
struct tcp *conn = context->tcp;
13881438

1439+
k_mutex_lock(&conn->lock, K_FOREVER);
1440+
13891441
NET_DBG("%s", conn ? log_strdup(tcp_conn_state(conn, NULL)) : "");
13901442
NET_DBG("context %p %s", context,
13911443
log_strdup(({ const char *state = net_context_state(context);
13921444
state ? state : "<unknown>"; })));
13931445

13941446
if (conn && conn->state == TCP_ESTABLISHED) {
1395-
k_mutex_lock(&conn->lock, K_FOREVER);
1447+
/* Send all remaining data if possible. */
1448+
if (conn->send_data_total > 0) {
1449+
NET_DBG("conn %p pending %u bytes", conn,
1450+
conn->send_data_total);
1451+
conn->in_close = true;
1452+
1453+
/* How long to wait until all the data has been sent?
1454+
*/
1455+
k_delayed_work_submit(&conn->send_data_timer,
1456+
K_MSEC(tcp_rto));
1457+
} else {
1458+
int ret;
13961459

1397-
tcp_out_ext(conn, FIN | ACK, NULL,
1398-
conn->seq + conn->unacked_len);
1399-
conn_seq(conn, + 1);
1400-
conn_state(conn, TCP_FIN_WAIT_1);
1460+
NET_DBG("TCP connection in active close, not "
1461+
"disposing yet (waiting %dms)", FIN_TIMEOUT_MS);
1462+
k_delayed_work_submit(&conn->fin_timer, FIN_TIMEOUT);
14011463

1402-
k_mutex_unlock(&conn->lock);
1464+
ret = tcp_out_ext(conn, FIN | ACK, NULL,
1465+
conn->seq + conn->unacked_len);
1466+
if (ret == 0) {
1467+
conn_seq(conn, + 1);
1468+
}
1469+
1470+
conn_state(conn, TCP_FIN_WAIT_1);
1471+
}
1472+
1473+
/* Make sure we do not delete the connection yet until we have
1474+
* sent the final ACK.
1475+
*/
1476+
net_context_ref(context);
14031477
}
14041478

1479+
k_mutex_unlock(&conn->lock);
1480+
14051481
net_context_unref(context);
14061482

14071483
return 0;

subsys/net/ip/tcp2_priv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ struct tcp { /* TCP connection */
190190
struct k_delayed_work send_timer;
191191
struct k_delayed_work send_data_timer;
192192
struct k_delayed_work timewait_timer;
193+
struct k_delayed_work fin_timer;
193194
union tcp_endpoint src;
194195
union tcp_endpoint dst;
195196
size_t send_data_total;
@@ -205,6 +206,7 @@ struct tcp { /* TCP connection */
205206
uint8_t send_data_retries;
206207
bool in_retransmission : 1;
207208
bool in_connect : 1;
209+
bool in_close : 1;
208210
};
209211

210212
#define _flags(_fl, _op, _mask, _cond) \

0 commit comments

Comments
 (0)