X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Ftcp%2Ftcp_input.c;h=4e3987eeaed7abd94b3fedd5250e9408c7e87c82;hb=b7b929931a07fbb27b43d5cd105f366c3e29807e;hp=289a6d66e3e327649e6080875e5b8d493da217b3;hpb=4df3871a98e4f06c23e8466eef303adcd1c83953;p=vpp.git diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c index 289a6d66e3e..4e3987eeaed 100644 --- a/src/vnet/tcp/tcp_input.c +++ b/src/vnet/tcp/tcp_input.c @@ -123,7 +123,7 @@ tcp_segment_in_rcv_wnd (tcp_connection_t * tc, u32 seq, u32 end_seq) * @param to TCP options data structure to be populated * @return -1 if parsing failed */ -int +static int tcp_options_parse (tcp_header_t * th, tcp_options_t * to) { const u8 *data; @@ -135,7 +135,8 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to) data = (const u8 *) (th + 1); /* Zero out all flags but those set in SYN */ - to->flags &= (TCP_OPTS_FLAG_SACK_PERMITTED | TCP_OPTS_FLAG_WSCALE); + to->flags &= (TCP_OPTS_FLAG_SACK_PERMITTED | TCP_OPTS_FLAG_WSCALE + | TCP_OPTS_FLAG_SACK); for (; opts_len > 0; opts_len -= opt_len, data += opt_len) { @@ -453,7 +454,11 @@ tcp_update_rtt (tcp_connection_t * tc, u32 ack) /* Karn's rule, part 1. Don't use retransmitted segments to estimate * RTT because they're ambiguous. */ if (tcp_in_cong_recovery (tc) || tc->sack_sb.sacked_bytes) - goto done; + { + if (tcp_in_recovery (tc)) + return 0; + goto done; + } if (tc->rtt_ts && seq_geq (ack, tc->rtt_seq)) { @@ -479,7 +484,7 @@ done: tc->rtt_ts = 0; /* If we got here something must've been ACKed so make sure boff is 0, - * even if mrrt is not valid since we update the rto lower */ + * even if mrtt is not valid since we update the rto lower */ tc->rto_boff = 0; tcp_update_rto (tc); @@ -534,7 +539,60 @@ tcp_ack_is_cc_event (tcp_connection_t * tc, vlib_buffer_t * b, return ((*is_dack || tcp_in_cong_recovery (tc)) && !tcp_is_lost_fin (tc)); } -void +static u32 +scoreboard_hole_index (sack_scoreboard_t * sb, sack_scoreboard_hole_t * hole) +{ + ASSERT (!pool_is_free_index (sb->holes, hole - sb->holes)); + return hole - sb->holes; +} + +static u32 +scoreboard_hole_bytes (sack_scoreboard_hole_t * hole) +{ + return hole->end - hole->start; +} + +sack_scoreboard_hole_t * +scoreboard_get_hole (sack_scoreboard_t * sb, u32 index) +{ + if (index != TCP_INVALID_SACK_HOLE_INDEX) + return pool_elt_at_index (sb->holes, index); + return 0; +} + +sack_scoreboard_hole_t * +scoreboard_next_hole (sack_scoreboard_t * sb, sack_scoreboard_hole_t * hole) +{ + if (hole->next != TCP_INVALID_SACK_HOLE_INDEX) + return pool_elt_at_index (sb->holes, hole->next); + return 0; +} + +sack_scoreboard_hole_t * +scoreboard_prev_hole (sack_scoreboard_t * sb, sack_scoreboard_hole_t * hole) +{ + if (hole->prev != TCP_INVALID_SACK_HOLE_INDEX) + return pool_elt_at_index (sb->holes, hole->prev); + return 0; +} + +sack_scoreboard_hole_t * +scoreboard_first_hole (sack_scoreboard_t * sb) +{ + if (sb->head != TCP_INVALID_SACK_HOLE_INDEX) + return pool_elt_at_index (sb->holes, sb->head); + return 0; +} + +sack_scoreboard_hole_t * +scoreboard_last_hole (sack_scoreboard_t * sb) +{ + if (sb->tail != TCP_INVALID_SACK_HOLE_INDEX) + return pool_elt_at_index (sb->holes, sb->tail); + return 0; +} + +static void scoreboard_remove_hole (sack_scoreboard_t * sb, sack_scoreboard_hole_t * hole) { sack_scoreboard_hole_t *next, *prev; @@ -564,12 +622,12 @@ scoreboard_remove_hole (sack_scoreboard_t * sb, sack_scoreboard_hole_t * hole) /* Poison the entry */ if (CLIB_DEBUG > 0) - memset (hole, 0xfe, sizeof (*hole)); + clib_memset (hole, 0xfe, sizeof (*hole)); pool_put (sb->holes, hole); } -sack_scoreboard_hole_t * +static sack_scoreboard_hole_t * scoreboard_insert_hole (sack_scoreboard_t * sb, u32 prev_index, u32 start, u32 end) { @@ -577,7 +635,7 @@ scoreboard_insert_hole (sack_scoreboard_t * sb, u32 prev_index, u32 hole_index; pool_get (sb->holes, hole); - memset (hole, 0, sizeof (*hole)); + clib_memset (hole, 0, sizeof (*hole)); hole->start = start; hole->end = end; @@ -606,42 +664,48 @@ scoreboard_insert_hole (sack_scoreboard_t * sb, u32 prev_index, return hole; } -void +static void scoreboard_update_bytes (tcp_connection_t * tc, sack_scoreboard_t * sb) { - sack_scoreboard_hole_t *hole, *prev; + sack_scoreboard_hole_t *left, *right; u32 bytes = 0, blks = 0; sb->lost_bytes = 0; sb->sacked_bytes = 0; - hole = scoreboard_last_hole (sb); - if (!hole) + left = scoreboard_last_hole (sb); + if (!left) return; - if (seq_gt (sb->high_sacked, hole->end)) + if (seq_gt (sb->high_sacked, left->end)) { - bytes = sb->high_sacked - hole->end; + bytes = sb->high_sacked - left->end; blks = 1; } - while ((prev = scoreboard_prev_hole (sb, hole)) - && (bytes < (TCP_DUPACK_THRESHOLD - 1) * tc->snd_mss - && blks < TCP_DUPACK_THRESHOLD)) + while ((right = left) + && bytes < (TCP_DUPACK_THRESHOLD - 1) * tc->snd_mss + && blks < TCP_DUPACK_THRESHOLD + /* left not updated if above conditions fail */ + && (left = scoreboard_prev_hole (sb, right))) { - bytes += hole->start - prev->end; + bytes += right->start - left->end; blks++; - hole = prev; } - while (hole) + /* left is first lost */ + if (left) { - sb->lost_bytes += scoreboard_hole_bytes (hole); - hole->is_lost = 1; - prev = hole; - hole = scoreboard_prev_hole (sb, hole); - if (hole) - bytes += prev->start - hole->end; + do + { + sb->lost_bytes += scoreboard_hole_bytes (right); + left->is_lost = 1; + left = scoreboard_prev_hole (sb, right); + if (left) + bytes += right->start - left->end; + } + while ((right = left)); } + sb->sacked_bytes = bytes; } @@ -705,7 +769,7 @@ scoreboard_next_rxt_hole (sack_scoreboard_t * sb, return hole; } -void +static void scoreboard_init_high_rxt (sack_scoreboard_t * sb, u32 seq) { sack_scoreboard_hole_t *hole; @@ -718,18 +782,47 @@ scoreboard_init_high_rxt (sack_scoreboard_t * sb, u32 seq) sb->high_rxt = seq; } +void +scoreboard_init (sack_scoreboard_t * sb) +{ + sb->head = TCP_INVALID_SACK_HOLE_INDEX; + sb->tail = TCP_INVALID_SACK_HOLE_INDEX; + sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX; +} + +void +scoreboard_clear (sack_scoreboard_t * sb) +{ + sack_scoreboard_hole_t *hole; + while ((hole = scoreboard_first_hole (sb))) + { + scoreboard_remove_hole (sb, hole); + } + ASSERT (sb->head == sb->tail && sb->head == TCP_INVALID_SACK_HOLE_INDEX); + ASSERT (pool_elts (sb->holes) == 0); + sb->sacked_bytes = 0; + sb->last_sacked_bytes = 0; + sb->last_bytes_delivered = 0; + sb->snd_una_adv = 0; + sb->high_sacked = 0; + sb->high_rxt = 0; + sb->lost_bytes = 0; + sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX; +} + /** * Test that scoreboard is sane after recovery * * Returns 1 if scoreboard is empty or if first hole beyond * snd_una. */ -u8 +static 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)); + return (!hole || (seq_geq (hole->start, tc->snd_una) + && seq_lt (hole->end, tc->snd_una_max))); } void @@ -742,14 +835,15 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) int i, j; sb->last_sacked_bytes = 0; - sb->snd_una_adv = 0; - old_sacked_bytes = sb->sacked_bytes; sb->last_bytes_delivered = 0; + sb->snd_una_adv = 0; if (!tcp_opts_sack (&tc->rcv_opts) && sb->head == TCP_INVALID_SACK_HOLE_INDEX) return; + old_sacked_bytes = sb->sacked_bytes; + /* Remove invalid blocks */ blk = tc->rcv_opts.sacks; while (blk < vec_end (tc->rcv_opts.sacks)) @@ -887,6 +981,14 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) } } + if (pool_elts (sb->holes) == 1) + { + hole = scoreboard_first_hole (sb); + if (hole->start == ack + sb->snd_una_adv + && hole->end == tc->snd_una_max) + scoreboard_remove_hole (sb, hole); + } + scoreboard_update_bytes (tc, sb); sb->last_sacked_bytes = sb->sacked_bytes - (old_sacked_bytes - sb->last_bytes_delivered); @@ -1000,7 +1102,7 @@ tcp_cc_is_spurious_retransmit (tcp_connection_t * tc) && timestamp_lt (tc->rcv_opts.tsecr, tc->snd_rxt_ts)); } -int +static int tcp_cc_recover (tcp_connection_t * tc) { ASSERT (tcp_in_cong_recovery (tc)); @@ -1158,7 +1260,10 @@ partial_ack: * Legitimate ACK. 1) See if we can exit recovery */ /* XXX limit this only to first partial ack? */ - tcp_retransmit_timer_update (tc); + if (seq_lt (tc->snd_una, tc->snd_congestion)) + tcp_retransmit_timer_force_update (tc); + else + tcp_retransmit_timer_update (tc); if (seq_geq (tc->snd_una, tc->snd_congestion)) { @@ -1190,6 +1295,7 @@ partial_ack: { tc->cc_algo->rcv_ack (tc); tc->tsecr_last_ack = tc->rcv_opts.tsecr; + transport_add_tx_event (&tc->connection); return; } @@ -1222,13 +1328,6 @@ partial_ack: tcp_fast_retransmit (tc); } -void -tcp_cc_init (tcp_connection_t * tc) -{ - tc->cc_algo = tcp_cc_algo_get (TCP_CC_NEWRENO); - tc->cc_algo->init (tc); -} - /** * Process incoming ACK */ @@ -1247,10 +1346,11 @@ tcp_rcv_ack (tcp_connection_t * tc, vlib_buffer_t * b, /* When we entered recovery, we reset snd_nxt to snd_una. Seems peer * still has the data so accept the ack */ if (tcp_in_recovery (tc) - && seq_leq (vnet_buffer (b)->tcp.ack_number, tc->snd_congestion) - && seq_geq (vnet_buffer (b)->tcp.ack_number, tc->snd_una)) + && seq_leq (vnet_buffer (b)->tcp.ack_number, tc->snd_congestion)) { - tc->snd_una_max = tc->snd_nxt = vnet_buffer (b)->tcp.ack_number; + tc->snd_nxt = vnet_buffer (b)->tcp.ack_number; + if (seq_gt (tc->snd_nxt, tc->snd_una_max)) + tc->snd_una_max = tc->snd_nxt; goto process_ack; } @@ -1317,7 +1417,9 @@ process_ack: if (!tcp_in_cong_recovery (tc)) return 0; *error = TCP_ERROR_ACK_DUP; - return vnet_buffer (b)->tcp.data_len ? 0 : -1; + if (vnet_buffer (b)->tcp.data_len || tcp_is_fin (th)) + return 0; + return -1; } /* @@ -1415,7 +1517,7 @@ tcp_sack_list_bytes (tcp_connection_t * tc) } /** Enqueue data for delivery to application */ -always_inline int +static int tcp_session_enqueue_data (tcp_connection_t * tc, vlib_buffer_t * b, u16 data_len) { @@ -1470,7 +1572,7 @@ tcp_session_enqueue_data (tcp_connection_t * tc, vlib_buffer_t * b, } /** Enqueue out-of-order data */ -always_inline int +static int tcp_session_enqueue_ooo (tcp_connection_t * tc, vlib_buffer_t * b, u16 data_len) { @@ -1648,7 +1750,7 @@ typedef struct tcp_connection_t tcp_connection; } tcp_rx_trace_t; -u8 * +static u8 * format_tcp_rx_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); @@ -1664,7 +1766,7 @@ format_tcp_rx_trace (u8 * s, va_list * args) return s; } -u8 * +static u8 * format_tcp_rx_trace_short (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); @@ -2198,8 +2300,8 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (tcp_opts_wscale (&new_tc0->rcv_opts)) new_tc0->snd_wscale = new_tc0->rcv_opts.wscale; - /* RFC1323: SYN and SYN-ACK wnd not scaled */ - new_tc0->snd_wnd = clib_net_to_host_u16 (tcp0->window); + 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; @@ -2588,9 +2690,12 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, tc0->state = TCP_STATE_CLOSED; TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc0); - /* Delete the connection/session since the pipes should be - * clear by now */ - tcp_connection_del (tc0); + + /* 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); goto drop; @@ -2651,10 +2756,10 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, case TCP_STATE_SYN_RCVD: /* Send FIN-ACK notify app and enter CLOSE-WAIT */ tcp_connection_timers_reset (tc0); - tcp_retransmit_timer_set (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; @@ -3357,7 +3462,11 @@ do { \ /* ACK for for a SYN-ACK -> tcp-rcv-process. */ _(SYN_RCVD, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE); _(SYN_RCVD, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE); + _(SYN_RCVD, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, + TCP_ERROR_NONE); _(SYN_RCVD, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE); + _(SYN_RCVD, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, + TCP_ERROR_NONE); /* SYN-ACK for a SYN */ _(SYN_SENT, TCP_FLAG_SYN | TCP_FLAG_ACK, TCP_INPUT_NEXT_SYN_SENT, TCP_ERROR_NONE); @@ -3398,6 +3507,7 @@ do { \ _(LAST_ACK, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE); _(LAST_ACK, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE); + _(LAST_ACK, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE); _(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); @@ -3410,7 +3520,7 @@ do { \ #undef _ } -clib_error_t * +static clib_error_t * tcp_input_init (vlib_main_t * vm) { clib_error_t *error = 0;