Add sack tx unit test
[vpp.git] / src / vnet / tcp / tcp_input.c
index a12ad8c..e184a4d 100644 (file)
@@ -211,8 +211,6 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
 always_inline int
 tcp_segment_check_paws (tcp_connection_t * tc)
 {
-  /* XXX normally test for timestamp should be lt instead of leq, but for
-   * local testing this is not enough */
   return tcp_opts_tstamp (&tc->opt) && tc->tsval_recent
     && timestamp_lt (tc->opt.tsval, tc->tsval_recent);
 }
@@ -896,37 +894,51 @@ tcp_rcv_ack (tcp_connection_t * tc, vlib_buffer_t * b,
  * @param start Start sequence number of the newest SACK block
  * @param end End sequence of the newest SACK block
  */
-static void
+void
 tcp_update_sack_list (tcp_connection_t * tc, u32 start, u32 end)
 {
-  sack_block_t *new_list = 0, block;
+  sack_block_t *new_list = 0, *block = 0;
   int i;
 
   /* If the first segment is ooo add it to the list. Last write might've moved
    * rcv_nxt over the first segment. */
   if (seq_lt (tc->rcv_nxt, start))
     {
-      block.start = start;
-      block.end = end;
-      vec_add1 (new_list, block);
+      vec_add2 (new_list, block, 1);
+      block->start = start;
+      block->end = end;
     }
 
   /* Find the blocks still worth keeping. */
   for (i = 0; i < vec_len (tc->snd_sacks); i++)
     {
-      /* Discard if:
-       * 1) rcv_nxt advanced beyond current block OR
-       * 2) Segment overlapped by the first segment, i.e., it has been merged
-       *    into it.*/
-      if (seq_leq (tc->snd_sacks[i].start, tc->rcv_nxt)
-         || seq_leq (tc->snd_sacks[i].start, end))
+      /* Discard if rcv_nxt advanced beyond current block */
+      if (seq_leq (tc->snd_sacks[i].start, tc->rcv_nxt))
        continue;
 
-      /* Save to new SACK list. */
-      vec_add1 (new_list, tc->snd_sacks[i]);
+      /* Merge or drop if segment overlapped by the new segment */
+      if (block && (seq_geq (tc->snd_sacks[i].end, new_list[0].start)
+                   && seq_leq (tc->snd_sacks[i].start, new_list[0].end)))
+       {
+         if (seq_lt (tc->snd_sacks[i].start, new_list[0].start))
+           new_list[0].start = tc->snd_sacks[i].start;
+         if (seq_lt (new_list[0].end, tc->snd_sacks[i].end))
+           new_list[0].end = tc->snd_sacks[i].end;
+         continue;
+       }
+
+      /* 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 ("dropped sack blocks");
+       }
     }
 
-  ASSERT (vec_len (new_list) < TCP_MAX_SACK_BLOCKS);
+  ASSERT (vec_len (new_list) <= TCP_MAX_SACK_BLOCKS);
 
   /* Replace old vector with new one */
   vec_free (tc->snd_sacks);
@@ -999,7 +1011,7 @@ tcp_session_enqueue_ooo (tcp_connection_t * tc, vlib_buffer_t * b,
                         u16 data_len)
 {
   stream_session_t *s0;
-  u32 offset, seq;
+  u32 offset;
   int rv;
 
   /* Pure ACK. Do nothing */
@@ -1009,11 +1021,12 @@ tcp_session_enqueue_ooo (tcp_connection_t * tc, vlib_buffer_t * b,
     }
 
   s0 = stream_session_get (tc->c_s_index, tc->c_thread_index);
-  seq = vnet_buffer (b)->tcp.seq_number;
-  offset = seq - tc->rcv_nxt;
+  offset = vnet_buffer (b)->tcp.seq_number - tc->irs;
 
-  rv = svm_fifo_enqueue_with_offset (s0->server_rx_fifo, s0->pid, offset,
-                                    data_len, vlib_buffer_get_current (b));
+  clib_warning ("ooo: offset %d len %d", offset, data_len);
+
+  rv = svm_fifo_enqueue_with_offset (s0->server_rx_fifo, offset, data_len,
+                                    vlib_buffer_get_current (b));
 
   /* Nothing written */
   if (rv)
@@ -1032,8 +1045,8 @@ tcp_session_enqueue_ooo (tcp_connection_t * tc, vlib_buffer_t * b,
 
       /* Get the newest segment from the fifo */
       newest = svm_fifo_newest_ooo_segment (s0->server_rx_fifo);
-      start = tc->rcv_nxt + ooo_segment_offset (s0->server_rx_fifo, newest);
-      end = tc->rcv_nxt + ooo_segment_end_offset (s0->server_rx_fifo, newest);
+      start = ooo_segment_offset (s0->server_rx_fifo, newest);
+      end = ooo_segment_end_offset (s0->server_rx_fifo, newest);
 
       tcp_update_sack_list (tc, start, end);
     }
@@ -1072,6 +1085,7 @@ tcp_segment_rcv (tcp_main_t * tm, tcp_connection_t * tc, vlib_buffer_t * b,
     {
       /* Old sequence numbers allowed through because they overlapped
        * the rx window */
+
       if (seq_lt (vnet_buffer (b)->tcp.seq_number, tc->rcv_nxt))
        {
          error = TCP_ERROR_SEGMENT_OLD;
@@ -1181,6 +1195,7 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   u32 n_left_from, next_index, *from, *to_next;
   u32 my_thread_index = vm->thread_index, errors = 0;
   tcp_main_t *tm = vnet_get_tcp_main ();
+  u8 is_fin = 0;
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -1243,9 +1258,11 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
              n_advance_bytes0 += sizeof (ip60[0]);
            }
 
+         is_fin = (th0->flags & TCP_FLAG_FIN) != 0;
+
          /* SYNs, FINs and data consume sequence numbers */
          vnet_buffer (b0)->tcp.seq_end = vnet_buffer (b0)->tcp.seq_number
-           + tcp_is_syn (th0) + tcp_is_fin (th0) + n_data_bytes0;
+           + tcp_is_syn (th0) + is_fin + n_data_bytes0;
 
          /* TODO header prediction fast path */
 
@@ -1272,8 +1289,11 @@ tcp46_established_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          vlib_buffer_advance (b0, n_advance_bytes0);
          error0 = tcp_segment_rcv (tm, tc0, b0, n_data_bytes0, &next0);
 
+         /* N.B. buffer is rewritten if segment is ooo. Thus, th0 becomes a
+          * dangling reference. */
+
          /* 8: check the FIN bit */
-         if (tcp_fin (th0))
+         if (is_fin)
            {
              /* Enter CLOSE-WAIT and notify session. Don't send ACK, instead
               * wait for session to call close. To avoid lingering
@@ -2365,8 +2385,12 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 
              if (PREDICT_FALSE (error0 == TCP_ERROR_DISPATCH))
                {
+                 tcp_state_t state0 = tc0->state;
                  /* Overload tcp flags to store state */
                  vnet_buffer (b0)->tcp.flags = tc0->state;
+                 clib_warning ("disp error state %U flags %U",
+                               format_tcp_state, &state0,
+                               format_tcp_flags, (int) flags0);
                }
            }
          else
@@ -2382,8 +2406,8 @@ tcp46_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
            {
              t0 = vlib_add_trace (vm, node, b0, sizeof (*t0));
              clib_memcpy (&t0->tcp_header, tcp0, sizeof (t0->tcp_header));
-             clib_memcpy (&t0->tcp_connection, tc0,
-                          sizeof (t0->tcp_connection));
+             if (tc0)
+               clib_memcpy (&t0->tcp_connection, tc0, sizeof (*tc0));
            }
 
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,