SCTP: 'multi-home' support

This patch addresses the SCTP requirement for multiple sub-connections
to implement the so called 'multi-homed' scenario.

Change-Id: Ibce18f216e9d2bebe318992c441bf278e16aad17
Signed-off-by: Marco Varlese <marco.varlese@suse.com>
diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h
index 807cd28..4e01a06 100644
--- a/src/vnet/buffer.h
+++ b/src/vnet/buffer.h
@@ -333,6 +333,7 @@
       u16 hdr_offset;		/**< offset relative to ip hdr */
       u16 data_offset;		/**< offset relative to ip hdr */
       u16 data_len;		/**< data len */
+      u8 conn_idx;
       u8 flags;
     } sctp;
 
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c
index 529e408..224c97d 100644
--- a/src/vnet/sctp/sctp.c
+++ b/src/vnet/sctp/sctp.c
@@ -360,7 +360,7 @@
   uword thread_id;
   int rv;
 
-  u8 idx = sctp_pick_conn_idx_on_state (SCTP_STATE_CLOSED);
+  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
 
   /*
    * Allocate local endpoint
@@ -452,6 +452,21 @@
 u16
 sctp_check_outstanding_data_chunks (sctp_connection_t * sctp_conn)
 {
+  u8 i;
+  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
+    {
+      if (sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN)
+	continue;
+
+      if (sctp_conn->sub_conn[i].is_retransmitting == 1 ||
+	  sctp_conn->sub_conn[i].enqueue_state != SCTP_ERROR_ENQUEUED)
+	{
+	  SCTP_DBG_OUTPUT
+	    ("Connection %u has still DATA to be enqueued inboud / outboud");
+	  return 1;
+	}
+
+    }
   return 0;			/* Indicates no more data to be read/sent */
 }
 
@@ -622,8 +637,6 @@
     case SCTP_TIMER_T1_COOKIE:
     case SCTP_TIMER_T2_SHUTDOWN:
     case SCTP_TIMER_T3_RXTX:
-      clib_smp_atomic_add (&sctp_conn->sub_conn[conn_index].unacknowledged_hb,
-			   1);
       sctp_timer_reset (sctp_conn, conn_index, timer_id);
       break;
     case SCTP_TIMER_T4_HEARTBEAT:
@@ -632,18 +645,34 @@
     }
 
   if (sctp_conn->sub_conn[conn_index].unacknowledged_hb >
-      SCTP_ASSOCIATION_MAX_RETRANS)
+      SCTP_PATH_MAX_RETRANS)
     {
       // The remote-peer is considered to be unreachable hence shutting down
+      u8 i, total_subs_down = 1;
+      for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
+	{
+	  if (sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN)
+	    continue;
 
-      /* Start cleanup. App wasn't notified yet so use delete notify as
-       * opposed to delete to cleanup session layer state. */
-      stream_session_delete_notify (&sctp_conn->sub_conn
-				    [MAIN_SCTP_SUB_CONN_IDX].connection);
+	  u32 now = sctp_time_now ();
+	  if (now > (sctp_conn->sub_conn[i].last_seen + SCTP_HB_INTERVAL))
+	    {
+	      total_subs_down += 1;
+	      sctp_conn->sub_conn[i].state = SCTP_SUBCONN_STATE_DOWN;
+	    }
+	}
 
-      sctp_connection_timers_reset (sctp_conn);
+      if (total_subs_down == MAX_SCTP_CONNECTIONS)
+	{
+	  /* Start cleanup. App wasn't notified yet so use delete notify as
+	   * opposed to delete to cleanup session layer state. */
+	  stream_session_delete_notify (&sctp_conn->sub_conn
+					[MAIN_SCTP_SUB_CONN_IDX].connection);
 
-      sctp_connection_cleanup (sctp_conn);
+	  sctp_connection_timers_reset (sctp_conn);
+
+	  sctp_connection_cleanup (sctp_conn);
+	}
     }
   return;
 
diff --git a/src/vnet/sctp/sctp.h b/src/vnet/sctp/sctp.h
index 25fae37..af652dc 100644
--- a/src/vnet/sctp/sctp.h
+++ b/src/vnet/sctp/sctp.h
@@ -89,6 +89,13 @@
 #define sctp_trajectory_add_start(b, start)
 #endif
 
