diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index bdb5be1fc10..6a75ac87f61 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -267,34 +267,14 @@ static void queue_fin(struct net_context *ctx) return; } + ctx->tcp->fin_queued = 1; + ret = net_tcp_send_buf(buf); if (ret < 0) { net_nbuf_unref(buf); } } -static enum net_verdict tcp_active_close(struct net_conn *conn, - struct net_buf *buf, - void *user_data); - -static bool send_fin_if_active_close(struct net_context *context) -{ - NET_ASSERT(context->tcp); - - switch (net_tcp_get_state(context->tcp)) { - case NET_TCP_SYN_RCVD: - case NET_TCP_ESTABLISHED: - /* Sending a packet with the FIN flag automatically - * transitions to FIN_WAIT_1 - */ - queue_fin(context); - net_conn_change_callback(context->conn_handler, - tcp_active_close, context); - return true; - default: - return false; - } -} #endif /* CONFIG_NET_TCP */ int net_context_ref(struct net_context *context) @@ -350,9 +330,10 @@ int net_context_put(struct net_context *context) #if defined(CONFIG_NET_TCP) if (net_context_get_ip_proto(context) == IPPROTO_TCP) { - if (send_fin_if_active_close(context)) { + if (!context->tcp->fin_rcvd) { NET_DBG("TCP connection in active close, not " "disposing yet"); + queue_fin(context); return 0; } } @@ -684,13 +665,6 @@ static inline int send_syn_ack(struct net_context *context, "SYN_ACK"); } -static inline int send_fin_ack(struct net_context *context, - struct sockaddr *remote) -{ - return send_control_segment(context, NULL, remote, - NET_TCP_FIN | NET_TCP_ACK, "FIN_ACK"); -} - static inline int send_ack(struct net_context *context, struct sockaddr *remote) { @@ -748,33 +722,6 @@ static int tcp_hdr_len(struct net_buf *buf) return 4 * (hdr->offset >> 4); } -NET_CONN_CB(tcp_passive_close) -{ - struct net_context *context = (struct net_context *)user_data; - - NET_ASSERT(context && context->tcp); - - switch (net_tcp_get_state(context->tcp)) { - case NET_TCP_CLOSE_WAIT: - case NET_TCP_LAST_ACK: - break; - default: - NET_DBG("Context %p in wrong state %d", - context, net_tcp_get_state(context->tcp)); - return NET_DROP; - } - - net_tcp_print_recv_info("PASSCLOSE", buf, NET_TCP_BUF(buf)->src_port); - - if (net_tcp_get_state(context->tcp) == NET_TCP_LAST_ACK && - NET_TCP_FLAGS(buf) & NET_TCP_ACK) { - NET_DBG("ACK received in LAST_ACK, disposing of connection"); - net_context_unref(context); - } - - return NET_DROP; -} - /* This is called when we receive data after the connection has been * established. The core TCP logic is located here. */ @@ -817,9 +764,8 @@ NET_CONN_CB(tcp_established) /* Sending an ACK in the CLOSE_WAIT state will transition to * LAST_ACK state */ + context->tcp->fin_rcvd = 1; net_tcp_change_state(context->tcp, NET_TCP_CLOSE_WAIT); - net_conn_change_callback(context->conn_handler, - tcp_passive_close, context); context->tcp->send_ack += 1; @@ -831,47 +777,15 @@ NET_CONN_CB(tcp_established) send_ack(context, &conn->remote_addr); + if (sys_slist_is_empty(&context->tcp->sent_list) + && context->tcp->fin_rcvd + && context->tcp->fin_sent) { + net_context_unref(context); + } + return ret; } -NET_CONN_CB(tcp_active_close) -{ - struct net_context *context = (struct net_context *)user_data; - struct net_tcp *tcp; - - NET_ASSERT(context && context->tcp); - - tcp = context->tcp; - - if (NET_TCP_FLAGS(buf) == NET_TCP_FIN) { - if (net_tcp_get_state(tcp) == NET_TCP_FIN_WAIT_1 || - net_tcp_get_state(tcp) == NET_TCP_FIN_WAIT_2) { - /* Sending an ACK in FIN_WAIT_1 will transition - * to CLOSING, and to TIME_WAIT if on FIN_WAIT_2 - */ - send_ack(context, &context->remote); - return NET_DROP; - } - } else if (NET_TCP_FLAGS(buf) == NET_TCP_ACK) { - if (net_tcp_get_state(tcp) == NET_TCP_FIN_WAIT_1) { - net_tcp_change_state(tcp, NET_TCP_FIN_WAIT_2); - return NET_DROP; - } - - if (net_tcp_get_state(tcp) == NET_TCP_CLOSING) { - net_tcp_change_state(tcp, NET_TCP_TIME_WAIT); - return NET_DROP; - } - } else if (NET_TCP_FLAGS(buf) == (NET_TCP_FIN | NET_TCP_ACK)) { - if (net_tcp_get_state(tcp) == NET_TCP_FIN_WAIT_1) { - send_fin_ack(context, &context->remote); - return NET_DROP; - } - } - - NET_DBG("Context %p in wrong state %d", context, tcp->state); - return NET_DROP; -} NET_CONN_CB(tcp_synack_received) { diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 38ef90962f3..cec7f702f34 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -165,7 +165,6 @@ int net_tcp_release(struct net_tcp *tcp) return -EINVAL; } - k_delayed_work_cancel(&tcp->fin_timer); k_delayed_work_cancel(&tcp->ack_timer); k_timer_stop(&tcp->retry_timer); k_sem_reset(&tcp->connect_wait); @@ -625,6 +624,10 @@ int net_tcp_send_buf(struct net_buf *buf) tcphdr->flags |= NET_TCP_ACK; } + if (tcphdr->flags & NET_TCP_FIN) { + ctx->tcp->fin_sent = 1; + } + ctx->tcp->sent_ack = ctx->tcp->send_ack; net_nbuf_set_buf_sent(buf, true); @@ -664,6 +667,7 @@ int net_tcp_send_data(struct net_context *context) void net_tcp_ack_received(struct net_context *ctx, uint32_t ack) { + struct net_tcp *tcp = ctx->tcp; sys_slist_t *list = &ctx->tcp->sent_list; sys_snode_t *head; struct net_buf *buf; @@ -678,13 +682,23 @@ void net_tcp_ack_received(struct net_context *ctx, uint32_t ack) seq = sys_get_be32(tcphdr->seq) + net_nbuf_appdatalen(buf) - 1; - if (seq_greater(ack, seq)) { - sys_slist_remove(list, NULL, head); - net_nbuf_unref(buf); - valid_ack = true; - } else { + if (!seq_greater(ack, seq)) { break; } + + if (tcphdr->flags & NET_TCP_FIN) { + enum net_tcp_state s = net_tcp_get_state(tcp); + + if (s == NET_TCP_FIN_WAIT_1) { + net_tcp_change_state(tcp, NET_TCP_FIN_WAIT_2); + } else if (s == NET_TCP_CLOSING) { + net_tcp_change_state(tcp, NET_TCP_TIME_WAIT); + } + } + + sys_slist_remove(list, NULL, head); + net_nbuf_unref(buf); + valid_ack = true; } if (valid_ack) { @@ -720,21 +734,6 @@ void net_tcp_init(void) { } -#define FIN_TIMEOUT (2 * NET_TCP_MAX_SEG_LIFETIME * MSEC_PER_SEC) - -static void fin_timeout(struct k_work *work) -{ - struct net_tcp *tcp = CONTAINER_OF(work, struct net_tcp, fin_timer); - int rc; - - NET_DBG("Remote peer didn't confirm connection close"); - - rc = net_context_unref(tcp->context); - if (rc < 0) { - NET_DBG("Cannot close TCP context"); - } -} - #if defined(CONFIG_NET_DEBUG_TCP) static void validate_state_transition(enum net_tcp_state current, enum net_tcp_state new) @@ -793,13 +792,6 @@ void net_tcp_change_state(struct net_tcp *tcp, net_tcp_set_state(tcp, new_state); - if (net_tcp_get_state(tcp) == NET_TCP_FIN_WAIT_1) { - /* Wait up to 2 * MSL before destroying this socket. */ - k_delayed_work_cancel(&tcp->fin_timer); - k_delayed_work_init(&tcp->fin_timer, fin_timeout); - k_delayed_work_submit(&tcp->fin_timer, FIN_TIMEOUT); - } - if (net_tcp_get_state(tcp) != NET_TCP_CLOSED) { return; } diff --git a/subsys/net/ip/tcp.h b/subsys/net/ip/tcp.h index 5a4867e5138..0f721893ce6 100644 --- a/subsys/net/ip/tcp.h +++ b/subsys/net/ip/tcp.h @@ -104,9 +104,6 @@ struct net_tcp { /** ACK message timer */ struct k_delayed_work ack_timer; - /** Active close timer */ - struct k_delayed_work fin_timer; - /** Retransmit timer */ struct k_timer retry_timer; @@ -131,8 +128,14 @@ struct net_tcp { uint32_t flags : 8; /** Current TCP state */ uint32_t state : 4; + /* A FIN packet has been queued for transmission */ + uint32_t fin_queued : 1; + /* An outbound FIN packet has been sent */ + uint32_t fin_sent : 1; + /* An inbound FIN packet has been received */ + uint32_t fin_rcvd : 1; /** Remaining bits in this uint32_t */ - uint32_t _padding : 15; + uint32_t _padding : 12; /** Accept callback to be called when the connection has been * established.