session tcp: handle rxt and acks as custom events
[vpp.git] / src / plugins / quic / quic.c
index 8732b7d..435113a 100644 (file)
@@ -30,6 +30,8 @@
 #include <picotls/openssl.h>
 #include <picotls/pembase64.h>
 
+#include <quic/quic_crypto.h>
+
 static quic_main_t quic_main;
 static void quic_update_timer (quic_ctx_t * ctx);
 
@@ -83,8 +85,8 @@ quic_format_err (u8 * s, va_list * args)
     case QUICLY_TRANSPORT_ERROR_FLOW_CONTROL:
       s = format (s, "QUICLY_TRANSPORT_ERROR_FLOW_CONTROL");
       break;
-    case QUICLY_TRANSPORT_ERROR_STREAM_ID:
-      s = format (s, "QUICLY_TRANSPORT_ERROR_STREAM_ID");
+    case QUICLY_TRANSPORT_ERROR_STREAM_LIMIT:
+      s = format (s, "QUICLY_TRANSPORT_ERROR_STREAM_LIMIT");
       break;
     case QUICLY_TRANSPORT_ERROR_STREAM_STATE:
       s = format (s, "QUICLY_TRANSPORT_ERROR_STREAM_STATE");
@@ -306,6 +308,14 @@ quic_ctx_get (u32 ctx_index, u32 thread_index)
   return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
 }
 
+static quic_ctx_t *
+quic_ctx_get_if_valid (u32 ctx_index, u32 thread_index)
+{
+  if (pool_is_free_index (quic_main.ctx_pool[thread_index], ctx_index))
+    return 0;
+  return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
+}
+
 static quic_ctx_t *
 quic_get_conn_ctx (quicly_conn_t * conn)
 {
@@ -402,42 +412,88 @@ quic_disconnect_transport (quic_ctx_t * ctx)
 }
 
 static void
-quic_connection_closed (u32 ctx_index, u32 thread_index, u8 notify_transport)
+quic_connection_delete (quic_ctx_t * ctx)
 {
-  QUIC_DBG (2, "QUIC connection closed");
   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
   clib_bihash_kv_16_8_t kv;
   quicly_conn_t *conn;
-  quic_ctx_t *ctx;
 
-  ctx = quic_ctx_get (ctx_index, thread_index);
+  QUIC_DBG (2, "Deleting connection %u", ctx->c_c_index);
+
   ASSERT (!quic_ctx_is_stream (ctx));
-  /*  TODO if connection is not established, just delete the session? */
 
   /*  Stop the timer */
   if (ctx->timer_handle != QUIC_TIMER_HANDLE_INVALID)
     {
-      tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
+      tw = &quic_main.wrk_ctx[ctx->c_thread_index].timer_wheel;
       tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->timer_handle);
     }
 
   /*  Delete the connection from the connection map */
   conn = ctx->c_quic_ctx_id.conn;
   quic_make_connection_key (&kv, quicly_get_master_id (conn));
-  QUIC_DBG (2, "Deleting conn with id %lu %lu", kv.key[0], kv.key[1]);
+  QUIC_DBG (2, "Deleting conn with id %lu %lu from map", kv.key[0],
+           kv.key[1]);
   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 0 /* is_add */ );
 
   quic_disconnect_transport (ctx);
-  if (notify_transport)
-    session_transport_closing_notify (&ctx->connection);
-  else
-    session_transport_delete_notify (&ctx->connection);
-  /*  Do not try to send anything anymore */
-  quicly_free (ctx->c_quic_ctx_id.conn);
+
+  if (ctx->c_quic_ctx_id.conn)
+    quicly_free (ctx->c_quic_ctx_id.conn);
   ctx->c_quic_ctx_id.conn = NULL;
+
+  session_transport_delete_notify (&ctx->connection);
   quic_ctx_free (ctx);
 }
 