+enum _sctp_subconn_state
+{
+  SCTP_SUBCONN_STATE_DOWN = 0,
+  SCTP_SUBCONN_STATE_UP,
+  SCTP_SUBCONN_STATE_ALLOW_HB
+};
+
 typedef struct _sctp_sub_connection
 {
   transport_connection_t connection;	      /**< Common transport data. First! */
@@ -122,14 +129,16 @@
   	  	  	  	  Every time the RTT calculation completes (i.e., the DATA chunk is SACK'd),
   	  	  	  	  clear this flag. */
 
-  u32 last_time; /**< The time to which this destination was last sent a packet to.
+  u32 last_seen; /**< The time to which this destination was last sent a packet to.
   	  	  	  	  This can be used to determine if a HEARTBEAT is needed. */
 
   u8 unacknowledged_hb;	/**< Used to track how many unacknowledged heartbeats we had;
-  	  	  	  	  If more than Max.Retransmit then connetion is considered unreachable. */
+  	  	  	  	  If more than SCTP_PATH_MAX_RETRANS then connection is considered unreachable. */
 
   u8 is_retransmitting;	/**< A flag (0 = no, 1 = yes) indicating whether the connection is retransmitting a previous packet */
 
+  u8 enqueue_state;
+
 } sctp_sub_connection_t;
 
 typedef struct
@@ -249,9 +258,9 @@
 u32 sctp_push_header (transport_connection_t * tconn, vlib_buffer_t * b);
 void sctp_send_init (sctp_connection_t * sctp_conn);
 void sctp_send_shutdown (sctp_connection_t * sctp_conn);
-void sctp_send_shutdown_ack (sctp_connection_t * sctp_conn,
+void sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, u8 idx,
 			     vlib_buffer_t * b);
-void sctp_send_shutdown_complete (sctp_connection_t * sctp_conn,
+void sctp_send_shutdown_complete (sctp_connection_t * sctp_conn, u8 idx,
 				  vlib_buffer_t * b0);
 void sctp_send_heartbeat (sctp_connection_t * sctp_conn);
 void sctp_flush_frame_to_output (vlib_main_t * vm, u8 thread_index,
@@ -268,24 +277,25 @@
 u8 *format_sctp_tx_trace (u8 * s, va_list * args);
 
 clib_error_t *sctp_init (vlib_main_t * vm);
-void sctp_connection_timers_init (sctp_connection_t * tc);
-void sctp_connection_timers_reset (sctp_connection_t * tc);
-void sctp_init_snd_vars (sctp_connection_t * tc);
-void sctp_init_mss (sctp_connection_t * tc);
+void sctp_connection_timers_init (sctp_connection_t * sctp_conn);
+void sctp_connection_timers_reset (sctp_connection_t * sctp_conn);
+void sctp_init_snd_vars (sctp_connection_t * sctp_conn);
+void sctp_init_mss (sctp_connection_t * sctp_conn);
 
-void sctp_prepare_initack_chunk (sctp_connection_t * ts, vlib_buffer_t * b,
-				 ip4_address_t * ip4_addr,
+void sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx,
+				 vlib_buffer_t * b, ip4_address_t * ip4_addr,
 				 ip6_address_t * ip6_addr);
-void sctp_prepare_cookie_echo_chunk (sctp_connection_t * tc,
+void sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				     vlib_buffer_t * b,
 				     sctp_state_cookie_param_t * sc);
-void sctp_prepare_cookie_ack_chunk (sctp_connection_t * tc,
+void sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				    vlib_buffer_t * b);
-void sctp_prepare_sack_chunk (sctp_connection_t * tc, vlib_buffer_t * b);
-void sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn,
+void sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, u8 idx,
+			      vlib_buffer_t * b);
+void sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				       vlib_buffer_t * b);
 
-u16 sctp_check_outstanding_data_chunks (sctp_connection_t * tc);
+u16 sctp_check_outstanding_data_chunks (sctp_connection_t * sctp_conn);
 
 #define IP_PROTOCOL_SCTP	132
 
@@ -407,7 +417,11 @@
 #define SCTP_RTO_ALPHA 1/8
 #define SCTP_RTO_BETA 1/4
 #define SCTP_VALID_COOKIE_LIFE 60 * SHZ	/* 60 seconds */
-#define SCTP_ASSOCIATION_MAX_RETRANS 10
+#define SCTP_ASSOCIATION_MAX_RETRANS 10	// the overall connection
+#define SCTP_PATH_MAX_RETRANS 5	// number of attempts per destination address
+#define SCTP_MAX_INIT_RETRANS 8	// number of attempts
+#define SCTP_HB_INTERVAL 30 * SHZ
+#define SCTP_HB_MAX_BURST 1
 
 #define SCTP_TO_TIMER_TICK       SCTP_TICK*10	/* Period for converting from SCTP_TICK */
 
@@ -696,56 +710,64 @@
   return pool_elt_at_index (sctp_main.connections[thread_index], conn_index);
 }
 
