X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Ftcp%2Ftcp_input.c;h=4695fbb1161b0140487336067fcfd57eb87f256d;hb=26dd6de91b4d36ac04154c7eb6339684db6684a0;hp=c5dfb3f7372842906c5b0c21709a744bc109ee7d;hpb=a27a46eaebee7b1d84a6ce998d9c92048b0654b6;p=vpp.git diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c old mode 100644 new mode 100755 index c5dfb3f7372..4695fbb1161 --- a/src/vnet/tcp/tcp_input.c +++ b/src/vnet/tcp/tcp_input.c @@ -79,9 +79,6 @@ typedef enum _tcp_state_next #define tcp_next_drop(is_ip4) (is_ip4 ? TCP_NEXT_DROP4 \ : TCP_NEXT_DROP6) -vlib_node_registration_t tcp4_established_node; -vlib_node_registration_t tcp6_established_node; - /** * Validate segment sequence number. As per RFC793: * @@ -320,7 +317,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0, * SEG.TSval */ else if (!tcp_rst (th0)) { - tcp_program_ack (wrk, tc0); + tcp_program_ack (tc0); TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc0, vnet_buffer (b0)->tcp); goto error; } @@ -343,7 +340,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0, } else { - tcp_program_ack (wrk, tc0); + tcp_program_ack (tc0); TCP_EVT_DBG (TCP_EVT_SYNACK_RCVD, tc0); *error0 = TCP_ERROR_SYN_ACKS_RCVD; } @@ -352,7 +349,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0, /* If our window is 0 and the packet is in sequence, let it pass * through for ack processing. It should be dropped later. */ - if (tc0->rcv_wnd == 0 + if (tc0->rcv_wnd < tc0->snd_mss && tc0->rcv_nxt == vnet_buffer (b0)->tcp.seq_number) goto check_reset; @@ -371,7 +368,7 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0, /* If not RST, send dup ack */ if (!tcp_rst (th0)) { - tcp_program_dupack (wrk, tc0); + tcp_program_dupack (tc0); TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc0, vnet_buffer (b0)->tcp); } goto error; @@ -393,8 +390,9 @@ tcp_segment_validate (tcp_worker_ctx_t * wrk, tcp_connection_t * tc0, /* 4th: check the SYN bit (in window) */ if (PREDICT_FALSE (tcp_syn (th0))) { + /* As per RFC5961 send challenge ack instead of reset */ + tcp_program_ack (tc0); *error0 = TCP_ERROR_SPURIOUS_SYN; - tcp_send_reset (tc0); goto error; } @@ -408,11 +406,27 @@ error: } always_inline int -tcp_rcv_ack_is_acceptable (tcp_connection_t * tc0, vlib_buffer_t * tb0) +tcp_rcv_ack_no_cc (tcp_connection_t * tc, vlib_buffer_t * b, u32 * error) { /* SND.UNA =< SEG.ACK =< SND.NXT */ - return (seq_leq (tc0->snd_una, vnet_buffer (tb0)->tcp.ack_number) - && seq_leq (vnet_buffer (tb0)->tcp.ack_number, tc0->snd_nxt)); + if (!(seq_leq (tc->snd_una, vnet_buffer (b)->tcp.ack_number) + && seq_leq (vnet_buffer (b)->tcp.ack_number, tc->snd_nxt))) + { + if (seq_leq (vnet_buffer (b)->tcp.ack_number, tc->snd_una_max) + && seq_gt (vnet_buffer (b)->tcp.ack_number, tc->snd_una)) + { + tc->snd_nxt = vnet_buffer (b)->tcp.ack_number; + goto acceptable; + } + *error = TCP_ERROR_ACK_INVALID; + return -1; + } + +acceptable: + tc->bytes_acked = vnet_buffer (b)->tcp.ack_number - tc->snd_una; + tc->snd_una = vnet_buffer (b)->tcp.ack_number; + *error = TCP_ERROR_ACK_OK; + return 0; } /** @@ -447,12 +461,14 @@ tcp_estimate_rtt (tcp_connection_t * tc, u32 mrtt) } } +#ifndef CLIB_MARCH_VARIANT void tcp_update_rto (tcp_connection_t * tc) { tc->rto = clib_min (tc->srtt + (tc->rttvar << 2), TCP_RTO_MAX); tc->rto = clib_max (tc->rto, TCP_RTO_MIN); } +#endif /* CLIB_MARCH_VARIANT */ /** * Update RTT estimate and RTO timer @@ -492,7 +508,7 @@ tcp_update_rtt (tcp_connection_t * tc, u32 ack) * seq_lt (tc->snd_una, ack). This is a condition for calling update_rtt */ else if (tcp_opts_tstamp (&tc->rcv_opts) && tc->rcv_opts.tsecr) { - u32 now = tcp_time_now_w_thread (tc->c_thread_index); + u32 now = tcp_tstamp (tc); mrtt = clib_max (now - tc->rcv_opts.tsecr, 1); } @@ -564,7 +580,7 @@ tcp_handle_postponed_dequeues (tcp_worker_ctx_t * wrk) continue; /* Dequeue the newly ACKed bytes */ - stream_session_dequeue_drop (&tc->connection, tc->burst_acked); + session_tx_fifo_dequeue_drop (&tc->connection, tc->burst_acked); tc->burst_acked = 0; tcp_validate_txf_size (tc, tc->snd_una_max - tc->snd_una); @@ -605,7 +621,7 @@ tcp_ack_is_dupack (tcp_connection_t * tc, vlib_buffer_t * b, u32 prev_snd_wnd, u32 prev_snd_una) { return ((vnet_buffer (b)->tcp.ack_number == prev_snd_una) - && seq_gt (tc->snd_una_max, tc->snd_una) + && seq_gt (tc->snd_nxt, tc->snd_una) && (vnet_buffer (b)->tcp.seq_end == vnet_buffer (b)->tcp.seq_number) && (prev_snd_wnd == tc->snd_wnd)); } @@ -625,6 +641,7 @@ 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)); } +#ifndef CLIB_MARCH_VARIANT static u32 scoreboard_hole_index (sack_scoreboard_t * sb, sack_scoreboard_hole_t * hole) { @@ -749,13 +766,16 @@ scoreboard_insert_hole (sack_scoreboard_t * sb, u32 prev_index, return hole; } +#endif /* CLIB_MARCH_VARIANT */ +#ifndef CLIB_MARCH_VARIANT static void scoreboard_update_bytes (tcp_connection_t * tc, sack_scoreboard_t * sb) { sack_scoreboard_hole_t *left, *right; u32 bytes = 0, blks = 0; + sb->last_lost_bytes = 0; sb->lost_bytes = 0; sb->sacked_bytes = 0; left = scoreboard_last_hole (sb); @@ -784,6 +804,7 @@ scoreboard_update_bytes (tcp_connection_t * tc, sack_scoreboard_t * sb) do { sb->lost_bytes += scoreboard_hole_bytes (right); + sb->last_lost_bytes += left->is_lost ? 0 : left->end - left->start; left->is_lost = 1; left = scoreboard_prev_hole (sb, right); if (left) @@ -853,6 +874,7 @@ scoreboard_next_rxt_hole (sack_scoreboard_t * sb, return hole; } +#endif /* CLIB_MARCH_VARIANT */ static void scoreboard_init_high_rxt (sack_scoreboard_t * sb, u32 snd_una) @@ -868,6 +890,7 @@ scoreboard_init_high_rxt (sack_scoreboard_t * sb, u32 snd_una) sb->rescue_rxt = snd_una - 1; } +#ifndef CLIB_MARCH_VARIANT void scoreboard_init (sack_scoreboard_t * sb) { @@ -893,8 +916,10 @@ scoreboard_clear (sack_scoreboard_t * sb) sb->high_sacked = 0; sb->high_rxt = 0; sb->lost_bytes = 0; + sb->last_lost_bytes = 0; sb->cur_rxt_hole = TCP_INVALID_SACK_HOLE_INDEX; } +#endif /* CLIB_MARCH_VARIANT */ /** * Test that scoreboard is sane after recovery @@ -908,16 +933,18 @@ 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) - && seq_lt (hole->end, tc->snd_una_max))); + && seq_lt (hole->end, tc->snd_nxt))); } +#ifndef CLIB_MARCH_VARIANT + void tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) { - sack_scoreboard_t *sb = &tc->sack_sb; - sack_block_t *blk, tmp; sack_scoreboard_hole_t *hole, *next_hole, *last_hole; u32 blk_index = 0, old_sacked_bytes, hole_index; + sack_scoreboard_t *sb = &tc->sack_sb; + sack_block_t *blk, tmp; int i, j; sb->last_sacked_bytes = 0; @@ -937,8 +964,8 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) if (seq_lt (blk->start, blk->end) && seq_gt (blk->start, tc->snd_una) && seq_gt (blk->start, ack) - && seq_lt (blk->start, tc->snd_una_max) - && seq_leq (blk->end, tc->snd_una_max)) + && seq_lt (blk->start, tc->snd_nxt) + && seq_leq (blk->end, tc->snd_nxt)) { blk++; continue; @@ -973,7 +1000,7 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) { /* If no holes, insert the first that covers all outstanding bytes */ last_hole = scoreboard_insert_hole (sb, TCP_INVALID_SACK_HOLE_INDEX, - tc->snd_una, tc->snd_una_max); + tc->snd_una, tc->snd_nxt); sb->tail = scoreboard_hole_index (sb, last_hole); tmp = tc->rcv_opts.sacks[vec_len (tc->rcv_opts.sacks) - 1]; sb->high_sacked = tmp.end; @@ -984,17 +1011,17 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) * last hole end */ tmp = tc->rcv_opts.sacks[vec_len (tc->rcv_opts.sacks) - 1]; last_hole = scoreboard_last_hole (sb); - if (seq_gt (tc->snd_una_max, last_hole->end)) + if (seq_gt (tc->snd_nxt, last_hole->end)) { if (seq_geq (last_hole->start, sb->high_sacked)) { - last_hole->end = tc->snd_una_max; + last_hole->end = tc->snd_nxt; } /* New hole after high sacked block */ - else if (seq_lt (sb->high_sacked, tc->snd_una_max)) + else if (seq_lt (sb->high_sacked, tc->snd_nxt)) { scoreboard_insert_hole (sb, sb->tail, sb->high_sacked, - tc->snd_una_max); + tc->snd_nxt); } } /* Keep track of max byte sacked for when the last hole @@ -1032,7 +1059,6 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack) sb->last_bytes_delivered += sb->high_sacked - hole->end; } } - scoreboard_remove_hole (sb, hole); hole = next_hole; } @@ -1072,23 +1098,26 @@ 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) + if (hole->start == ack + sb->snd_una_adv && hole->end == tc->snd_nxt) scoreboard_remove_hole (sb, hole); } scoreboard_update_bytes (tc, sb); sb->last_sacked_bytes = sb->sacked_bytes - (old_sacked_bytes - sb->last_bytes_delivered); + ASSERT (sb->last_sacked_bytes <= sb->sacked_bytes || tcp_in_recovery (tc)); ASSERT (sb->sacked_bytes == 0 || tcp_in_recovery (tc) - || sb->sacked_bytes < tc->snd_una_max - seq_max (tc->snd_una, ack)); - ASSERT (sb->last_sacked_bytes + sb->lost_bytes <= tc->snd_una_max + || sb->sacked_bytes < tc->snd_nxt - seq_max (tc->snd_una, ack)); + ASSERT (sb->last_sacked_bytes + sb->lost_bytes <= tc->snd_nxt - seq_max (tc->snd_una, ack) || tcp_in_recovery (tc)); ASSERT (sb->head == TCP_INVALID_SACK_HOLE_INDEX || tcp_in_recovery (tc) || sb->holes[sb->head].start == ack + sb->snd_una_adv); + ASSERT (sb->last_lost_bytes <= sb->lost_bytes); + TCP_EVT_DBG (TCP_EVT_CC_SCOREBOARD, tc); } +#endif /* CLIB_MARCH_VARIANT */ /** * Try to update snd_wnd based on feedback received from peer. @@ -1128,6 +1157,7 @@ tcp_update_snd_wnd (tcp_connection_t * tc, u32 seq, u32 ack, u32 snd_wnd) } } +#ifndef CLIB_MARCH_VARIANT /** * Init loss recovery/fast recovery. * @@ -1138,7 +1168,7 @@ void tcp_cc_init_congestion (tcp_connection_t * tc) { tcp_fastrecovery_on (tc); - tc->snd_congestion = tc->snd_una_max; + tc->snd_congestion = tc->snd_nxt; tc->cwnd_acc_bytes = 0; tc->snd_rxt_bytes = 0; tc->prev_ssthresh = tc->ssthresh; @@ -1146,6 +1176,7 @@ tcp_cc_init_congestion (tcp_connection_t * tc) tc->cc_algo->congestion (tc); TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 4); } +#endif /* CLIB_MARCH_VARIANT */ static void tcp_cc_recovery_exit (tcp_connection_t * tc) @@ -1153,39 +1184,43 @@ tcp_cc_recovery_exit (tcp_connection_t * tc) tc->rto_boff = 0; tcp_update_rto (tc); tc->snd_rxt_ts = 0; - tc->snd_nxt = tc->snd_una_max; tc->rtt_ts = 0; tcp_recovery_off (tc); TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3); } +#ifndef CLIB_MARCH_VARIANT void -tcp_cc_fastrecovery_exit (tcp_connection_t * tc) +tcp_cc_fastrecovery_clear (tcp_connection_t * tc) { - tc->cc_algo->recovered (tc); tc->snd_rxt_bytes = 0; tc->rcv_dupacks = 0; - tc->snd_nxt = tc->snd_una_max; - tc->snd_rxt_bytes = 0; tc->rtt_ts = 0; tcp_fastrecovery_off (tc); tcp_fastrecovery_first_off (tc); + tc->flags &= ~TCP_CONN_FRXT_PENDING; TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 3); } +#endif /* CLIB_MARCH_VARIANT */ static void tcp_cc_congestion_undo (tcp_connection_t * tc) { tc->cwnd = tc->prev_cwnd; tc->ssthresh = tc->prev_ssthresh; - tc->snd_nxt = tc->snd_una_max; tc->rcv_dupacks = 0; if (tcp_in_recovery (tc)) - tcp_cc_recovery_exit (tc); + { + tcp_cc_recovery_exit (tc); + tc->snd_nxt = seq_max (tc->snd_nxt, tc->snd_congestion); + } else if (tcp_in_fastrecovery (tc)) - tcp_cc_fastrecovery_exit (tc); + { + tcp_cc_fastrecovery_clear (tc); + } + tcp_cc_undo_recovery (tc); ASSERT (tc->rto_boff == 0); TCP_EVT_DBG (TCP_EVT_CC_EVT, tc, 5); } @@ -1226,7 +1261,10 @@ tcp_cc_recover (tcp_connection_t * tc) if (tcp_in_recovery (tc)) tcp_cc_recovery_exit (tc); else if (tcp_in_fastrecovery (tc)) - tcp_cc_fastrecovery_exit (tc); + { + tcp_cc_recovered (tc); + tcp_cc_fastrecovery_clear (tc); + } ASSERT (tc->rto_boff == 0); ASSERT (!tcp_in_cong_recovery (tc)); @@ -1235,12 +1273,12 @@ tcp_cc_recover (tcp_connection_t * tc) } static void -tcp_cc_update (tcp_connection_t * tc, vlib_buffer_t * b) +tcp_cc_update (tcp_connection_t * tc, tcp_rate_sample_t * rs) { ASSERT (!tcp_in_cong_recovery (tc) || tcp_is_lost_fin (tc)); /* Congestion avoidance */ - tcp_cc_rcv_ack (tc); + tcp_cc_rcv_ack (tc, rs); /* If a cumulative ack, make sure dupacks is 0 */ tc->rcv_dupacks = 0; @@ -1268,80 +1306,12 @@ tcp_should_fastrecover (tcp_connection_t * tc) || tcp_should_fastrecover_sack (tc)); } -void -tcp_program_fastretransmit (tcp_worker_ctx_t * wrk, tcp_connection_t * tc) -{ - if (!(tc->flags & TCP_CONN_FRXT_PENDING)) - { - vec_add1 (wrk->pending_fast_rxt, tc->c_c_index); - tc->flags |= TCP_CONN_FRXT_PENDING; - } -} - -void -tcp_do_fastretransmits (tcp_worker_ctx_t * wrk) -{ - u32 *ongoing_fast_rxt, burst_bytes, sent_bytes, thread_index; - u32 max_burst_size, burst_size, n_segs = 0, n_segs_now; - tcp_connection_t *tc; - u64 last_cpu_time; - int i; - - if (vec_len (wrk->pending_fast_rxt) == 0 - && vec_len (wrk->postponed_fast_rxt) == 0) - return; - - thread_index = wrk->vm->thread_index; - last_cpu_time = wrk->vm->clib_time.last_cpu_time; - ongoing_fast_rxt = wrk->ongoing_fast_rxt; - vec_append (ongoing_fast_rxt, wrk->postponed_fast_rxt); - vec_append (ongoing_fast_rxt, wrk->pending_fast_rxt); - - _vec_len (wrk->postponed_fast_rxt) = 0; - _vec_len (wrk->pending_fast_rxt) = 0; - - max_burst_size = VLIB_FRAME_SIZE / vec_len (ongoing_fast_rxt); - max_burst_size = clib_max (max_burst_size, 1); - - for (i = 0; i < vec_len (ongoing_fast_rxt); i++) - { - if (n_segs >= VLIB_FRAME_SIZE) - { - vec_add1 (wrk->postponed_fast_rxt, ongoing_fast_rxt[i]); - continue; - } - - tc = tcp_connection_get (ongoing_fast_rxt[i], thread_index); - tc->flags &= ~TCP_CONN_FRXT_PENDING; - - if (!tcp_in_fastrecovery (tc)) - continue; - - burst_size = clib_min (max_burst_size, VLIB_FRAME_SIZE - n_segs); - burst_bytes = transport_connection_tx_pacer_burst (&tc->connection, - last_cpu_time); - burst_size = clib_min (burst_size, burst_bytes / tc->snd_mss); - if (!burst_size) - { - tcp_program_fastretransmit (wrk, tc); - continue; - } - - n_segs_now = tcp_fast_retransmit (wrk, tc, burst_size); - sent_bytes = clib_min (n_segs_now * tc->snd_mss, burst_bytes); - transport_connection_tx_pacer_update_bytes (&tc->connection, - sent_bytes); - n_segs += n_segs_now; - } - _vec_len (ongoing_fast_rxt) = 0; - wrk->ongoing_fast_rxt = ongoing_fast_rxt; -} - /** * One function to rule them all ... and in the darkness bind them */ static void -tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack) +tcp_cc_handle_event (tcp_connection_t * tc, tcp_rate_sample_t * rs, + u32 is_dack) { u32 rxt_delivered; @@ -1349,7 +1319,7 @@ tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack) { if (tc->bytes_acked) goto partial_ack; - tcp_program_fastretransmit (tcp_get_worker (tc->c_thread_index), tc); + tcp_program_fastretransmit (tc); return; } /* @@ -1359,8 +1329,7 @@ tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack) else if (is_dack && !tcp_in_recovery (tc)) { TCP_EVT_DBG (TCP_EVT_DUPACK_RCVD, tc, 1); - ASSERT (tc->snd_una != tc->snd_una_max - || tc->sack_sb.last_sacked_bytes); + ASSERT (tc->snd_una != tc->snd_nxt || tc->sack_sb.last_sacked_bytes); tc->rcv_dupacks++; @@ -1368,7 +1337,7 @@ tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack) if (tc->rcv_dupacks > TCP_DUPACK_THRESHOLD && !tc->bytes_acked) { ASSERT (tcp_in_fastrecovery (tc)); - tc->cc_algo->rcv_cong_ack (tc, TCP_CC_DUPACK); + tcp_cc_rcv_cong_ack (tc, TCP_CC_DUPACK, rs); return; } else if (tcp_should_fastrecover (tc)) @@ -1387,7 +1356,7 @@ tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack) } tcp_cc_init_congestion (tc); - tc->cc_algo->rcv_cong_ack (tc, TCP_CC_DUPACK); + tcp_cc_rcv_cong_ack (tc, TCP_CC_DUPACK, rs); if (tcp_opts_sack_permitted (&tc->rcv_opts)) { @@ -1406,14 +1375,13 @@ tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack) pacer_wnd = clib_max (0.1 * tc->cwnd, 2 * tc->snd_mss); tcp_connection_tx_pacer_reset (tc, pacer_wnd, 0 /* start bucket */ ); - tcp_program_fastretransmit (tcp_get_worker (tc->c_thread_index), - tc); + tcp_program_fastretransmit (tc); return; } else if (!tc->bytes_acked || (tc->bytes_acked && !tcp_in_cong_recovery (tc))) { - tc->cc_algo->rcv_cong_ack (tc, TCP_CC_DUPACK); + tcp_cc_rcv_cong_ack (tc, TCP_CC_DUPACK, rs); return; } else @@ -1467,10 +1435,8 @@ partial_ack: return; } - tc->snd_nxt = tc->snd_una_max; - /* Treat as congestion avoidance ack */ - tcp_cc_rcv_ack (tc); + tcp_cc_rcv_ack (tc, rs); return; } @@ -1488,7 +1454,7 @@ partial_ack: /* Post RTO timeout don't try anything fancy */ if (tcp_in_recovery (tc)) { - tcp_cc_rcv_ack (tc); + tcp_cc_rcv_ack (tc, rs); transport_add_tx_event (&tc->connection); return; } @@ -1519,20 +1485,18 @@ partial_ack: else { tcp_fastrecovery_first_on (tc); - /* Reuse last bytes delivered to track total bytes acked */ - tc->sack_sb.last_bytes_delivered += tc->bytes_acked; if (tc->snd_rxt_bytes > tc->bytes_acked) tc->snd_rxt_bytes -= tc->bytes_acked; else tc->snd_rxt_bytes = 0; } - tc->cc_algo->rcv_cong_ack (tc, TCP_CC_PARTIALACK); + tcp_cc_rcv_cong_ack (tc, TCP_CC_PARTIALACK, rs); /* * Since this was a partial ack, try to retransmit some more data */ - tcp_program_fastretransmit (tcp_get_worker (tc->c_thread_index), tc); + tcp_program_fastretransmit (tc); } /** @@ -1543,6 +1507,7 @@ tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b, tcp_header_t * th, u32 * error) { u32 prev_snd_wnd, prev_snd_una; + tcp_rate_sample_t rs = { 0 }; u8 is_dack; TCP_EVT_DBG (TCP_EVT_CC_STAT, tc); @@ -1550,33 +1515,19 @@ tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b, /* If the ACK acks something not yet sent (SEG.ACK > SND.NXT) */ if (PREDICT_FALSE (seq_gt (vnet_buffer (b)->tcp.ack_number, tc->snd_nxt))) { - /* 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)) + /* We've probably entered recovery and the peer still has some + * of the data we've sent. Update snd_nxt and accept the ack */ + if (seq_leq (vnet_buffer (b)->tcp.ack_number, tc->snd_una_max) + && seq_gt (vnet_buffer (b)->tcp.ack_number, tc->snd_una)) { 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; } - /* If we have outstanding data and this is within the window, accept it, - * probably retransmit has timed out. Otherwise ACK segment and then - * drop it */ - if (seq_gt (vnet_buffer (b)->tcp.ack_number, tc->snd_una_max)) - { - 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); - return -1; - } - - TCP_EVT_DBG (TCP_EVT_ACK_RCV_ERR, tc, 2, + *error = TCP_ERROR_ACK_FUTURE; + TCP_EVT_DBG (TCP_EVT_ACK_RCV_ERR, tc, 0, vnet_buffer (b)->tcp.ack_number); - - tc->snd_nxt = vnet_buffer (b)->tcp.ack_number; + return -1; } /* If old ACK, probably it's an old dupack */ @@ -1586,15 +1537,16 @@ tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b, TCP_EVT_DBG (TCP_EVT_ACK_RCV_ERR, tc, 1, vnet_buffer (b)->tcp.ack_number); if (tcp_in_fastrecovery (tc) && tc->rcv_dupacks == TCP_DUPACK_THRESHOLD) - tcp_cc_handle_event (tc, 1); + tcp_cc_handle_event (tc, 0, 1); /* Don't drop yet */ return 0; } +process_ack: + /* * Looks okay, process feedback */ -process_ack: if (tcp_opts_sack_permitted (&tc->rcv_opts)) tcp_rcv_sacks (tc, vnet_buffer (b)->tcp.ack_number); @@ -1613,6 +1565,9 @@ process_ack: tcp_update_rtt (tc, vnet_buffer (b)->tcp.ack_number); } + if (tc->flags & TCP_CONN_RATE_SAMPLE) + tcp_bt_sample_delivery_rate (tc, &rs); + TCP_EVT_DBG (TCP_EVT_ACK_RCVD, tc); /* @@ -1621,7 +1576,7 @@ process_ack: if (tcp_ack_is_cc_event (tc, b, prev_snd_wnd, prev_snd_una, &is_dack)) { - tcp_cc_handle_event (tc, is_dack); + tcp_cc_handle_event (tc, &rs, is_dack); if (!tcp_in_cong_recovery (tc)) { *error = TCP_ERROR_ACK_OK; @@ -1636,7 +1591,7 @@ process_ack: /* * Update congestion control (slow start/congestion avoidance) */ - tcp_cc_update (tc, b); + tcp_cc_update (tc, &rs); *error = TCP_ERROR_ACK_OK; return 0; } @@ -1676,9 +1631,13 @@ static void tcp_rcv_fin (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b, u32 * error) { + /* Reject out-of-order fins */ + if (vnet_buffer (b)->tcp.seq_end != tc->rcv_nxt) + return; + /* Account for the FIN and send ack */ tc->rcv_nxt += 1; - tcp_program_ack (wrk, tc); + tcp_program_ack (tc); /* Enter CLOSE-WAIT and notify session. To avoid lingering * in CLOSE-WAIT, set timer (reuse WAITCLOSE). */ tcp_connection_set_state (tc, TCP_STATE_CLOSE_WAIT); @@ -1688,6 +1647,7 @@ tcp_rcv_fin (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b, *error = TCP_ERROR_FIN_RCVD; } +#ifndef CLIB_MARCH_VARIANT static u8 tcp_sack_vector_is_sane (sack_block_t * sacks) { @@ -1714,7 +1674,7 @@ tcp_sack_vector_is_sane (sack_block_t * sacks) void tcp_update_sack_list (tcp_connection_t * tc, u32 start, u32 end) { - sack_block_t *new_list = 0, *block = 0; + sack_block_t *new_list = tc->snd_sacks_fl, *block = 0; int i; /* If the first segment is ooo add it to the list. Last write might've moved @@ -1746,19 +1706,14 @@ tcp_update_sack_list (tcp_connection_t * tc, u32 start, u32 end) /* Save to new SACK list if we have space. */ if (vec_len (new_list) < TCP_MAX_SACK_BLOCKS) - { - vec_add1 (new_list, tc->snd_sacks[i]); - } - else - { - clib_warning ("sack discarded"); - } + vec_add1 (new_list, tc->snd_sacks[i]); } ASSERT (vec_len (new_list) <= TCP_MAX_SACK_BLOCKS); /* Replace old vector with new one */ - vec_free (tc->snd_sacks); + vec_reset_length (tc->snd_sacks); + tc->snd_sacks_fl = tc->snd_sacks; tc->snd_sacks = new_list; /* Segments should not 'touch' */ @@ -1773,6 +1728,7 @@ tcp_sack_list_bytes (tcp_connection_t * tc) bytes += tc->snd_sacks[i].end - tc->snd_sacks[i].start; return bytes; } +#endif /* CLIB_MARCH_VARIANT */ /** Enqueue data for delivery to application */ static int @@ -1858,7 +1814,7 @@ tcp_session_enqueue_ooo (tcp_connection_t * tc, vlib_buffer_t * b, newest = svm_fifo_newest_ooo_segment (s0->rx_fifo); if (newest) { - offset = ooo_segment_offset (s0->rx_fifo, newest); + offset = ooo_segment_offset_prod (s0->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->rx_fifo, newest); @@ -1880,8 +1836,8 @@ tcp_can_delack (tcp_connection_t * tc) { /* Send ack if ... */ if (TCP_ALWAYS_ACK - /* just sent a rcv wnd 0 */ - || (tc->flags & TCP_CONN_SENT_RCV_WND0) != 0 + /* just sent a rcv wnd 0 + || (tc->flags & TCP_CONN_SENT_RCV_WND0) != 0 */ /* constrained to send ack */ || (tc->flags & TCP_CONN_SNDACK) != 0 /* we're almost out of tx wnd */ @@ -1945,7 +1901,7 @@ tcp_segment_rcv (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, * retransmissions since we may not have any data to send */ if (seq_leq (vnet_buffer (b)->tcp.seq_end, tc->rcv_nxt)) { - tcp_program_ack (wrk, tc); + tcp_program_ack (tc); error = TCP_ERROR_SEGMENT_OLD; goto done; } @@ -1965,7 +1921,7 @@ tcp_segment_rcv (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, /* RFC2581: Enqueue and send DUPACK for fast retransmit */ error = tcp_session_enqueue_ooo (tc, b, n_data_bytes); - tcp_program_dupack (wrk, tc); + tcp_program_dupack (tc); TCP_EVT_DBG (TCP_EVT_DUPACK_SENT, tc, vnet_buffer (b)->tcp); goto done; } @@ -1982,7 +1938,7 @@ in_order: goto done; } - tcp_program_ack (wrk, tc); + tcp_program_ack (tc); done: return error; @@ -2186,8 +2142,8 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node, tcp_inc_err_counter (err_counters, error0, 1); } - errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP, - thread_index); + errors = session_main_flush_enqueue_events (TRANSPORT_PROTO_TCP, + thread_index); err_counters[TCP_ERROR_MSG_QUEUE_FULL] = errors; tcp_store_err_counters (established, err_counters); tcp_handle_postponed_dequeues (wrk); @@ -2197,16 +2153,16 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node, return frame->n_vectors; } -static uword -tcp4_established (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp4_established_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_established_inline (vm, node, from_frame, 1 /* is_ip4 */ ); } -static uword -tcp6_established (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp6_established_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_established_inline (vm, node, from_frame, 0 /* is_ip4 */ ); } @@ -2214,7 +2170,6 @@ tcp6_established (vlib_main_t * vm, vlib_node_runtime_t * node, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp4_established_node) = { - .function = tcp4_established, .name = "tcp4-established", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -2231,12 +2186,9 @@ VLIB_REGISTER_NODE (tcp4_established_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp4_established_node, tcp4_established); - /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp6_established_node) = { - .function = tcp6_established, .name = "tcp6-established", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -2254,11 +2206,6 @@ VLIB_REGISTER_NODE (tcp6_established_node) = /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp6_established_node, tcp6_established); - -vlib_node_registration_t tcp4_syn_sent_node; -vlib_node_registration_t tcp6_syn_sent_node; - static u8 tcp_lookup_is_valid (tcp_connection_t * tc, tcp_header_t * hdr) { @@ -2522,9 +2469,9 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, * 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, my_thread_index, is_ip4); tcp_connection_cleanup (new_tc0); + error0 = TCP_ERROR_CREATE_SESSION_FAIL; goto drop; } @@ -2546,6 +2493,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, tcp_connection_cleanup (new_tc0); tcp_send_reset_w_pkt (tc0, b0, my_thread_index, is_ip4); TCP_EVT_DBG (TCP_EVT_RST_SENT, tc0); + error0 = TCP_ERROR_CREATE_SESSION_FAIL; goto drop; } @@ -2568,7 +2516,7 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } else { - tcp_program_ack (wrk, new_tc0); + tcp_program_ack (new_tc0); } drop: @@ -2583,24 +2531,24 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } } - errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP, - my_thread_index); + errors = session_main_flush_enqueue_events (TRANSPORT_PROTO_TCP, + my_thread_index); tcp_inc_counter (syn_sent, TCP_ERROR_MSG_QUEUE_FULL, errors); vlib_buffer_free (vm, first_buffer, from_frame->n_vectors); return from_frame->n_vectors; } -static uword -tcp4_syn_sent (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp4_syn_sent_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_syn_sent_inline (vm, node, from_frame, 1 /* is_ip4 */ ); } -static uword -tcp6_syn_sent_rcv (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp6_syn_sent_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_syn_sent_inline (vm, node, from_frame, 0 /* is_ip4 */ ); } @@ -2608,7 +2556,6 @@ tcp6_syn_sent_rcv (vlib_main_t * vm, vlib_node_runtime_t * node, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp4_syn_sent_node) = { - .function = tcp4_syn_sent, .name = "tcp4-syn-sent", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -2625,12 +2572,9 @@ VLIB_REGISTER_NODE (tcp4_syn_sent_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp4_syn_sent_node, tcp4_syn_sent); - /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp6_syn_sent_node) = { - .function = tcp6_syn_sent_rcv, .name = "tcp6-syn-sent", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -2647,11 +2591,6 @@ VLIB_REGISTER_NODE (tcp6_syn_sent_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp6_syn_sent_node, tcp6_syn_sent_rcv); - -vlib_node_registration_t tcp4_rcv_process_node; -vlib_node_registration_t tcp6_rcv_process_node; - /** * Handles reception for all states except LISTEN, SYN-SENT and ESTABLISHED * as per RFC793 p. 64 @@ -2725,24 +2664,24 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, switch (tc0->state) { case TCP_STATE_SYN_RCVD: + + /* Make sure the segment is exactly right */ + if (tc0->rcv_nxt != vnet_buffer (b0)->tcp.seq_number || is_fin0) + { + tcp_connection_reset (tc0); + error0 = TCP_ERROR_SEGMENT_INVALID; + goto drop; + } + /* * If the segment acknowledgment is not acceptable, form a * reset segment, * * and send it. */ - if (!tcp_rcv_ack_is_acceptable (tc0, b0)) - { - tcp_connection_reset (tc0); - error0 = TCP_ERROR_ACK_INVALID; - goto drop; - } - - /* Make sure the ack is exactly right */ - if (tc0->rcv_nxt != vnet_buffer (b0)->tcp.seq_number || is_fin0) + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) { tcp_connection_reset (tc0); - error0 = TCP_ERROR_SEGMENT_INVALID; goto drop; } @@ -2789,25 +2728,40 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (tc0->flags & TCP_CONN_FINPNDG) { /* TX fifo finally drained */ - max_dequeue = session_tx_fifo_max_dequeue (&tc0->connection); + max_dequeue = transport_max_tx_dequeue (&tc0->connection); if (max_dequeue <= tc0->burst_acked) tcp_send_fin (tc0); + /* If a fin was received and data was acked extend wait */ + else if ((tc0->flags & TCP_CONN_FINRCVD) && tc0->bytes_acked) + tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, + TCP_CLOSEWAIT_TIME); } /* If FIN is ACKed */ - else if (tc0->snd_una == tc0->snd_una_max) + else if (tc0->snd_una == tc0->snd_nxt) { - tcp_connection_set_state (tc0, TCP_STATE_FIN_WAIT_2); - /* 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. */ + * to send. */ tcp_connection_timers_reset (tc0); + + /* We already have a FIN but didn't transition to CLOSING + * because of outstanding tx data. Close the connection. */ + if (tc0->flags & TCP_CONN_FINRCVD) + { + tcp_connection_set_state (tc0, TCP_STATE_CLOSED); + tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); + session_transport_closed_notify (&tc0->connection); + goto drop; + } + + tcp_connection_set_state (tc0, TCP_STATE_FIN_WAIT_2); + /* Enable waitclose because we're willing to wait for peer's + * FIN but not indefinitely. */ tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME); /* Don't try to deq the FIN acked */ if (tc0->burst_acked > 1) - stream_session_dequeue_drop (&tc0->connection, - tc0->burst_acked - 1); + session_tx_fifo_dequeue_drop (&tc0->connection, + tc0->burst_acked - 1); tc0->burst_acked = 0; } break; @@ -2815,7 +2769,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, /* 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)) + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) goto drop; tc0->burst_acked = 0; break; @@ -2824,28 +2778,33 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0)) 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); - tcp_connection_set_state (tc0, TCP_STATE_LAST_ACK); - tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME); - } - } + if (!(tc0->flags & TCP_CONN_FINPNDG)) + break; + + /* Still have outstanding tx data */ + max_dequeue = transport_max_tx_dequeue (&tc0->connection); + if (max_dequeue > tc0->burst_acked) + break; + + tcp_send_fin (tc0); + tcp_connection_timers_reset (tc0); + tcp_connection_set_state (tc0, TCP_STATE_LAST_ACK); + tcp_timer_set (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, &error0)) + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) + goto drop; + + if (tc0->snd_una != tc0->snd_nxt) goto drop; tcp_connection_timers_reset (tc0); tcp_connection_set_state (tc0, TCP_STATE_TIME_WAIT); tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME); + session_transport_closed_notify (&tc0->connection); goto drop; break; @@ -2854,21 +2813,18 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, * 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; + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) + goto drop; + /* Apparently our ACK for the peer's FIN was lost */ - if (is_fin0 && tc0->snd_una != tc0->snd_una_max) + if (is_fin0 && tc0->snd_una != tc0->snd_nxt) { tcp_send_fin (tc0); goto drop; } tcp_connection_set_state (tc0, TCP_STATE_CLOSED); + session_transport_closed_notify (&tc0->connection); /* Don't free the connection from the data path since * we can't ensure that we have no packets already enqueued @@ -2884,10 +2840,13 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, * retransmission of the remote FIN. Acknowledge it, and restart * the 2 MSL timeout. */ - if (tcp_rcv_ack (wrk, tc0, b0, tcp0, &error0)) + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) goto drop; - tcp_program_ack (wrk, tc0); + if (!is_fin0) + goto drop; + + tcp_program_ack (tc0); tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME); goto drop; @@ -2927,7 +2886,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, case TCP_STATE_ESTABLISHED: /* Account for the FIN and send ack */ tc0->rcv_nxt += 1; - tcp_program_ack (wrk, tc0); + tcp_program_ack (tc0); tcp_connection_set_state (tc0, TCP_STATE_CLOSE_WAIT); tcp_program_disconnect (wrk, tc0); tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME); @@ -2949,10 +2908,22 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, break; case TCP_STATE_FIN_WAIT_1: tc0->rcv_nxt += 1; - tcp_connection_set_state (tc0, TCP_STATE_CLOSING); - tcp_program_ack (wrk, tc0); - /* Wait for ACK but not forever */ - tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME); + + if (tc0->flags & TCP_CONN_FINPNDG) + { + /* If data is outstanding, stay in FIN_WAIT_1 and try to finish + * sending it. Since we already received a fin, do not wait + * for too long. */ + tc0->flags |= TCP_CONN_FINRCVD; + tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME); + } + else + { + tcp_connection_set_state (tc0, TCP_STATE_CLOSING); + tcp_program_ack (tc0); + /* Wait for ACK for our FIN 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 */ @@ -2960,7 +2931,8 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, tcp_connection_set_state (tc0, TCP_STATE_TIME_WAIT); tcp_connection_timers_reset (tc0); tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_TIMEWAIT_TIME); - tcp_program_ack (wrk, tc0); + tcp_program_ack (tc0); + session_transport_closed_notify (&tc0->connection); break; case TCP_STATE_TIME_WAIT: /* Remain in the TIME-WAIT state. Restart the time-wait @@ -2981,25 +2953,26 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } } - errors = session_manager_flush_enqueue_events (TRANSPORT_PROTO_TCP, - thread_index); + errors = session_main_flush_enqueue_events (TRANSPORT_PROTO_TCP, + thread_index); tcp_inc_counter (rcv_process, TCP_ERROR_MSG_QUEUE_FULL, errors); tcp_handle_postponed_dequeues (wrk); + tcp_handle_disconnects (wrk); vlib_buffer_free (vm, first_buffer, from_frame->n_vectors); return from_frame->n_vectors; } -static uword -tcp4_rcv_process (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp4_rcv_process_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_rcv_process_inline (vm, node, from_frame, 1 /* is_ip4 */ ); } -static uword -tcp6_rcv_process (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp6_rcv_process_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_rcv_process_inline (vm, node, from_frame, 0 /* is_ip4 */ ); } @@ -3007,7 +2980,6 @@ tcp6_rcv_process (vlib_main_t * vm, vlib_node_runtime_t * node, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp4_rcv_process_node) = { - .function = tcp4_rcv_process, .name = "tcp4-rcv-process", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -3024,12 +2996,9 @@ VLIB_REGISTER_NODE (tcp4_rcv_process_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp4_rcv_process_node, tcp4_rcv_process); - /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp6_rcv_process_node) = { - .function = tcp6_rcv_process, .name = "tcp6-rcv-process", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -3046,11 +3015,6 @@ VLIB_REGISTER_NODE (tcp6_rcv_process_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp6_rcv_process_node, tcp6_rcv_process); - -vlib_node_registration_t tcp4_listen_node; -vlib_node_registration_t tcp6_listen_node; - /** * LISTEN state processing as per RFC 793 p. 65 */ @@ -3171,16 +3135,16 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node, tcp_connection_init_vars (child0); child0->rto = TCP_RTO_MIN; - TCP_EVT_DBG (TCP_EVT_SYN_RCVD, child0, 1); if (session_stream_accept (&child0->connection, lc0->c_s_index, - 0 /* notify */ )) + lc0->c_thread_index, 0 /* notify */ )) { tcp_connection_cleanup (child0); error0 = TCP_ERROR_CREATE_SESSION_FAIL; goto drop; } + TCP_EVT_DBG (TCP_EVT_SYN_RCVD, child0, 1); child0->tx_fifo_size = transport_tx_fifo_size (&child0->connection); tcp_send_synack (child0); tcp_timer_set (child0, TCP_TIMER_ESTABLISH, TCP_SYN_RCVD_TIME); @@ -3204,16 +3168,14 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node, return from_frame->n_vectors; } -static uword -tcp4_listen (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp4_listen_node) (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_listen_inline (vm, node, from_frame, 1 /* is_ip4 */ ); } -static uword -tcp6_listen (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp6_listen_node) (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { return tcp46_listen_inline (vm, node, from_frame, 0 /* is_ip4 */ ); } @@ -3221,7 +3183,6 @@ tcp6_listen (vlib_main_t * vm, vlib_node_runtime_t * node, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp4_listen_node) = { - .function = tcp4_listen, .name = "tcp4-listen", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -3238,12 +3199,9 @@ VLIB_REGISTER_NODE (tcp4_listen_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp4_listen_node, tcp4_listen); - /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp6_listen_node) = { - .function = tcp6_listen, .name = "tcp6-listen", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -3260,11 +3218,6 @@ VLIB_REGISTER_NODE (tcp6_listen_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp6_listen_node, tcp6_listen); - -vlib_node_registration_t tcp4_input_node; -vlib_node_registration_t tcp6_input_node; - typedef enum _tcp_input_next { TCP_INPUT_NEXT_DROP, @@ -3338,9 +3291,9 @@ tcp_input_set_error_next (tcp_main_t * tm, u16 * next, u32 * error, u8 is_ip4) } } -static inline tcp_connection_t * +always_inline tcp_connection_t * tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error, - u8 is_ip4) + u8 is_ip4, u8 is_nolookup) { u32 fib_index = vnet_buffer (b)->ip.fib_index; int n_advance_bytes, n_data_bytes; @@ -3369,10 +3322,12 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error, return 0; } - tc = session_lookup_connection_wt4 (fib_index, &ip4->dst_address, - &ip4->src_address, tcp->dst_port, - tcp->src_port, TRANSPORT_PROTO_TCP, - thread_index, &result); + if (!is_nolookup) + tc = session_lookup_connection_wt4 (fib_index, &ip4->dst_address, + &ip4->src_address, tcp->dst_port, + tcp->src_port, + TRANSPORT_PROTO_TCP, thread_index, + &result); } else { @@ -3394,20 +3349,31 @@ tcp_input_lookup_buffer (vlib_buffer_t * b, u8 thread_index, u32 * error, *error = TCP_ERROR_LENGTH; return 0; } - if (PREDICT_FALSE - (ip6_address_is_link_local_unicast (&ip6->dst_address))) + + if (!is_nolookup) { - ip4_main_t *im = &ip4_main; - fib_index = vec_elt (im->fib_index_by_sw_if_index, - vnet_buffer (b)->sw_if_index[VLIB_RX]); - } + if (PREDICT_FALSE + (ip6_address_is_link_local_unicast (&ip6->dst_address))) + { + ip4_main_t *im = &ip4_main; + fib_index = vec_elt (im->fib_index_by_sw_if_index, + vnet_buffer (b)->sw_if_index[VLIB_RX]); + } - tc = session_lookup_connection_wt6 (fib_index, &ip6->dst_address, - &ip6->src_address, tcp->dst_port, - tcp->src_port, TRANSPORT_PROTO_TCP, - thread_index, &result); + tc = session_lookup_connection_wt6 (fib_index, &ip6->dst_address, + &ip6->src_address, + tcp->dst_port, tcp->src_port, + TRANSPORT_PROTO_TCP, + thread_index, &result); + } } + if (is_nolookup) + tc = + (transport_connection_t *) tcp_connection_get (vnet_buffer (b)-> + tcp.connection_index, + thread_index); + vnet_buffer (b)->tcp.seq_number = clib_net_to_host_u32 (tcp->seq_number); vnet_buffer (b)->tcp.ack_number = clib_net_to_host_u32 (tcp->ack_number); vnet_buffer (b)->tcp.data_offset = n_advance_bytes; @@ -3449,7 +3415,7 @@ tcp_input_dispatch_buffer (tcp_main_t * tm, tcp_connection_t * tc, always_inline uword tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame, int is_ip4) + vlib_frame_t * frame, int is_ip4, u8 is_nolookup) { u32 n_left_from, *from, thread_index = vm->thread_index; tcp_main_t *tm = vnet_get_tcp_main (); @@ -3480,8 +3446,10 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, next[0] = next[1] = TCP_INPUT_NEXT_DROP; - tc0 = tcp_input_lookup_buffer (b[0], thread_index, &error0, is_ip4); - tc1 = tcp_input_lookup_buffer (b[1], thread_index, &error1, is_ip4); + tc0 = tcp_input_lookup_buffer (b[0], thread_index, &error0, is_ip4, + is_nolookup); + tc1 = tcp_input_lookup_buffer (b[1], thread_index, &error1, is_ip4, + is_nolookup); if (PREDICT_TRUE (!tc0 + !tc1 == 0)) { @@ -3531,7 +3499,8 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } next[0] = TCP_INPUT_NEXT_DROP; - tc0 = tcp_input_lookup_buffer (b[0], thread_index, &error0, is_ip4); + tc0 = tcp_input_lookup_buffer (b[0], thread_index, &error0, is_ip4, + is_nolookup); if (PREDICT_TRUE (tc0 != 0)) { ASSERT (tcp_lookup_is_valid (tc0, tcp_buffer_hdr (b[0]))); @@ -3553,24 +3522,79 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, return frame->n_vectors; } -static uword -tcp4_input (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp4_input_nolookup_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return tcp46_input_inline (vm, node, from_frame, 1 /* is_ip4 */ , + 1 /* is_nolookup */ ); +} + +VLIB_NODE_FN (tcp6_input_nolookup_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return tcp46_input_inline (vm, node, from_frame, 0 /* is_ip4 */ , + 1 /* is_nolookup */ ); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (tcp4_input_nolookup_node) = +{ + .name = "tcp4-input-nolookup", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + .n_errors = TCP_N_ERROR, + .error_strings = tcp_error_strings, + .n_next_nodes = TCP_INPUT_N_NEXT, + .next_nodes = + { +#define _(s,n) [TCP_INPUT_NEXT_##s] = n, + foreach_tcp4_input_next +#undef _ + }, + .format_buffer = format_tcp_header, + .format_trace = format_tcp_rx_trace, +}; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (tcp6_input_nolookup_node) = { - return tcp46_input_inline (vm, node, from_frame, 1 /* is_ip4 */ ); + .name = "tcp6-input-nolookup", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + .n_errors = TCP_N_ERROR, + .error_strings = tcp_error_strings, + .n_next_nodes = TCP_INPUT_N_NEXT, + .next_nodes = + { +#define _(s,n) [TCP_INPUT_NEXT_##s] = n, + foreach_tcp6_input_next +#undef _ + }, + .format_buffer = format_tcp_header, + .format_trace = format_tcp_rx_trace, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FN (tcp4_input_node) (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return tcp46_input_inline (vm, node, from_frame, 1 /* is_ip4 */ , + 0 /* is_nolookup */ ); } -static uword -tcp6_input (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (tcp6_input_node) (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) { - return tcp46_input_inline (vm, node, from_frame, 0 /* is_ip4 */ ); + return tcp46_input_inline (vm, node, from_frame, 0 /* is_ip4 */ , + 0 /* is_nolookup */ ); } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp4_input_node) = { - .function = tcp4_input, .name = "tcp4-input", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -3588,12 +3612,9 @@ VLIB_REGISTER_NODE (tcp4_input_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp4_input_node, tcp4_input); - /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp6_input_node) = { - .function = tcp6_input, .name = "tcp6-input", /* Takes a vector of packets. */ .vector_size = sizeof (u32), @@ -3611,8 +3632,7 @@ VLIB_REGISTER_NODE (tcp6_input_node) = }; /* *INDENT-ON* */ -VLIB_NODE_FUNCTION_MULTIARCH (tcp6_input_node, tcp6_input); - +#ifndef CLIB_MARCH_VARIANT static void tcp_dispatch_table_init (tcp_main_t * tm) { @@ -3858,6 +3878,8 @@ tcp_input_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (tcp_input_init); +#endif /* CLIB_MARCH_VARIANT */ + /* * fd.io coding-style-patch-verification: ON *