X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Ftcp%2Ftcp_input.c;h=6c78af0fc88e7fb9d1629a1485a63343f8d9d246;hb=2f0f96b9dbcb198dede1e5ebd4d880129d5d52c8;hp=cc630f8ae5fcd554f8c32154eb1a20cbbc9c1190;hpb=74b7437bd118e104f50831f8a8f3cc8d10aa2530;p=vpp.git diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c index cc630f8ae5f..6c78af0fc88 100644 --- a/src/vnet/tcp/tcp_input.c +++ b/src/vnet/tcp/tcp_input.c @@ -390,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 (wrk, tc0); *error0 = TCP_ERROR_SPURIOUS_SYN; - tcp_send_reset (tc0); goto error; } @@ -405,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_una_max)); + 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; } /** @@ -1319,6 +1336,8 @@ tcp_do_fastretransmits (tcp_worker_ctx_t * wrk) for (i = 0; i < vec_len (ongoing_fast_rxt); i++) { tc = tcp_connection_get (ongoing_fast_rxt[i], thread_index); + if (!tc) + continue; if (!tcp_in_fastrecovery (tc)) { tc->flags &= ~TCP_CONN_FRXT_PENDING; @@ -1532,8 +1551,6 @@ 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 @@ -1565,7 +1582,8 @@ tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b, { /* 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)) + 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 process_ack; @@ -1854,7 +1872,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); @@ -2509,9 +2527,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; } @@ -2533,6 +2551,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; } @@ -2703,24 +2722,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)) + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) { 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) - { - tcp_connection_reset (tc0); - error0 = TCP_ERROR_SEGMENT_INVALID; goto drop; } @@ -2770,16 +2789,30 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, 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_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); + 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 */ @@ -2793,7 +2826,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; @@ -2802,37 +2835,27 @@ 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 (!transport_max_tx_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 */ + if (transport_max_tx_dequeue (&tc0->connection)) + 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_is_acceptable (tc0, b0)) - { - error0 = TCP_ERROR_ACK_INVALID; - goto drop; - } + if (tcp_rcv_ack_no_cc (tc0, b0, &error0)) + goto drop; - error0 = TCP_ERROR_ACK_OK; - tc0->snd_una = vnet_buffer (b0)->tcp.ack_number; - /* Ack moved snd_una beyond snd_nxt so reprogram fin */ - if (seq_gt (tc0->snd_una, tc0->snd_nxt)) - { - tc0->snd_nxt = tc0->snd_una; - tc0->flags &= ~TCP_CONN_FINSNT; - 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); @@ -2845,13 +2868,9 @@ 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_nxt) { @@ -2875,7 +2894,7 @@ 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; if (!is_fin0) @@ -2943,28 +2962,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); + if (tc0->flags & TCP_CONN_FINPNDG) { - /* Drop all outstanding tx data. */ - session_tx_fifo_dequeue_drop (&tc0->connection, - transport_max_tx_dequeue - (&tc0->connection)); - /* Make it look as if we've recovered, if needed */ - if (tcp_in_cong_recovery (tc0)) - { - scoreboard_clear (&tc0->sack_sb); - tcp_fastrecovery_off (tc0); - tcp_recovery_off (tc0); - tcp_connection_timers_reset (tc0); - tc0->snd_nxt = tc0->snd_una; - } - tcp_send_fin (tc0); + /* 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_program_ack (wrk, tc0); - /* Wait for ACK for our FIN but not forever */ - tcp_timer_update (tc0, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME); + { + tcp_connection_set_state (tc0, TCP_STATE_CLOSING); + tcp_program_ack (wrk, 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 */ @@ -2997,6 +3010,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node, 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; @@ -3174,16 +3188,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);