diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 7881dbc1b373f..493ada5e843fb 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -2801,11 +2801,11 @@ static void tcp_check_sock_options(struct tcp *conn) /* TCP state machine, everything happens here */ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) { - struct tcphdr *th = pkt ? th_get(pkt) : NULL; + struct tcphdr *th; uint8_t next = 0, fl = 0; bool do_close = false; bool connection_ok = false; - size_t tcp_options_len = th ? (th_off(th) - 5) * 4 : 0; + size_t tcp_options_len; struct net_conn *conn_handler = NULL; struct net_pkt *recv_pkt; void *recv_user_data; @@ -2815,11 +2815,22 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) int close_status = 0; enum net_verdict verdict = NET_DROP; - if (th) { - /* Currently we ignore ECN and CWR flags */ - fl = th_flags(th) & ~(ECN | CWR); + if (conn == NULL || pkt == NULL) { + NET_ERR("Invalid parameters"); + return NET_DROP; + } + + th = th_get(pkt); + if (th == NULL) { + NET_ERR("Failed to get TCP header"); + return NET_DROP; } + tcp_options_len = (th_off(th) - 5) * 4; + + /* Currently we ignore ECN and CWR flags */ + fl = th_flags(th) & ~(ECN | CWR); + if (conn->state != TCP_SYN_SENT) { tcp_check_sock_options(conn); } @@ -2834,7 +2845,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) NET_DBG("%s", tcp_conn_state(conn, pkt)); - if (th && th_off(th) < 5) { + if (th_off(th) < 5) { tcp_out(conn, RST); do_close = true; close_status = -ECONNRESET; @@ -2879,7 +2890,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) goto out; } - if (th && (conn->state != TCP_LISTEN) && (conn->state != TCP_SYN_SENT) && + if ((conn->state != TCP_LISTEN) && (conn->state != TCP_SYN_SENT) && tcp_validate_seq(conn, th) && FL(&fl, &, SYN)) { /* According to RFC 793, ch 3.9 Event Processing, receiving SYN * once the connection has been established is an error @@ -2894,34 +2905,29 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) goto out; } - if (th) { - conn->send_win = ntohs(th_win(th)); - if (conn->send_win > conn->send_win_max) { - NET_DBG("Lowering send window from %u to %u", - conn->send_win, conn->send_win_max); - - conn->send_win = conn->send_win_max; - } + conn->send_win = ntohs(th_win(th)); + if (conn->send_win > conn->send_win_max) { + NET_DBG("Lowering send window from %u to %u", conn->send_win, conn->send_win_max); + conn->send_win = conn->send_win_max; + } - if (conn->send_win == 0) { - if (!k_work_delayable_is_pending(&conn->persist_timer)) { - conn->zwp_retries = 0; - (void)k_work_reschedule_for_queue( - &tcp_work_q, &conn->persist_timer, - K_MSEC(TCP_RTO_MS)); - } - } else { - (void)k_work_cancel_delayable(&conn->persist_timer); + if (conn->send_win == 0) { + if (!k_work_delayable_is_pending(&conn->persist_timer)) { + conn->zwp_retries = 0; + (void)k_work_reschedule_for_queue(&tcp_work_q, &conn->persist_timer, + K_MSEC(TCP_RTO_MS)); } + } else { + (void)k_work_cancel_delayable(&conn->persist_timer); + } - if (tcp_window_full(conn)) { - (void)k_sem_take(&conn->tx_sem, K_NO_WAIT); - } else { - k_sem_give(&conn->tx_sem); - } + if (tcp_window_full(conn)) { + (void)k_sem_take(&conn->tx_sem, K_NO_WAIT); + } else { + k_sem_give(&conn->tx_sem); } - len = pkt ? tcp_data_len(pkt) : 0; + len = tcp_data_len(pkt); switch (conn->state) { case TCP_LISTEN: @@ -2941,17 +2947,8 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) ACK_TIMEOUT); verdict = NET_OK; } else { - conn->send_options.mss_found = true; - ret = tcp_out_ext(conn, SYN, NULL /* no data */, conn->seq); - if (ret < 0) { - do_close = true; - close_status = ret; - } else { - conn->send_options.mss_found = false; - conn_seq(conn, + 1); - next = TCP_SYN_SENT; - tcp_conn_ref(conn); - } + k_mutex_unlock(&conn->lock); + return NET_DROP; } break; case TCP_SYN_RECEIVED: @@ -3082,14 +3079,14 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) * the connection makes a "forward progress". */ tcp_nbr_reachability_hint(conn); - } else if (pkt) { + } else { net_tcp_reply_rst(pkt); } break; case TCP_ESTABLISHED: /* full-close */ - if (th && FL(&fl, &, FIN, th_seq(th) == conn->ack)) { + if (FL(&fl, &, FIN, th_seq(th) == conn->ack)) { if (len) { verdict = tcp_data_get(conn, pkt, &len); if (verdict == NET_OK) { @@ -3106,11 +3103,11 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) if (FL(&fl, &, ACK) && (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0)) { uint32_t len_acked = th_ack(th) - conn->seq; - conn_seq(conn, len_acked); + conn_seq(conn, + len_acked); } tcp_out(conn, FIN | ACK); - conn_seq(conn, 1); + conn_seq(conn, + 1); tcp_setup_last_ack_timer(conn); next = TCP_LAST_ACK; @@ -3123,7 +3120,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) keep_alive_timer_restart(conn); #ifdef CONFIG_NET_TCP_FAST_RETRANSMIT - if (th && (net_tcp_seq_cmp(th_ack(th), conn->seq) == 0)) { + if (net_tcp_seq_cmp(th_ack(th), conn->seq) == 0) { /* Only if there is pending data, increment the duplicate ack count */ if (conn->send_data_total > 0) { /* There could be also payload, only without payload account them */ @@ -3164,7 +3161,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) "conn: %p, Missing a subscription " "of the send_data queue timer", conn); - if (th && (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0)) { + if (net_tcp_seq_cmp(th_ack(th), conn->seq) > 0) { uint32_t len_acked = th_ack(th) - conn->seq; NET_DBG("conn: %p len_acked=%u", conn, len_acked); @@ -3251,42 +3248,39 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) } } - if (th) { - if (th_seq(th) == conn->ack) { - if (len > 0) { - bool psh = FL(&fl, &, PSH); + if (th_seq(th) == conn->ack) { + if (len > 0) { + bool psh = FL(&fl, &, PSH); - verdict = tcp_data_received(conn, pkt, &len, psh); - if (verdict == NET_OK) { - /* net_pkt owned by the recv fifo now */ - pkt = NULL; - } - } else { - /* ACK, no data */ - verdict = NET_OK; + verdict = tcp_data_received(conn, pkt, &len, psh); + if (verdict == NET_OK) { + /* net_pkt owned by the recv fifo now */ + pkt = NULL; } - } else if (net_tcp_seq_greater(conn->ack, th_seq(th))) { - /* This should handle the acknowledgements of keep alive - * packets and retransmitted data. - * RISK: - * There is a tiny risk of creating a ACK loop this way when - * both ends of the connection are out of order due to packet - * loss is a simultaneous bidirectional data flow. - */ - tcp_out(conn, ACK); /* peer has resent */ - - net_stats_update_tcp_seg_ackerr(conn->iface); + } else { + /* ACK, no data */ verdict = NET_OK; - } else if (CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT) { - tcp_out_of_order_data(conn, pkt, len, - th_seq(th)); - /* Send out a duplicated ACK */ - if ((len > 0) || FL(&fl, &, FIN)) { - tcp_out(conn, ACK); - } + } + } else if (net_tcp_seq_greater(conn->ack, th_seq(th))) { + /* This should handle the acknowledgements of keep alive + * packets and retransmitted data. + * RISK: + * There is a tiny risk of creating a ACK loop this way when + * both ends of the connection are out of order due to packet + * loss is a simultaneous bidirectional data flow. + */ + tcp_out(conn, ACK); /* peer has resent */ - verdict = NET_OK; + net_stats_update_tcp_seg_ackerr(conn->iface); + verdict = NET_OK; + } else if (CONFIG_NET_TCP_RECV_QUEUE_TIMEOUT) { + tcp_out_of_order_data(conn, pkt, len, th_seq(th)); + /* Send out a duplicated ACK */ + if ((len > 0) || FL(&fl, &, FIN)) { + tcp_out(conn, ACK); } + + verdict = NET_OK; } /* Check if there is any data left to retransmit possibly*/ @@ -3307,7 +3301,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) /* Half-close is not supported, so do nothing here */ break; case TCP_LAST_ACK: - if (th && FL(&fl, ==, ACK, th_ack(th) == conn->seq)) { + if (FL(&fl, ==, ACK, th_ack(th) == conn->seq)) { tcp_send_timer_cancel(conn); do_close = true; verdict = NET_OK; @@ -3319,7 +3313,7 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) break; case TCP_CLOSED: break; - case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_1: { /* * FIN1: * Acknowledge path and sequence path are independent, treat them that way @@ -3329,75 +3323,75 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) * - & ACK -> TCP_FIN_WAIT_2 * FIN & ACK -> TCP_TIME_WAIT */ - if (th) { - bool fin_acked = false; + bool fin_acked = false; - if (tcp_compute_new_length(conn, th, len, false) > 0) { - /* We do not implement half closed sockets, therefore - * cannot accept new data in after sending our FIN, as - * we are in sequence can send a reset now. - */ - net_stats_update_tcp_seg_drop(conn->iface); + if (tcp_compute_new_length(conn, th, len, false) > 0) { + /* We do not implement half closed sockets, therefore + * cannot accept new data in after sending our FIN, as + * we are in sequence can send a reset now. + */ + net_stats_update_tcp_seg_drop(conn->iface); - next = tcp_enter_time_wait(conn); + next = tcp_enter_time_wait(conn); - tcp_out(conn, RST); - break; - } - if (FL(&fl, &, ACK, th_ack(th) == conn->seq)) { - NET_DBG("conn %p: FIN acknowledged, going to FIN_WAIT_2 " - "state seq %u, ack %u" - , conn, conn->seq, conn->ack); - tcp_send_timer_cancel(conn); - fin_acked = true; - next = TCP_FIN_WAIT_2; - verdict = NET_OK; - } + tcp_out(conn, RST); + break; + } + if (FL(&fl, &, ACK, th_ack(th) == conn->seq)) { + NET_DBG("conn %p: FIN acknowledged, going to FIN_WAIT_2 " + "state seq %u, ack %u", + conn, conn->seq, conn->ack); + tcp_send_timer_cancel(conn); + fin_acked = true; + next = TCP_FIN_WAIT_2; + verdict = NET_OK; + } - /* - * There can also be data in the message, so compute with the length - * of the packet to check the sequence number of the FIN flag with the ACK - */ - if (FL(&fl, &, FIN, net_tcp_seq_cmp(th_seq(th) + len, conn->ack) == 0)) { - conn_ack(conn, + 1); + /* + * There can also be data in the message, so compute with the length + * of the packet to check the sequence number of the FIN flag with the ACK + */ + if (FL(&fl, &, FIN, net_tcp_seq_cmp(th_seq(th) + len, conn->ack) == 0)) { + conn_ack(conn, + 1); - /* State path is dependent on if the acknowledge is in */ - if (fin_acked) { - /* Already acknowledged, we can go further */ - NET_DBG("conn %p: FIN received, going to TIME WAIT", conn); + /* State path is dependent on if the acknowledge is in */ + if (fin_acked) { + /* Already acknowledged, we can go further */ + NET_DBG("conn %p: FIN received, going to TIME WAIT", conn); - next = tcp_enter_time_wait(conn); + next = tcp_enter_time_wait(conn); + tcp_out(conn, ACK); + } else { + /* Fin not yet acknowledged, waiting for the ack in CLOSING + */ + NET_DBG("conn %p: FIN received, going to CLOSING as no " + "ACK has been received", + conn); + tcp_send_timer_cancel(conn); + tcp_out_ext(conn, FIN | ACK, NULL, conn->seq - 1); + next = TCP_CLOSING; + } + verdict = NET_OK; + } else { + if (len > 0) { + if (fin_acked) { + /* Send out a duplicate ACK */ + tcp_send_timer_cancel(conn); tcp_out(conn, ACK); } else { - /* Fin not yet acknowledged, waiting for the ack in CLOSING + /* In FIN1 state + * Send out a duplicate ACK, with the pending FIN + * flag */ - NET_DBG("conn %p: FIN received, going to CLOSING as no " - "ACK has been received", conn); tcp_send_timer_cancel(conn); tcp_out_ext(conn, FIN | ACK, NULL, conn->seq - 1); - next = TCP_CLOSING; } verdict = NET_OK; - } else { - if (len > 0) { - if (fin_acked) { - /* Send out a duplicate ACK */ - tcp_send_timer_cancel(conn); - tcp_out(conn, ACK); - } else { - /* In FIN1 state - * Send out a duplicate ACK, with the pending FIN - * flag - */ - tcp_send_timer_cancel(conn); - tcp_out_ext(conn, FIN | ACK, NULL, conn->seq - 1); - } - verdict = NET_OK; - } } } - break; + } + break; case TCP_FIN_WAIT_2: /* * FIN2: @@ -3405,140 +3399,135 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt) * - -> TCP_FIN_WAIT_2 * FIN -> TCP_TIME_WAIT */ - if (th) { - /* No tcp_send_timer_cancel call required here, as is has been called - * before entering this state, only allowed through the - * tcp_enter_time_wait function. - */ + /* No tcp_send_timer_cancel call required here, as is has been called + * before entering this state, only allowed through the + * tcp_enter_time_wait function. + */ - /* Compute if there is new data after our close */ - if (tcp_compute_new_length(conn, th, len, false) > 0) { - /* We do not implement half closed sockets, therefore - * cannot accept new data in after sending our FIN, as - * we are in sequence can send a reset now. - */ - net_stats_update_tcp_seg_drop(conn->iface); + /* Compute if there is new data after our close */ + if (tcp_compute_new_length(conn, th, len, false) > 0) { + /* We do not implement half closed sockets, therefore + * cannot accept new data in after sending our FIN, as + * we are in sequence can send a reset now. + */ + net_stats_update_tcp_seg_drop(conn->iface); - next = tcp_enter_time_wait(conn); + next = tcp_enter_time_wait(conn); - tcp_out(conn, RST); - break; - } - /* - * There can also be data in the message, so compute with the length - * of the packet to check the sequence number of the FIN flag with the ACK - */ - if (FL(&fl, &, FIN, net_tcp_seq_cmp(th_seq(th) + len, conn->ack) == 0)) { - conn_ack(conn, + 1); - NET_DBG("conn %p: FIN received, going to TIME WAIT", conn); + tcp_out(conn, RST); + break; + } + /* + * There can also be data in the message, so compute with the length + * of the packet to check the sequence number of the FIN flag with the ACK + */ + if (FL(&fl, &, FIN, net_tcp_seq_cmp(th_seq(th) + len, conn->ack) == 0)) { + conn_ack(conn, + 1); + NET_DBG("conn %p: FIN received, going to TIME WAIT", conn); - next = tcp_enter_time_wait(conn); + next = tcp_enter_time_wait(conn); - verdict = NET_OK; + verdict = NET_OK; + tcp_out(conn, ACK); + } else { + if (len > 0) { + /* Send out a duplicate ACK */ tcp_out(conn, ACK); - } else { - if (len > 0) { - /* Send out a duplicate ACK */ - tcp_out(conn, ACK); - verdict = NET_OK; - } + verdict = NET_OK; } } break; - case TCP_CLOSING: - if (th) { - bool fin_acked = false; - - /* - * Closing: - * Our FIN has to be acknowledged - * - -> TCP_CLOSING - * ACK -> TCP_TIME_WAIT - */ - int32_t new_len = tcp_compute_new_length(conn, th, len, true); + case TCP_CLOSING: { + bool fin_acked = false; - if (new_len > 0) { - /* This should not happen here, as no data can be send after - * the FIN flag has been send. - */ - NET_ERR("conn: %p, new bytes %u during CLOSING state " - "sending reset", conn, new_len); - net_stats_update_tcp_seg_drop(conn->iface); + /* + * Closing: + * Our FIN has to be acknowledged + * - -> TCP_CLOSING + * ACK -> TCP_TIME_WAIT + */ + int32_t new_len = tcp_compute_new_length(conn, th, len, true); - next = tcp_enter_time_wait(conn); + if (new_len > 0) { + /* This should not happen here, as no data can be send after + * the FIN flag has been send. + */ + NET_ERR("conn: %p, new bytes %u during CLOSING state " + "sending reset", + conn, new_len); + net_stats_update_tcp_seg_drop(conn->iface); - tcp_out(conn, RST); - break; - } + next = tcp_enter_time_wait(conn); - if (FL(&fl, &, ACK, th_ack(th) == conn->seq)) { - NET_DBG("conn %p: FIN acknowledged, going to TIME WAIT " - "state seq %u, ack %u" - , conn, conn->seq, conn->ack); + tcp_out(conn, RST); + break; + } - next = tcp_enter_time_wait(conn); - fin_acked = true; + if (FL(&fl, &, ACK, th_ack(th) == conn->seq)) { + NET_DBG("conn %p: FIN acknowledged, going to TIME WAIT " + "state seq %u, ack %u", + conn, conn->seq, conn->ack); - verdict = NET_OK; - } + next = tcp_enter_time_wait(conn); + fin_acked = true; - /* - * There can also be data in the message, so compute with the length - * of the packet to check with the ack - * Since the conn->ack was already incremented in TCP_FIN_WAIT_1 - * add 1 in the comparison sequence - */ - if ((FL(&fl, &, FIN, - net_tcp_seq_cmp(th_seq(th) + len + 1, conn->ack) == 0)) || - (len > 0)) { - tcp_send_timer_cancel(conn); - if (fin_acked) { - /* Send out a duplicate ACK */ - tcp_out(conn, ACK); - } else { - /* Send out a duplicate ACK, with the pending FIN - * flag - */ - tcp_out_ext(conn, FIN | ACK, NULL, conn->seq - 1); - } - verdict = NET_OK; + verdict = NET_OK; + } + + /* + * There can also be data in the message, so compute with the length + * of the packet to check with the ack + * Since the conn->ack was already incremented in TCP_FIN_WAIT_1 + * add 1 in the comparison sequence + */ + if ((FL(&fl, &, FIN, net_tcp_seq_cmp(th_seq(th) + len + 1, conn->ack) == 0)) || + (len > 0)) { + tcp_send_timer_cancel(conn); + if (fin_acked) { + /* Send out a duplicate ACK */ + tcp_out(conn, ACK); + } else { + /* Send out a duplicate ACK, with the pending FIN + * flag + */ + tcp_out_ext(conn, FIN | ACK, NULL, conn->seq - 1); } + verdict = NET_OK; } - break; - case TCP_TIME_WAIT: - if (th) { - int32_t new_len = tcp_compute_new_length(conn, th, len, true); + } + break; + case TCP_TIME_WAIT: { + int32_t new_len = tcp_compute_new_length(conn, th, len, true); - /* No tcp_send_timer_cancel call required here, as is has been called - * before entering this state, only allowed through the - * tcp_enter_time_wait function. - */ + /* No tcp_send_timer_cancel call required here, as is has been called + * before entering this state, only allowed through the + * tcp_enter_time_wait function. + */ - if (new_len > 0) { - /* This should not happen here, as no data can be send after - * the FIN flag has been send. - */ - NET_ERR("conn: %p, new bytes %u during TIME-WAIT state " - "sending reset", conn, new_len); - net_stats_update_tcp_seg_drop(conn->iface); + if (new_len > 0) { + /* This should not happen here, as no data can be send after + * the FIN flag has been send. + */ + NET_ERR("conn: %p, new bytes %u during TIME-WAIT state " + "sending reset", + conn, new_len); + net_stats_update_tcp_seg_drop(conn->iface); - tcp_out(conn, RST); - } else { - /* Acknowledge any FIN attempts, in case retransmission took - * place. - */ - if ((FL(&fl, &, FIN, - net_tcp_seq_cmp(th_seq(th) + 1, conn->ack) == 0)) || - (len > 0)) { - tcp_out(conn, ACK); - verdict = NET_OK; - } + tcp_out(conn, RST); + } else { + /* Acknowledge any FIN attempts, in case retransmission took + * place. + */ + if ((FL(&fl, &, FIN, net_tcp_seq_cmp(th_seq(th) + 1, conn->ack) == 0)) || + (len > 0)) { + tcp_out(conn, ACK); + verdict = NET_OK; } } - break; + } + break; default: - NET_ASSERT(false, "%s is unimplemented", - tcp_state_to_str(conn->state, true)); + NET_ASSERT(false, "%s is unimplemented", tcp_state_to_str(conn->state, true)); } out: @@ -3799,6 +3788,28 @@ int net_tcp_send_data(struct net_context *context, net_context_send_cb_t cb, return 0; } +static int tcp_start_handshake(struct tcp *conn) +{ + int ret; + + /* Start the connection handshake */ + k_mutex_lock(&conn->lock, K_FOREVER); + tcp_check_sock_options(conn); + conn->send_options.mss_found = true; + ret = tcp_out_ext(conn, SYN, NULL /* no data */, conn->seq); + if (ret < 0) { + k_mutex_unlock(&conn->lock); + return ret; + } + conn->send_options.mss_found = false; + conn_seq(conn, + 1); + conn_state(conn, TCP_SYN_SENT); + tcp_conn_ref(conn); + k_mutex_unlock(&conn->lock); + + return 0; +} + /* When connect() is called on a TCP socket, register the socket for incoming * traffic with net context and give the TCP packet receiving function, which * in turn will call tcp_in() to deliver the TCP packet to the stack @@ -3931,11 +3942,10 @@ int net_tcp_connect(struct net_context *context, */ conn->in_connect = !IS_ENABLED(CONFIG_NET_TEST_PROTOCOL); - /* The ref will make sure that if the connection is closed in tcp_in(), - * we do not access already freed connection. - */ - tcp_conn_ref(conn); - (void)tcp_in(conn, NULL); + ret = tcp_start_handshake(conn); + if (ret < 0) { + goto out; + } if (!IS_ENABLED(CONFIG_NET_TEST_PROTOCOL)) { if (conn->state == TCP_UNUSED || conn->state == TCP_CLOSED) { @@ -3944,11 +3954,10 @@ int net_tcp_connect(struct net_context *context, } else { ret = -ENOTCONN; } - goto out_unref; - } else if ((K_TIMEOUT_EQ(timeout, K_NO_WAIT)) && - conn->state != TCP_ESTABLISHED) { + goto out; + } else if ((K_TIMEOUT_EQ(timeout, K_NO_WAIT)) && conn->state != TCP_ESTABLISHED) { ret = -EINPROGRESS; - goto out_unref; + goto out; } else if (k_sem_take(&conn->connect_sem, timeout) != 0 && conn->state != TCP_ESTABLISHED) { if (conn->in_connect) { @@ -3961,14 +3970,11 @@ int net_tcp_connect(struct net_context *context, } else { ret = -ETIMEDOUT; } - goto out_unref; + goto out; } conn->in_connect = false; } -out_unref: - tcp_conn_unref(conn); - out: NET_DBG("conn: %p, ret=%d", conn, ret); @@ -4271,7 +4277,10 @@ enum net_verdict tp_input(struct net_conn *net_conn, tcp_conn_ref(conn); } conn->seq = tp->seq; - verdict = tcp_in(conn, NULL); + + if (tcp_start_handshake(conn) == 0) { + verdict = NET_OK; + } } if (is("CLOSE", tp->op)) { tp_trace = false;