SCTP: Handle a COOKIE ECHO/ACK when a TCB Exists

This patch addresses the requirements depicted in section 5.2.4 of the
RFC 4960. It also takes care of handling the ERROR chunk and obviously
the STALE COOKIE error.

Change-Id: I6b88a9371546b18a52abac22f7c593a5f16be838
Signed-off-by: Marco Varlese <marco.varlese@suse.com>
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c
index 046eb18..20f23f2 100644
--- a/src/vnet/sctp/sctp.c
+++ b/src/vnet/sctp/sctp.c
@@ -417,24 +417,16 @@
 				&sctp_conn->sub_conn[i].connection.lcl_ip,
 				sctp_conn->sub_conn[i].connection.lcl_port);
 
-  /* Check if connection is not yet fully established */
-  if (sctp_conn->state == SCTP_STATE_COOKIE_WAIT)
-    {
+  int thread_index =
+    sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.thread_index;
 
-    }
-  else
-    {
-      int thread_index =
-	sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.thread_index;
+  /* Make sure all timers are cleared */
+  sctp_connection_timers_reset (sctp_conn);
 
-      /* Make sure all timers are cleared */
-      sctp_connection_timers_reset (sctp_conn);
-
-      /* Poison the entry */
-      if (CLIB_DEBUG > 0)
-	memset (sctp_conn, 0xFA, sizeof (*sctp_conn));
-      pool_put (tm->connections[thread_index], sctp_conn);
-    }
+  /* Poison the entry */
+  if (CLIB_DEBUG > 0)
+    memset (sctp_conn, 0xFA, sizeof (*sctp_conn));
+  pool_put (tm->connections[thread_index], sctp_conn);
 }
 
 int
diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c
index 82adc2a..1863c89 100644
--- a/src/vnet/sctp/sctp_input.c
+++ b/src/vnet/sctp/sctp_input.c
@@ -283,6 +283,35 @@
 }
 
 always_inline u16
+sctp_handle_operation_err (sctp_header_t * sctp_hdr,
+			   sctp_connection_t * sctp_conn, u8 idx,
+			   vlib_buffer_t * b, u16 * next0)
+{
+  sctp_operation_error_t *op_err = (sctp_operation_error_t *) sctp_hdr;
+
+  /* Check that the LOCALLY generated tag is being used by the REMOTE peer as the verification tag */
+  if (sctp_conn->local_tag != sctp_hdr->verification_tag)
+    {
+      return SCTP_ERROR_INVALID_TAG;
+    }
+
+  if (op_err->err_causes[0].cause_info == STALE_COOKIE_ERROR)
+    {
+      if (sctp_conn->state != SCTP_STATE_COOKIE_ECHOED)
+	*next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4);
+      else
+	{
+	  sctp_connection_cleanup (sctp_conn);
+
+	  stream_session_disconnect_notify (&sctp_conn->
+					    sub_conn[idx].connection);
+	}
+    }
+
+  return SCTP_ERROR_NONE;
+}
+
+always_inline u16
 sctp_handle_init (sctp_header_t * sctp_hdr,
 		  sctp_chunks_common_hdr_t * sctp_chunk_hdr,
 		  sctp_connection_t * sctp_conn, vlib_buffer_t * b0,
@@ -980,6 +1009,12 @@
 		}
 	      break;
 
+	    case OPERATION_ERROR:
+	      error0 =
+		sctp_handle_operation_err (sctp_hdr, sctp_conn, idx, b0,
+					   &next0);
+	      break;
+
 	      /* All UNEXPECTED scenarios (wrong chunk received per state-machine)
 	       * are handled by the input-dispatcher function using the table-lookup
 	       * hence we should never get to the "default" case below.
@@ -1309,6 +1344,12 @@
 				  sctp_conn, idx, b0, &next0);
 	      break;
 
+	    case OPERATION_ERROR:
+	      error0 =
+		sctp_handle_operation_err (sctp_hdr, sctp_conn, idx, b0,
+					   &next0);
+	      break;
+
 	      /* All UNEXPECTED scenarios (wrong chunk received per state-machine)
 	       * are handled by the input-dispatcher function using the table-lookup
 	       * hence we should never get to the "default" case below.
@@ -1660,6 +1701,13 @@
 	       */
 	    case DATA:
 	      break;