+/**
+ * Called when quicly return an error
+ * This function interacts tightly with quic_proto_on_close
+ */
+static void
+quic_connection_closed (quic_ctx_t * ctx)
+{
+  QUIC_DBG (2, "QUIC connection %u/%u closed", ctx->c_thread_index,
+           ctx->c_c_index);
+
+  /* TODO if connection is not established, just delete the session? */
+  /* Actually should send connect or accept error */
+
+  switch (ctx->c_quic_ctx_id.conn_state)
+    {
+    case QUIC_CONN_STATE_READY:
+      /* Error on an opened connection (timeout...)
+         This puts the session in closing state, we should receive a notification
+         when the app has closed its session */
+      session_transport_reset_notify (&ctx->connection);
+      /* This ensures we delete the connection when the app confirms the close */
+      ctx->c_quic_ctx_id.conn_state =
+       QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED;
+      break;
+    case QUIC_CONN_STATE_PASSIVE_CLOSING:
+      ctx->c_quic_ctx_id.conn_state =
+       QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED;
+      /* quic_proto_on_close will eventually be called when the app confirms the close
+         , we delete the connection at that point */
+      break;
+    case QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED:
+      /* App already confirmed close, we can delete the connection */
+      session_transport_delete_notify (&ctx->connection);
+      quic_connection_delete (ctx);
+      break;
+    case QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED:
+      QUIC_DBG (0, "BUG");
+      break;
+    case QUIC_CONN_STATE_ACTIVE_CLOSING:
+      session_transport_delete_notify (&ctx->connection);
+      quic_connection_delete (ctx);
+      break;
+    default:
+      QUIC_DBG (0, "BUG");
+      break;
+    }
+}
+
 static int
 quic_send_datagram (session_t * udp_session, quicly_datagram_t * packet)
 {
@@ -534,8 +590,7 @@ quic_send_packets (quic_ctx_t * ctx)
     {
       clib_warning ("Tried to send packets on non existing app worker %u",
                    ctx->parent_app_wrk_id);
-      quic_connection_closed (ctx->c_c_index, ctx->c_thread_index,
-                             1 /* notify_transport */ );
+      quic_connection_delete (ctx);
       return 1;
     }
   app = application_get (app_wrk->app_index);
@@ -577,8 +632,7 @@ quicly_error:
   if (err && err != QUICLY_ERROR_PACKET_IGNORED
       && err != QUICLY_ERROR_FREE_CONNECTION)
     clib_warning ("Quic error '%U'.", quic_format_err, err);
-  quic_connection_closed (ctx->c_c_index, ctx->c_thread_index,
-                         1 /* notify_transport */ );
+  quic_connection_closed (ctx);
   return 1;
 }
 
@@ -1298,8 +1352,8 @@ allocate_quicly_ctx (application_t * app, u8 is_client)
   quicly_ctx->event_log.cb = quicly_new_default_event_logger (stderr);
 
   quicly_ctx->transport_params.max_data = QUIC_INT_MAX;
-  quicly_ctx->transport_params.max_streams_uni = QUIC_INT_MAX;
-  quicly_ctx->transport_params.max_streams_bidi = QUIC_INT_MAX;
+  quicly_ctx->transport_params.max_streams_uni = (uint64_t) 1 << 60;
+  quicly_ctx->transport_params.max_streams_bidi = (uint64_t) 1 << 60;
   quicly_ctx->transport_params.max_stream_data.bidi_local = (QUIC_FIFO_SIZE - 1);      /* max_enq is SIZE - 1 */
   quicly_ctx->transport_params.max_stream_data.bidi_remote = (QUIC_FIFO_SIZE - 1);     /* max_enq is SIZE - 1 */
   quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX;
@@ -1324,10 +1378,10 @@ allocate_quicly_ctx (application_t * app, u8 is_client)
  *****************************************************************************/
 
 static int
-quic_connect_new_stream (session_endpoint_cfg_t * sep)
+quic_connect_new_stream (session_t * quic_session, u32 opaque)
 {
   uint64_t quic_session_handle;
-  session_t *quic_session, *stream_session;
+  session_t *stream_session;
   quic_stream_data_t *stream_data;
   quicly_stream_t *stream;
   quicly_conn_t *conn;
@@ -1337,12 +1391,11 @@ quic_connect_new_stream (session_endpoint_cfg_t * sep)
   int rv;
 
   /*  Find base session to which the user want to attach a stream */
-  quic_session_handle = sep->transport_opts;
-  QUIC_DBG (2, "Opening new stream (qsession %u)", sep->transport_opts);
-  quic_session = session_get_from_handle (quic_session_handle);
+  quic_session_handle = session_handle (quic_session);
+  QUIC_DBG (2, "Opening new stream (qsession %u)", quic_session_handle);
 
-  if (quic_session->session_type !=
-      session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, sep->is_ip4))
+  if (session_type_transport_proto (quic_session->session_type) !=
+      TRANSPORT_PROTO_QUIC)
     {
       QUIC_DBG (1, "received incompatible session");
       return -1;
@@ -1405,7 +1458,7 @@ quic_connect_new_stream (session_endpoint_cfg_t * sep)
       quicly_reset_stream (stream, QUIC_APP_ALLOCATION_ERROR);
       session_free_w_fifos (stream_session);
       quic_ctx_free (sctx);
-      return app_worker_connect_notify (app_wrk, NULL, sep->opaque);
+      return app_worker_connect_notify (app_wrk, NULL, opaque);
     }
 
   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
@@ -1413,7 +1466,7 @@ quic_connect_new_stream (session_endpoint_cfg_t * sep)
                             SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
 
   stream_session->session_state = SESSION_STATE_READY;
-  if (app_worker_connect_notify (app_wrk, stream_session, sep->opaque))
+  if (app_worker_connect_notify (app_wrk, stream_session, opaque))
     {
       QUIC_DBG (1, "failed to notify app");
       quicly_reset_stream (stream, QUIC_APP_CONNECT_NOTIFY_ERROR);
@@ -1484,9 +1537,12 @@ quic_connect (transport_endpoint_cfg_t * tep)
 {
   QUIC_DBG (2, "Called quic_connect");
   session_endpoint_cfg_t *sep = (session_endpoint_cfg_t *) tep;
+  session_t *quic_session;
   sep = (session_endpoint_cfg_t *) tep;
-  if (sep->transport_opts)
-    return quic_connect_new_stream (sep);
+
+  quic_session = session_get_from_handle_if_valid (sep->parent_handle);
+  if (quic_session)
+    return quic_connect_new_stream (quic_session, sep->opaque);
   else
     return quic_connect_new_connection (sep);
 }
@@ -1494,7 +1550,9 @@ quic_connect (transport_endpoint_cfg_t * tep)
 static void
 quic_proto_on_close (u32 ctx_index, u32 thread_index)
 {
-  quic_ctx_t *ctx = quic_ctx_get (ctx_index, thread_index);
+  quic_ctx_t *ctx = quic_ctx_get_if_valid (ctx_index, thread_index);
+  if (!ctx)
+    return;
 #if QUIC_DEBUG >= 2
   session_t *stream_session =
     session_get (ctx->c_s_index, ctx->c_thread_index);
@@ -1506,17 +1564,30 @@ quic_proto_on_close (u32 ctx_index, u32 thread_index)
       quicly_reset_stream (stream, QUIC_APP_ERROR_CLOSE_NOTIFY);
       quic_send_packets (ctx);
     }
-  else if (ctx->c_quic_ctx_id.conn_state == QUIC_CONN_STATE_PASSIVE_CLOSING)
-    quic_connection_closed (ctx->c_c_index, ctx->c_thread_index,
-                           0 /* notify_transport */ );
-  else
+
+  switch (ctx->c_quic_ctx_id.conn_state)
     {
+    case QUIC_CONN_STATE_READY:
+      ctx->c_quic_ctx_id.conn_state = QUIC_CONN_STATE_ACTIVE_CLOSING;
       quicly_conn_t *conn = ctx->c_quic_ctx_id.conn;
       /* Start connection closing. Keep sending packets until quicly_send
          returns QUICLY_ERROR_FREE_CONNECTION */
       quicly_close (conn, QUIC_APP_ERROR_CLOSE_NOTIFY, "Closed by peer");
       /* This also causes all streams to be closed (and the cb called) */
       quic_send_packets (ctx);
+      break;
+    case QUIC_CONN_STATE_PASSIVE_CLOSING:
+      ctx->c_quic_ctx_id.conn_state =
+       QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED;
+      /* send_packets will eventually return an error, we delete the conn at
+         that point */
+      break;
+    case QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED:
+      quic_connection_delete (ctx);
+      break;
+    default:
+      QUIC_DBG (0, "BUG");
+      break;
     }
 }
 
@@ -1622,32 +1693,30 @@ format_quic_ctx (u8 * s, va_list * args)
 {
   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
   u32 verbose = va_arg (*args, u32);
+  u8 *str = 0;
 
   if (!ctx)
     return s;
-  s = format (s, "[#%d][Q] ", ctx->c_thread_index);
+  str = format (str, "[#%d][Q] ", ctx->c_thread_index);
 
-  if (!quic_ctx_is_listener (ctx))
-    {
-      s = format (s, "%s Session: ", quic_ctx_is_stream (ctx) ?
-                 "Stream" : "Quic");
-      if (verbose)
-       s = format (s, "app %d wrk %d", ctx->parent_app_id,
-                   ctx->parent_app_wrk_id);
-    }
+  if (quic_ctx_is_listener (ctx))
+    str = format (str, "Listener, UDP %ld", ctx->udp_session_handle);
+  else if (quic_ctx_is_stream (ctx))
+    str = format (str, "Stream %ld conn %d",
+                 ctx->c_quic_ctx_id.stream->stream_id,
+                 ctx->c_quic_ctx_id.quic_connection_ctx_id);
+  else                         /* connection */
+    str = format (str, "Conn %d UDP %d", ctx->c_c_index,
+                 ctx->udp_session_handle);
+
+  str = format (str, " app %d wrk %d", ctx->parent_app_id,
+               ctx->parent_app_wrk_id);
+
+  if (verbose == 1)
+    s = format (s, "%-50s%-15d", str, ctx->c_quic_ctx_id.conn_state);
   else
-    {
-      if (ctx->c_is_ip4)
-       s = format (s, "%U:%d->%U:%d", format_ip4_address, &ctx->c_lcl_ip4,
-                   clib_net_to_host_u16 (ctx->c_lcl_port),
-                   format_ip4_address, &ctx->c_rmt_ip4,
-                   clib_net_to_host_u16 (ctx->c_rmt_port));
-      else
-       s = format (s, "%U:%d->%U:%d", format_ip6_address, &ctx->c_lcl_ip6,
-                   clib_net_to_host_u16 (ctx->c_lcl_port),
-                   format_ip6_address, &ctx->c_rmt_ip6,
-                   clib_net_to_host_u16 (ctx->c_rmt_port));
-    }
+    s = format (s, "%s\n", str);
+  vec_free (str);
   return s;
 }
 
