diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 3518f499fcb..0619e865497 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -816,6 +816,28 @@ NET_CONN_CB(tcp_established) sys_get_be32(NET_TCP_HDR(pkt)->ack)); } + /* + * If we receive RST here, we close the socket. See RFC 793 chapter + * called "Reset Processing" for details. + */ + if (tcp_flags & NET_TCP_RST) { + /* We only accept RST packet that has valid seq field. */ + if (!net_tcp_validate_seq(context->tcp, pkt)) { + return NET_DROP; + } + + net_tcp_print_recv_info("RST", pkt, NET_TCP_HDR(pkt)->src_port); + + if (context->recv_cb) { + context->recv_cb(context, NULL, -ECONNRESET, + context->tcp->recv_user_data); + } + + net_context_unref(context); + + return NET_DROP; + } + if (sys_get_be32(NET_TCP_HDR(pkt)->seq) - context->tcp->send_ack) { /* Don't try to reorder packets. If it doesn't * match the next segment exactly, drop and wait for @@ -881,6 +903,11 @@ NET_CONN_CB(tcp_synack_received) NET_ASSERT(net_pkt_iface(pkt)); if (NET_TCP_FLAGS(pkt) & NET_TCP_RST) { + /* We only accept RST packet that has valid seq field. */ + if (!net_tcp_validate_seq(context->tcp, pkt)) { + return NET_DROP; + } + if (context->connect_cb) { context->connect_cb(context, -ECONNREFUSED, context->user_data); @@ -1303,14 +1330,24 @@ NET_CONN_CB(tcp_syn_rcvd) } /* - * If we receive RST, we go back to LISTEN state. + * If we receive RST, we go back to LISTEN state if the previous state + * was LISTEN, otherwise we go to CLOSED state. + * See RFC 793 chapter 3.4 "Reset Processing" for more details. */ if (NET_TCP_FLAGS(pkt) == NET_TCP_RST) { + + /* We only accept RST packet that has valid seq field. */ + if (!net_tcp_validate_seq(tcp, pkt)) { + return NET_DROP; + } + ack_timer_cancel(tcp); net_tcp_print_recv_info("RST", pkt, NET_TCP_HDR(pkt)->src_port); - net_tcp_change_state(tcp, NET_TCP_LISTEN); + if (net_tcp_get_state(tcp) != NET_TCP_LISTEN) { + net_tcp_change_state(tcp, NET_TCP_CLOSED); + } return NET_DROP; } diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c index 240f93396fa..7a8cdafa8f8 100644 --- a/subsys/net/ip/tcp.c +++ b/subsys/net/ip/tcp.c @@ -855,16 +855,22 @@ static void validate_state_transition(enum net_tcp_state current, 1 << NET_TCP_CLOSED, [NET_TCP_SYN_SENT] = 1 << NET_TCP_CLOSED | 1 << NET_TCP_ESTABLISHED | - 1 << NET_TCP_SYN_RCVD, + 1 << NET_TCP_SYN_RCVD | + 1 << NET_TCP_CLOSED, [NET_TCP_ESTABLISHED] = 1 << NET_TCP_CLOSE_WAIT | - 1 << NET_TCP_FIN_WAIT_1, - [NET_TCP_CLOSE_WAIT] = 1 << NET_TCP_LAST_ACK, + 1 << NET_TCP_FIN_WAIT_1 | + 1 << NET_TCP_CLOSED, + [NET_TCP_CLOSE_WAIT] = 1 << NET_TCP_LAST_ACK | + 1 << NET_TCP_CLOSED, [NET_TCP_LAST_ACK] = 1 << NET_TCP_CLOSED, [NET_TCP_FIN_WAIT_1] = 1 << NET_TCP_CLOSING | 1 << NET_TCP_FIN_WAIT_2 | - 1 << NET_TCP_TIME_WAIT, - [NET_TCP_FIN_WAIT_2] = 1 << NET_TCP_TIME_WAIT, - [NET_TCP_CLOSING] = 1 << NET_TCP_TIME_WAIT, + 1 << NET_TCP_TIME_WAIT | + 1 << NET_TCP_CLOSED, + [NET_TCP_FIN_WAIT_2] = 1 << NET_TCP_TIME_WAIT | + 1 << NET_TCP_CLOSED, + [NET_TCP_CLOSING] = 1 << NET_TCP_TIME_WAIT | + 1 << NET_TCP_CLOSED, [NET_TCP_TIME_WAIT] = 1 << NET_TCP_CLOSED }; @@ -941,3 +947,9 @@ void net_tcp_foreach(net_tcp_cb_t cb, void *user_data) irq_unlock(key); } + +bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt) +{ + return !net_tcp_seq_greater(tcp->send_ack + get_recv_wnd(tcp), + sys_get_be32(NET_TCP_HDR(pkt)->seq)); +} diff --git a/subsys/net/ip/tcp.h b/subsys/net/ip/tcp.h index 844c9f2031e..c80602d1a01 100644 --- a/subsys/net/ip/tcp.h +++ b/subsys/net/ip/tcp.h @@ -338,6 +338,16 @@ static inline enum net_tcp_state net_tcp_get_state(const struct net_tcp *tcp) return (enum net_tcp_state)tcp->state; } +/** + * @brief Check if the sequence number is valid i.e., it is inside the window. + * + * @param tcp TCP context + * @param pkt Network packet + * + * @return true if network packet sequence number is valid, false otherwise + */ +bool net_tcp_validate_seq(struct net_tcp *tcp, struct net_pkt *pkt); + #if defined(CONFIG_NET_TCP) void net_tcp_init(void); #else