+
+	    case OPERATION_ERROR:
+	      error0 =
+		sctp_handle_operation_err (sctp_hdr, child_conn,
+					   MAIN_SCTP_SUB_CONN_IDX, b0,
+					   &next0);
+	      break;
 	    }
 
 	drop:
@@ -1807,6 +1855,12 @@
 				  sctp_conn, idx, b0, &next0);
 	      break;
 
+	    case OPERATION_ERROR:
+	      error0 =
+		sctp_handle_operation_err (sctp_hdr, sctp_conn, idx, b0,
+					   &next0);
+	      break;
+
 	      /* All UNEXPECTED scenarios (wrong chunk received per state-machine)
 	       * are handled by the input-dispatcher function using the table-lookup
 	       * hence we should never get to the "default" case below.
@@ -2261,8 +2315,9 @@
   _(CLOSED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(CLOSED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(CLOSED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(CLOSED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, SCTP_ERROR_NONE);
 
-  _(COOKIE_WAIT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_NONE);
+  _(COOKIE_WAIT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_NONE);	/* UNEXPECTED DATA chunk which requires special handling */
   _(COOKIE_WAIT, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE);	/* UNEXPECTED INIT chunk which requires special handling */
   _(COOKIE_WAIT, INIT_ACK, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE);
   _(COOKIE_WAIT, SACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SACK_CHUNK_VIOLATION);	/* UNEXPECTED SACK chunk */