@@ -1658,8 +1727,7 @@ format_quic_connection (u8 * s, va_list * args)
   u32 thread_index = va_arg (*args, u32);
   u32 verbose = va_arg (*args, u32);
   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
-  if (ctx)
-    s = format (s, "%-50U", format_quic_ctx, ctx, verbose);
+  s = format (s, "%U", format_quic_ctx, ctx, verbose);
   return s;
 }
 
@@ -1667,8 +1735,10 @@ static u8 *
 format_quic_half_open (u8 * s, va_list * args)
 {
   u32 qc_index = va_arg (*args, u32);
-  quic_ctx_t *ctx = quic_ctx_get (qc_index, vlib_get_thread_index ());
-  s = format (s, "[QUIC] half-open app %u", ctx->parent_app_id);
+  u32 thread_index = va_arg (*args, u32);
+  quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
+  s =
+    format (s, "[#%d][Q] half-open app %u", thread_index, ctx->parent_app_id);
   return s;
 }
 
@@ -1677,13 +1747,10 @@ static u8 *
 format_quic_listener (u8 * s, va_list * args)
 {
   u32 tci = va_arg (*args, u32);
+  u32 thread_index = va_arg (*args, u32);
   u32 verbose = va_arg (*args, u32);
-  quic_ctx_t *ctx = quic_ctx_get (tci, vlib_get_thread_index ());
-  if (ctx)
-    {
-      ASSERT (quic_ctx_is_listener (ctx));
-      s = format (s, "%-50U", format_quic_ctx, ctx, verbose);
-    }
+  quic_ctx_t *ctx = quic_ctx_get (tci, thread_index);
+  s = format (s, "%U", format_quic_ctx, ctx, verbose);
   return s;
 }
 
@@ -2033,7 +2100,7 @@ quic_custom_app_rx_callback (transport_connection_t * tc)
 }
 
 static int
-quic_custom_tx_callback (void *s)
+quic_custom_tx_callback (void *s, u32 max_burst_size)
 {
   session_t *stream_session = (session_t *) s;
   quicly_stream_t *stream;
@@ -2471,7 +2538,7 @@ static const transport_proto_vft_t quic_proto = {
 static clib_error_t *
 quic_init (vlib_main_t * vm)
 {
-  u32 add_segment_size = (4096ULL << 20) - 1, segment_size = 512 << 20;
+  u32 segment_size = 256 << 20;
   vlib_thread_main_t *vtm = vlib_get_thread_main ();
   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
   vnet_app_attach_args_t _a, *a = &_a;
@@ -2490,7 +2557,7 @@ quic_init (vlib_main_t * vm)
   a->options = options;
   a->name = format (0, "quic");
   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
-  a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = add_segment_size;
+  a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
   a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
   a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
@@ -2533,7 +2600,40 @@ quic_init (vlib_main_t * vm)
 
 VLIB_INIT_FUNCTION (quic_init);
 
+static clib_error_t *
+quic_plugin_crypto_command_fn (vlib_main_t * vm,
+                              unformat_input_t * input,
+                              vlib_cli_command_t * cmd)
+{
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "vpp"))
+       {
+         quic_tlsctx.cipher_suites = vpp_crypto_cipher_suites;
+         return 0;
+       }
+      else if (unformat (input, "picotls"))
+       {
+         quic_tlsctx.cipher_suites = ptls_openssl_cipher_suites;
+         return 0;
+       }
+      else
+       return clib_error_return (0, "unknown input '%U'",
+                                 format_unformat_error, input);
+    }
+
+  return clib_error_return (0, "unknown input '%U'",
+                           format_unformat_error, input);
+}
+
 /* *INDENT-OFF* */
+VLIB_CLI_COMMAND(quic_plugin_crypto_command, static)=
+{
+       .path = "quic set crypto api",
+       .short_help = "quic set crypto api [picotls, vpp]",
+       .function = quic_plugin_crypto_command_fn,
+};
+
 VLIB_PLUGIN_REGISTER () =
 {
   .version = VPP_BUILD_VER,