SCTP: Handle a COOKIE ECHO/ACK when a TCB Exists 22/10822/2
authorMarco Varlese <marco.varlese@suse.com>
Mon, 26 Feb 2018 15:33:54 +0000 (16:33 +0100)
committerDamjan Marion <dmarion.lists@gmail.com>
Mon, 26 Feb 2018 22:27:50 +0000 (22:27 +0000)
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>
src/vnet/sctp/sctp.c
src/vnet/sctp/sctp_input.c
src/vnet/sctp/sctp_output.c
src/vnet/sctp/sctp_packet.h

index 046eb18..20f23f2 100644 (file)
@@ -417,24 +417,16 @@ sctp_connection_cleanup (sctp_connection_t * sctp_conn)
                                &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)
-    {
-
-    }
-  else
-    {
-      int thread_index =
-       sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.thread_index;
+  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
index 82adc2a..1863c89 100644 (file)
@@ -282,6 +282,35 @@ sctp_is_bundling (u16 sctp_implied_length,
   return 0;
 }
 
+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,
@@ -980,6 +1009,12 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
                }
              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 @@ sctp46_shutdown_phase_inline (vlib_main_t * vm,
                                  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 @@ sctp46_listen_process_inline (vlib_main_t * vm,
               */
            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 @@ sctp46_established_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
                                  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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 @@ do {                                                              \
   _(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 */
 
index e44451c..5e64ca7 100644 (file)
@@ -588,6 +588,48 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
   vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
+/**
+ * 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
  */
index 8109efc..0cee3f2 100644 (file)
@@ -1315,6 +1315,32 @@ typedef struct
 
 } 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)
  *