SCTP: refactoring

This patch takes care of some refactoring, including the initialization
of the timestamp to calculate the RTO, the output state-machine
validation which can be enabled (disabled by default) when debugging and
some clean-up of unused fields.
It also addresses the requirement of Karn's algorithm when computing the
RTO.

Change-Id: I6b875152369bff23cad085708cec1f7e1151cfa8
Signed-off-by: Marco Varlese <marco.varlese@suse.com>
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c
index 61c0252..8fc5d9b 100644
--- a/src/vnet/sctp/sctp.c
+++ b/src/vnet/sctp/sctp.c
@@ -470,10 +470,10 @@
 sctp_session_close (u32 conn_index, u32 thread_index)
 {
   ASSERT (thread_index == 0);
-
   sctp_connection_t *sctp_conn;
   sctp_conn = sctp_connection_get (conn_index, thread_index);
-  sctp_connection_close (sctp_conn);
+  if (sctp_conn != NULL)
+    sctp_connection_close (sctp_conn);
 }
 
 void
@@ -481,10 +481,13 @@
 {
   sctp_connection_t *sctp_conn;
   sctp_conn = sctp_connection_get (conn_index, thread_index);
-  sctp_connection_timers_reset (sctp_conn);
 
-  /* Wait for the session tx events to clear */
-  sctp_conn->state = SCTP_STATE_CLOSED;
+  if (sctp_conn != NULL)
+    {
+      sctp_connection_timers_reset (sctp_conn);
+      /* Wait for the session tx events to clear */
+      sctp_conn->state = SCTP_STATE_CLOSED;
+    }
 }
 
 /**
diff --git a/src/vnet/sctp/sctp.h b/src/vnet/sctp/sctp.h
index 60a195f..bc974d9 100644
--- a/src/vnet/sctp/sctp.h
+++ b/src/vnet/sctp/sctp.h
@@ -128,6 +128,8 @@
   u8 unacknowledged_hb;	/**< Used to track how many unacknowledged heartbeats we had;
   	  	  	  	  If more than Max.Retransmit then connetion is considered unreachable. */
 
+  u8 is_retransmitting;	/**< A flag (0 = no, 1 = yes) indicating whether the connection is retransmitting a previous packet */
+
 } sctp_sub_connection_t;
 
 typedef struct
@@ -284,20 +286,6 @@
 
 u16 sctp_check_outstanding_data_chunks (sctp_connection_t * tc);
 
-#define SCTP_TICK 0.001			/**< SCTP tick period (s) */
-#define STHZ (u32) (1/SCTP_TICK)		/**< SCTP tick frequency */
-#define SCTP_TSTAMP_RESOLUTION SCTP_TICK	/**< Time stamp resolution */
-#define SCTP_PAWS_IDLE 24 * 24 * 60 * 60 * THZ /**< 24 days */
-#define SCTP_FIB_RECHECK_PERIOD	1 * THZ	/**< Recheck every 1s */
-#define SCTP_MAX_OPTION_SPACE 40
-
-#define SCTP_DUPACK_THRESHOLD 	3
-#define SCTP_MAX_RX_FIFO_SIZE 	4 << 20
-#define SCTP_MIN_RX_FIFO_SIZE	4 << 10
-#define SCTP_IW_N_SEGMENTS 	10
-#define SCTP_ALWAYS_ACK		1	/**< On/off delayed acks */
-#define SCTP_USE_SACKS		1	/**< Disable only for testing */
-
 #define IP_PROTOCOL_SCTP	132
 
 /** SSCTP FSM state definitions as per RFC4960. */
@@ -408,6 +396,7 @@
 
 #define SCTP_TICK 0.001			/**< SCTP tick period (s) */
 #define SHZ (u32) (1/SCTP_TICK)		/**< SCTP tick frequency */
+#define SCTP_TSTAMP_RESOLUTION SCTP_TICK	/**< Time stamp resolution */
 
 /* As per RFC4960, page 83 */
 #define SCTP_RTO_INIT 3 * SHZ	/* 3 seconds */
diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c
index 8df9564..3f7ab4b 100644
--- a/src/vnet/sctp/sctp_input.c
+++ b/src/vnet/sctp/sctp_input.c
@@ -645,14 +645,14 @@
 }
 
 always_inline u8
-sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 gapping)
+sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 is_gapping)
 {
-  if (gapping != 0)
+  if (is_gapping != 0)
     {
       SCTP_CONN_TRACKING_DBG
 	("gapping != 0: CONN_INDEX = %u, sctp_conn->ack_state = %u",
 	 sctp_conn->sub_conn[idx].connection.c_index, sctp_conn->ack_state);
-      return 1;
+      return 0;
     }
 
   if (sctp_conn->ack_state >= MAX_ENQUEABLE_SACKS)
@@ -660,12 +660,12 @@
       SCTP_CONN_TRACKING_DBG
 	("sctp_conn->ack_state >= MAX_ENQUEABLE_SACKS: CONN_INDEX = %u, sctp_conn->ack_state = %u",
 	 sctp_conn->sub_conn[idx].connection.c_index, sctp_conn->ack_state);
-      return 1;
+      return 0;
     }
 
   sctp_conn->ack_state += 1;
 
-  return 0;
+  return 1;
 }
 
 always_inline void
@@ -748,7 +748,7 @@
 
   SCTP_ADV_DBG ("POINTER_WITH_DATA = %p", b->data);
 
-  if (sctp_is_sack_delayable (sctp_conn, is_gapping) != 0)
+  if (!sctp_is_sack_delayable (sctp_conn, is_gapping))
     sctp_prepare_sack_chunk (sctp_conn, b);
 
   return error;
@@ -1152,6 +1152,7 @@
    */
   sctp_timer_reset (sctp_conn, MAIN_SCTP_SUB_CONN_IDX,
 		    SCTP_TIMER_T2_SHUTDOWN);
+
   sctp_send_shutdown_complete (sctp_conn);
 
   *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4);
diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c
index 7657d0c..bb8332f 100644
--- a/src/vnet/sctp/sctp_output.c
+++ b/src/vnet/sctp/sctp_output.c
@@ -554,9 +554,6 @@
   vnet_sctp_set_chunk_type (&cookie_ack_chunk->chunk_hdr, COOKIE_ACK);
   vnet_sctp_set_chunk_length (&cookie_ack_chunk->chunk_hdr, chunk_len);
 
-  /* Measure RTT with this */
-  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
-
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
 }
@@ -591,9 +588,6 @@
   clib_memcpy (&(cookie_echo_chunk->cookie), sc,
 	       sizeof (sctp_state_cookie_param_t));
 
-  /* Measure RTT with this */
-  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
-
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
 }
@@ -736,9 +730,6 @@
 
   sctp_conn->local_tag = init_ack_chunk->initiate_tag;
 
-  /* Measure RTT with this */
-  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
-
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
 }
@@ -802,9 +793,6 @@
   u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN);
   sctp_enqueue_to_output_now (vm, b, bi,
 			      sctp_conn->sub_conn[idx].connection.is_ip4);
-
-  /* Measure RTT with this */
-  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
 }
 
 /**
@@ -852,11 +840,6 @@
   sctp_reuse_buffer (vm, b);
 
   sctp_prepare_shutdown_ack_chunk (sctp_conn, b);
-
-  u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN_ACK);
-
-  /* Measure RTT with this */
-  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
 }
 
 /**
@@ -1041,8 +1024,6 @@
   u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN_COMPLETE);
   sctp_enqueue_to_output (vm, b, bi,
 			  sctp_conn->sub_conn[idx].connection.is_ip4);
-
-  sctp_conn->state = SCTP_STATE_CLOSED;
 }
 
 
@@ -1069,15 +1050,15 @@
   sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b);
   sctp_enqueue_to_ip_lookup (vm, b, bi, sctp_conn->sub_conn[idx].c_is_ip4);
 
-  /* Measure RTT with this */
-  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
-
   /* Start the T1_INIT timer */
   sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_INIT,
 		  sctp_conn->sub_conn[idx].RTO);
 
   /* Change state to COOKIE_WAIT */
   sctp_conn->state = SCTP_STATE_COOKIE_WAIT;
