From 00b4081da3e0d2eae74ed28658804984f73f108f Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 10 May 2017 10:06:37 +0300 Subject: [PATCH] net: tcp: Handle case when RST is received in any state We must check if we receive RST in any of the TCP states. If we do not do this, then the net_context might leak as it would never be released in some of the states. Receiving RST in any TCP state is not described in TCP state diagram but is described in RFC 793 which says in chapter "Reset Processing" that system "...aborts the connection and advises the user and goes to the CLOSED state." We need to also validate the received RST and accept only those TCP reset packets that contain valid sequence number. The validate_state_transitions() function is also changed to accept CLOSED state transition from various other states. Signed-off-by: Jukka Rissanen --- subsys/net/ip/net_context.c | 41 +++++++++++++++++++++++++++++++++++-- subsys/net/ip/tcp.c | 24 ++++++++++++++++------ subsys/net/ip/tcp.h | 10 +++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) 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