vec_reset_length (to->sacks);
for (j = 0; j < to->n_sack_blocks; j++)
{
- b.start = clib_net_to_host_u32 (*(u32 *) (data + 2 + 4 * j));
- b.end = clib_net_to_host_u32 (*(u32 *) (data + 6 + 4 * j));
+ b.start = clib_net_to_host_u32 (*(u32 *) (data + 2 + 8 * j));
+ b.end = clib_net_to_host_u32 (*(u32 *) (data + 6 + 8 * j));
vec_add1 (to->sacks, b);
}
break;
/* 4th: check the SYN bit */
if (tcp_syn (th0))
{
- tcp_send_reset (b0, tc0->c_is_ip4);
+ /* TODO implement RFC 5961 */
+ tcp_make_ack (tc0, b0);
+ *next0 = tcp_next_output (tc0->c_is_ip4);
+ TCP_EVT_DBG (TCP_EVT_SYN_RCVD, tc0);
return -1;
}
if (scoreboard_hole_index (sb, hole) == sb->cur_rxt_hole)
sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX;
+ /* Poison the entry */
+ if (CLIB_DEBUG > 0)
+ memset (hole, 0xfe, sizeof (*hole));
+
pool_put (sb->holes, hole);
}
hole->start = start;
hole->end = end;
- hole_index = hole - sb->holes;
+ hole_index = scoreboard_hole_index (sb, hole);
prev = scoreboard_get_hole (sb, prev_index);
if (prev)
}
void
-scoreboard_init_high_rxt (sack_scoreboard_t * sb)
+scoreboard_init_high_rxt (sack_scoreboard_t * sb, u32 seq)
{
sack_scoreboard_hole_t *hole;
hole = scoreboard_first_hole (sb);
- sb->high_rxt = hole->start;
- sb->cur_rxt_hole = sb->head;
+ if (hole)
+ {
+ seq = seq_gt (seq, hole->start) ? seq : hole->start;
+ sb->cur_rxt_hole = sb->head;
+ }
+ sb->high_rxt = seq;
+}
+
+/**
+ * Test that scoreboard is sane after recovery
+ *
+ * Returns 1 if scoreboard is empty or if first hole beyond
+ * snd_una.
+ */
+u8
+tcp_scoreboard_is_sane_post_recovery (tcp_connection_t * tc)
+{
+ sack_scoreboard_hole_t *hole;
+ hole = scoreboard_first_hole (&tc->sack_sb);
+ return (!hole || seq_geq (hole->start, tc->snd_una));
}
void
{
if (seq_lt (blk->start, blk->end)
&& seq_gt (blk->start, tc->snd_una)
- && seq_gt (blk->start, ack) && seq_leq (blk->end, tc->snd_nxt))
+ && seq_gt (blk->start, ack) && seq_leq (blk->end, tc->snd_una_max))
{
blk++;
continue;
if (vec_len (tc->rcv_opts.sacks) == 0)
return;
+ tcp_scoreboard_trace_add (tc, ack);
+
/* Make sure blocks are ordered */
for (i = 0; i < vec_len (tc->rcv_opts.sacks); i++)
for (j = i + 1; j < vec_len (tc->rcv_opts.sacks); j++)
sb->last_bytes_delivered +=
next_hole->start - hole->end;
}
- else if (!next_hole)
+ else
{
ASSERT (seq_geq (sb->high_sacked, ack));
sb->snd_una_adv = sb->high_sacked - ack;
if (seq_lt (blk->end, hole->end))
{
hole_index = scoreboard_hole_index (sb, hole);
- scoreboard_insert_hole (sb, hole_index, blk->end, hole->end);
+ next_hole = scoreboard_insert_hole (sb, hole_index, blk->end,
+ hole->end);
/* Pool might've moved */
hole = scoreboard_get_hole (sb, hole_index);
hole->end = blk->start;
blk_index++;
+ ASSERT (hole->next == scoreboard_hole_index (sb, next_hole));
}
else if (seq_lt (blk->start, hole->end))
{
ASSERT (tc->rto_boff == 0);
ASSERT (!tcp_in_cong_recovery (tc));
-
+ ASSERT (tcp_scoreboard_is_sane_post_recovery (tc));
TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3);
return 0;
}
static void
tcp_cc_update (tcp_connection_t * tc, vlib_buffer_t * b)
{
- ASSERT (!tcp_in_cong_recovery (tc));
+ ASSERT (!tcp_in_cong_recovery (tc) || tcp_is_lost_fin (tc));
/* Congestion avoidance */
tc->cc_algo->rcv_ack (tc);
ASSERT (tc->cwnd >= tc->snd_mss);
/* If cwnd allows, send more data */
- if (tcp_opts_sack_permitted (&tc->rcv_opts)
- && scoreboard_first_hole (&tc->sack_sb))
+ if (tcp_opts_sack_permitted (&tc->rcv_opts))
{
- scoreboard_init_high_rxt (&tc->sack_sb);
+ scoreboard_init_high_rxt (&tc->sack_sb,
+ tc->snd_una + tc->snd_mss);
tcp_fast_retransmit_sack (tc);
}
else
/* Remove retransmitted bytes that have been delivered */
ASSERT (tc->bytes_acked + tc->sack_sb.snd_una_adv
>= tc->sack_sb.last_bytes_delivered);
- rxt_delivered = tc->bytes_acked + tc->sack_sb.snd_una_adv
- - tc->sack_sb.last_bytes_delivered;
- if (0 && rxt_delivered && seq_gt (tc->sack_sb.high_rxt, tc->snd_una))
+
+ if (seq_lt (tc->snd_una, tc->sack_sb.high_rxt))
{
/* If we have sacks and we haven't gotten an ack beyond high_rxt,
* remove sacked bytes delivered */
+ rxt_delivered = tc->bytes_acked + tc->sack_sb.snd_una_adv
+ - tc->sack_sb.last_bytes_delivered;
ASSERT (tc->snd_rxt_bytes >= rxt_delivered);
tc->snd_rxt_bytes -= rxt_delivered;
}
* Looks okay, process feedback
*/
- TCP_EVT_DBG (TCP_EVT_ACK_RCVD, tc);
-
if (tcp_opts_sack_permitted (&tc->rcv_opts))
tcp_rcv_sacks (tc, vnet_buffer (b)->tcp.ack_number);
if (tc->bytes_acked)
tcp_dequeue_acked (tc, vnet_buffer (b)->tcp.ack_number);
+ TCP_EVT_DBG (TCP_EVT_ACK_RCVD, tc);
+
/*
* Check if we have congestion event
*/
return 0;
}
+static u8
+tcp_sack_vector_is_sane (sack_block_t * sacks)
+{
+ int i;
+ for (i = 1; i < vec_len (sacks); i++)
+ {
+ if (sacks[i - 1].end == sacks[i].start)
+ return 0;
+ }
+ return 1;
+}
+
/**
* Build SACK list as per RFC2018.
*
/* Replace old vector with new one */
vec_free (tc->snd_sacks);
tc->snd_sacks = new_list;
+
+ /* Segments should not 'touch' */
+ ASSERT (tcp_sack_vector_is_sane (tc->snd_sacks));
}
/** Enqueue data for delivery to application */
/* Pure ACK. Update rcv_nxt and be done. */
if (PREDICT_FALSE (data_len == 0))
{
- tc->rcv_nxt = vnet_buffer (b)->tcp.seq_end;
return TCP_ERROR_PURE_ACK;
}
u16 data_len)
{
stream_session_t *s0;
- int rv;
+ int rv, offset;
ASSERT (seq_gt (vnet_buffer (b)->tcp.seq_number, tc->rcv_nxt));
newest = svm_fifo_newest_ooo_segment (s0->server_rx_fifo);
if (newest)
{
- start =
- tc->rcv_nxt + ooo_segment_offset (s0->server_rx_fifo, newest);
+ offset = ooo_segment_offset (s0->server_rx_fifo, newest);
+ ASSERT (offset <= vnet_buffer (b)->tcp.seq_number - tc->rcv_nxt);
+ start = tc->rcv_nxt + offset;
end = start + ooo_segment_length (s0->server_rx_fifo, newest);
tcp_update_sack_list (tc, start, end);
-
- ASSERT (seq_gt (start, tc->rcv_nxt));
+ svm_fifo_newest_ooo_segment_reset (s0->server_rx_fifo);
}
}
static int
tcp_segment_rcv (tcp_main_t * tm, tcp_connection_t * tc, vlib_buffer_t * b,
- u16 n_data_bytes, u32 * next0)
+ u32 * next0)
{
- u32 error = 0, n_bytes_to_drop;
+ u32 error = 0, n_bytes_to_drop, n_data_bytes;
+
+ vlib_buffer_advance (b, vnet_buffer (b)->tcp.data_offset);
+ n_data_bytes = vnet_buffer (b)->tcp.data_len;
+ ASSERT (n_data_bytes);
/* Handle out-of-order data */
if (PREDICT_FALSE (vnet_buffer (b)->tcp.seq_number != tc->rcv_nxt))
/* Completely in the past (possible retransmit) */
if (seq_leq (vnet_buffer (b)->tcp.seq_end, tc->rcv_nxt))
- goto done;
+ {
+ /* Ack retransmissions since we may not have any data to send */
+ tcp_make_ack (tc, b);
+ *next0 = tcp_next_output (tc->c_is_ip4);
+ goto done;
+ }
/* Chop off the bytes in the past */
n_bytes_to_drop = tc->rcv_nxt - vnet_buffer (b)->tcp.seq_number;
* segments can be enqueued after fifo tail offset changes. */
error = tcp_session_enqueue_data (tc, b, n_data_bytes);
- if (n_data_bytes == 0)
- {
- *next0 = TCP_NEXT_DROP;
- goto done;
- }
-
/* Check if ACK can be delayed */
if (tcp_can_delack (tc))
{
}
th0 = tcp_buffer_hdr (b0);
- is_fin = (th0->flags & TCP_FLAG_FIN) != 0;
+ /* N.B. buffer is rewritten if segment is ooo. Thus, th0 becomes a
+ * dangling reference. */
+ is_fin = tcp_is_fin (th0);
/* SYNs, FINs and data consume sequence numbers */
vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
/* 5: check the ACK field */
if (tcp_rcv_ack (tc0, b0, th0, &next0, &error0))
- {
- goto done;
- }
+ goto done;
/* 6: check the URG bit TODO */
/* 7: process the segment text */
-
- vlib_buffer_advance (b0, vnet_buffer (b0)->tcp.data_offset);
- error0 = tcp_segment_rcv (tm, tc0, b0,
- vnet_buffer (b0)->tcp.data_len, &next0);
-
- /* N.B. buffer is rewritten if segment is ooo. Thus, th0 becomes a
- * dangling reference. */
+ if (vnet_buffer (b0)->tcp.data_len)
+ error0 = tcp_segment_rcv (tm, tc0, b0, &next0);
/* 8: check the FIN bit */
- if (is_fin)
+ if (PREDICT_FALSE (is_fin))
{
/* Enter CLOSE-WAIT and notify session. Don't send ACK, instead
* wait for session to call close. To avoid lingering
* in CLOSE-WAIT, set timer (reuse WAITCLOSE). */
tc0->state = TCP_STATE_CLOSE_WAIT;
TCP_EVT_DBG (TCP_EVT_FIN_RCVD, tc0);
+ tc0->rcv_nxt += (vnet_buffer (b0)->tcp.data_len == 0);
stream_session_disconnect_notify (&tc0->connection);
tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
}
seq0 = vnet_buffer (b0)->tcp.seq_number;
tcp0 = tcp_buffer_hdr (b0);
+ if (!tc0)
+ {
+ ip4_header_t *ip40 = vlib_buffer_get_current (b0);
+ tcp0 = ip4_next_header (ip40);
+ tc0 =
+ (tcp_connection_t *)
+ stream_session_lookup_transport_wt4 (&ip40->dst_address,
+ &ip40->src_address,
+ tcp0->dst_port,
+ tcp0->src_port,
+ SESSION_TYPE_IP4_TCP,
+ my_thread_index);
+ ASSERT (0);
+ goto drop;
+ }
if (PREDICT_FALSE
(!tcp_ack (tcp0) && !tcp_rst (tcp0) && !tcp_syn (tcp0)))
goto drop;
if (ack0 <= tc0->iss || ack0 > tc0->snd_nxt)
{
if (!tcp_rst (tcp0))
- tcp_send_reset (b0, is_ip4);
-
+ tcp_send_reset (tc0, b0, is_ip4);
goto drop;
}
/* If ACK is acceptable, signal client that peer is not
* willing to accept connection and drop connection*/
if (tcp_ack (tcp0))
- {
- stream_session_connect_notify (&tc0->connection, sst,
- 1 /* fail */ );
- tcp_connection_cleanup (tc0);
- }
+ tcp_connection_reset (tc0);
goto drop;
}
if (!tcp_syn (tcp0))
goto drop;
+ /* Parse options */
+ if (tcp_options_parse (tcp0, &tc0->rcv_opts))
+ goto drop;
+
/* Stop connection establishment and retransmit timers */
tcp_timer_reset (tc0, TCP_TIMER_ESTABLISH);
tcp_timer_reset (tc0, TCP_TIMER_RETRANSMIT_SYN);
* current thread pool. */
pool_get (tm->connections[my_thread_index], new_tc0);
clib_memcpy (new_tc0, tc0, sizeof (*new_tc0));
-
- new_tc0->c_thread_index = my_thread_index;
new_tc0->c_c_index = new_tc0 - tm->connections[my_thread_index];
-
- /* Cleanup half-open connection XXX lock */
- pool_put (tm->half_open_connections, tc0);
-
+ new_tc0->c_thread_index = my_thread_index;
new_tc0->rcv_nxt = vnet_buffer (b0)->tcp.seq_end;
new_tc0->irs = seq0;
-
- /* Parse options */
- if (tcp_options_parse (tcp0, &new_tc0->rcv_opts))
- goto drop;
+ tcp_half_open_connection_del (tc0);
if (tcp_opts_tstamp (&new_tc0->rcv_opts))
{
tcp_connection_init_vars (new_tc0);
/* SYN-ACK: See if we can switch to ESTABLISHED state */
- if (tcp_ack (tcp0))
+ if (PREDICT_TRUE (tcp_ack (tcp0)))
{
/* Our SYN is ACKed: we have iss < ack = snd_una */
0))
{
tcp_connection_cleanup (new_tc0);
- tcp_send_reset (b0, is_ip4);
+ tcp_send_reset (tc0, b0, is_ip4);
goto drop;
}
/* Update rtt with the syn-ack sample */
new_tc0->bytes_acked = 1;
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->connection, sst, 0))
{
tcp_connection_cleanup (new_tc0);
- tcp_send_reset (b0, is_ip4);
+ tcp_send_reset (tc0, b0, is_ip4);
+ TCP_EVT_DBG (TCP_EVT_RST_SENT, tc0);
goto drop;
}
tc0->rtt_ts = 0;
-
tcp_make_synack (new_tc0, b0);
next0 = tcp_next_output (is_ip4);
}
/* Read data, if any */
- if (vnet_buffer (b0)->tcp.data_len)
+ if (PREDICT_FALSE (vnet_buffer (b0)->tcp.data_len))
{
- vlib_buffer_advance (b0, vnet_buffer (b0)->tcp.data_offset);
- error0 = tcp_segment_rcv (tm, new_tc0, b0,
- vnet_buffer (b0)->tcp.data_len,
- &next0);
+ ASSERT (0);
+ error0 = tcp_segment_rcv (tm, new_tc0, b0, &next0);
if (error0 == TCP_ERROR_PURE_ACK)
error0 = TCP_ERROR_SYN_ACKS_RCVD;
}
/* *INDENT-ON* */
VLIB_NODE_FUNCTION_MULTIARCH (tcp6_syn_sent_node, tcp6_syn_sent_rcv);
+
/**
* Handles reception for all states except LISTEN, SYN-SENT and ESTABLISHED
* as per RFC793 p. 64
*/
if (!tcp_rcv_ack_is_acceptable (tc0, b0))
{
- tcp_send_reset (b0, is_ip4);
+ tcp_send_reset (tc0, b0, is_ip4);
goto drop;
}
{
ASSERT (tcp_fin (tcp0));
tc0->state = TCP_STATE_FIN_WAIT_2;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
+
/* Stop all timers, 2MSL will be set lower */
tcp_connection_timers_reset (tc0);
}
/* XXX test that send queue empty */
tc0->state = TCP_STATE_TIME_WAIT;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
goto drop;
break;
}
tc0->state = TCP_STATE_CLOSED;
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
/* Don't delete the connection/session yet. Instead, wait a
* reasonable amount of time until the pipes are cleared. In
case TCP_STATE_ESTABLISHED:
case TCP_STATE_FIN_WAIT_1:
case TCP_STATE_FIN_WAIT_2:
- vlib_buffer_advance (b0, vnet_buffer (b0)->tcp.data_offset);
- error0 = tcp_segment_rcv (tm, tc0, b0,
- vnet_buffer (b0)->tcp.data_len,
- &next0);
+ if (vnet_buffer (b0)->tcp.data_len)
+ error0 = tcp_segment_rcv (tm, tc0, b0, &next0);
break;
case TCP_STATE_CLOSE_WAIT:
case TCP_STATE_CLOSING:
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:
tc0->state = TCP_STATE_TIME_WAIT;
tcp_connection_timers_reset (tc0);
tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+ TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0);
break;
case TCP_STATE_FIN_WAIT_2:
/* Got FIN, send ACK! */
tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_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 2 MSL time-wait
{
u32 n_left_from, next_index, *from, *to_next;
u32 my_thread_index = vm->thread_index;
- tcp_main_t *tm = vnet_get_tcp_main ();
u8 sst = is_ip4 ? SESSION_TYPE_IP4_TCP : SESSION_TYPE_IP6_TCP;
from = vlib_frame_vector_args (from_frame);
/* 3. check for a SYN (did that already) */
/* Create child session and send SYN-ACK */
- pool_get (tm->connections[my_thread_index], child0);
- memset (child0, 0, sizeof (*child0));
-
- child0->c_c_index = child0 - tm->connections[my_thread_index];
+ child0 = tcp_connection_new (my_thread_index);
child0->c_lcl_port = lc0->c_lcl_port;
child0->c_rmt_port = th0->src_port;
child0->c_is_ip4 = is_ip4;
- child0->c_thread_index = my_thread_index;
child0->state = TCP_STATE_SYN_RCVD;
if (is_ip4)
child0->snd_wl2 = vnet_buffer (b0)->tcp.ack_number;
tcp_connection_init_vars (child0);
-
TCP_EVT_DBG (TCP_EVT_SYN_RCVD, child0);
/* Reuse buffer to make syn-ack and send */
#define filter_flags (TCP_FLAG_SYN|TCP_FLAG_ACK|TCP_FLAG_RST|TCP_FLAG_FIN)
+static u8
+tcp_lookup_is_valid (tcp_connection_t * tc, tcp_header_t * hdr)
+{
+ transport_connection_t *tmp;
+ if (!tc)
+ return 1;
+
+ u8 is_valid = (tc->c_lcl_port == hdr->dst_port
+ && (tc->state == TCP_STATE_LISTEN
+ || tc->c_rmt_port == hdr->src_port));
+
+ if (!is_valid)
+ {
+ if ((tmp = stream_session_lookup_half_open (&tc->connection)))
+ {
+ if (tmp->lcl_port == hdr->dst_port
+ && tmp->rmt_port == hdr->src_port)
+ {
+ clib_warning ("half-open is valid!");
+ }
+ }
+ }
+ return is_valid;
+}
+
always_inline uword
tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vlib_frame_t * from_frame, int is_ip4)
n_data_bytes0 = clib_net_to_host_u16 (ip40->length)
- n_advance_bytes0;
- /* lookup session */
tc0 =
(tcp_connection_t *)
- stream_session_lookup_transport4 (&ip40->dst_address,
- &ip40->src_address,
- tcp0->dst_port,
- tcp0->src_port,
- SESSION_TYPE_IP4_TCP,
- my_thread_index);
+ stream_session_lookup_transport_wt4 (&ip40->dst_address,
+ &ip40->src_address,
+ tcp0->dst_port,
+ tcp0->src_port,
+ SESSION_TYPE_IP4_TCP,
+ my_thread_index);
+ ASSERT (tcp_lookup_is_valid (tc0, tcp0));
}
else
{
tc0 =
(tcp_connection_t *)
- stream_session_lookup_transport6 (&ip60->src_address,
- &ip60->dst_address,
- tcp0->src_port,
- tcp0->dst_port,
- SESSION_TYPE_IP6_TCP,
- my_thread_index);
+ stream_session_lookup_transport_wt6 (&ip60->dst_address,
+ &ip60->src_address,
+ tcp0->dst_port,
+ tcp0->src_port,
+ SESSION_TYPE_IP6_TCP,
+ my_thread_index);
+ ASSERT (tcp_lookup_is_valid (tc0, tcp0));
}
/* Length check */
_(ESTABLISHED, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_ESTABLISHED,
TCP_ERROR_NONE);
_(ESTABLISHED, TCP_FLAG_SYN, TCP_INPUT_NEXT_ESTABLISHED, TCP_ERROR_NONE);
+ _(ESTABLISHED, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_ESTABLISHED,
+ TCP_ERROR_NONE);
/* ACK or FIN-ACK to our FIN */
_(FIN_WAIT_1, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
_(FIN_WAIT_1, TCP_FLAG_ACK | TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS,
_(TIME_WAIT, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
_(TIME_WAIT, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
TCP_ERROR_NONE);
+ _(TIME_WAIT, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
_(CLOSED, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_CONNECTION_CLOSED);
_(CLOSED, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_CONNECTION_CLOSED);
_(CLOSED, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET,