summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vnet/session/session.h7
-rw-r--r--src/vnet/session/session_node.c3
-rw-r--r--src/vnet/session/transport.c11
-rw-r--r--src/vnet/tcp/tcp.c15
-rw-r--r--src/vnet/tcp/tcp.h16
-rw-r--r--src/vnet/tcp/tcp_input.c1383
-rw-r--r--src/vnet/tcp/tcp_output.c95
7 files changed, 727 insertions, 803 deletions
diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h
index 3bdf6c9d93e..35c8fb4069f 100644
--- a/src/vnet/session/session.h
+++ b/src/vnet/session/session.h
@@ -524,6 +524,13 @@ transport_tx_fifo_size (transport_connection_t * tc)
return s->server_tx_fifo->nitems;
}
+always_inline u8
+transport_rx_fifo_has_ooo_data (transport_connection_t * tc)
+{
+ stream_session_t *s = session_get (tc->c_index, tc->thread_index);
+ return svm_fifo_has_ooo_data (s->server_rx_fifo);
+}
+
always_inline f64
transport_dispatch_period (u32 thread_index)
{
diff --git a/src/vnet/session/session_node.c b/src/vnet/session/session_node.c
index edc518ee872..64c873cc758 100644
--- a/src/vnet/session/session_node.c
+++ b/src/vnet/session/session_node.c
@@ -751,10 +751,9 @@ static void
session_update_dispatch_period (session_manager_worker_t * wrk, f64 now,
u32 thread_index)
{
- if (wrk->last_tx_packets > 8)
+ if (wrk->last_tx_packets > 1)
{
f64 sample = now - wrk->last_vlib_time;
- sample = (sample * wrk->last_tx_packets) / VLIB_FRAME_SIZE;
wrk->dispatch_period = (wrk->dispatch_period + sample) * 0.5;
}
wrk->last_vlib_time = now;
diff --git a/src/vnet/session/transport.c b/src/vnet/session/transport.c
index 42149eb36a6..06c541a9e2b 100644
--- a/src/vnet/session/transport.c
+++ b/src/vnet/session/transport.c
@@ -47,7 +47,8 @@ static clib_spinlock_t local_endpoints_lock;
*/
static double transport_pacer_period;
-#define TRANSPORT_PACER_MIN_MSS 1460
+#define TRANSPORT_PACER_MIN_MSS 1460
+#define TRANSPORT_PACER_MIN_BURST TRANSPORT_PACER_MIN_MSS
u8 *
format_transport_proto (u8 * s, va_list * args)
@@ -518,7 +519,8 @@ spacer_update_bucket (spacer_t * pacer, u32 bytes)
static inline void
spacer_update_max_burst_size (spacer_t * pacer, u32 max_burst_bytes)
{
- pacer->max_burst_size = clib_max (max_burst_bytes, TRANSPORT_PACER_MIN_MSS);
+ pacer->max_burst_size = clib_max (max_burst_bytes,
+ TRANSPORT_PACER_MIN_BURST);
}
static inline void
@@ -561,9 +563,8 @@ void
transport_connection_tx_pacer_update (transport_connection_t * tc,
u64 bytes_per_sec)
{
- u32 burst_size;
-
- burst_size = bytes_per_sec * transport_dispatch_period (tc->thread_index);
+ f64 dispatch_period = transport_dispatch_period (tc->thread_index);
+ u32 burst_size = 1.1 * bytes_per_sec * dispatch_period;
spacer_set_pace_rate (&tc->pacer, bytes_per_sec);
spacer_update_max_burst_size (&tc->pacer, burst_size);
}
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index a466e3c935c..ea350dddc69 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -1105,6 +1105,7 @@ tcp_update_time (f64 now, u8 thread_index)
tcp_set_time_now (wrk);
tw_timer_expire_timers_16t_2w_512sl (&wrk->timer_wheel, now);
tcp_do_fastretransmits (wrk);
+ tcp_send_acks (wrk);
tcp_flush_frames_to_output (wrk);
}
@@ -1143,13 +1144,16 @@ void
tcp_connection_tx_pacer_update (tcp_connection_t * tc)
{
f64 srtt;
+ u64 rate;
if (!transport_connection_is_tx_paced (&tc->connection))
return;
srtt = clib_min ((f64) tc->srtt * TCP_TICK, tc->mrtt_us);
- transport_connection_tx_pacer_update (&tc->connection,
- ((f64) tc->cwnd) / srtt);
+ /* TODO should constrain to interface's max throughput but
+ * we don't have link speeds for sw ifs ..*/
+ rate = tc->cwnd / srtt;
+ transport_connection_tx_pacer_update (&tc->connection, rate);
}
void
@@ -1326,10 +1330,12 @@ tcp_main_enable (vlib_main_t * vm)
vec_validate (tm->wrk_ctx[thread].ongoing_fast_rxt, 255);
vec_validate (tm->wrk_ctx[thread].postponed_fast_rxt, 255);
vec_validate (tm->wrk_ctx[thread].pending_deq_acked, 255);
+ vec_validate (tm->wrk_ctx[thread].pending_acks, 255);
vec_reset_length (tm->wrk_ctx[thread].pending_fast_rxt);
vec_reset_length (tm->wrk_ctx[thread].ongoing_fast_rxt);
vec_reset_length (tm->wrk_ctx[thread].postponed_fast_rxt);
vec_reset_length (tm->wrk_ctx[thread].pending_deq_acked);
+ vec_reset_length (tm->wrk_ctx[thread].pending_acks);
tm->wrk_ctx[thread].vm = vlib_mains[thread];
/*
@@ -1417,6 +1423,7 @@ tcp_init (vlib_main_t * vm)
FIB_PROTOCOL_IP6, tcp6_output_node.index);
tcp_api_reference ();
+ tm->tx_pacing = 1;
return 0;
}
@@ -1441,8 +1448,8 @@ tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
else if (unformat (input, "max-rx-fifo %U", unformat_memory_size,
&tm->max_rx_fifo))
;
- else if (unformat (input, "tx-pacing"))
- tm->tx_pacing = 1;
+ else if (unformat (input, "no-tx-pacing"))
+ tm->tx_pacing = 0;
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
diff --git a/src/vnet/tcp/tcp.h b/src/vnet/tcp/tcp.h
index 480b924c882..5a3a96570d2 100644
--- a/src/vnet/tcp/tcp.h
+++ b/src/vnet/tcp/tcp.h
@@ -300,7 +300,7 @@ typedef struct _tcp_connection
sack_scoreboard_t sack_sb; /**< SACK "scoreboard" that tracks holes */
u16 rcv_dupacks; /**< Number of DUPACKs received */
- u8 snt_dupacks; /**< Number of DUPACKs sent in a burst */
+ u8 pending_dupacks; /**< Number of DUPACKs to be sent */
/* Congestion control */
u32 cwnd; /**< Congestion window */
@@ -395,6 +395,7 @@ typedef struct tcp_worker_ctx_
u32 *postponed_fast_rxt; /**< vector of connections
that will do fast rxt */
u32 *pending_deq_acked;
+ u32 *pending_acks;
vlib_main_t *vm; /**< pointer to vm */
CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
@@ -564,6 +565,7 @@ void tcp_send_reset_w_pkt (tcp_connection_t * tc, vlib_buffer_t * pkt,
u8 is_ip4);
void tcp_send_reset (tcp_connection_t * tc);
void tcp_send_syn (tcp_connection_t * tc);
+void tcp_send_synack (tcp_connection_t * tc);
void tcp_send_fin (tcp_connection_t * tc);
void tcp_init_mss (tcp_connection_t * tc);
void tcp_update_burst_snd_vars (tcp_connection_t * tc);
@@ -574,6 +576,10 @@ void tcp_program_fastretransmit (tcp_worker_ctx_t * wrk,
tcp_connection_t * tc);
void tcp_do_fastretransmits (tcp_worker_ctx_t * wrk);
+void tcp_program_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc);
+void tcp_program_dupack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc);
+void tcp_send_acks (tcp_worker_ctx_t * wrk);
+
always_inline u32
tcp_end_seq (tcp_header_t * th, u32 len)
{
@@ -751,14 +757,6 @@ tcp_cc_rcv_ack (tcp_connection_t * tc)
}
always_inline void
-tcp_connection_force_ack (tcp_connection_t * tc, vlib_buffer_t * b)
-{
- /* Reset flags, make sure ack is sent */
- tc->flags = TCP_CONN_SNDACK;
- vnet_buffer (b)->tcp.flags &= ~TCP_BUF_FLAG_DUPACK;
-}
-
-always_inline void
tcp_timer_set (tcp_connection_t * tc, u8 timer_id, u32 interval)
{
ASSERT (tc->c_thread_index == vlib_get_thread_index ());
diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c
index 9fc601b9788..9c303eb01a5 100644
--- a/src/vnet/tcp/tcp_input.c
+++ b/src/vnet/tcp/tcp_input.c
@@ -272,29 +272,28 @@ tcp_update_timestamp (tcp_connection_t * tc, u32 seq, u32 seq_end)
* @return 0 if segments passes validation.
*/
static int
-tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
- vlib_buffer_t * b0, tcp_header_t * th0,
- u32 * next0, u32 * error0)
+tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0,
+ vlib_buffer_t * b0, tcp_header_t * th0, u32 * error0)
{
/* We could get a burst of RSTs interleaved with acks */
if (PREDICT_FALSE (tc0->state == TCP_STATE_CLOSED))
{
tcp_send_reset (tc0);
*error0 = TCP_ERROR_CONNECTION_CLOSED;
- goto drop;
+ goto error;
}
if (PREDICT_FALSE (!tcp_ack (th0) && !tcp_rst (th0) && !tcp_syn (th0)))
{
*error0 = TCP_ERROR_SEGMENT_INVALID;
- goto drop;
+ goto error;
}
if (PREDICT_FALSE (tcp_options_parse (th0, &tc0->rcv_opts)))
{
clib_warning ("options parse error");
*error0 = TCP_ERROR_OPTIONS;
- goto drop;
+ goto error;
}
if (PREDICT_FALSE (tcp_segment_check_paws (tc0)))
@@ -319,12 +318,11 @@ tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
/* Drop after ack if not rst */
if (!tcp_rst (th0))
{
- tcp_make_ack (tc0, b0);
+ tcp_program_ack (wrk, tc0);
TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc0, vnet_buffer (b0)->tcp);
- goto error;
}
}
- goto drop;
+ goto error;
}
/* 1st: check sequence number */
@@ -340,11 +338,10 @@ tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
/* If not RST, send dup ack */
if (!tcp_rst (th0))
{
- tcp_make_ack (tc0, b0);
+ tcp_program_dupack (wrk, tc0);
TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc0, vnet_buffer (b0)->tcp);
- goto error;
}
- goto drop;
+ goto error;
}
}
@@ -353,7 +350,7 @@ tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
{
tcp_connection_reset (tc0);
*error0 = TCP_ERROR_RST_RCVD;
- goto drop;
+ goto error;
}
/* 3rd: check security and precedence (skip) */
@@ -364,12 +361,12 @@ tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
/* TODO implement RFC 5961 */
if (tc0->state == TCP_STATE_SYN_RCVD)
{
- tcp_make_synack (tc0, b0);
+ tcp_send_synack (tc0);
TCP_EVT_DBG (TCP_EVT_SYN_RCVD, tc0, 0);
}
else
{
- tcp_make_ack (tc0, b0);
+ tcp_program_ack (wrk, tc0);
TCP_EVT_DBG (TCP_EVT_SYNACK_RCVD, tc0);
}
goto error;
@@ -380,11 +377,7 @@ tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
vnet_buffer (b0)->tcp.seq_end);
return 0;
-drop:
- *next0 = tcp_next_drop (tc0->c_is_ip4);
- return -1;
error:
- *next0 = tcp_next_output (tc0->c_is_ip4);
return -1;
}
@@ -1476,7 +1469,7 @@ partial_ack:
*/
static int
tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b,
- tcp_header_t * th, u32 * next, u32 * error)
+ tcp_header_t * th, u32 * error)
{
u32 prev_snd_wnd, prev_snd_una;
u8 is_dack;
@@ -1502,8 +1495,7 @@ tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b,
* drop it */
if (seq_gt (vnet_buffer (b)->tcp.ack_number, tc->snd_una_max))
{
- tcp_make_ack (tc, b);
- *next = tcp_next_output (tc->c_is_ip4);
+ tcp_program_ack (wrk, tc);
*error = TCP_ERROR_ACK_FUTURE;
TCP_EVT_DBG (TCP_EVT_ACK_RCV_ERR, tc, 0,
vnet_buffer (b)->tcp.ack_number);
@@ -1684,25 +1676,16 @@ tcp_session_enqueue_data (tcp_connection_t * tc, vlib_buffer_t * b,
else if (written > data_len)
{
tc->rcv_nxt += written;
-
- /* Send ACK confirming the update */
- tc->flags |= TCP_CONN_SNDACK;
TCP_EVT_DBG (TCP_EVT_CC_INPUT, tc, data_len, written);
}
else if (written > 0)
{
/* We've written something but FIFO is probably full now */
tc->rcv_nxt += written;
-
- /* Depending on how fast the app is, all remaining buffers in burst will
- * not be enqueued. Inform peer */
- tc->flags |= TCP_CONN_SNDACK;
-
error = TCP_ERROR_PARTIALLY_ENQUEUED;
}
else
{
- tc->flags |= TCP_CONN_SNDACK;
return TCP_ERROR_FIFO_FULL;
}
@@ -1821,7 +1804,8 @@ tcp_buffer_discard_bytes (vlib_buffer_t * b, u32 n_bytes_to_drop)
* It handles both in order or out-of-order data.
*/
static int
-tcp_segment_rcv (tcp_connection_t * tc, vlib_buffer_t * b, u32 * next0)
+tcp_segment_rcv (tcp_worker_ctx_t * wrk, tcp_connection_t * tc,
+ vlib_buffer_t * b)
{
u32 error, n_bytes_to_drop, n_data_bytes;
@@ -1840,9 +1824,8 @@ tcp_segment_rcv (tcp_connection_t * tc, vlib_buffer_t * b, u32 * next0)
* retransmissions since we may not have any data to send */
if (seq_leq (vnet_buffer (b)->tcp.seq_end, tc->rcv_nxt))
{
- tcp_make_ack (tc, b);
+ tcp_program_ack (wrk, tc);
error = TCP_ERROR_SEGMENT_OLD;
- *next0 = tcp_next_output (tc->c_is_ip4);
goto done;
}
@@ -1854,7 +1837,6 @@ tcp_segment_rcv (tcp_connection_t * tc, vlib_buffer_t * b, u32 * next0)
if (tcp_buffer_discard_bytes (b, n_bytes_to_drop))
{
error = TCP_ERROR_SEGMENT_OLD;
- *next0 = tcp_next_drop (tc->c_is_ip4);
goto done;
}
goto in_order;
@@ -1862,9 +1844,7 @@ tcp_segment_rcv (tcp_connection_t * tc, vlib_buffer_t * b, u32 * next0)
/* RFC2581: Enqueue and send DUPACK for fast retransmit */
error = tcp_session_enqueue_ooo (tc, b, n_data_bytes);
- *next0 = tcp_next_output (tc->c_is_ip4);
- tcp_make_ack (tc, b);
- vnet_buffer (b)->tcp.flags = TCP_BUF_FLAG_DUPACK;
+ tcp_program_dupack (wrk, tc);
TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc, vnet_buffer (b)->tcp);
goto done;
}
@@ -1876,14 +1856,12 @@ in_order:
error = tcp_session_enqueue_data (tc, b, n_data_bytes);
if (tcp_can_delack (tc))
{
- *next0 = tcp_next_drop (tc->c_is_ip4);
if (!tcp_timer_is_active (tc, TCP_TIMER_DELACK))
tcp_timer_set (tc, TCP_TIMER_DELACK, TCP_DELACK_TIME);
goto done;
}
- *next0 = tcp_next_output (tc->c_is_ip4);
- tcp_make_ack (tc, b);
+ tcp_program_ack (wrk, tc);
done:
return error;
@@ -2019,116 +1997,91 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
{
u32 thread_index = vm->thread_index, errors = 0;
tcp_worker_ctx_t *wrk = tcp_get_worker (thread_index);
- u32 n_left_from, next_index, *from, *to_next;
+ u32 n_left_from, *from, *first_buffer;
u16 err_counters[TCP_N_ERROR] = { 0 };
u8 is_fin = 0;
if (node->flags & VLIB_NODE_FLAG_TRACE)
tcp_established_trace_frame (vm, node, frame, is_ip4);
- from = vlib_frame_vector_args (frame);
+ first_buffer = from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
- next_index = node->cached_next_index;
while (n_left_from > 0)
{
- u32 n_left_to_next;
+ u32 bi0, error0 = TCP_ERROR_ACK_OK;
+ vlib_buffer_t *b0;
+ tcp_header_t *th0 = 0;
+ tcp_connection_t *tc0;
- vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
- while (n_left_from > 0 && n_left_to_next > 0)
+ if (n_left_from > 1)
{
- u32 bi0;
- vlib_buffer_t *b0;
- tcp_header_t *th0 = 0;
- tcp_connection_t *tc0;
- u32 next0 = tcp_next_drop (is_ip4), error0 = TCP_ERROR_ACK_OK;
-
- if (n_left_from > 1)
- {
- vlib_buffer_t *pb;
- pb = vlib_get_buffer (vm, from[1]);
- vlib_prefetch_buffer_header (pb, LOAD);
- CLIB_PREFETCH (pb->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
- }
-
- bi0 = from[0];
- to_next[0] = bi0;
- from += 1;
- to_next += 1;
- n_left_from -= 1;
- n_left_to_next -= 1;
+ vlib_buffer_t *pb;
+ pb = vlib_get_buffer (vm, from[1]);
+ vlib_prefetch_buffer_header (pb, LOAD);
+ CLIB_PREFETCH (pb->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
+ }
- b0 = vlib_get_buffer (vm, bi0);
- tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index,
- thread_index);
+ bi0 = from[0];
+ from += 1;
+ n_left_from -= 1;
- if (PREDICT_FALSE (tc0 == 0))
- {
- error0 = TCP_ERROR_INVALID_CONNECTION;
- goto done;
- }
+ b0 = vlib_get_buffer (vm, bi0);
+ tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index,
+ thread_index);
- th0 = tcp_buffer_hdr (b0);
- /* N.B. buffer is rewritten if segment is ooo. Thus, th0 becomes a
- * dangling reference. */
- is_fin = tcp_is_fin (th0);
+ if (PREDICT_FALSE (tc0 == 0))
+ {
+ error0 = TCP_ERROR_INVALID_CONNECTION;
+ goto done;
+ }
- /* SYNs, FINs and data consume sequence numbers */
- vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
- + tcp_is_syn (th0) + is_fin + vnet_buffer (b0)->tcp.data_len;
+ th0 = tcp_buffer_hdr (b0);
+ /* N.B. buffer is rewritten if segment is ooo. Thus, th0 becomes a
+ * dangling reference. */
+ is_fin = tcp_is_fin (th0);
- /* TODO header prediction fast path */
+ /* SYNs, FINs and data consume sequence numbers */
+ vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
+ + tcp_is_syn (th0) + is_fin + vnet_buffer (b0)->tcp.data_len;
- /* 1-4: check SEQ, RST, SYN */
- if (PREDICT_FALSE (tcp_segment_validate (vm, tc0, b0, th0, &next0,
- &error0)))
- {
- tcp_maybe_inc_err_counter (err_counters, error0);
- TCP_EVT_DBG (TCP_EVT_SEG_INVALID, tc0, vnet_buffer (b0)->tcp);
- goto done;
- }
+ /* TODO header prediction fast path */
- /* 5: check the ACK field */
- if (PREDICT_FALSE (tcp_rcv_ack (wrk, tc0, b0, th0, &next0,
- &error0)))
- {
- tcp_maybe_inc_err_counter (err_counters, error0);
- goto done;
- }
+ /* 1-4: check SEQ, RST, SYN */
+ if (PREDICT_FALSE (tcp_segment_validate (wrk, tc0, b0, th0, &error0)))
+ {
+ TCP_EVT_DBG (TCP_EVT_SEG_INVALID, tc0, vnet_buffer (b0)->tcp);
+ goto done;
+ }
- /* 6: check the URG bit TODO */
+ /* 5: check the ACK field */
+ if (PREDICT_FALSE (tcp_rcv_ack (wrk, tc0, b0, th0, &error0)))
+ goto done;
- /* 7: process the segment text */
- if (vnet_buffer (b0)->tcp.data_len)
- {
- error0 = tcp_segment_rcv (tc0, b0, &next0);
- tcp_maybe_inc_err_counter (err_counters, error0);
- }
+ /* 6: check the URG bit TODO */
- /* 8: check the FIN bit */
- if (PREDICT_FALSE (is_fin))
- {
- /* Enter CLOSE-WAIT and notify session. To avoid lingering
- * in CLOSE-WAIT, set timer (reuse WAITCLOSE). */
- /* Account for the FIN if nothing else was received */
- if (vnet_buffer (b0)->tcp.data_len == 0)
- tc0->rcv_nxt += 1;
- tcp_make_ack (tc0, b0);
- next0 = tcp_next_output (tc0->c_is_ip4);
- tc0->state = TCP_STATE_CLOSE_WAIT;
- stream_session_disconnect_notify (&tc0->connection);
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
- TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
- tcp_inc_err_counter (err_counters, TCP_ERROR_FIN_RCVD, 1);
- }
+ /* 7: process the segment text */
+ if (vnet_buffer (b0)->tcp.data_len)
+ error0 = tcp_segment_rcv (wrk, tc0, b0);
- done:
- b0->error = node->errors[error0];
- vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
- n_left_to_next, bi0, next0);
+ /* 8: check the FIN bit */
+ if (PREDICT_FALSE (is_fin))
+ {
+ /* Enter CLOSE-WAIT and notify session. To avoid lingering
+ * in CLOSE-WAIT, set timer (reuse WAITCLOSE). */
+ /* Account for the FIN if nothing else was received */
+ if (vnet_buffer (b0)->tcp.data_len == 0)
+ tc0->rcv_nxt += 1;
+ tcp_program_ack (wrk, tc0);
+ tc0->state = TCP_STATE_CLOSE_WAIT;
+ stream_session_disconnect_notify (&tc0->connection);
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
+ TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
+ error0 = TCP_ERROR_FIN_RCVD;
}
- vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ done:
+ tcp_inc_err_counter (err_counters, error0, 1);
}
errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP,
@@ -2136,7 +2089,7 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
err_counters[TCP_ERROR_EVENT_FIFO_FULL] = errors;
tcp_store_err_counters (established, err_counters);
tcp_handle_postponed_dequeues (wrk);
- tcp_flush_frame_to_output (wrk, is_ip4);
+ vlib_buffer_free (vm, first_buffer, frame->n_vectors);
return frame->n_vectors;
}
@@ -2287,262 +2240,247 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vlib_frame_t * from_frame, int is_ip4)
{
tcp_main_t *tm = vnet_get_tcp_main ();
- u32 n_left_from, next_index, *from, *to_next;
- u32 my_thread_index = vm->thread_index, errors = 0;
+ u32 n_left_from, *from, *first_buffer, errors = 0;
+ u32 my_thread_index = vm->thread_index;
+ tcp_worker_ctx_t *wrk = tcp_get_worker (my_thread_index);
- from = vlib_frame_vector_args (from_frame);
+ from = first_buffer = vlib_frame_vector_args (from_frame);
n_left_from = from_frame->n_vectors;
- next_index = node->cached_next_index;
-
while (n_left_from > 0)
{
- u32 n_left_to_next;
+ u32 bi0, ack0, seq0, error0 = TCP_ERROR_NONE;
+ tcp_connection_t *tc0, *new_tc0;
+ tcp_header_t *tcp0 = 0;
+ tcp_rx_trace_t *t0;
+ vlib_buffer_t *b0;
- vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+ bi0 = from[0];
+ from += 1;
+ n_left_from -= 1;
- while (n_left_from > 0 && n_left_to_next > 0)
+ b0 = vlib_get_buffer (vm, bi0);
+ tc0 =
+ tcp_half_open_connection_get (vnet_buffer (b0)->tcp.connection_index);
+ if (PREDICT_FALSE (tc0 == 0))
{
- u32 bi0, ack0, seq0;
- vlib_buffer_t *b0;
- tcp_rx_trace_t *t0;
- tcp_header_t *tcp0 = 0;
- tcp_connection_t *tc0;
- tcp_connection_t *new_tc0;
- u32 next0 = tcp_next_drop (is_ip4), error0 = TCP_ERROR_ENQUEUED;
-
- bi0 = from[0];
- to_next[0] = bi0;
- from += 1;
- to_next += 1;
- n_left_from -= 1;
- n_left_to_next -= 1;
-
- b0 = vlib_get_buffer (vm, bi0);
- tc0 =
- tcp_half_open_connection_get (vnet_buffer (b0)->
- tcp.connection_index);
- if (PREDICT_FALSE (tc0 == 0))
- {
- error0 = TCP_ERROR_INVALID_CONNECTION;
- goto drop;
- }
-
- /* Half-open completed recently but the connection was't removed
- * yet by the owning thread */
- if (PREDICT_FALSE (tc0->flags & TCP_CONN_HALF_OPEN_DONE))
- {
- /* Make sure the connection actually exists */
- ASSERT (tcp_lookup_connection (tc0->c_fib_index, b0,
- my_thread_index, is_ip4));
- goto drop;
- }
-
- ack0 = vnet_buffer (b0)->tcp.ack_number;
- seq0 = vnet_buffer (b0)->tcp.seq_number;
- tcp0 = tcp_buffer_hdr (b0);
-
- /* Crude check to see if the connection handle does not match
- * the packet. Probably connection just switched to established */
- if (PREDICT_FALSE (tcp0->dst_port != tc0->c_lcl_port
- || tcp0->src_port != tc0->c_rmt_port))
- goto drop;
-
- if (PREDICT_FALSE
- (!tcp_ack (tcp0) && !tcp_rst (tcp0) && !tcp_syn (tcp0)))
- goto drop;
-
- /* SYNs, FINs and data consume sequence numbers */
- vnet_buffer (b0)->tcp.seq_end = seq0 + tcp_is_syn (tcp0)
- + tcp_is_fin (tcp0) + vnet_buffer (b0)->tcp.data_len;
-
- /*
- * 1. check the ACK bit
- */
+ error0 = TCP_ERROR_INVALID_CONNECTION;
+ goto drop;
+ }
- /*
- * If the ACK bit is set
- * If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send a reset (unless
- * the RST bit is set, if so drop the segment and return)
- * <SEQ=SEG.ACK><CTL=RST>
- * and discard the segment. Return.
- * If SND.UNA =< SEG.ACK =< SND.NXT then the ACK is acceptable.
- */
- if (tcp_ack (tcp0))
- {
- if (seq_leq (ack0, tc0->iss) || seq_gt (ack0, tc0->snd_nxt))
- {
- clib_warning ("ack not in rcv wnd");
- if (!tcp_rst (tcp0))
- tcp_send_reset_w_pkt (tc0, b0, is_ip4);
- goto drop;
- }
+ /* Half-open completed recently but the connection was't removed
+ * yet by the owning thread */
+ if (PREDICT_FALSE (tc0->flags & TCP_CONN_HALF_OPEN_DONE))
+ {
+ /* Make sure the connection actually exists */
+ ASSERT (tcp_lookup_connection (tc0->c_fib_index, b0,
+ my_thread_index, is_ip4));
+ goto drop;
+ }
- /* Make sure ACK is valid */
- if (seq_gt (tc0->snd_una, ack0))
- {
- clib_warning ("ack invalid");
- goto drop;
- }
- }
+ ack0 = vnet_buffer (b0)->tcp.ack_number;
+ seq0 = vnet_buffer (b0)->tcp.seq_number;
+ tcp0 = tcp_buffer_hdr (b0);
- /*
- * 2. check the RST bit
- */
+ /* Crude check to see if the connection handle does not match
+ * the packet. Probably connection just switched to established */
+ if (PREDICT_FALSE (tcp0->dst_port != tc0->c_lcl_port
+ || tcp0->src_port != tc0->c_rmt_port))
+ {
+ error0 = TCP_ERROR_INVALID_CONNECTION;
+ goto drop;
+ }
- if (tcp_rst (tcp0))
- {
- /* 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);
- goto drop;
- }
+ if (PREDICT_FALSE (!tcp_ack (tcp0) && !tcp_rst (tcp0)
+ && !tcp_syn (tcp0)))
+ {
+ error0 = TCP_ERROR_SEGMENT_INVALID;
+ goto drop;
+ }
- /*
- * 3. check the security and precedence (skipped)
- */
+ /* SYNs, FINs and data consume sequence numbers */
+ vnet_buffer (b0)->tcp.seq_end =
+ seq0 + tcp_is_syn (tcp0) + tcp_is_fin (tcp0) +
+ vnet_buffer (b0)->tcp.data_len;
- /*
- * 4. check the SYN bit
- */
+ /*
+ * 1. check the ACK bit
+ */
- /* No SYN flag. Drop. */
- if (!tcp_syn (tcp0))
+ /*
+ * If the ACK bit is set
+ * If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send a reset (unless
+ * the RST bit is set, if so drop the segment and return)
+ * <SEQ=SEG.ACK><CTL=RST>
+ * and discard the segment. Return.
+ * If SND.UNA =< SEG.ACK =< SND.NXT then the ACK is acceptable.
+ */
+ if (tcp_ack (tcp0))
+ {
+ if (seq_leq (ack0, tc0->iss) || seq_gt (ack0, tc0->snd_nxt))
{
- clib_warning ("not synack");
+ if (!tcp_rst (tcp0))
+ tcp_send_reset_w_pkt (tc0, b0, is_ip4);
+ error0 = TCP_ERROR_RCV_WND;
goto drop;
}
- /* Parse options */
- if (tcp_options_parse (tcp0, &tc0->rcv_opts))
+ /* Make sure ACK is valid */
+ if (seq_gt (tc0->snd_una, ack0))
{
- clib_warning ("options parse fail");
+ error0 = TCP_ERROR_ACK_INVALID;
goto drop;
}
+ }
- /* Valid SYN or SYN-ACK. Move connection from half-open pool to
- * current thread pool. */
- pool_get (tm->connections[my_thread_index], new_tc0);
- clib_memcpy (new_tc0, tc0, sizeof (*new_tc0));
- new_tc0->c_c_index = new_tc0 - tm->connections[my_thread_index];
- new_tc0->c_thread_index = my_thread_index;
- new_tc0->rcv_nxt = vnet_buffer (b0)->tcp.seq_end;
- new_tc0->irs = seq0;
- new_tc0->timers[TCP_TIMER_ESTABLISH] = TCP_TIMER_HANDLE_INVALID;
- new_tc0->timers[TCP_TIMER_RETRANSMIT_SYN] =
- TCP_TIMER_HANDLE_INVALID;
- new_tc0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-
- /* If this is not the owning thread, wait for syn retransmit to
- * expire and cleanup then */
- if (tcp_half_open_connection_cleanup (tc0))
- tc0->flags |= TCP_CONN_HALF_OPEN_DONE;
-
- if (tcp_opts_tstamp (&new_tc0->rcv_opts))
- {
- new_tc0->tsval_recent = new_tc0->rcv_opts.tsval;
- new_tc0->tsval_recent_age = tcp_time_now ();
- }
+ /*
+ * 2. check the RST bit
+ */
- if (tcp_opts_wscale (&new_tc0->rcv_opts))
- new_tc0->snd_wscale = new_tc0->rcv_opts.wscale;
+ if (tcp_rst (tcp0))
+ {
+ /* 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);
+ error0 = TCP_ERROR_RST_RCVD;
+ goto drop;
+ }
- new_tc0->snd_wnd = clib_net_to_host_u16 (tcp0->window)
- << new_tc0->snd_wscale;
- new_tc0->snd_wl1 = seq0;
- new_tc0->snd_wl2 = ack0;
+ /*
+ * 3. check the security and precedence (skipped)
+ */
- tcp_connection_init_vars (new_tc0);
+ /*
+ * 4. check the SYN bit
+ */
- /* SYN-ACK: See if we can switch to ESTABLISHED state */
- if (PREDICT_TRUE (tcp_ack (tcp0)))
- {
- /* Our SYN is ACKed: we have iss < ack = snd_una */
+ /* No SYN flag. Drop. */
+ if (!tcp_syn (tcp0))
+ {
+ clib_warning ("not synack");
+ error0 = TCP_ERROR_SEGMENT_INVALID;
+ goto drop;
+ }
- /* TODO Dequeue acknowledged segments if we support Fast Open */
- new_tc0->snd_una = ack0;
- new_tc0->state = TCP_STATE_ESTABLISHED;
+ /* Parse options */
+ if (tcp_options_parse (tcp0, &tc0->rcv_opts))
+ {
+ clib_warning ("options parse fail");
+ error0 = TCP_ERROR_OPTIONS;
+ goto drop;
+ }
- /* Make sure las is initialized for the wnd computation */
- new_tc0->rcv_las = new_tc0->rcv_nxt;
+ /* Valid SYN or SYN-ACK. Move connection from half-open pool to
+ * current thread pool. */
+ pool_get (tm->connections[my_thread_index], new_tc0);
+ clib_memcpy (new_tc0, tc0, sizeof (*new_tc0));
+ new_tc0->c_c_index = new_tc0 - tm->connections[my_thread_index];
+ new_tc0->c_thread_index = my_thread_index;
+ new_tc0->rcv_nxt = vnet_buffer (b0)->tcp.seq_end;
+ new_tc0->irs = seq0;
+ new_tc0->timers[TCP_TIMER_ESTABLISH] = TCP_TIMER_HANDLE_INVALID;
+ new_tc0->timers[TCP_TIMER_RETRANSMIT_SYN] = TCP_TIMER_HANDLE_INVALID;
+ new_tc0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+ /* If this is not the owning thread, wait for syn retransmit to
+ * expire and cleanup then */
+ if (tcp_half_open_connection_cleanup (tc0))
+ tc0->flags |= TCP_CONN_HALF_OPEN_DONE;
+
+ if (tcp_opts_tstamp (&new_tc0->rcv_opts))
+ {
+ new_tc0->tsval_recent = new_tc0->rcv_opts.tsval;
+ new_tc0->tsval_recent_age = tcp_time_now ();
+ }
- /* Notify app that we have connection. If session layer can't
- * allocate session send reset */
- if (session_stream_connect_notify (&new_tc0->connection, 0))
- {
- clib_warning ("connect notify fail");
- tcp_send_reset_w_pkt (new_tc0, b0, is_ip4);
- tcp_connection_cleanup (new_tc0);
- goto drop;
- }
+ if (tcp_opts_wscale (&new_tc0->rcv_opts))
+ new_tc0->snd_wscale = new_tc0->rcv_opts.wscale;
- /* Make sure after data segment processing ACK is sent */
- new_tc0->flags |= TCP_CONN_SNDACK;
+ new_tc0->snd_wnd = clib_net_to_host_u16 (tcp0->window)
+ << new_tc0->snd_wscale;
+ new_tc0->snd_wl1 = seq0;
+ new_tc0->snd_wl2 = ack0;
- /* Update rtt with the syn-ack sample */
- tcp_update_rtt (new_tc0, vnet_buffer (b0)->tcp.ack_number);
- TCP_EVT_DBG (TCP_EVT_SYNACK_RCVD, new_tc0);
- }
- /* SYN: Simultaneous open. Change state to SYN-RCVD and send SYN-ACK */
- else
- {
- new_tc0->state = TCP_STATE_SYN_RCVD;
+ tcp_connection_init_vars (new_tc0);
- /* Notify app that we have connection */
- if (session_stream_connect_notify (&new_tc0->connection, 0))
- {
- tcp_connection_cleanup (new_tc0);
- tcp_send_reset_w_pkt (tc0, b0, is_ip4);
- TCP_EVT_DBG (TCP_EVT_RST_SENT, tc0);
- goto drop;
- }
+ /* SYN-ACK: See if we can switch to ESTABLISHED state */
+ if (PREDICT_TRUE (tcp_ack (tcp0)))
+ {
+ /* Our SYN is ACKed: we have iss < ack = snd_una */
- tc0->rtt_ts = 0;
- tcp_init_snd_vars (tc0);
- tcp_make_synack (new_tc0, b0);
- next0 = tcp_next_output (is_ip4);
+ /* TODO Dequeue acknowledged segments if we support Fast Open */
+ new_tc0->snd_una = ack0;
+ new_tc0->state = TCP_STATE_ESTABLISHED;
- goto drop;
- }
+ /* Make sure las is initialized for the wnd computation */
+ new_tc0->rcv_las = new_tc0->rcv_nxt;
- /* Read data, if any */
- if (PREDICT_FALSE (vnet_buffer (b0)->tcp.data_len))
+ /* Notify app that we have connection. If session layer can't
+ * allocate session send reset */
+ if (session_stream_connect_notify (&new_tc0->connection, 0))
{
- clib_warning ("rcvd data in syn-sent");
- error0 = tcp_segment_rcv (new_tc0, b0, &next0);
- if (error0 == TCP_ERROR_ACK_OK)
- error0 = TCP_ERROR_SYN_ACKS_RCVD;
- tcp_maybe_inc_counter (syn_sent, error0, 1);
- }
- else
- {
- tcp_make_ack (new_tc0, b0);
- next0 = tcp_next_output (new_tc0->c_is_ip4);
+ clib_warning ("connect notify fail");
+ tcp_send_reset_w_pkt (new_tc0, b0, is_ip4);
+ tcp_connection_cleanup (new_tc0);
+ goto drop;
}
- drop:
+ /* Update rtt with the syn-ack sample */
+ tcp_update_rtt (new_tc0, vnet_buffer (b0)->tcp.ack_number);
+ TCP_EVT_DBG (TCP_EVT_SYNACK_RCVD, new_tc0);
+ error0 = TCP_ERROR_SYN_ACKS_RCVD;
+ }
+ /* SYN: Simultaneous open. Change state to SYN-RCVD and send SYN-ACK */
+ else
+ {
+ new_tc0->state = TCP_STATE_SYN_RCVD;
- b0->error = error0 ? node->errors[error0] : 0;
- if (PREDICT_FALSE
- ((b0->flags & VLIB_BUFFER_IS_TRACED) && tcp0 != 0))
+ /* Notify app that we have connection */
+ if (session_stream_connect_notify (&new_tc0->connection, 0))
{
- t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
- clib_memcpy (&t0->tcp_header, tcp0, sizeof (t0->tcp_header));
- clib_memcpy (&t0->tcp_connection, tc0,
- sizeof (t0->tcp_connection));
+ tcp_connection_cleanup (new_tc0);
+ tcp_send_reset_w_pkt (tc0, b0, is_ip4);
+ TCP_EVT_DBG (TCP_EVT_RST_SENT, tc0);
+ goto drop;
}
- vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
- n_left_to_next, bi0, next0);
+ tc0->rtt_ts = 0;
+ tcp_init_snd_vars (tc0);
+ tcp_send_synack (new_tc0);
+ error0 = TCP_ERROR_SYNS_RCVD;
+ goto drop;
}
- vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ /* Read data, if any */
+ if (PREDICT_FALSE (vnet_buffer (b0)->tcp.data_len))
+ {
+ clib_warning ("rcvd data in syn-sent");
+ error0 = tcp_segment_rcv (wrk, new_tc0, b0);
+ if (error0 == TCP_ERROR_ACK_OK)
+ error0 = TCP_ERROR_SYN_ACKS_RCVD;
+ }
+ else
+ {
+ tcp_program_ack (wrk, new_tc0);
+ }
+
+ drop:
+
+ tcp_inc_counter (syn_sent, error0, 1);
+ if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED) && tcp0 != 0))
+ {
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ clib_memcpy (&t0->tcp_header, tcp0, sizeof (t0->tcp_header));
+ clib_memcpy (&t0->tcp_connection, tc0, sizeof (t0->tcp_connection));
+ }
}
errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP,
my_thread_index);
tcp_inc_counter (syn_sent, TCP_ERROR_EVENT_FIFO_FULL, errors);
+ vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
+
return from_frame->n_vectors;
}
@@ -2615,359 +2553,310 @@ always_inline uword
tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vlib_frame_t * from_frame, int is_ip4)
{
- u32 n_left_from, next_index, *from, *to_next, n_fins = 0;
- u32 my_thread_index = vm->thread_index, errors = 0;
- tcp_worker_ctx_t *wrk = tcp_get_worker (my_thread_index);
+ u32 thread_index = vm->thread_index, errors = 0, *first_buffer;
+ tcp_worker_ctx_t *wrk = tcp_get_worker (thread_index);
+ u32 n_left_from, *from;
- from = vlib_frame_vector_args (from_frame);
+ from = first_buffer = vlib_frame_vector_args (from_frame);
n_left_from = from_frame->n_vectors;
- next_index = node->cached_next_index;
while (n_left_from > 0)
{
- u32 n_left_to_next;
+ u32 bi0, error0 = TCP_ERROR_NONE;
+ tcp_header_t *tcp0 = 0;
+ tcp_connection_t *tc0;
+ vlib_buffer_t *b0;
+ u8 is_fin0;
- vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+ bi0 = from[0];
+ from += 1;
+ n_left_from -= 1;
- while (n_left_from > 0 && n_left_to_next > 0)
+ b0 = vlib_get_buffer (vm, bi0);
+ tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index,
+ thread_index);
+ if (PREDICT_FALSE (tc0 == 0))
{
- u32 bi0;
- vlib_buffer_t *b0;
- tcp_header_t *tcp0 = 0;
- tcp_connection_t *tc0;
- u32 next0 = tcp_next_drop (is_ip4), error0 = TCP_ERROR_NONE;
- u8 is_fin0;
+ error0 = TCP_ERROR_INVALID_CONNECTION;
+ goto drop;
+ }
- bi0 = from[0];
- to_next[0] = bi0;
- from += 1;
- to_next += 1;
- n_left_from -= 1;
- n_left_to_next -= 1;
+ tcp0 = tcp_buffer_hdr (b0);
+ is_fin0 = tcp_is_fin (tcp0);
- b0 = vlib_get_buffer (vm, bi0);
- tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index,
- my_thread_index);
- if (PREDICT_FALSE (tc0 == 0))
+ /* SYNs, FINs and data consume sequence numbers */
+ vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
+ + tcp_is_syn (tcp0) + is_fin0 + vnet_buffer (b0)->tcp.data_len;
+
+ if (CLIB_DEBUG)
+ {
+ tcp_connection_t *tmp;
+ tmp = tcp_lookup_connection (tc0->c_fib_index, b0, thread_index,
+ is_ip4);
+ if (tmp->state != tc0->state)
{
- error0 = TCP_ERROR_INVALID_CONNECTION;
+ clib_warning ("state changed");
goto drop;
}
+ }
- tcp0 = tcp_buffer_hdr (b0);
- is_fin0 = tcp_is_fin (tcp0);
+ /*
+ * Special treatment for CLOSED
+ */
+ if (PREDICT_FALSE (tc0->state == TCP_STATE_CLOSED))
+ {
+ error0 = TCP_ERROR_CONNECTION_CLOSED;
+ goto drop;
+ }
- /* SYNs, FINs and data consume sequence numbers */
- vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
- + tcp_is_syn (tcp0) + is_fin0 + vnet_buffer (b0)->tcp.data_len;
+ /*
+ * For all other states (except LISTEN)
+ */
- if (CLIB_DEBUG)
- {
- tcp_connection_t *tmp;
- tmp = tcp_lookup_connection (tc0->c_fib_index, b0,
- my_thread_index, is_ip4);
- if (tmp->state != tc0->state)
- {
- clib_warning ("state changed");
- goto drop;
- }
- }
+ /* 1-4: check SEQ, RST, SYN */
+ if (PREDICT_FALSE (tcp_segment_validate (wrk, tc0, b0, tcp0, &error0)))
+ goto drop;
+ /* 5: check the ACK field */
+ switch (tc0->state)
+ {
+ case TCP_STATE_SYN_RCVD:
/*
- * Special treatment for CLOSED
+ * If the segment acknowledgment is not acceptable, form a
+ * reset segment,
+ * <SEQ=SEG.ACK><CTL=RST>
+ * and send it.
*/
- if (PREDICT_FALSE (tc0->state == TCP_STATE_CLOSED))
+ if (!tcp_rcv_ack_is_acceptable (tc0, b0))
{
- error0 = TCP_ERROR_CONNECTION_CLOSED;
+ TCP_DBG ("connection not accepted");
+ tcp_send_reset_w_pkt (tc0, b0, is_ip4);
+ error0 = TCP_ERROR_ACK_INVALID;
goto drop;
}
- /*
- * For all other states (except LISTEN)
- */
+ /* Update rtt and rto */
+ tcp_update_rtt (tc0, vnet_buffer (b0)->tcp.ack_number);
+
+ /* Switch state to ESTABLISHED */
+ tc0->state = TCP_STATE_ESTABLISHED;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+
+ /* Initialize session variables */
+ tc0->snd_una = vnet_buffer (b0)->tcp.ack_number;
+ tc0->snd_wnd = clib_net_to_host_u16 (tcp0->window)
+ << tc0->rcv_opts.wscale;
+ tc0->snd_wl1 = vnet_buffer (b0)->tcp.seq_number;
+ tc0->snd_wl2 = vnet_buffer (b0)->tcp.ack_number;
+
+ /* Reset SYN-ACK retransmit and SYN_RCV establish timers */
+ tcp_retransmit_timer_reset (tc0);
+ tcp_timer_reset (tc0, TCP_TIMER_ESTABLISH);
+ stream_session_accept_notify (&tc0->connection);
+ error0 = TCP_ERROR_ACK_OK;
+ break;
+ case TCP_STATE_ESTABLISHED:
+ /* We can get packets in established state here because they
+ * were enqueued before state change */
+ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
+ goto drop;
+
+ break;
+ case TCP_STATE_FIN_WAIT_1:
+ /* In addition to the processing for the ESTABLISHED state, if
+ * our FIN is now acknowledged then enter FIN-WAIT-2 and
+ * continue processing in that state. */
+ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
+ goto drop;
- /* 1-4: check SEQ, RST, SYN */
- if (PREDICT_FALSE (tcp_segment_validate (vm, tc0, b0, tcp0,
- &next0, &error0)))
+ /* Still have to send the FIN */
+ if (tc0->flags & TCP_CONN_FINPNDG)
{
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
+ /* TX fifo finally drained */
+ if (!session_tx_fifo_max_dequeue (&tc0->connection))
+ tcp_send_fin (tc0);
}
-
- /* 5: check the ACK field */
- switch (tc0->state)
+ /* If FIN is ACKed */
+ else if (tc0->snd_una == tc0->snd_una_max)
{
- case TCP_STATE_SYN_RCVD:
- /*
- * If the segment acknowledgment is not acceptable, form a
- * reset segment,
- * <SEQ=SEG.ACK><CTL=RST>
- * and send it.
- */
- if (!tcp_rcv_ack_is_acceptable (tc0, b0))
- {
- TCP_DBG ("connection not accepted");
- tcp_send_reset_w_pkt (tc0, b0, is_ip4);
- error0 = TCP_ERROR_ACK_INVALID;
- goto drop;
- }
-
- /* Update rtt and rto */
- tcp_update_rtt (tc0, vnet_buffer (b0)->tcp.ack_number);
-
- /* Switch state to ESTABLISHED */
- tc0->state = TCP_STATE_ESTABLISHED;
+ tc0->state = TCP_STATE_FIN_WAIT_2;
TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
- /* Initialize session variables */
- tc0->snd_una = vnet_buffer (b0)->tcp.ack_number;
- tc0->snd_wnd = clib_net_to_host_u16 (tcp0->window)
- << tc0->rcv_opts.wscale;
- tc0->snd_wl1 = vnet_buffer (b0)->tcp.seq_number;
- tc0->snd_wl2 = vnet_buffer (b0)->tcp.ack_number;
-
- /* Reset SYN-ACK retransmit and SYN_RCV establish timers */
- tcp_retransmit_timer_reset (tc0);
- tcp_timer_reset (tc0, TCP_TIMER_ESTABLISH);
- stream_session_accept_notify (&tc0->connection);
- error0 = TCP_ERROR_ACK_OK;
- break;
- case TCP_STATE_ESTABLISHED:
- /* We can get packets in established state here because they
- * were enqueued before state change */
- if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &next0, &error0))
- {
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
- }
-
- break;
- case TCP_STATE_FIN_WAIT_1:
- /* In addition to the processing for the ESTABLISHED state, if
- * our FIN is now acknowledged then enter FIN-WAIT-2 and
- * continue processing in that state. */
- if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &next0, &error0))
- {
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
- }
+ /* Stop all retransmit timers because we have nothing more
+ * to send. Enable waitclose though because we're willing to
+ * wait for peer's FIN but not indefinitely. */
+ tcp_connection_timers_reset (tc0);
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+ }
+ break;
+ case TCP_STATE_FIN_WAIT_2:
+ /* In addition to the processing for the ESTABLISHED state, if
+ * the retransmission queue is empty, the user's CLOSE can be
+ * acknowledged ("ok") but do not delete the TCB. */
+ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
+ goto drop;
+ break;
+ case TCP_STATE_CLOSE_WAIT:
+ /* Do the same processing as for the ESTABLISHED state. */
+ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
+ goto drop;
- /* Still have to send the FIN */
- if (tc0->flags & TCP_CONN_FINPNDG)
- {
- /* TX fifo finally drained */
- if (!session_tx_fifo_max_dequeue (&tc0->connection))
- tcp_send_fin (tc0);
- }
- /* If FIN is ACKed */
- else if (tc0->snd_una == tc0->snd_una_max)
+ if (tc0->flags & TCP_CONN_FINPNDG)
+ {
+ /* TX fifo finally drained */
+ if (!session_tx_fifo_max_dequeue (&tc0->connection))
{
- tc0->state = TCP_STATE_FIN_WAIT_2;
- TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
-
- /* Stop all retransmit timers because we have nothing more
- * to send. Enable waitclose though because we're willing to
- * wait for peer's FIN but not indefinitely. */
+ tcp_send_fin (tc0);
tcp_connection_timers_reset (tc0);
+ tc0->state = TCP_STATE_LAST_ACK;
tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
}
- break;
- case TCP_STATE_FIN_WAIT_2:
- /* In addition to the processing for the ESTABLISHED state, if
- * the retransmission queue is empty, the user's CLOSE can be
- * acknowledged ("ok") but do not delete the TCB. */
- if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &next0, &error0))
- {
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
- }
- break;
- case TCP_STATE_CLOSE_WAIT:
- /* Do the same processing as for the ESTABLISHED state. */
- if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &next0, &error0))
- {
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
- }
- if (tc0->flags & TCP_CONN_FINPNDG)
- {
- /* TX fifo finally drained */
- if (!session_tx_fifo_max_dequeue (&tc0->connection))
- {
- tcp_send_fin (tc0);
- tcp_connection_timers_reset (tc0);
- tc0->state = TCP_STATE_LAST_ACK;
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE,
- TCP_2MSL_TIME);
- }
- }
- break;
- case TCP_STATE_CLOSING:
- /* In addition to the processing for the ESTABLISHED state, if
- * the ACK acknowledges our FIN then enter the TIME-WAIT state,
- * otherwise ignore the segment. */
- if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &next0, &error0))
- {
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
- }
-
- tc0->state = TCP_STATE_TIME_WAIT;
- TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
- goto drop;
-
- break;
- case TCP_STATE_LAST_ACK:
- /* The only thing that [should] arrive in this state is an
- * acknowledgment of our FIN. If our FIN is now acknowledged,
- * delete the TCB, enter the CLOSED state, and return. */
-
- if (!tcp_rcv_ack_is_acceptable (tc0, b0))
- {
- error0 = TCP_ERROR_ACK_INVALID;
- goto drop;
- }
- error0 = TCP_ERROR_ACK_OK;
- tc0->snd_una = vnet_buffer (b0)->tcp.ack_number;
- /* Apparently our ACK for the peer's FIN was lost */
- if (is_fin0 && tc0->snd_una != tc0->snd_una_max)
- {
- tcp_send_fin (tc0);
- goto drop;
- }
+ }
+ break;
+ case TCP_STATE_CLOSING:
+ /* In addition to the processing for the ESTABLISHED state, if
+ * the ACK acknowledges our FIN then enter the TIME-WAIT state,
+ * otherwise ignore the segment. */
+ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
+ goto drop;
- tc0->state = TCP_STATE_CLOSED;
- TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+ tc0->state = TCP_STATE_TIME_WAIT;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
+ goto drop;
- /* Don't free the connection from the data path since
- * we can't ensure that we have no packets already enqueued
- * to output. Rely instead on the waitclose timer */
- tcp_connection_timers_reset (tc0);
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, 1);
+ break;
+ case TCP_STATE_LAST_ACK:
+ /* The only thing that [should] arrive in this state is an
+ * acknowledgment of our FIN. If our FIN is now acknowledged,
+ * delete the TCB, enter the CLOSED state, and return. */
+ if (!tcp_rcv_ack_is_acceptable (tc0, b0))
+ {
+ error0 = TCP_ERROR_ACK_INVALID;
+ goto drop;
+ }
+ error0 = TCP_ERROR_ACK_OK;
+ tc0->snd_una = vnet_buffer (b0)->tcp.ack_number;
+ /* Apparently our ACK for the peer's FIN was lost */
+ if (is_fin0 && tc0->snd_una != tc0->snd_una_max)
+ {
+ tcp_send_fin (tc0);
goto drop;
+ }
- break;
- case TCP_STATE_TIME_WAIT:
- /* The only thing that can arrive in this state is a
- * retransmission of the remote FIN. Acknowledge it, and restart
- * the 2 MSL timeout. */
+ tc0->state = TCP_STATE_CLOSED;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
- if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &next0, &error0))
- {
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- goto drop;
- }
+ /* Don't free the connection from the data path since
+ * we can't ensure that we have no packets already enqueued
+ * to output. Rely instead on the waitclose timer */
+ tcp_connection_timers_reset (tc0);
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, 1);
- tcp_make_ack (tc0, b0);
- next0 = tcp_next_output (is_ip4);
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
- goto drop;
+ goto drop;
- break;
- default:
- ASSERT (0);
- }
+ break;
+ case TCP_STATE_TIME_WAIT:
+ /* The only thing that can arrive in this state is a
+ * retransmission of the remote FIN. Acknowledge it, and restart
+ * the 2 MSL timeout. */
- /* 6: check the URG bit TODO */
+ if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0))
+ goto drop;
- /* 7: process the segment text */
- switch (tc0->state)
- {
- case TCP_STATE_ESTABLISHED:
- case TCP_STATE_FIN_WAIT_1:
- case TCP_STATE_FIN_WAIT_2:
- if (vnet_buffer (b0)->tcp.data_len)
- {
- error0 = tcp_segment_rcv (tc0, b0, &next0);
- tcp_maybe_inc_counter (rcv_process, error0, 1);
- }
- else if (is_fin0)
- tc0->rcv_nxt += 1;
- break;
- case TCP_STATE_CLOSE_WAIT:
- case TCP_STATE_CLOSING:
- case TCP_STATE_LAST_ACK:
- case TCP_STATE_TIME_WAIT:
- /* This should not occur, since a FIN has been received from the
- * remote side. Ignore the segment text. */
- break;
- }
+ tcp_program_ack (wrk, tc0);
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
+ goto drop;
- /* 8: check the FIN bit */
- if (!is_fin0)
- goto drop;
+ break;
+ default:
+ ASSERT (0);
+ }
- switch (tc0->state)
- {
- case TCP_STATE_ESTABLISHED:
- case TCP_STATE_SYN_RCVD:
- /* Send FIN-ACK notify app and enter CLOSE-WAIT */
- tcp_connection_timers_reset (tc0);
- tcp_make_fin (tc0, b0);
- tc0->snd_nxt += 1;
- tc0->snd_una_max = tc0->snd_nxt;
- tcp_retransmit_timer_set (tc0);
- next0 = tcp_next_output (tc0->c_is_ip4);
- stream_session_disconnect_notify (&tc0->connection);
- tc0->state = TCP_STATE_CLOSE_WAIT;
- TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
- break;
- case TCP_STATE_CLOSE_WAIT:
- case TCP_STATE_CLOSING:
- case TCP_STATE_LAST_ACK:
- /* move along .. */
- break;
- case TCP_STATE_FIN_WAIT_1:
- tc0->state = TCP_STATE_CLOSING;
- tcp_make_ack (tc0, b0);
- next0 = tcp_next_output (is_ip4);
- TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
- /* Wait for ACK but not forever */
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
- break;
- case TCP_STATE_FIN_WAIT_2:
- /* Got FIN, send ACK! Be more aggressive with resource cleanup */
- tc0->state = TCP_STATE_TIME_WAIT;
- tcp_connection_timers_reset (tc0);
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
- tcp_make_ack (tc0, b0);
- next0 = tcp_next_output (is_ip4);
- TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
- break;
- case TCP_STATE_TIME_WAIT:
- /* Remain in the TIME-WAIT state. Restart the time-wait
- * timeout.
- */
- tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
- break;
- }
- TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
- n_fins += 1;
+ /* 6: check the URG bit TODO */
- drop:
- b0->error = error0 ? node->errors[error0] : 0;
+ /* 7: process the segment text */
+ switch (tc0->state)
+ {
+ case TCP_STATE_ESTABLISHED:
+ case TCP_STATE_FIN_WAIT_1:
+ case TCP_STATE_FIN_WAIT_2:
+ if (vnet_buffer (b0)->tcp.data_len)
+ error0 = tcp_segment_rcv (wrk, tc0, b0);
+ else if (is_fin0)
+ tc0->rcv_nxt += 1;
+ break;
+ case TCP_STATE_CLOSE_WAIT:
+ case TCP_STATE_CLOSING:
+ case TCP_STATE_LAST_ACK:
+ case TCP_STATE_TIME_WAIT:
+ /* This should not occur, since a FIN has been received from the
+ * remote side. Ignore the segment text. */
+ break;
+ }
- if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
- {
- tcp_rx_trace_t *t0 =
- vlib_add_trace (vm, node, b0, sizeof (*t0));
- tcp_set_rx_trace_data (t0, tc0, tcp0, b0, is_ip4);
- }
+ /* 8: check the FIN bit */
+ if (!is_fin0)
+ goto drop;
- vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
- n_left_to_next, bi0, next0);
+ switch (tc0->state)
+ {
+ case TCP_STATE_ESTABLISHED:
+ case TCP_STATE_SYN_RCVD:
+ /* Send FIN-ACK notify app and enter CLOSE-WAIT */
+ tcp_connection_timers_reset (tc0);
+ tcp_send_fin (tc0);
+ stream_session_disconnect_notify (&tc0->connection);
+ tc0->state = TCP_STATE_CLOSE_WAIT;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+ break;
+ case TCP_STATE_CLOSE_WAIT:
+ case TCP_STATE_CLOSING:
+ case TCP_STATE_LAST_ACK:
+ /* move along .. */
+ break;
+ case TCP_STATE_FIN_WAIT_1:
+ tc0->state = TCP_STATE_CLOSING;
+ tcp_program_ack (wrk, tc0);
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+ /* Wait for ACK but not forever */
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+ break;
+ case TCP_STATE_FIN_WAIT_2:
+ /* Got FIN, send ACK! Be more aggressive with resource cleanup */
+ tc0->state = TCP_STATE_TIME_WAIT;
+ tcp_connection_timers_reset (tc0);
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
+ tcp_program_ack (wrk, tc0);
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+ break;
+ case TCP_STATE_TIME_WAIT:
+ /* Remain in the TIME-WAIT state. Restart the time-wait
+ * timeout.
+ */
+ tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME);
+ break;
}
+ error0 = TCP_ERROR_FIN_RCVD;
+ TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
+
+ drop:
- vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ tcp_inc_counter (rcv_process, error0, 1);
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ tcp_rx_trace_t *t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ tcp_set_rx_trace_data (t0, tc0, tcp0, b0, is_ip4);
+ }
}
errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP,
- my_thread_index);
+ thread_index);
tcp_inc_counter (rcv_process, TCP_ERROR_EVENT_FIFO_FULL, errors);
- tcp_inc_counter (rcv_process, TCP_ERROR_FIN_RCVD, n_fins);
tcp_handle_postponed_dequeues (wrk);
+ vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
+
return from_frame->n_vectors;
}
@@ -3039,162 +2928,146 @@ always_inline uword
tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vlib_frame_t * from_frame, int is_ip4)
{
- u32 n_left_from, next_index, *from, *to_next, n_syns = 0;
+ u32 n_left_from, *from, n_syns = 0, *first_buffer;
u32 my_thread_index = vm->thread_index;
- from = vlib_frame_vector_args (from_frame);
+ from = first_buffer = vlib_frame_vector_args (from_frame);
n_left_from = from_frame->n_vectors;
- next_index = node->cached_next_index;
-
while (n_left_from > 0)
{
- u32 n_left_to_next;
-
- vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-
- while (n_left_from > 0 && n_left_to_next > 0)
- {
- u32 bi0;
- vlib_buffer_t *b0;
- tcp_rx_trace_t *t0;
- tcp_header_t *th0 = 0;
- tcp_connection_t *lc0;
- ip4_header_t *ip40;
- ip6_header_t *ip60;
- tcp_connection_t *child0;
- u32 error0 = TCP_ERROR_NONE, next0 = tcp_next_drop (is_ip4);
-
- bi0 = from[0];
- to_next[0] = bi0;
- from += 1;
- to_next += 1;
- n_left_from -= 1;
- n_left_to_next -= 1;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ tcp_rx_trace_t *t0;
+ tcp_header_t *th0 = 0;
+ tcp_connection_t *lc0;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ tcp_connection_t *child0;
+ u32 error0 = TCP_ERROR_NONE;
- b0 = vlib_get_buffer (vm, bi0);
- lc0 = tcp_listener_get (vnet_buffer (b0)->tcp.connection_index);
+ bi0 = from[0];
+ from += 1;
+ n_left_from -= 1;
- if (is_ip4)
- {
- ip40 = vlib_buffer_get_current (b0);
- th0 = ip4_next_header (ip40);
- }
- else
- {
- ip60 = vlib_buffer_get_current (b0);
- th0 = ip6_next_header (ip60);
- }
+ b0 = vlib_get_buffer (vm, bi0);
+ lc0 = tcp_listener_get (vnet_buffer (b0)->tcp.connection_index);
- /* Create child session. For syn-flood protection use filter */
+ if (is_ip4)
+ {
+ ip40 = vlib_buffer_get_current (b0);
+ th0 = ip4_next_header (ip40);
+ }
+ else
+ {
+ ip60 = vlib_buffer_get_current (b0);
+ th0 = ip6_next_header (ip60);
+ }
- /* 1. first check for an RST: handled in dispatch */
- /* if (tcp_rst (th0))
- goto drop; */
+ /* Create child session. For syn-flood protection use filter */
- /* 2. second check for an ACK: handled in dispatch */
- /* if (tcp_ack (th0))
- {
- tcp_send_reset (b0, is_ip4);
- goto drop;
- } */
+ /* 1. first check for an RST: handled in dispatch */
+ /* if (tcp_rst (th0))
+ goto drop;
+ */
- /* 3. check for a SYN (did that already) */
+ /* 2. second check for an ACK: handled in dispatch */
+ /* if (tcp_ack (th0))
+ {
+ tcp_send_reset (b0, is_ip4);
+ goto drop;
+ }
+ */
- /* Make sure connection wasn't just created */
- child0 = tcp_lookup_connection (lc0->c_fib_index, b0,
- my_thread_index, is_ip4);
- if (PREDICT_FALSE (child0->state != TCP_STATE_LISTEN))
- {
- error0 = TCP_ERROR_CREATE_EXISTS;
- goto drop;
- }
+ /* 3. check for a SYN (did that already) */
- /* Create child session and send SYN-ACK */
- child0 = tcp_connection_new (my_thread_index);
- child0->c_lcl_port = th0->dst_port;
- child0->c_rmt_port = th0->src_port;
- child0->c_is_ip4 = is_ip4;
- child0->state = TCP_STATE_SYN_RCVD;
- child0->c_fib_index = lc0->c_fib_index;
+ /* Make sure connection wasn't just created */
+ child0 = tcp_lookup_connection (lc0->c_fib_index, b0, my_thread_index,
+ is_ip4);
+ if (PREDICT_FALSE (child0->state != TCP_STATE_LISTEN))
+ {
+ error0 = TCP_ERROR_CREATE_EXISTS;
+ goto drop;
+ }
- if (is_ip4)
- {
- child0->c_lcl_ip4.as_u32 = ip40->dst_address.as_u32;
- child0->c_rmt_ip4.as_u32 = ip40->src_address.as_u32;
- }
- else
- {
- clib_memcpy (&child0->c_lcl_ip6, &ip60->dst_address,
- sizeof (ip6_address_t));
- clib_memcpy (&child0->c_rmt_ip6, &ip60->src_address,
- sizeof (ip6_address_t));
- }
+ /* Create child session and send SYN-ACK */
+ child0 = tcp_connection_new (my_thread_index);
+ child0->c_lcl_port = th0->dst_port;
+ child0->c_rmt_port = th0->src_port;
+ child0->c_is_ip4 = is_ip4;
+ child0->state = TCP_STATE_SYN_RCVD;
+ child0->c_fib_index = lc0->c_fib_index;
- if (tcp_options_parse (th0, &child0->rcv_opts))
- {
- clib_warning ("options parse fail");
- goto drop;
- }
+ if (is_ip4)
+ {
+ child0->c_lcl_ip4.as_u32 = ip40->dst_address.as_u32;
+ child0->c_rmt_ip4.as_u32 = ip40->src_address.as_u32;
+ }
+ else
+ {
+ clib_memcpy (&child0->c_lcl_ip6, &ip60->dst_address,
+ sizeof (ip6_address_t));
+ clib_memcpy (&child0->c_rmt_ip6, &ip60->src_address,
+ sizeof (ip6_address_t));
+ }
- child0->irs = vnet_buffer (b0)->tcp.seq_number;
- child0->rcv_nxt = vnet_buffer (b0)->tcp.seq_number + 1;
- child0->rcv_las = child0->rcv_nxt;
- child0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ if (tcp_options_parse (th0, &child0->rcv_opts))
+ {
+ clib_warning ("options parse fail");
+ goto drop;
+ }
- /* RFC1323: TSval timestamps sent on {SYN} and {SYN,ACK}
- * segments are used to initialize PAWS. */
- if (tcp_opts_tstamp (&child0->rcv_opts))
- {
- child0->tsval_recent = child0->rcv_opts.tsval;
- child0->tsval_recent_age = tcp_time_now ();
- }
+ child0->irs = vnet_buffer (b0)->tcp.seq_number;
+ child0->rcv_nxt = vnet_buffer (b0)->tcp.seq_number + 1;
+ child0->rcv_las = child0->rcv_nxt;
+ child0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
- if (tcp_opts_wscale (&child0->rcv_opts))
- child0->snd_wscale = child0->rcv_opts.wscale;
+ /* RFC1323: TSval timestamps sent on {SYN} and {SYN,ACK}
+ * segments are used to initialize PAWS. */
+ if (tcp_opts_tstamp (&child0->rcv_opts))
+ {
+ child0->tsval_recent = child0->rcv_opts.tsval;
+ child0->tsval_recent_age = tcp_time_now ();
+ }
- child0->snd_wnd = clib_net_to_host_u16 (th0->window)
- << child0->snd_wscale;
- child0->snd_wl1 = vnet_buffer (b0)->tcp.seq_number;
- child0->snd_wl2 = vnet_buffer (b0)->tcp.ack_number;
+ if (tcp_opts_wscale (&child0->rcv_opts))
+ child0->snd_wscale = child0->rcv_opts.wscale;
- tcp_connection_init_vars (child0);
- TCP_EVT_DBG (TCP_EVT_SYN_RCVD, child0, 1);
+ child0->snd_wnd = clib_net_to_host_u16 (th0->window)
+ << child0->snd_wscale;
+ child0->snd_wl1 = vnet_buffer (b0)->tcp.seq_number;
+ child0->snd_wl2 = vnet_buffer (b0)->tcp.ack_number;
- if (stream_session_accept (&child0->connection, lc0->c_s_index,
- 0 /* notify */ ))
- {
- clib_warning ("session accept fail");
- tcp_connection_cleanup (child0);
- error0 = TCP_ERROR_CREATE_SESSION_FAIL;
- goto drop;
- }
+ tcp_connection_init_vars (child0);
+ TCP_EVT_DBG (TCP_EVT_SYN_RCVD, child0, 1);
- /* Reuse buffer to make syn-ack and send */
- tcp_make_synack (child0, b0);
- next0 = tcp_next_output (is_ip4);
- tcp_timer_set (child0, TCP_TIMER_ESTABLISH, TCP_SYN_RCVD_TIME);
+ if (stream_session_accept (&child0->connection, lc0->c_s_index,
+ 0 /* notify */ ))
+ {
+ clib_warning ("session accept fail");
+ tcp_connection_cleanup (child0);
+ error0 = TCP_ERROR_CREATE_SESSION_FAIL;
+ goto drop;
+ }
- drop:
- if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
- {
- t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
- clib_memcpy (&t0->tcp_header, th0, sizeof (t0->tcp_header));
- clib_memcpy (&t0->tcp_connection, lc0,
- sizeof (t0->tcp_connection));
- }
+ tcp_send_synack (child0);
+ tcp_timer_set (child0, TCP_TIMER_ESTABLISH, TCP_SYN_RCVD_TIME);
- n_syns += (error0 == TCP_ERROR_NONE);
- b0->error = node->errors[error0];
+ drop:
- vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
- n_left_to_next, bi0, next0);
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
+ clib_memcpy (&t0->tcp_header, th0, sizeof (t0->tcp_header));
+ clib_memcpy (&t0->tcp_connection, lc0, sizeof (t0->tcp_connection));
}
- vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ n_syns += (error0 == TCP_ERROR_NONE);
}
tcp_inc_counter (listen, TCP_ERROR_SYNS_RCVD, n_syns);
+ vlib_buffer_free (vm, first_buffer, from_frame->n_vectors);
+
return from_frame->n_vectors;
}
diff --git a/src/vnet/tcp/tcp_output.c b/src/vnet/tcp/tcp_output.c
index 29a919bd160..089f85a0ea0 100644
--- a/src/vnet/tcp/tcp_output.c
+++ b/src/vnet/tcp/tcp_output.c
@@ -559,7 +559,6 @@ tcp_make_ack (tcp_connection_t * tc, vlib_buffer_t * b)
tcp_reuse_buffer (vm, b);
tcp_make_ack_i (tc, b, TCP_STATE_ESTABLISHED, TCP_FLAG_ACK);
TCP_EVT_DBG (TCP_EVT_ACK_SENT, tc);
- vnet_buffer (b)->tcp.flags = TCP_BUF_FLAG_ACK;
tc->rcv_las = tc->rcv_nxt;
}
@@ -631,7 +630,6 @@ tcp_make_synack (tcp_connection_t * tc, vlib_buffer_t * b)
tcp_options_write ((u8 *) (th + 1), snd_opts);
vnet_buffer (b)->tcp.connection_index = tc->c_c_index;
- vnet_buffer (b)->tcp.flags = TCP_BUF_FLAG_ACK;
/* Init retransmit timer. Use update instead of set because of
* retransmissions */
@@ -1011,6 +1009,23 @@ tcp_send_syn (tcp_connection_t * tc)
TCP_EVT_DBG (TCP_EVT_SYN_SENT, tc);
}
+void
+tcp_send_synack (tcp_connection_t * tc)
+{
+ tcp_worker_ctx_t *wrk = tcp_get_worker (tc->c_thread_index);
+ vlib_main_t *vm = wrk->vm;
+ vlib_buffer_t *b;
+ u32 bi;
+
+ /* Get buffer */
+ if (PREDICT_FALSE (tcp_get_free_buffer_index (wrk, &bi)))
+ return;
+
+ b = vlib_get_buffer (vm, bi);
+ tcp_make_synack (tc, b);
+ tcp_enqueue_to_output (wrk, b, bi, tc->c_is_ip4);
+}
+
/**
* Flush tx frame populated by retransmits and timer pops
*/
@@ -1223,6 +1238,56 @@ tcp_send_ack (tcp_connection_t * tc)
tcp_enqueue_to_output (wrk, b, bi, tc->c_is_ip4);
}
+void
+tcp_program_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
+{
+ if (!(tc->flags & TCP_CONN_SNDACK))
+ {
+ vec_add1 (wrk->pending_acks, tc->c_c_index);
+ tc->flags |= TCP_CONN_SNDACK;
+ }
+}
+
+void
+tcp_program_dupack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
+{
+ if (!(tc->flags & TCP_CONN_SNDACK))
+ {
+ vec_add1 (wrk->pending_acks, tc->c_c_index);
+ tc->flags |= TCP_CONN_SNDACK;
+ }
+ if (tc->pending_dupacks < 255)
+ tc->pending_dupacks += 1;
+}
+
+void
+tcp_send_acks (tcp_worker_ctx_t * wrk)
+{
+ u32 thread_index, *pending_acks;
+ tcp_connection_t *tc;
+ int i, j, n_acks;
+
+ if (!vec_len (wrk->pending_acks))
+ return;
+
+ thread_index = wrk->vm->thread_index;
+ pending_acks = wrk->pending_acks;
+ for (i = 0; i < vec_len (pending_acks); i++)
+ {
+ tc = tcp_connection_get (pending_acks[i], thread_index);
+ tc->flags &= ~TCP_CONN_SNDACK;
+ n_acks = clib_max (1, tc->pending_dupacks);
+ /* If we're supposed to send dupacks but have no ooo data
+ * send only one ack */
+ if (tc->pending_dupacks && !vec_len (tc->snd_sacks))
+ n_acks = 1;
+ for (j = 0; j < n_acks; j++)
+ tcp_send_ack (tc);
+ tc->pending_dupacks = 0;
+ }
+ _vec_len (wrk->pending_acks) = 0;
+}
+
/**
* Delayed ack timer handler
*
@@ -1944,13 +2009,6 @@ tcp_fast_retransmit (tcp_worker_ctx_t * wrk, tcp_connection_t * tc,
return tcp_fast_retransmit_no_sack (wrk, tc, burst_size);
}
-static u32
-tcp_session_has_ooo_data (tcp_connection_t * tc)
-{
- stream_session_t *s = session_get (tc->c_s_index, tc->c_thread_index);
- return svm_fifo_has_ooo_data (s->server_rx_fifo);
-}
-
static void
tcp_output_handle_link_local (tcp_connection_t * tc0, vlib_buffer_t * b0,
u16 * next0, u32 * error0)
@@ -2055,25 +2113,6 @@ tcp_output_handle_packet (tcp_connection_t * tc0, vlib_buffer_t * b0,
tcp_output_handle_link_local (tc0, b0, next0, error0);
}
- /* Filter out DUPACKs if there are no OOO segments left */
- if (PREDICT_FALSE (vnet_buffer (b0)->tcp.flags & TCP_BUF_FLAG_DUPACK))
- {
- /* N.B. Should not filter burst of dupacks. Two issues:
- * 1) dupacks open cwnd on remote peer when congested
- * 2) acks leaving should have the latest rcv_wnd since the
- * burst may have eaten up all of it, so only the old ones
- * could be filtered.
- */
- if (!tcp_session_has_ooo_data (tc0))
- {
- *error0 = TCP_ERROR_FILTERED_DUPACKS;
- *next0 = TCP_OUTPUT_NEXT_DROP;
- return;
- }
- }
-
- /* Stop DELACK timer and fix flags */
- tc0->flags &= ~(TCP_CONN_SNDACK);
if (!TCP_ALWAYS_ACK)
tcp_timer_reset (tc0, TCP_TIMER_DELACK);
}