tcp: round down rcv wnd even if avoiding retraction

Type: improvement

Signed-off-by: Florin Coras <fcoras@cisco.com>
Change-Id: I7fb3a4a2ffc4c5f42aa043e3a317b52d7767fb81
diff --git a/src/vnet/tcp/tcp_output.c b/src/vnet/tcp/tcp_output.c
index 3b1bbac..46c5661 100644
--- a/src/vnet/tcp/tcp_output.c
+++ b/src/vnet/tcp/tcp_output.c
@@ -121,33 +121,29 @@
    */
   available_space = transport_max_rx_enqueue (&tc->connection);
 
-  /* Make sure we have a multiple of 1 << rcv_wscale. We round down to
-   * avoid advertising a window larger than what can be buffered */
-  available_space = round_down_pow2 (available_space, 1 << tc->rcv_wscale);
-
-  if (PREDICT_FALSE (available_space < tc->rcv_opts.mss))
-    {
-      tc->rcv_wnd = 0;
-      return;
-    }
-
   /*
    * Use the above and what we know about what we've previously advertised
    * to compute the new window
    */
   observed_wnd = (i32) tc->rcv_wnd - (tc->rcv_nxt - tc->rcv_las);
 
-  /* Bad. Thou shalt not shrink */
+  /* Check if we are about to retract the window. Do the comparison before
+   * rounding to avoid errors. Per RFC7323 sec. 2.4 we could remove this */
   if (PREDICT_FALSE ((i32) available_space < observed_wnd))
     {
-      wnd = round_pow2 (clib_max (observed_wnd, 0), 1 << tc->rcv_wscale);
+      wnd = round_down_pow2 (clib_max (observed_wnd, 0), 1 << tc->rcv_wscale);
       TCP_EVT (TCP_EVT_RCV_WND_SHRUNK, tc, observed_wnd, available_space);
     }
   else
     {
-      wnd = available_space;
+      /* Make sure we have a multiple of 1 << rcv_wscale. We round down to
+       * avoid advertising a window larger than what can be buffered */
+      wnd = round_down_pow2 (available_space, 1 << tc->rcv_wscale);
     }
 
+  if (PREDICT_FALSE (wnd < tc->rcv_opts.mss))
+    wnd = 0;
+
   tc->rcv_wnd = clib_min (wnd, TCP_WND_MAX << tc->rcv_wscale);
 }