SCTP: fix corrupted buffers seen in output node
[vpp.git] / src / vnet / sctp / sctp_output.c
index 276d7e7..6012e50 100644 (file)
@@ -351,13 +351,6 @@ sctp_enqueue_to_output_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
     }
 }
 
-always_inline void
-sctp_enqueue_to_output (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
-                       u8 is_ip4)
-{
-  sctp_enqueue_to_output_i (vm, b, bi, is_ip4, 0);
-}
-
 always_inline void
 sctp_enqueue_to_output_now (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
                            u8 is_ip4)
@@ -503,13 +496,12 @@ sctp_compute_mac (sctp_connection_t * sctp_conn,
   HMAC_CTX *ctx;
 #else
   HMAC_CTX ctx;
-  const EVP_MD *md = EVP_sha1 ();
 #endif
   unsigned int len = 0;
-
+  const EVP_MD *md = EVP_sha1 ();
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
   ctx = HMAC_CTX_new ();
-  HMAC_Init_ex (&ctx, &state_cookie->creation_time,
+  HMAC_Init_ex (ctx, &state_cookie->creation_time,
                sizeof (state_cookie->creation_time), md, NULL);
   HMAC_Update (ctx, (const unsigned char *) &sctp_conn, sizeof (sctp_conn));
   HMAC_Final (ctx, state_cookie->mac, &len);
@@ -517,7 +509,6 @@ sctp_compute_mac (sctp_connection_t * sctp_conn,
   HMAC_CTX_init (&ctx);
   HMAC_Init_ex (&ctx, &state_cookie->creation_time,
                sizeof (state_cookie->creation_time), md, NULL);
-
   HMAC_Update (&ctx, (const unsigned char *) &sctp_conn, sizeof (sctp_conn));
   HMAC_Final (&ctx, state_cookie->mac, &len);
   HMAC_CTX_cleanup (&ctx);
@@ -556,9 +547,6 @@ sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn,
   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;
 }
@@ -593,9 +581,6 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn,
   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;
 }
@@ -738,9 +723,6 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
 
   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;
 }
@@ -804,9 +786,6 @@ sctp_send_shutdown (sctp_connection_t * sctp_conn)
   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 ();
 }
 
 /**
@@ -854,11 +833,6 @@ sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
   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 ();
 }
 
 /**
@@ -1026,23 +1000,17 @@ sctp_prepare_shutdown_complete_chunk (sctp_connection_t * sctp_conn,
 }
 
 void
-sctp_send_shutdown_complete (sctp_connection_t * sctp_conn)
+sctp_send_shutdown_complete (sctp_connection_t * sctp_conn,
+                            vlib_buffer_t * b0)
 {
-  vlib_buffer_t *b;
-  u32 bi;
-  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)))
+  if (sctp_check_outstanding_data_chunks (sctp_conn) > 0)
     return;
 
-  b = vlib_get_buffer (vm, bi);
-  sctp_init_buffer (vm, b);
-  sctp_prepare_shutdown_complete_chunk (sctp_conn, b);
+  sctp_reuse_buffer (vm, b0);
 
-  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_prepare_shutdown_complete_chunk (sctp_conn, b0);
 
   sctp_conn->state = SCTP_STATE_CLOSED;
 }
@@ -1071,15 +1039,15 @@ sctp_send_init (sctp_connection_t * sctp_conn)
   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 ();
 }
 
 /**
@@ -1158,18 +1126,53 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b)
 
   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;
 
 }
 
+#if SCTP_DEBUG_STATE_MACHINE
+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;
+}
+#endif
+
+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,
@@ -1281,6 +1284,18 @@ sctp46_output_inline (vlib_main_t * vm,
 #endif
            }
 
+         sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr;
+         u8 chunk_type = vnet_sctp_get_chunk_type (&full_hdr->common_hdr);
+         if (chunk_type >= UNKNOWN)
+           {
+             clib_warning
+               ("Trying to send an unrecognized chunk... something is really bad.");
+             error0 = SCTP_ERROR_UNKOWN_CHUNK;
+             next0 = SCTP_OUTPUT_NEXT_DROP;
+             goto done;
+           }
+
+#if SCTP_DEBUG_STATE_MACHINE
          u8 is_valid =
            (sctp_conn->sub_conn[idx].connection.lcl_port ==
             sctp_hdr->src_port
@@ -1291,9 +1306,6 @@ sctp46_output_inline (vlib_main_t * vm,
                || sctp_conn->sub_conn[idx].connection.rmt_port ==
                sctp_hdr->src_port);
 
-         sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr;
-         u8 chunk_type = vnet_sctp_get_chunk_type (&full_hdr->common_hdr);
-
          if (!is_valid)
            {
              SCTP_DBG_STATE_MACHINE ("BUFFER IS INCORRECT: conn_index = %u, "
@@ -1301,8 +1313,8 @@ sctp46_output_inline (vlib_main_t * vm,
                                      "chunk_type = %u [%s], "
                                      "connection.lcl_port = %u, sctp_hdr->src_port = %u, "
                                      "connection.rmt_port = %u, sctp_hdr->dst_port = %u",
-                                     sctp_conn->sub_conn
-                                     [idx].connection.c_index, packet_length,
+                                     sctp_conn->sub_conn[idx].
+                                     connection.c_index, packet_length,
                                      chunk_type,
                                      sctp_chunk_to_string (chunk_type),
                                      sctp_conn->sub_conn[idx].
@@ -1315,7 +1327,7 @@ sctp46_output_inline (vlib_main_t * vm,
              next0 = SCTP_OUTPUT_NEXT_DROP;
              goto done;
            }
-
+#endif
          SCTP_DBG_STATE_MACHINE
            ("CONN_INDEX = %u, CURR_CONN_STATE = %u (%s), "
             "CHUNK_TYPE = %s, " "SRC_PORT = %u, DST_PORT = %u",
@@ -1324,93 +1336,49 @@ sctp46_output_inline (vlib_main_t * vm,
             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;
@@ -1431,6 +1399,11 @@ sctp46_output_inline (vlib_main_t * vm,
                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;