summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vnet/tcp/tcp.c50
-rw-r--r--src/vnet/tcp/tcp.h16
-rwxr-xr-xsrc/vnet/tcp/tcp_input.c138
3 files changed, 133 insertions, 71 deletions
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index 0c2523c5bff..c3fa6d49da5 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -335,56 +335,6 @@ tcp_connection_free (tcp_connection_t * tc)
pool_put (tm->connections[tc->c_thread_index], tc);
}
-/** Notify session that connection has been reset.
- *
- * Switch state to closed and wait for session to call cleanup.
- */
-void
-tcp_connection_reset (tcp_connection_t * tc)
-{
- TCP_EVT (TCP_EVT_RST_RCVD, tc);
- switch (tc->state)
- {
- case TCP_STATE_SYN_RCVD:
- /* Cleanup everything. App wasn't notified yet */
- session_transport_delete_notify (&tc->connection);
- tcp_connection_cleanup (tc);
- break;
- case TCP_STATE_SYN_SENT:
- session_stream_connect_notify (&tc->connection, 1 /* fail */ );
- tcp_connection_cleanup (tc);
- break;
- case TCP_STATE_ESTABLISHED:
- tcp_connection_timers_reset (tc);
- /* Set the cleanup timer, in case the session layer/app don't
- * cleanly close the connection */
- tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.closewait_time);
- session_transport_reset_notify (&tc->connection);
- tcp_cong_recovery_off (tc);
- tcp_connection_set_state (tc, TCP_STATE_CLOSED);
- session_transport_closed_notify (&tc->connection);
- break;
- case TCP_STATE_CLOSE_WAIT:
- case TCP_STATE_FIN_WAIT_1:
- case TCP_STATE_FIN_WAIT_2:
- case TCP_STATE_CLOSING:
- case TCP_STATE_LAST_ACK:
- tcp_connection_timers_reset (tc);
- tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.closewait_time);
- tcp_cong_recovery_off (tc);
- /* Make sure we mark the session as closed. In some states we may
- * be still trying to send data */
- tcp_connection_set_state (tc, TCP_STATE_CLOSED);
- session_transport_closed_notify (&tc->connection);
- break;
- case TCP_STATE_CLOSED:
- case TCP_STATE_TIME_WAIT:
- break;
- default:
- TCP_DBG ("reset state: %u", tc->state);
- }
-}
-
/**
* Begin connection closing procedure.
*
diff --git a/src/vnet/tcp/tcp.h b/src/vnet/tcp/tcp.h
index 73d59706da5..36e98a98dd6 100644
--- a/src/vnet/tcp/tcp.h
+++ b/src/vnet/tcp/tcp.h
@@ -436,6 +436,8 @@ typedef struct _tcp_connection
u16 mss; /**< Our max seg size that includes options */
u32 timestamp_delta; /**< Offset for timestamp */
u32 ipv6_flow_label; /**< flow label for ipv6 header */
+
+#define rst_state snd_wl1
} tcp_connection_t;
/* *INDENT-OFF* */
@@ -503,11 +505,6 @@ typedef struct _tcp_lookup_dispatch
typedef struct tcp_worker_ctx_
{
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
- /** worker time */
- u32 time_now;
-
- /** worker timer wheel */
- tw_timer_wheel_16t_2w_512sl_t timer_wheel;
/** tx buffer free list */
u32 *tx_buffers;
@@ -521,14 +518,22 @@ typedef struct tcp_worker_ctx_
/** vector of pending disconnect notifications */
u32 *pending_disconnects;
+ u32 *pending_resets;
+
/** convenience pointer to this thread's vlib main */
vlib_main_t *vm;
+ /** worker time */
+ u32 time_now;
+
CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
/** cached 'on the wire' options for bursts */
u8 cached_opts[40];
+ /** worker timer wheel */
+ tw_timer_wheel_16t_2w_512sl_t timer_wheel;
+
} tcp_worker_ctx_t;
typedef struct tcp_iss_seed_
@@ -753,7 +758,6 @@ tcp_connection_t *tcp_connection_alloc (u8 thread_index);
tcp_connection_t *tcp_connection_alloc_w_base (u8 thread_index,
tcp_connection_t * base);
void tcp_connection_free (tcp_connection_t * tc);
-void tcp_connection_reset (tcp_connection_t * tc);
int tcp_configure_v4_source_address_range (vlib_main_t * vm,
ip4_address_t * start,
ip4_address_t * end, u32 table_id);
diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c
index f981351c4b9..164a1b3431f 100755
--- a/src/vnet/tcp/tcp_input.c
+++ b/src/vnet/tcp/tcp_input.c
@@ -266,6 +266,100 @@ tcp_update_timestamp (tcp_connection_t * tc, u32 seq, u32 seq_end)
}
}
+static void
+tcp_handle_rst (tcp_connection_t * tc)
+{
+ switch (tc->rst_state)
+ {
+ case TCP_STATE_SYN_RCVD:
+ /* Cleanup everything. App wasn't notified yet */
+ session_transport_delete_notify (&tc->connection);
+ tcp_connection_cleanup (tc);
+ break;
+ case TCP_STATE_SYN_SENT:
+ session_stream_connect_notify (&tc->connection, 1 /* fail */ );
+ tcp_connection_cleanup (tc);
+ break;
+ case TCP_STATE_ESTABLISHED:
+ session_transport_reset_notify (&tc->connection);
+ session_transport_closed_notify (&tc->connection);
+ break;
+ case TCP_STATE_CLOSE_WAIT:
+ case TCP_STATE_FIN_WAIT_1:
+ case TCP_STATE_FIN_WAIT_2:
+ case TCP_STATE_CLOSING:
+ case TCP_STATE_LAST_ACK:
+ session_transport_closed_notify (&tc->connection);
+ break;
+ case TCP_STATE_CLOSED:
+ case TCP_STATE_TIME_WAIT:
+ break;
+ default:
+ TCP_DBG ("reset state: %u", tc->state);
+ }
+}
+
+static void
+tcp_program_reset_ntf (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
+{
+ if (!tcp_disconnect_pending (tc))
+ {
+ tc->rst_state = tc->state;
+ vec_add1 (wrk->pending_resets, tc->c_c_index);
+ tcp_disconnect_pending_on (tc);
+ }
+}
+
+/**
+ * Handle reset packet
+ *
+ * Programs disconnect/reset notification that should be sent
+ * later by calling @ref tcp_handle_disconnects
+ */
+static void
+tcp_rcv_rst (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
+{
+ TCP_EVT (TCP_EVT_RST_RCVD, tc);
+ switch (tc->state)
+ {
+ case TCP_STATE_SYN_RCVD:
+ tcp_program_reset_ntf (wrk, tc);
+ tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+ break;
+ case TCP_STATE_SYN_SENT:
+ tcp_program_reset_ntf (wrk, tc);
+ tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+ break;
+ case TCP_STATE_ESTABLISHED:
+ tcp_connection_timers_reset (tc);
+ /* Set the cleanup timer, in case the session layer/app don't
+ * cleanly close the connection */
+ tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.closewait_time);
+ tcp_cong_recovery_off (tc);
+ tcp_program_reset_ntf (wrk, tc);
+ tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+ break;
+ case TCP_STATE_CLOSE_WAIT:
+ case TCP_STATE_FIN_WAIT_1:
+ case TCP_STATE_FIN_WAIT_2:
+ case TCP_STATE_CLOSING:
+ case TCP_STATE_LAST_ACK:
+ tcp_connection_timers_reset (tc);
+ tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.closewait_time);
+ tcp_cong_recovery_off (tc);
+ tcp_program_reset_ntf (wrk, tc);
+ /* Make sure we mark the session as closed. In some states we may
+ * be still trying to send data */
+ tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+ break;
+ case TCP_STATE_CLOSED:
+ case TCP_STATE_TIME_WAIT:
+ break;
+ default:
+ TCP_DBG ("reset state: %u", tc->state);
+ }
+}
+
/**
* Validate incoming segment as per RFC793 p. 69 and RFC1323 p. 19
*
@@ -392,7 +486,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
/* 2nd: check the RST bit */
if (PREDICT_FALSE (tcp_rst (th0)))
{
- tcp_connection_reset (tc0);
+ tcp_rcv_rst (wrk, tc0);
*error0 = TCP_ERROR_RST_RCVD;
goto error;
}
@@ -1676,22 +1770,35 @@ tcp_program_disconnect (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
static void
tcp_handle_disconnects (tcp_worker_ctx_t * wrk)
{
- u32 thread_index, *pending_disconnects;
+ u32 thread_index, *pending_disconnects, *pending_resets;
tcp_connection_t *tc;
int i;
- if (!vec_len (wrk->pending_disconnects))
- return;
+ if (vec_len (wrk->pending_disconnects))
+ {
+ thread_index = wrk->vm->thread_index;
+ pending_disconnects = wrk->pending_disconnects;
+ for (i = 0; i < vec_len (pending_disconnects); i++)
+ {
+ tc = tcp_connection_get (pending_disconnects[i], thread_index);
+ tcp_disconnect_pending_off (tc);
+ session_transport_closing_notify (&tc->connection);
+ }
+ _vec_len (wrk->pending_disconnects) = 0;
+ }
- thread_index = wrk->vm->thread_index;
- pending_disconnects = wrk->pending_disconnects;
- for (i = 0; i < vec_len (pending_disconnects); i++)
+ if (vec_len (wrk->pending_resets))
{
- tc = tcp_connection_get (pending_disconnects[i], thread_index);
- tcp_disconnect_pending_off (tc);
- session_transport_closing_notify (&tc->connection);
+ thread_index = wrk->vm->thread_index;
+ pending_resets = wrk->pending_resets;
+ for (i = 0; i < vec_len (pending_resets); i++)
+ {
+ tc = tcp_connection_get (pending_resets[i], thread_index);
+ tcp_disconnect_pending_off (tc);
+ tcp_handle_rst (tc);
+ }
+ _vec_len (wrk->pending_resets) = 0;
}
- _vec_len (wrk->pending_disconnects) = 0;
}
static void
@@ -2556,7 +2663,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
/* If ACK is acceptable, signal client that peer is not
* willing to accept connection and drop connection*/
if (tcp_ack (tcp0))
- tcp_connection_reset (tc0);
+ tcp_rcv_rst (wrk, tc0);
error0 = TCP_ERROR_RST_RCVD;
goto drop;
}
@@ -2701,6 +2808,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
my_thread_index);
tcp_inc_counter (syn_sent, TCP_ERROR_MSG_QUEUE_FULL, errors);
vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
+ tcp_handle_disconnects (wrk);
return from_frame->n_vectors;
}
@@ -2837,7 +2945,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
/* Make sure the segment is exactly right */
if (tc0->rcv_nxt != vnet_buffer (b0)->tcp.seq_number || is_fin0)
{
- tcp_connection_reset (tc0);
+ tcp_rcv_rst (wrk, tc0);
error0 = TCP_ERROR_SEGMENT_INVALID;
goto drop;
}
@@ -2850,7 +2958,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
*/
if (tcp_rcv_ack_no_cc (tc0, b0, &error0))
{
- tcp_connection_reset (tc0);
+ tcp_rcv_rst (wrk, tc0);
goto drop;
}
@@ -2877,7 +2985,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
if (session_stream_accept_notify (&tc0->connection))
{
error0 = TCP_ERROR_MSG_QUEUE_FULL;
- tcp_connection_reset (tc0);
+ tcp_rcv_rst (wrk, tc0);
goto drop;
}
error0 = TCP_ERROR_ACK_OK;