@@ -2277,6 +2332,8 @@
   _(COOKIE_WAIT, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(COOKIE_WAIT, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(COOKIE_WAIT, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(COOKIE_WAIT, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   _(COOKIE_ECHOED, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_NONE);
   _(COOKIE_ECHOED, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE);	/* UNEXPECTED INIT chunk which requires special handling */
@@ -2294,6 +2351,8 @@
   _(COOKIE_ECHOED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(COOKIE_ECHOED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(COOKIE_ECHOED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(COOKIE_ECHOED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   _(ESTABLISHED, DATA, SCTP_INPUT_NEXT_ESTABLISHED_PHASE, SCTP_ERROR_NONE);
   _(ESTABLISHED, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION);	/* UNEXPECTED INIT chunk */
@@ -2312,6 +2371,8 @@
   _(ESTABLISHED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(ESTABLISHED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(ESTABLISHED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(ESTABLISHED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   _(SHUTDOWN_PENDING, DATA, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE);
   _(SHUTDOWN_PENDING, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION);	/* UNEXPECTED INIT chunk */
@@ -2331,6 +2392,8 @@
   _(SHUTDOWN_PENDING, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(SHUTDOWN_PENDING, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(SHUTDOWN_PENDING, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(SHUTDOWN_PENDING, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   _(SHUTDOWN_SENT, DATA, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE);
   _(SHUTDOWN_SENT, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION);	/* UNEXPECTED INIT chunk */
@@ -2347,6 +2410,8 @@
   _(SHUTDOWN_SENT, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(SHUTDOWN_SENT, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(SHUTDOWN_SENT, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(SHUTDOWN_SENT, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   _(SHUTDOWN_RECEIVED, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_DATA_CHUNK_VIOLATION);	/* UNEXPECTED DATA chunk */
   _(SHUTDOWN_RECEIVED, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION);	/* UNEXPECTED INIT chunk */
@@ -2363,6 +2428,8 @@
   _(SHUTDOWN_RECEIVED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION);	/* UNEXPECTED ECNE chunk */
   _(SHUTDOWN_RECEIVED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(SHUTDOWN_RECEIVED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION);	/* UNEXPECTED SHUTDOWN_COMPLETE chunk */
+  _(SHUTDOWN_RECEIVED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   _(SHUTDOWN_ACK_SENT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_DATA_CHUNK_VIOLATION);	/* UNEXPECTED DATA chunk */
   _(SHUTDOWN_ACK_SENT, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE);	/* UNEXPECTED INIT chunk */
@@ -2379,6 +2446,8 @@
   _(SHUTDOWN_ACK_SENT, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION);	/* UNEXPECTED CWR chunk */
   _(SHUTDOWN_ACK_SENT, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_SHUTDOWN_PHASE,
     SCTP_ERROR_NONE);
+  _(SHUTDOWN_ACK_SENT, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE,
+    SCTP_ERROR_NONE);
 
   /* TODO: Handle COOKIE ECHO when a TCB Exists */
 
diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c
index e44451c..5e64ca7 100644
--- a/src/vnet/sctp/sctp_output.c
+++ b/src/vnet/sctp/sctp_output.c
@@ -591,6 +591,48 @@
 /**
  * Convert buffer to ABORT
  */
+/*
+void
+sctp_prepare_operation_error (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_reuse_buffer (vm, b);
+
+  // The minimum size of the message is given by the sctp_operation_error_t
+  u16 alloc_bytes = sizeof (sctp_operation_error_t);
+
+  // As per RFC 4960 the chunk_length value does NOT contemplate
+  // the size of the first header (see sctp_header_t) and any padding
+  //
+  u16 chunk_len = alloc_bytes - sizeof (sctp_header_t);
+
+  alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes);
+
+  sctp_operation_error_t *err_chunk =
+    vlib_buffer_push_uninit (b, alloc_bytes);
+
+  // src_port & dst_port are already in network byte-order
+  err_chunk->sctp_hdr.checksum = 0;
+  err_chunk->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port;
+  err_chunk->sctp_hdr.dst_port = sctp_conn->sub_conn[idx].connection.rmt_port;
+  // As per RFC4960 Section 5.2.2: copy the INITIATE_TAG into the VERIFICATION_TAG of the ABORT chunk
+  err_chunk->sctp_hdr.verification_tag = sctp_conn->local_tag;
+
+  vnet_sctp_set_chunk_type (&err_chunk->chunk_hdr, OPERATION_ERROR);
+  vnet_sctp_set_chunk_length (&err_chunk->chunk_hdr, chunk_len);
+
+  vnet_buffer (b)->sctp.connection_index =
+    sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
+}
+*/
+
+/**
+ * Convert buffer to ABORT
+ */
 void
 sctp_prepare_abort_for_collision (sctp_connection_t * sctp_conn, u8 idx,
 				  vlib_buffer_t * b, ip4_address_t * ip4_addr,
diff --git a/src/vnet/sctp/sctp_packet.h b/src/vnet/sctp/sctp_packet.h
index 8109efc..0cee3f2 100644
--- a/src/vnet/sctp/sctp_packet.h
+++ b/src/vnet/sctp/sctp_packet.h
@@ -1315,6 +1315,32 @@
 
 } sctp_err_cause_param_t;
 
+
+/*
+ * An end-point sends this chunk to its peer end-point to notify it of
+ * certain error conditions.  It contains one or more error causes.
+ * An Operation Error is not considered fatal in and of itself, but may be
+ * used with an ABORT chunk to report a fatal condition.  It has the
+ * following parameters:
+ *
+ * 0                   1                   2                   3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |   Type = 9    | Chunk  Flags  |           Length              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * \                                                               \
+ * /                    one or more Error Causes                   /
+ * \                                                               \
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+typedef struct
+{
+  sctp_header_t sctp_hdr;
+  sctp_chunks_common_hdr_t chunk_hdr;
+  sctp_err_cause_param_t err_causes[];
+
+} sctp_operation_error_t;
+
 /*
  * Abort Association (ABORT)
  *