-always_inline u8
-sctp_pick_conn_idx_on_chunk (sctp_chunk_type chunk_type)
-{
-  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
+#define SELECT_MAX_RETRIES 8
 
-  switch (chunk_type)
+always_inline u8
+sctp_data_subconn_select (sctp_connection_t * sctp_conn)
+{
+  u8 i = 0;
+  u8 state = SCTP_SUBCONN_STATE_DOWN;
+  u32 sub = MAIN_SCTP_SUB_CONN_IDX;
+  u32 data_subconn_seed = random_default_seed ();
+
+  while (state == SCTP_SUBCONN_STATE_DOWN && i < SELECT_MAX_RETRIES)
     {
-    case DATA:
-    case INIT:
-    case INIT_ACK:
-    case SACK:
-    case HEARTBEAT:
-    case HEARTBEAT_ACK:
-    case ABORT:
-    case SHUTDOWN:
-    case SHUTDOWN_ACK:
-    case OPERATION_ERROR:
-    case COOKIE_ECHO:
-    case COOKIE_ACK:
-    case ECNE:
-    case CWR:
-    case SHUTDOWN_COMPLETE:
-      idx = MAIN_SCTP_SUB_CONN_IDX;
-      break;
-    default:
-      idx = 0;
+      u32 sub = random_u32 (&data_subconn_seed) % MAX_SCTP_CONNECTIONS;
+      if (sctp_conn->sub_conn[sub].state == SCTP_SUBCONN_STATE_UP)
+	break;
+      i++;
     }
-  return idx;
+  return sub;
 }
 
 always_inline u8
-sctp_pick_conn_idx_on_state (sctp_state_t state)
+sctp_sub_conn_id_via_ip6h (sctp_connection_t * sctp_conn, ip6_header_t * ip6h)
 {
-  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
+  u8 i;
 
-  switch (state)
+  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
     {
-    case SCTP_STATE_CLOSED:
-    case SCTP_STATE_COOKIE_WAIT:
-    case SCTP_STATE_COOKIE_ECHOED:
-    case SCTP_STATE_ESTABLISHED:
-    case SCTP_STATE_SHUTDOWN_PENDING:
-    case SCTP_STATE_SHUTDOWN_SENT:
-    case SCTP_STATE_SHUTDOWN_RECEIVED:
-    case SCTP_STATE_SHUTDOWN_ACK_SENT:
-      idx = MAIN_SCTP_SUB_CONN_IDX;
-    default:
-      idx = MAIN_SCTP_SUB_CONN_IDX;
+      if (sctp_conn->sub_conn[i].connection.lcl_ip.ip6.as_u64[0] ==
+	  ip6h->dst_address.as_u64[0] &&
+	  sctp_conn->sub_conn[i].connection.lcl_ip.ip6.as_u64[1] ==
+	  ip6h->dst_address.as_u64[1] &&
+	  sctp_conn->sub_conn[i].connection.rmt_ip.ip6.as_u64[0] ==
+	  ip6h->src_address.as_u64[0] &&
+	  sctp_conn->sub_conn[i].connection.rmt_ip.ip6.as_u64[1] ==
+	  ip6h->src_address.as_u64[1])
+	return i;
     }
-  return idx;
+  clib_warning ("Did not find a sub-connection; defaulting to %u",
+		MAIN_SCTP_SUB_CONN_IDX);
+  return MAIN_SCTP_SUB_CONN_IDX;
+}
+
+always_inline u8
+sctp_sub_conn_id_via_ip4h (sctp_connection_t * sctp_conn, ip4_header_t * ip4h)
+{
+  u8 i;
+
+  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
+    {
+      if (sctp_conn->sub_conn[i].connection.lcl_ip.ip4.as_u32 ==
+	  ip4h->dst_address.as_u32
+	  && sctp_conn->sub_conn[i].connection.rmt_ip.ip4.as_u32 ==
+	  ip4h->src_address.as_u32)
+	return i;
+    }
+  clib_warning ("Did not find a sub-connection; defaulting to %u",
+		MAIN_SCTP_SUB_CONN_IDX);
+  return MAIN_SCTP_SUB_CONN_IDX;
 }
 
 /**
diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c
index 8f4b043..6c80488 100644
--- a/src/vnet/sctp/sctp_input.c
+++ b/src/vnet/sctp/sctp_input.c
@@ -399,7 +399,8 @@
     }
 
   /* Reuse buffer to make init-ack and send */
-  sctp_prepare_initack_chunk (sctp_conn, b0, ip4_addr, ip6_addr);
+  sctp_prepare_initack_chunk (sctp_conn, MAIN_SCTP_SUB_CONN_IDX, b0, ip4_addr,
+			      ip6_addr);
   return SCTP_ERROR_NONE;
 }
 
@@ -530,10 +531,10 @@
 	}
     }
 