+
+  /* Measure RTT with this */
+  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
 }
 
 /**
@@ -1156,18 +1137,51 @@
 
   sctp_push_hdr_i (sctp_conn, idx, b, SCTP_STATE_ESTABLISHED);
 
-  if (sctp_conn->sub_conn[idx].RTO_pending == 0)
-    {
-      sctp_conn->sub_conn[idx].RTO_pending = 1;
-      sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
-    }
-
   sctp_trajectory_add_start (b0, 3);
 
   return 0;
 
 }
 
+always_inline u8
+sctp_validate_output_state_machine (sctp_connection_t * sctp_conn,
+				    u8 chunk_type)
+{
+  u8 result = 0;
+  switch (sctp_conn->state)
+    {
+    case SCTP_STATE_CLOSED:
+      if (chunk_type != INIT && chunk_type != INIT_ACK)
+	result = 1;
+      break;
+    case SCTP_STATE_ESTABLISHED:
+      if (chunk_type != DATA && chunk_type != HEARTBEAT &&
+	  chunk_type != HEARTBEAT_ACK && chunk_type != SACK &&
+	  chunk_type != COOKIE_ACK && chunk_type != SHUTDOWN)
+	result = 1;
+      break;
+    case SCTP_STATE_COOKIE_WAIT:
+      if (chunk_type != COOKIE_ECHO)
+	result = 1;
+      break;
+    case SCTP_STATE_SHUTDOWN_SENT:
+      if (chunk_type != SHUTDOWN_COMPLETE)
+	result = 1;
+      break;
+    case SCTP_STATE_SHUTDOWN_RECEIVED:
+      if (chunk_type != SHUTDOWN_ACK)
+	result = 1;
+      break;
+    }
+  return result;
+}
+
+always_inline u8
+sctp_is_retransmitting (sctp_connection_t * sctp_conn, u8 idx)
+{
+  return sctp_conn->sub_conn[idx].is_retransmitting;
+}
+
 always_inline uword
 sctp46_output_inline (vlib_main_t * vm,
 		      vlib_node_runtime_t * node,
@@ -1322,93 +1336,49 @@
 	     sctp_chunk_to_string (chunk_type), full_hdr->hdr.src_port,
 	     full_hdr->hdr.dst_port);
 
-	  if (chunk_type == DATA)
-	    SCTP_ADV_DBG_OUTPUT ("PACKET_LENGTH = %u", packet_length);
-
 	  /* Let's make sure the state-machine does not send anything crazy */
