tcp: fix last sacked with no holes
[vpp.git] / src / vnet / tcp / tcp_input.c
index de79e3e..b9df882 100755 (executable)
@@ -784,6 +784,8 @@ scoreboard_update_bytes (sack_scoreboard_t * sb, u32 ack, u32 snd_mss)
   if (!right)
     {
       sb->sacked_bytes = sb->high_sacked - ack;
+      sb->last_sacked_bytes = sb->sacked_bytes
+       - (old_sacked - sb->last_bytes_delivered);
       return;
     }
 
@@ -1097,7 +1099,11 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack)
   hole = pool_elt_at_index (sb->holes, sb->head);
 
   if (PREDICT_FALSE (sb->is_reneging))
-    sb->last_bytes_delivered += hole->start - tc->snd_una;
+    {
+      sb->last_bytes_delivered += clib_min (hole->start - tc->snd_una,
+                                           ack - tc->snd_una);
+      sb->is_reneging = seq_lt (ack, hole->start);
+    }
 
   while (hole && blk_index < vec_len (rcv_sacks))
     {
@@ -1180,7 +1186,7 @@ tcp_rcv_sacks (tcp_connection_t * tc, u32 ack)
          || sb->is_reneging || sb->holes[sb->head].start == ack);
   ASSERT (sb->last_lost_bytes <= sb->lost_bytes);
   ASSERT ((ack - tc->snd_una) + sb->last_sacked_bytes
-         - sb->last_bytes_delivered >= sb->rxt_sacked);
+         - sb->last_bytes_delivered >= sb->rxt_sacked || sb->is_reneging);
   ASSERT ((ack - tc->snd_una) >= tc->sack_sb.last_bytes_delivered
          || (tc->flags & TCP_CONN_FINSNT));
 
@@ -1509,6 +1515,23 @@ tcp_cc_handle_event (tcp_connection_t * tc, tcp_rate_sample_t * rs,
     tcp_cc_rcv_cong_ack (tc, TCP_CC_PARTIALACK, rs);
 }
 
+static void
+tcp_handle_old_ack (tcp_connection_t * tc, tcp_rate_sample_t * rs)
+{
+  if (!tcp_in_cong_recovery (tc))
+    return;
+
+  if (tcp_opts_sack_permitted (&tc->rcv_opts))
+    tcp_rcv_sacks (tc, tc->snd_una);
+
+  tc->bytes_acked = 0;
+
+  if (tc->cfg_flags & TCP_CFG_F_RATE_SAMPLE)
+    tcp_bt_sample_delivery_rate (tc, rs);
+
+  tcp_cc_handle_event (tc, rs, 1);
+}
+
 /**
  * Check if duplicate ack as per RFC5681 Sec. 2
  */
@@ -1574,8 +1597,12 @@ tcp_rcv_ack (tcp_worker_ctx_t * wrk, tcp_connection_t * tc, vlib_buffer_t * b,
       tc->errors.below_ack_wnd += 1;
       *error = TCP_ERROR_ACK_OLD;
       TCP_EVT (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, 0, 1);
+
+      if (seq_lt (vnet_buffer (b)->tcp.ack_number, tc->snd_una - tc->rcv_wnd))
+       return -1;
+
+      tcp_handle_old_ack (tc, &rs);
+
       /* Don't drop yet */
       return 0;
     }