-  sctp_prepare_cookie_echo_chunk (sctp_conn, b0, &state_cookie);
+  sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b0, &state_cookie);
 
   /* Start the T1_COOKIE timer */
-  sctp_timer_set (sctp_conn, sctp_pick_conn_idx_on_chunk (COOKIE_ECHO),
+  sctp_timer_set (sctp_conn, idx,
 		  SCTP_TIMER_T1_COOKIE, sctp_conn->sub_conn[idx].RTO);
 
   return SCTP_ERROR_NONE;
@@ -648,7 +649,7 @@
 }
 
 always_inline u8
-sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 is_gapping)
+sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 idx, u8 is_gapping)
 {
   if (is_gapping != 0)
     {
@@ -679,7 +680,7 @@
     {
       SCTP_CONN_TRACKING_DBG
 	("GAPPING: CONN_INDEX = %u, sctp_conn->next_tsn_expected = %u, tsn = %u, diff = %u",
-	 sctp_conn->sub_conn[idx].connection.c_index,
+	 sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.c_index,
 	 sctp_conn->next_tsn_expected, tsn,
 	 sctp_conn->next_tsn_expected - tsn);
 
@@ -723,7 +724,11 @@
     {
       /* In order data, enqueue. Fifo figures out by itself if any out-of-order
        * segments can be enqueued after fifo tail offset changes. */
-      error = sctp_session_enqueue_data (sctp_conn, b, n_data_bytes, idx);
+      if (PREDICT_FALSE (is_gapping == 1))
+	error =
+	  sctp_session_enqueue_data_ooo (sctp_conn, b, n_data_bytes, idx);
+      else
+	error = sctp_session_enqueue_data (sctp_conn, b, n_data_bytes, idx);
     }
   else if (bbit == 1 && ebit == 0)	/* First piece of a fragmented user message */
     {
@@ -751,8 +756,10 @@
 
   SCTP_ADV_DBG ("POINTER_WITH_DATA = %p", b->data);
 
-  if (!sctp_is_sack_delayable (sctp_conn, is_gapping))
-    sctp_prepare_sack_chunk (sctp_conn, b);
+  if (!sctp_is_sack_delayable (sctp_conn, idx, is_gapping))
+    sctp_prepare_sack_chunk (sctp_conn, idx, b);
+
+  sctp_conn->sub_conn[idx].enqueue_state = error;
 
   return error;
 }
@@ -787,10 +794,11 @@
       return SCTP_ERROR_COOKIE_ECHO_VIOLATION;
     }
 
-  sctp_prepare_cookie_ack_chunk (sctp_conn, b0);
+  sctp_prepare_cookie_ack_chunk (sctp_conn, idx, b0);
 
   /* Change state */
   sctp_conn->state = SCTP_STATE_ESTABLISHED;
+  sctp_conn->sub_conn[idx].state = SCTP_SUBCONN_STATE_UP;
   *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4);
 
   sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T4_HEARTBEAT,
@@ -819,6 +827,8 @@
   sctp_timer_reset (sctp_conn, idx, SCTP_TIMER_T1_COOKIE);
   /* Change state */
   sctp_conn->state = SCTP_STATE_ESTABLISHED;
+  sctp_conn->sub_conn[idx].state = SCTP_SUBCONN_STATE_UP;
+
   *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4);
 
   sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T4_HEARTBEAT,
@@ -890,36 +900,18 @@
 	    {
 	      ip4_hdr = vlib_buffer_get_current (b0);
 	      sctp_hdr = ip4_next_header (ip4_hdr);
+	      idx = sctp_sub_conn_id_via_ip4h (sctp_conn, ip4_hdr);
 	    }
 	  else
 	    {
 	      ip6_hdr = vlib_buffer_get_current (b0);
 	      sctp_hdr = ip6_next_header (ip6_hdr);
+	      idx = sctp_sub_conn_id_via_ip6h (sctp_conn, ip6_hdr);
 	    }
-	  idx = sctp_pick_conn_idx_on_state (sctp_conn->state);
 
+	  sctp_conn->sub_conn[idx].parent = sctp_conn;
 	  sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr;
 
-	  transport_connection_t *trans_conn =
-	    &sctp_conn->sub_conn[idx].connection;
-
-	  trans_conn->lcl_port = sctp_hdr->dst_port;
-	  trans_conn->rmt_port = sctp_hdr->src_port;
-	  trans_conn->is_ip4 = is_ip4;
-
-	  if (is_ip4)
-	    {
-	      trans_conn->lcl_ip.ip4.as_u32 = ip4_hdr->dst_address.as_u32;
-	      trans_conn->rmt_ip.ip4.as_u32 = ip4_hdr->src_address.as_u32;
-	    }
-	  else
-	    {
-	      clib_memcpy (&trans_conn->lcl_ip.ip6, &ip6_hdr->dst_address,
-			   sizeof (ip6_address_t));
-	      clib_memcpy (&trans_conn->rmt_ip.ip6, &ip6_hdr->src_address,
-			   sizeof (ip6_address_t));
-	    }
-
 	  sctp_chunk_hdr =
 	    (sctp_chunks_common_hdr_t *) (&full_hdr->common_hdr);
 
