tcp: timestamp adjustment
[vpp.git] / src / vnet / tcp / tcp_input.c
index 4927031..1b4e8a6 100644 (file)
@@ -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;
     }
 
@@ -411,7 +412,8 @@ tcp_rcv_ack_no_cc (tcp_connection_t * tc, vlib_buffer_t * b, u32 * error)
   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))
+      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;
@@ -506,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);
     }
 
@@ -1183,12 +1185,10 @@ tcp_cc_recovery_exit (tcp_connection_t * tc)
 
 #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_rxt_bytes = 0;
   tc->rtt_ts = 0;
 
   tcp_fastrecovery_off (tc);
@@ -1211,8 +1211,9 @@ tcp_cc_congestion_undo (tcp_connection_t * tc)
     }
   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);
 }
@@ -1253,7 +1254,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));
@@ -1262,12 +1266,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;
@@ -1334,6 +1338,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;
@@ -1372,7 +1378,8 @@ tcp_do_fastretransmits (tcp_worker_ctx_t * wrk)
  * 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;
 
@@ -1398,7 +1405,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))
@@ -1417,7 +1424,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))
            {
@@ -1443,7 +1450,7 @@ tcp_cc_handle_event (tcp_connection_t * tc, u32 is_dack)
       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
@@ -1498,7 +1505,7 @@ partial_ack:
        }
 
       /* Treat as congestion avoidance ack */
-      tcp_cc_rcv_ack (tc);
+      tcp_cc_rcv_ack (tc, rs);
       return;
     }
 
@@ -1516,7 +1523,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;
     }
@@ -1547,15 +1554,13 @@ 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
@@ -1571,6 +1576,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);
@@ -1580,7 +1586,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;
@@ -1599,7 +1606,7 @@ 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;
     }
@@ -1627,6 +1634,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);
 
   /*
@@ -1635,7 +1645,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;
@@ -1650,7 +1660,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;
 }
@@ -1690,6 +1700,10 @@ 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);
@@ -1869,7 +1883,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);
@@ -2524,9 +2538,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;
            }
 
@@ -2548,6 +2562,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;
            }
 
@@ -2835,7 +2850,8 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
            break;
 
          /* Still have outstanding tx data */
-         if (transport_max_tx_dequeue (&tc0->connection))
+         max_dequeue = transport_max_tx_dequeue (&tc0->connection);
+         if (max_dequeue > tc0->burst_acked)
            break;
 
          tcp_send_fin (tc0);
@@ -3006,6 +3022,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;
@@ -3183,16 +3200,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);