session tcp: handle rxt and acks as custom events
[vpp.git] / src / plugins / quic / quic.c
index 6c1a3c3..435113a 100644 (file)
@@ -85,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");
@@ -412,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)
 {
@@ -544,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);
@@ -587,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;
 }
 
@@ -1308,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;
@@ -1334,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;
@@ -1347,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;
@@ -1415,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,
@@ -1423,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);
@@ -1494,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);
 }
@@ -1518,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;
     }
 }
 
@@ -2041,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;