@@ -1113,11 +1105,11 @@
     case SCTP_STATE_ESTABLISHED:
       if (sctp_check_outstanding_data_chunks (sctp_conn) == 0)
 	sctp_conn->state = SCTP_STATE_SHUTDOWN_RECEIVED;
-      sctp_send_shutdown_ack (sctp_conn, b0);
+      sctp_send_shutdown_ack (sctp_conn, idx, b0);
       break;
 
     case SCTP_STATE_SHUTDOWN_SENT:
-      sctp_send_shutdown_ack (sctp_conn, b0);
+      sctp_send_shutdown_ack (sctp_conn, idx, b0);
       break;
     }
 
@@ -1156,7 +1148,7 @@
   sctp_timer_reset (sctp_conn, MAIN_SCTP_SUB_CONN_IDX,
 		    SCTP_TIMER_T2_SHUTDOWN);
 
-  sctp_send_shutdown_complete (sctp_conn, b0);
+  sctp_send_shutdown_complete (sctp_conn, idx, b0);
 
   *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4);
 
@@ -1229,6 +1221,7 @@
 	  sctp_connection_t *sctp_conn;
 	  u16 sctp_implied_length = 0;
 	  u16 error0 = SCTP_ERROR_NONE, next0 = SCTP_RCV_PHASE_N_NEXT;
+	  u8 idx = 0;
 
 	  bi0 = from[0];
 	  to_next[0] = bi0;
@@ -1254,11 +1247,13 @@
 	    {
 	      ip4_hdr = vlib_buffer_get_current (b0);
 	      sctp_hdr = ip4_next_header (ip4_hdr);
+	      idx = sctp_sub_conn_id_via_ip4h (sctp_conn, ip4_hdr);
 	    }
 	  else
 	    {
 	      ip6_hdr = vlib_buffer_get_current (b0);
 	      sctp_hdr = ip6_next_header (ip6_hdr);
+	      idx = sctp_sub_conn_id_via_ip6h (sctp_conn, ip6_hdr);
 	    }
 
 	  sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr;
@@ -1267,8 +1262,6 @@
 	  sctp_implied_length =
 	    sctp_calculate_implied_length (ip4_hdr, ip6_hdr, is_ip4);
 
-	  u8 idx = sctp_pick_conn_idx_on_state (sctp_conn->state);
-
 	  u8 chunk_type = vnet_sctp_get_chunk_type (sctp_chunk_hdr);
 	  switch (chunk_type)
 	    {
@@ -1427,6 +1420,8 @@
       return SCTP_ERROR_INVALID_TAG;
     }
 
+  sctp_conn->sub_conn[idx].last_seen = sctp_time_now ();
+
   sctp_calculate_rto (sctp_conn, idx);
 
   sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX,
@@ -1450,7 +1445,7 @@
       return SCTP_ERROR_INVALID_TAG;
     }
 
-  sctp_prepare_heartbeat_ack_chunk (sctp_conn, b0);
+  sctp_prepare_heartbeat_ack_chunk (sctp_conn, idx, b0);
 
   *next0 = sctp_next_output (sctp_conn->sub_conn[idx].connection.is_ip4);
 