-	  switch (sctp_conn->state)
+#if SCTP_DEBUG_STATE_MACHINE
+	  if (sctp_validate_output_state_machine (sctp_conn, chunk_type) != 0)
 	    {
-	    case SCTP_STATE_CLOSED:
-	      {
-		if (chunk_type != INIT && chunk_type != INIT_ACK)
-		  {
-		    SCTP_DBG_STATE_MACHINE
-		      ("Sending the wrong chunk (%s) based on state-machine status (%s)",
-		       sctp_chunk_to_string (chunk_type),
-		       sctp_state_to_string (sctp_conn->state));
-
-		    error0 = SCTP_ERROR_UNKOWN_CHUNK;
-		    next0 = SCTP_OUTPUT_NEXT_DROP;
-		    goto done;
-		  }
-		break;
-	      }
-	    case SCTP_STATE_ESTABLISHED:
-	      if (chunk_type != DATA && chunk_type != HEARTBEAT &&
-		  chunk_type != HEARTBEAT_ACK && chunk_type != SACK &&
-		  chunk_type != COOKIE_ACK && chunk_type != SHUTDOWN)
-		{
-		  SCTP_DBG_STATE_MACHINE
-		    ("Sending the wrong chunk (%s) based on state-machine status (%s)",
-		     sctp_chunk_to_string (chunk_type),
-		     sctp_state_to_string (sctp_conn->state));
-
-		  error0 = SCTP_ERROR_UNKOWN_CHUNK;
-		  next0 = SCTP_OUTPUT_NEXT_DROP;
-		  goto done;
-		}
-	      break;
-	    case SCTP_STATE_COOKIE_WAIT:
-	      if (chunk_type != COOKIE_ECHO)
-		{
-		  SCTP_DBG_STATE_MACHINE
-		    ("Sending the wrong chunk (%s) based on state-machine status (%s)",
-		     sctp_chunk_to_string (chunk_type),
-		     sctp_state_to_string (sctp_conn->state));
-
-		  error0 = SCTP_ERROR_UNKOWN_CHUNK;
-		  next0 = SCTP_OUTPUT_NEXT_DROP;
-		  goto done;
-		}
-	      /* Change state */
-	      sctp_conn->state = SCTP_STATE_COOKIE_ECHOED;
-	      break;
-	    case SCTP_STATE_SHUTDOWN_SENT:
-	      if (chunk_type != SHUTDOWN_COMPLETE)
-		{
-		  SCTP_DBG_STATE_MACHINE
-		    ("Sending the wrong chunk (%s) based on state-machine status (%s)",
-		     sctp_chunk_to_string (chunk_type),
-		     sctp_state_to_string (sctp_conn->state));
-
-		  error0 = SCTP_ERROR_UNKOWN_CHUNK;
-		  next0 = SCTP_OUTPUT_NEXT_DROP;
-		  goto done;
-		}
-	    case SCTP_STATE_SHUTDOWN_RECEIVED:
-	      if (chunk_type != SHUTDOWN_ACK)
-		{
-		  SCTP_DBG_STATE_MACHINE
-		    ("Sending the wrong chunk (%s) based on state-machine status (%s)",
-		     sctp_chunk_to_string (chunk_type),
-		     sctp_state_to_string (sctp_conn->state));
-
-		  error0 = SCTP_ERROR_UNKOWN_CHUNK;
-		  next0 = SCTP_OUTPUT_NEXT_DROP;
-		  goto done;
-		}
-	    default:
 	      SCTP_DBG_STATE_MACHINE
-		("Sending chunk (%s) based on state-machine status (%s)",
+		("Sending the wrong chunk (%s) based on state-machine status (%s)",
 		 sctp_chunk_to_string (chunk_type),
 		 sctp_state_to_string (sctp_conn->state));
-	      break;
+
+	      error0 = SCTP_ERROR_UNKOWN_CHUNK;
+	      next0 = SCTP_OUTPUT_NEXT_DROP;
+	      goto done;
+	    }
+#endif
+
+	  /* Karn's algorithm: RTT measurements MUST NOT be made using
+	   * packets that were retransmitted
+	   */
+	  if (!sctp_is_retransmitting (sctp_conn, idx))
+	    {
+	      /* Measure RTT with this */
+	      if (chunk_type == DATA
+		  && sctp_conn->sub_conn[idx].RTO_pending == 0)
+		{
+		  sctp_conn->sub_conn[idx].RTO_pending = 1;
+		  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
+		}
+	      else
+		sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
 	    }
 
+	  /* Let's take care of TIMERS */
 	  switch (chunk_type)
 	    {
+	    case COOKIE_ECHO:
+	      {
+		sctp_conn->state = SCTP_STATE_COOKIE_ECHOED;
+		break;
+	      }
 	    case DATA:
 	      {
+		SCTP_ADV_DBG_OUTPUT ("PACKET_LENGTH = %u", packet_length);
+
 		sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX,
 				   sctp_conn->sub_conn[idx].RTO);
 		break;
@@ -1429,6 +1399,11 @@
 		sctp_conn->state = SCTP_STATE_SHUTDOWN_ACK_SENT;
 		break;
 	      }
+	    case SHUTDOWN_COMPLETE:
+	      {
+		sctp_conn->state = SCTP_STATE_CLOSED;
+		break;
+	      }
 	    }
 
 	  vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;