@@ -1462,6 +1457,8 @@
 			   sctp_connection_t * sctp_conn, u8 idx,
 			   vlib_buffer_t * b0, u16 * next0)
 {
+  sctp_conn->sub_conn[idx].last_seen = sctp_time_now ();
+
   sctp_conn->sub_conn[idx].unacknowledged_hb -= 1;
 
   sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T4_HEARTBEAT,
@@ -1729,39 +1726,18 @@
 	    {
 	      ip4_hdr = vlib_buffer_get_current (b0);
 	      sctp_hdr = ip4_next_header (ip4_hdr);
+	      idx = sctp_sub_conn_id_via_ip4h (sctp_conn, ip4_hdr);
 	    }
 	  else
 	    {
 	      ip6_hdr = vlib_buffer_get_current (b0);
 	      sctp_hdr = ip6_next_header (ip6_hdr);
+	      idx = sctp_sub_conn_id_via_ip6h (sctp_conn, ip6_hdr);
 	    }
 
-	  idx = sctp_pick_conn_idx_on_state (sctp_conn->state);
-
-	  sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr;
-
-	  transport_connection_t *trans_conn =
-	    &sctp_conn->sub_conn[idx].connection;
-
-	  trans_conn->lcl_port = sctp_hdr->dst_port;
-	  trans_conn->rmt_port = sctp_hdr->src_port;
-	  trans_conn->is_ip4 = is_ip4;
-
 	  sctp_conn->sub_conn[idx].parent = sctp_conn;
 
-	  if (is_ip4)
-	    {
-	      trans_conn->lcl_ip.ip4.as_u32 = ip4_hdr->dst_address.as_u32;
-	      trans_conn->rmt_ip.ip4.as_u32 = ip4_hdr->src_address.as_u32;
-	    }
-	  else
-	    {
-	      clib_memcpy (&trans_conn->lcl_ip.ip6, &ip6_hdr->dst_address,
-			   sizeof (ip6_address_t));
-	      clib_memcpy (&trans_conn->rmt_ip.ip6, &ip6_hdr->src_address,
-			   sizeof (ip6_address_t));
-	    }
-
+	  sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr;
 	  sctp_chunk_hdr =
 	    (sctp_chunks_common_hdr_t *) (&full_hdr->common_hdr);
 
@@ -2106,7 +2082,7 @@
 				      sctp_conn->sub_conn
 				      [idx].connection.c_index,
 				      sctp_state_to_string (sctp_conn->state),
-				      sctp_chunk_to_string (type),
+				      sctp_chunk_to_string (chunk_type),
 				      phase_to_string (next0));
 
 	      if (chunk_type == DATA)
diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c
index 6012e50..459b33d 100644
--- a/src/vnet/sctp/sctp_output.c
+++ b/src/vnet/sctp/sctp_output.c
@@ -261,6 +261,7 @@
   b->current_length = 0;
   b->total_length_not_including_first_buffer = 0;
   vnet_buffer (b)->sctp.flags = 0;
+  vnet_buffer (b)->sctp.conn_idx = MAX_SCTP_CONNECTIONS;
 
   /* Leave enough space for headers */
   return vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
@@ -274,6 +275,7 @@
   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
   b->total_length_not_including_first_buffer = 0;
   vnet_buffer (b)->sctp.flags = 0;
+  vnet_buffer (b)->sctp.conn_idx = MAX_SCTP_CONNECTIONS;
   VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b);
   /* Leave enough space for headers */
   return vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
@@ -410,12 +412,12 @@
  * Convert buffer to INIT
  */
 void
-sctp_prepare_init_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
+sctp_prepare_init_chunk (sctp_connection_t * sctp_conn, u8 idx,
+			 vlib_buffer_t * b)
 {
   u32 random_seed = random_default_seed ();
   u16 alloc_bytes = sizeof (sctp_init_chunk_t);
-  sctp_sub_connection_t *sub_conn =
-    &sctp_conn->sub_conn[sctp_pick_conn_idx_on_chunk (INIT)];
+  sctp_sub_connection_t *sub_conn = &sctp_conn->sub_conn[idx];
 
   sctp_ipv4_addr_param_t *ip4_param = 0;
   sctp_ipv6_addr_param_t *ip6_param = 0;
@@ -476,6 +478,7 @@
   sctp_conn->local_tag = init_chunk->initiate_tag;
 
   vnet_buffer (b)->sctp.connection_index = sub_conn->c_c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 
   SCTP_DBG_STATE_MACHINE ("CONN_INDEX = %u, CURR_CONN_STATE = %u (%s), "
 			  "CHUNK_TYPE = %s, "
@@ -518,11 +521,10 @@
 }
 
 void
-sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn,
+sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 			       vlib_buffer_t * b)
 {
   vlib_main_t *vm = vlib_get_main ();
-  u8 idx = sctp_pick_conn_idx_on_chunk (COOKIE_ACK);
 
   sctp_reuse_buffer (vm, b);
 
@@ -549,15 +551,15 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 void
-sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn,
+sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				vlib_buffer_t * b,
 				sctp_state_cookie_param_t * sc)
 {
   vlib_main_t *vm = vlib_get_main ();
-  u8 idx = sctp_pick_conn_idx_on_chunk (COOKIE_ECHO);
 
   sctp_reuse_buffer (vm, b);
 
@@ -583,20 +585,20 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 /**
  * Convert buffer to INIT-ACK
  */
 void
-sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
-			    ip4_address_t * ip4_addr,
+sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx,
+			    vlib_buffer_t * b, ip4_address_t * ip4_addr,
 			    ip6_address_t * ip6_addr)
 {
   vlib_main_t *vm = vlib_get_main ();
   sctp_ipv4_addr_param_t *ip4_param = 0;
   sctp_ipv6_addr_param_t *ip6_param = 0;
-  u8 idx = sctp_pick_conn_idx_on_chunk (INIT_ACK);
   u32 random_seed = random_default_seed ();
 
   sctp_reuse_buffer (vm, b);
@@ -725,15 +727,16 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 /**
  * Convert buffer to SHUTDOWN
  */
 void
-sctp_prepare_shutdown_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
+sctp_prepare_shutdown_chunk (sctp_connection_t * sctp_conn, u8 idx,
+			     vlib_buffer_t * b)
 {
-  u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN);
   u16 alloc_bytes = sizeof (sctp_shutdown_association_chunk_t);
 
   /* As per RFC 4960 the chunk_length value does NOT contemplate
@@ -760,6 +763,7 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 /*
@@ -779,11 +783,12 @@
   if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
     return;
 
+  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
+
   b = vlib_get_buffer (vm, bi);
   sctp_init_buffer (vm, b);
-  sctp_prepare_shutdown_chunk (sctp_conn, b);
+  sctp_prepare_shutdown_chunk (sctp_conn, idx, b);
 
-  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);
 }
@@ -792,10 +797,9 @@
  * Convert buffer to SHUTDOWN_ACK
  */
 void
-sctp_prepare_shutdown_ack_chunk (sctp_connection_t * sctp_conn,
+sctp_prepare_shutdown_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				 vlib_buffer_t * b)
 {
-  u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN_ACK);
   u16 alloc_bytes = sizeof (sctp_shutdown_association_chunk_t);
   alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes);
 
@@ -817,13 +821,15 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 /*
  * Send SHUTDOWN_ACK
  */
 void
-sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
+sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, u8 idx,
+			vlib_buffer_t * b)
 {
   vlib_main_t *vm = vlib_get_main ();
 
@@ -832,17 +838,17 @@
 
   sctp_reuse_buffer (vm, b);
 
-  sctp_prepare_shutdown_ack_chunk (sctp_conn, b);
+  sctp_prepare_shutdown_ack_chunk (sctp_conn, idx, b);
 }
 
 /**
  * Convert buffer to SACK
  */
 void
-sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
+sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, u8 idx,
+			 vlib_buffer_t * b)
 {
   vlib_main_t *vm = vlib_get_main ();
-  u8 idx = sctp_pick_conn_idx_on_chunk (SACK);
 
   sctp_reuse_buffer (vm, b);
 
@@ -870,18 +876,18 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 /**
  * Convert buffer to HEARTBEAT_ACK
  */
 void
-sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn,
+sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				  vlib_buffer_t * b)
 {
   vlib_main_t *vm = vlib_get_main ();
 
-  u8 idx = sctp_pick_conn_idx_on_chunk (HEARTBEAT_ACK);
   u16 alloc_bytes = sizeof (sctp_hb_ack_chunk_t);
 
   sctp_reuse_buffer (vm, b);
@@ -909,16 +915,16 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 /**
  * Convert buffer to HEARTBEAT
  */
 void
-sctp_prepare_heartbeat_chunk (sctp_connection_t * sctp_conn,
+sctp_prepare_heartbeat_chunk (sctp_connection_t * sctp_conn, u8 idx,
 			      vlib_buffer_t * b)
 {
-  u8 idx = sctp_pick_conn_idx_on_chunk (HEARTBEAT);
   u16 alloc_bytes = sizeof (sctp_hb_req_chunk_t);
 
   /* As per RFC 4960 the chunk_length value does NOT contemplate
@@ -944,6 +950,7 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 void
@@ -954,28 +961,39 @@
   sctp_main_t *tm = vnet_get_sctp_main ();
   vlib_main_t *vm = vlib_get_main ();
 
-  if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
-    return;
+  u8 i;
+  u32 now = sctp_time_now ();
 
-  b = vlib_get_buffer (vm, bi);
-  sctp_init_buffer (vm, b);
-  sctp_prepare_heartbeat_chunk (sctp_conn, b);
+  for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
+    {
+      if (sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN)
+	continue;
 
-  u8 idx = sctp_pick_conn_idx_on_state (SCTP_STATE_ESTABLISHED);
-  sctp_enqueue_to_output_now (vm, b, bi,
-			      sctp_conn->sub_conn[idx].connection.is_ip4);
+      if (now > (sctp_conn->sub_conn[i].last_seen + SCTP_HB_INTERVAL))
+	{
+	  if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
+	    return;
 
-  sctp_conn->sub_conn[idx].unacknowledged_hb += 1;
+	  b = vlib_get_buffer (vm, bi);
+	  sctp_init_buffer (vm, b);
+	  sctp_prepare_heartbeat_chunk (sctp_conn, i, b);
+
+	  sctp_enqueue_to_output_now (vm, b, bi,
+				      sctp_conn->sub_conn[i].
+				      connection.is_ip4);
+
+	  sctp_conn->sub_conn[i].unacknowledged_hb += 1;
+	}
+    }
 }
 
 /**
  * Convert buffer to SHUTDOWN_COMPLETE
  */
 void
-sctp_prepare_shutdown_complete_chunk (sctp_connection_t * sctp_conn,
+sctp_prepare_shutdown_complete_chunk (sctp_connection_t * sctp_conn, u8 idx,
 				      vlib_buffer_t * b)
 {
-  u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN_COMPLETE);
   u16 alloc_bytes = sizeof (sctp_shutdown_association_chunk_t);
   alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes);
 
@@ -997,10 +1015,11 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 void
-sctp_send_shutdown_complete (sctp_connection_t * sctp_conn,
+sctp_send_shutdown_complete (sctp_connection_t * sctp_conn, u8 idx,
 			     vlib_buffer_t * b0)
 {
   vlib_main_t *vm = vlib_get_main ();
@@ -1010,12 +1029,11 @@
 
   sctp_reuse_buffer (vm, b0);
 
-  sctp_prepare_shutdown_complete_chunk (sctp_conn, b0);
+  sctp_prepare_shutdown_complete_chunk (sctp_conn, idx, b0);
 
   sctp_conn->state = SCTP_STATE_CLOSED;
 }
 
-
 /*
  *  Send INIT
  */
@@ -1031,10 +1049,10 @@
     return;
 
   b = vlib_get_buffer (vm, bi);
-  u8 idx = sctp_pick_conn_idx_on_chunk (INIT);
+  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
 
   sctp_init_buffer (vm, b);
-  sctp_prepare_init_chunk (sctp_conn, b);
+  sctp_prepare_init_chunk (sctp_conn, idx, b);
 
   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);
@@ -1099,6 +1117,8 @@
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
+
+  vnet_buffer (b)->sctp.conn_idx = idx;
 }
 
 u32
@@ -1107,29 +1127,13 @@
   sctp_connection_t *sctp_conn =
     sctp_get_connection_from_transport (trans_conn);
 
-  u8 idx = sctp_pick_conn_idx_on_chunk (DATA);
-
-  if (sctp_conn->sub_conn[idx].unacknowledged_hb >
-      SCTP_ASSOCIATION_MAX_RETRANS)
-    {
-      // The remote-peer is considered to be unreachable hence shutting down
-
-      /* Start cleanup. App wasn't notified yet so use delete notify as
-       * opposed to delete to cleanup session layer state. */
-      stream_session_delete_notify (&sctp_conn->sub_conn
-				    [MAIN_SCTP_SUB_CONN_IDX].connection);
-
-      sctp_connection_timers_reset (sctp_conn);
-
-      sctp_connection_cleanup (sctp_conn);
-    }
+  u8 idx = sctp_data_subconn_select (sctp_conn);
 
   sctp_push_hdr_i (sctp_conn, idx, b, SCTP_STATE_ESTABLISHED);
 
   sctp_trajectory_add_start (b0, 3);
 
   return 0;
-
 }
 
 #if SCTP_DEBUG_STATE_MACHINE
@@ -1227,7 +1231,7 @@
 	      goto done;
 	    }
 
-	  u8 idx = sctp_pick_conn_idx_on_state (sctp_conn->state);
+	  u8 idx = vnet_buffer (b0)->sctp.conn_idx;
 
 	  th0 = vlib_buffer_get_current (b0);
 
diff --git a/src/vnet/sctp/sctp_packet.h b/src/vnet/sctp/sctp_packet.h
index 3ca05b5..9419c16 100644
--- a/src/vnet/sctp/sctp_packet.h
+++ b/src/vnet/sctp/sctp_packet.h
@@ -268,13 +268,13 @@
 #define CHUNK_FLAGS_MASK 0x00FF0000
 #define CHUNK_FLAGS_SHIFT 16
 
-#define CHUNK_UBIT_MASK 0x000F0000
+#define CHUNK_UBIT_MASK 0x00040000
 #define CHUNK_UBIT_SHIFT 18
 
-#define CHUNK_BBIT_MASK 0x000F0000
+#define CHUNK_BBIT_MASK 0x00020000
 #define CHUNK_BBIT_SHIFT 17
 
-#define CHUNK_EBIT_MASK 0x000F0000
+#define CHUNK_EBIT_MASK 0x00010000
 #define CHUNK_EBIT_SHIFT 16
 
 #define CHUNK_LENGTH_MASK 0x0000FFFF