quic: Create custom event logger
[vpp.git] / src / plugins / quic / quic.c
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <sys/socket.h>
17
18 #include <vnet/session/application.h>
19 #include <vnet/session/transport.h>
20 #include <vnet/session/session.h>
21 #include <vlib/unix/plugin.h>
22 #include <vpp/app/version.h>
23
24 #include <vppinfra/lock.h>
25
26 #include <quic/quic.h>
27 #include <quic/certs.h>
28 #include <quic/error.h>
29 #include <quic/quic_crypto.h>
30
31 #include <quicly/defaults.h>
32
33 static char *quic_error_strings[] = {
34 #define quic_error(n,s) s,
35 #include "quic_error.def"
36 #undef quic_error
37 };
38
39 static quic_main_t quic_main;
40 static void quic_update_timer (quic_ctx_t * ctx);
41 static int quic_on_client_connected (quic_ctx_t * ctx);
42
43 static u32
44 quic_ctx_alloc (u32 thread_index)
45 {
46   quic_main_t *qm = &quic_main;
47   quic_ctx_t *ctx;
48
49   pool_get (qm->ctx_pool[thread_index], ctx);
50
51   memset (ctx, 0, sizeof (quic_ctx_t));
52   ctx->c_thread_index = thread_index;
53   QUIC_DBG (3, "Allocated quic_ctx %u on thread %u",
54             ctx - qm->ctx_pool[thread_index], thread_index);
55   return ctx - qm->ctx_pool[thread_index];
56 }
57
58 static void
59 quic_ctx_free (quic_ctx_t * ctx)
60 {
61   QUIC_DBG (2, "Free ctx %u", ctx->c_c_index);
62   u32 thread_index = ctx->c_thread_index;
63   if (CLIB_DEBUG)
64     memset (ctx, 0xfb, sizeof (*ctx));
65   pool_put (quic_main.ctx_pool[thread_index], ctx);
66 }
67
68 static quic_ctx_t *
69 quic_ctx_get (u32 ctx_index, u32 thread_index)
70 {
71   return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
72 }
73
74 static quic_ctx_t *
75 quic_ctx_get_if_valid (u32 ctx_index, u32 thread_index)
76 {
77   if (pool_is_free_index (quic_main.ctx_pool[thread_index], ctx_index))
78     return 0;
79   return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
80 }
81
82 static quic_ctx_t *
83 quic_get_conn_ctx (quicly_conn_t * conn)
84 {
85   u64 conn_data;
86   conn_data = (u64) * quicly_get_data (conn);
87   return quic_ctx_get (conn_data & UINT32_MAX, conn_data >> 32);
88 }
89
90 static void
91 quic_store_conn_ctx (quicly_conn_t * conn, quic_ctx_t * ctx)
92 {
93   *quicly_get_data (conn) =
94     (void *) (((u64) ctx->c_thread_index) << 32 | (u64) ctx->c_c_index);
95 }
96
97 static inline int
98 quic_ctx_is_stream (quic_ctx_t * ctx)
99 {
100   return (ctx->flags & QUIC_F_IS_STREAM);
101 }
102
103 static inline int
104 quic_ctx_is_listener (quic_ctx_t * ctx)
105 {
106   return (ctx->flags & QUIC_F_IS_LISTENER);
107 }
108
109 static session_t *
110 get_stream_session_from_stream (quicly_stream_t * stream)
111 {
112   quic_ctx_t *ctx;
113   quic_stream_data_t *stream_data;
114
115   stream_data = (quic_stream_data_t *) stream->data;
116   ctx = quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
117   return session_get (ctx->c_s_index, stream_data->thread_index);
118 }
119
120 static inline void
121 quic_make_connection_key (clib_bihash_kv_16_8_t * kv,
122                           const quicly_cid_plaintext_t * id)
123 {
124   kv->key[0] = ((u64) id->master_id) << 32 | (u64) id->thread_id;
125   kv->key[1] = id->node_id;
126 }
127
128 static int
129 quic_sendable_packet_count (session_t * udp_session)
130 {
131   u32 max_enqueue;
132   u32 packet_size = QUIC_MAX_PACKET_SIZE + SESSION_CONN_HDR_LEN;
133   max_enqueue = svm_fifo_max_enqueue (udp_session->tx_fifo);
134   return clib_min (max_enqueue / packet_size, QUIC_SEND_PACKET_VEC_SIZE);
135 }
136
137 static quicly_context_t *
138 quic_get_quicly_ctx_from_ctx (quic_ctx_t * ctx)
139 {
140   app_worker_t *app_wrk;
141   application_t *app;
142   app_wrk = app_worker_get_if_valid (ctx->parent_app_wrk_id);
143   if (!app_wrk)
144     return 0;
145   app = application_get (app_wrk->app_index);
146   return (quicly_context_t *) app->quicly_ctx;
147 }
148
149 static quicly_context_t *
150 quic_get_quicly_ctx_from_udp (u64 udp_session_handle)
151 {
152   session_t *udp_session;
153   application_t *app;
154   udp_session = session_get_from_handle (udp_session_handle);
155   app = application_get (udp_session->opaque);
156   return (quicly_context_t *) app->quicly_ctx;
157 }
158
159 static void
160 quic_ack_rx_data (session_t * stream_session)
161 {
162   u32 max_deq;
163   quic_ctx_t *sctx;
164   svm_fifo_t *f;
165   quicly_stream_t *stream;
166   quic_stream_data_t *stream_data;
167
168   sctx =
169     quic_ctx_get (stream_session->connection_index,
170                   stream_session->thread_index);
171   ASSERT (quic_ctx_is_stream (sctx));
172   stream = sctx->stream;
173   stream_data = (quic_stream_data_t *) stream->data;
174
175   f = stream_session->rx_fifo;
176   max_deq = svm_fifo_max_dequeue (f);
177
178   ASSERT (stream_data->app_rx_data_len >= max_deq);
179   quicly_stream_sync_recvbuf (stream, stream_data->app_rx_data_len - max_deq);
180   QUIC_DBG (3, "Acking %u bytes", stream_data->app_rx_data_len - max_deq);
181   stream_data->app_rx_data_len = max_deq;
182 }
183
184 static void
185 quic_disconnect_transport (quic_ctx_t * ctx)
186 {
187   QUIC_DBG (2, "Disconnecting transport 0x%lx", ctx->udp_session_handle);
188   vnet_disconnect_args_t a = {
189     .handle = ctx->udp_session_handle,
190     .app_index = quic_main.app_index,
191   };
192
193   if (vnet_disconnect_session (&a))
194     clib_warning ("UDP session 0x%lx disconnect errored",
195                   ctx->udp_session_handle);
196 }
197
198 static void
199 quic_connection_delete (quic_ctx_t * ctx)
200 {
201   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
202   clib_bihash_kv_16_8_t kv;
203   quicly_conn_t *conn;
204
205   QUIC_DBG (2, "Deleting connection %u", ctx->c_c_index);
206
207   ASSERT (!quic_ctx_is_stream (ctx));
208
209   /*  Stop the timer */
210   if (ctx->timer_handle != QUIC_TIMER_HANDLE_INVALID)
211     {
212       tw = &quic_main.wrk_ctx[ctx->c_thread_index].timer_wheel;
213       tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->timer_handle);
214     }
215
216   /*  Delete the connection from the connection map */
217   conn = ctx->conn;
218   quic_make_connection_key (&kv, quicly_get_master_id (conn));
219   QUIC_DBG (2, "Deleting conn with id %lu %lu from map", kv.key[0],
220             kv.key[1]);
221   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 0 /* is_add */ );
222
223   quic_disconnect_transport (ctx);
224
225   if (ctx->conn)
226     quicly_free (ctx->conn);
227   ctx->conn = NULL;
228
229   session_transport_delete_notify (&ctx->connection);
230   quic_ctx_free (ctx);
231 }
232
233 void
234 quic_increment_counter (u8 evt, u8 val)
235 {
236   vlib_main_t *vm = vlib_get_main ();
237   vlib_node_increment_counter (vm, quic_input_node.index, evt, val);
238 }
239
240 struct st_quic_event_log_t
241 {
242   quicly_event_logger_t super;
243 };
244
245 void
246 quic_event_log (quicly_event_logger_t * _self, quicly_event_type_t type,
247                 const quicly_event_attribute_t * attributes,
248                 size_t num_attributes)
249 {
250   if (type == QUICLY_EVENT_TYPE_PACKET_LOST)
251     {
252       QUIC_DBG (1, "QUIC packet loss");
253       quic_increment_counter (QUIC_ERROR_PACKET_DROP, 1);
254     }
255 }
256
257 quicly_event_logger_t *
258 quic_new_event_logger ()
259 {
260   struct st_quic_event_log_t *self;
261
262   if ((self = clib_mem_alloc (sizeof (*self))) == NULL)
263     return NULL;
264   /* *INDENT-OFF* */
265   *self = (struct st_quic_event_log_t) {{quic_event_log}};
266   /* *INDENT-ON* */
267   return &self->super;
268 }
269
270 void
271 quic_free_event_logger (quicly_event_logger_t * _self)
272 {
273   struct st_quicly_default_event_log_t *self = (void *) _self;
274   clib_mem_free (self);
275 }
276
277 /**
278  * Called when quicly return an error
279  * This function interacts tightly with quic_proto_on_close
280  */
281 static void
282 quic_connection_closed (quic_ctx_t * ctx)
283 {
284   QUIC_DBG (2, "QUIC connection %u/%u closed", ctx->c_thread_index,
285             ctx->c_c_index);
286
287   /* TODO if connection is not established, just delete the session? */
288   /* Actually should send connect or accept error */
289
290   switch (ctx->conn_state)
291     {
292     case QUIC_CONN_STATE_READY:
293       /* Error on an opened connection (timeout...)
294          This puts the session in closing state, we should receive a notification
295          when the app has closed its session */
296       session_transport_reset_notify (&ctx->connection);
297       /* This ensures we delete the connection when the app confirms the close */
298       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED;
299       break;
300     case QUIC_CONN_STATE_PASSIVE_CLOSING:
301       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED;
302       /* quic_proto_on_close will eventually be called when the app confirms the close
303          , we delete the connection at that point */
304       break;
305     case QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED:
306       /* App already confirmed close, we can delete the connection */
307       session_transport_delete_notify (&ctx->connection);
308       quic_connection_delete (ctx);
309       break;
310     case QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED:
311       QUIC_DBG (0, "BUG");
312       break;
313     case QUIC_CONN_STATE_ACTIVE_CLOSING:
314       session_transport_delete_notify (&ctx->connection);
315       quic_connection_delete (ctx);
316       break;
317     default:
318       QUIC_DBG (0, "BUG");
319       break;
320     }
321 }
322
323 static int
324 quic_send_datagram (session_t * udp_session, quicly_datagram_t * packet)
325 {
326   u32 max_enqueue;
327   session_dgram_hdr_t hdr;
328   u32 len, ret;
329   svm_fifo_t *f;
330   transport_connection_t *tc;
331
332   len = packet->data.len;
333   f = udp_session->tx_fifo;
334   tc = session_get_transport (udp_session);
335   max_enqueue = svm_fifo_max_enqueue (f);
336   if (max_enqueue < SESSION_CONN_HDR_LEN + len)
337     {
338       QUIC_DBG (1, "Too much data to send, max_enqueue %u, len %u",
339                 max_enqueue, len + SESSION_CONN_HDR_LEN);
340       return QUIC_ERROR_FULL_FIFO;
341     }
342
343   /*  Build packet header for fifo */
344   hdr.data_length = len;
345   hdr.data_offset = 0;
346   hdr.is_ip4 = tc->is_ip4;
347   clib_memcpy (&hdr.lcl_ip, &tc->lcl_ip, sizeof (ip46_address_t));
348   hdr.lcl_port = tc->lcl_port;
349
350   /*  Read dest address from quicly-provided sockaddr */
351   if (hdr.is_ip4)
352     {
353       ASSERT (packet->sa.sa_family == AF_INET);
354       struct sockaddr_in *sa4 = (struct sockaddr_in *) &packet->sa;
355       hdr.rmt_port = sa4->sin_port;
356       hdr.rmt_ip.ip4.as_u32 = sa4->sin_addr.s_addr;
357     }
358   else
359     {
360       ASSERT (packet->sa.sa_family == AF_INET6);
361       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &packet->sa;
362       hdr.rmt_port = sa6->sin6_port;
363       clib_memcpy (&hdr.rmt_ip.ip6, &sa6->sin6_addr, 16);
364     }
365
366   ret = svm_fifo_enqueue (f, sizeof (hdr), (u8 *) & hdr);
367   if (ret != sizeof (hdr))
368     {
369       QUIC_DBG (1, "Not enough space to enqueue header");
370       return QUIC_ERROR_FULL_FIFO;
371     }
372   ret = svm_fifo_enqueue (f, len, packet->data.base);
373   if (ret != len)
374     {
375       QUIC_DBG (1, "Not enough space to enqueue payload");
376       return QUIC_ERROR_FULL_FIFO;
377     }
378
379   quic_increment_counter (QUIC_ERROR_TX_PACKETS, 1);
380
381   return 0;
382 }
383
384 static int
385 quic_send_packets (quic_ctx_t * ctx)
386 {
387   quicly_datagram_t *packets[QUIC_SEND_PACKET_VEC_SIZE];
388   session_t *udp_session;
389   quicly_conn_t *conn;
390   size_t num_packets, i, max_packets;
391   quicly_packet_allocator_t *pa;
392   quicly_context_t *quicly_context;
393   int err = 0;
394
395   /* We have sctx, get qctx */
396   if (quic_ctx_is_stream (ctx))
397     ctx = quic_ctx_get (ctx->quic_connection_ctx_id, ctx->c_thread_index);
398
399   ASSERT (!quic_ctx_is_stream (ctx));
400
401   udp_session = session_get_from_handle_if_valid (ctx->udp_session_handle);
402   if (!udp_session)
403     goto quicly_error;
404
405   conn = ctx->conn;
406
407   if (!conn)
408     return 0;
409
410   /* TODO : quicly can assert it can send min_packets up to 2 */
411   if (quic_sendable_packet_count (udp_session) < 2)
412     goto stop_sending;
413
414   quicly_context = quic_get_quicly_ctx_from_ctx (ctx);
415   if (!quicly_context)
416     {
417       clib_warning ("Tried to send packets on non existing app worker %u",
418                     ctx->parent_app_wrk_id);
419       quic_connection_delete (ctx);
420       return 1;
421     }
422   pa = quicly_context->packet_allocator;
423   do
424     {
425       max_packets = quic_sendable_packet_count (udp_session);
426       if (max_packets < 2)
427         break;
428       num_packets = max_packets;
429       if ((err = quicly_send (conn, packets, &num_packets)))
430         goto quicly_error;
431
432       for (i = 0; i != num_packets; ++i)
433         {
434           if ((err = quic_send_datagram (udp_session, packets[i])))
435             goto quicly_error;
436
437           pa->free_packet (pa, packets[i]);
438         }
439     }
440   while (num_packets > 0 && num_packets == max_packets);
441
442 stop_sending:
443   if (svm_fifo_set_event (udp_session->tx_fifo))
444     if ((err =
445          session_send_io_evt_to_thread (udp_session->tx_fifo,
446                                         SESSION_IO_EVT_TX)))
447       clib_warning ("Event enqueue errored %d", err);
448
449   QUIC_DBG (3, "%u[TX] %u[RX]", svm_fifo_max_dequeue (udp_session->tx_fifo),
450             svm_fifo_max_dequeue (udp_session->rx_fifo));
451   quic_update_timer (ctx);
452   return 0;
453
454 quicly_error:
455   if (err && err != QUICLY_ERROR_PACKET_IGNORED
456       && err != QUICLY_ERROR_FREE_CONNECTION)
457     clib_warning ("Quic error '%U'.", quic_format_err, err);
458   quic_connection_closed (ctx);
459   return 1;
460 }
461
462 /*****************************************************************************
463  *
464  * START QUICLY CALLBACKS
465  * Called from QUIC lib
466  *
467  *****************************************************************************/
468
469 static void
470 quic_on_stream_destroy (quicly_stream_t * stream, int err)
471 {
472   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
473   quic_ctx_t *sctx =
474     quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
475   session_t *stream_session =
476     session_get (sctx->c_s_index, sctx->c_thread_index);
477   QUIC_DBG (2, "DESTROYED_STREAM: session 0x%lx (%U)",
478             session_handle (stream_session), quic_format_err, err);
479
480   stream_session->session_state = SESSION_STATE_CLOSED;
481   session_transport_delete_notify (&sctx->connection);
482
483   quic_ctx_free (sctx);
484   free (stream->data);
485 }
486
487 static int
488 quic_on_stop_sending (quicly_stream_t * stream, int err)
489 {
490 #if QUIC_DEBUG >= 2
491   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
492   quic_ctx_t *sctx =
493     quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
494   session_t *stream_session =
495     session_get (sctx->c_s_index, sctx->c_thread_index);
496   clib_warning ("(NOT IMPLEMENTD) STOP_SENDING: session 0x%lx (%U)",
497                 session_handle (stream_session), quic_format_err, err);
498 #endif
499   /* TODO : handle STOP_SENDING */
500   return 0;
501 }
502
503 static int
504 quic_on_receive_reset (quicly_stream_t * stream, int err)
505 {
506   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
507   quic_ctx_t *sctx =
508     quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
509 #if QUIC_DEBUG >= 2
510   session_t *stream_session =
511     session_get (sctx->c_s_index, sctx->c_thread_index);
512   clib_warning ("RESET_STREAM: session 0x%lx (%U)",
513                 session_handle (stream_session), quic_format_err, err);
514 #endif
515   session_transport_closing_notify (&sctx->connection);
516   return 0;
517 }
518
519 static int
520 quic_on_receive (quicly_stream_t * stream, size_t off, const void *src,
521                  size_t len)
522 {
523   QUIC_DBG (3, "received data: %lu bytes, offset %lu", len, off);
524   u32 max_enq;
525   quic_ctx_t *sctx;
526   session_t *stream_session;
527   app_worker_t *app_wrk;
528   svm_fifo_t *f;
529   quic_stream_data_t *stream_data;
530   int rlen;
531
532   stream_data = (quic_stream_data_t *) stream->data;
533   sctx = quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
534   stream_session = session_get (sctx->c_s_index, stream_data->thread_index);
535   f = stream_session->rx_fifo;
536
537   max_enq = svm_fifo_max_enqueue_prod (f);
538   QUIC_DBG (3, "Enqueuing %u at off %u in %u space", len, off, max_enq);
539   if (off - stream_data->app_rx_data_len + len > max_enq)
540     {
541       QUIC_DBG (1, "Error RX fifo is full");
542       return 1;
543     }
544   if (off == stream_data->app_rx_data_len)
545     {
546       /* Streams live on the same thread so (f, stream_data) should stay consistent */
547       rlen = svm_fifo_enqueue (f, len, (u8 *) src);
548       stream_data->app_rx_data_len += rlen;
549       ASSERT (rlen >= len);
550       app_wrk = app_worker_get_if_valid (stream_session->app_wrk_index);
551       if (PREDICT_TRUE (app_wrk != 0))
552         app_worker_lock_and_send_event (app_wrk, stream_session,
553                                         SESSION_IO_EVT_RX);
554       quic_ack_rx_data (stream_session);
555     }
556   else
557     {
558       rlen =
559         svm_fifo_enqueue_with_offset (f, off - stream_data->app_rx_data_len,
560                                       len, (u8 *) src);
561       ASSERT (rlen == 0);
562     }
563   return 0;
564 }
565
566 void
567 quic_fifo_egress_shift (quicly_stream_t * stream, size_t delta)
568 {
569   session_t *stream_session;
570   svm_fifo_t *f;
571   int rv;
572
573   stream_session = get_stream_session_from_stream (stream);
574   f = stream_session->tx_fifo;
575
576   rv = svm_fifo_dequeue_drop (f, delta);
577   ASSERT (rv == delta);
578   quicly_stream_sync_sendbuf (stream, 0);
579 }
580
581 int
582 quic_fifo_egress_emit (quicly_stream_t * stream, size_t off, void *dst,
583                        size_t * len, int *wrote_all)
584 {
585   session_t *stream_session;
586   svm_fifo_t *f;
587   u32 deq_max, first_deq, max_rd_chunk, rem_offset;
588
589   stream_session = get_stream_session_from_stream (stream);
590   f = stream_session->tx_fifo;
591
592   QUIC_DBG (3, "Emitting %u, offset %u", *len, off);
593
594   deq_max = svm_fifo_max_dequeue_cons (f);
595   ASSERT (off <= deq_max);
596   if (off + *len < deq_max)
597     {
598       *wrote_all = 0;
599     }
600   else
601     {
602       *wrote_all = 1;
603       *len = deq_max - off;
604       QUIC_DBG (3, "Wrote ALL, %u", *len);
605     }
606
607   /* TODO, use something like : return svm_fifo_peek (f, off, *len, dst); */
608   max_rd_chunk = svm_fifo_max_read_chunk (f);
609
610   first_deq = 0;
611   if (off < max_rd_chunk)
612     {
613       first_deq = clib_min (*len, max_rd_chunk - off);
614       clib_memcpy_fast (dst, svm_fifo_head (f) + off, first_deq);
615     }
616
617   if (max_rd_chunk < off + *len)
618     {
619       rem_offset = max_rd_chunk < off ? off - max_rd_chunk : 0;
620       clib_memcpy_fast (dst + first_deq, f->head_chunk->data + rem_offset,
621                         *len - first_deq);
622     }
623
624   return 0;
625 }
626
627 static const quicly_stream_callbacks_t quic_stream_callbacks = {
628   .on_destroy = quic_on_stream_destroy,
629   .on_send_shift = quic_fifo_egress_shift,
630   .on_send_emit = quic_fifo_egress_emit,
631   .on_send_stop = quic_on_stop_sending,
632   .on_receive = quic_on_receive,
633   .on_receive_reset = quic_on_receive_reset
634 };
635
636 static void
637 quic_accept_stream (void *s)
638 {
639   quicly_stream_t *stream = (quicly_stream_t *) s;
640   session_t *stream_session, *quic_session;
641   quic_stream_data_t *stream_data;
642   app_worker_t *app_wrk;
643   quic_ctx_t *qctx, *sctx;
644   u32 sctx_id;
645   int rv;
646
647   sctx_id = quic_ctx_alloc (vlib_get_thread_index ());
648
649   qctx = quic_get_conn_ctx (stream->conn);
650
651   /* Might need to signal that the connection is ready if the first thing the
652    * server does is open a stream */
653   if (qctx->conn_state == QUIC_CONN_STATE_HANDSHAKE)
654     {
655       if (quicly_connection_is_ready (qctx->conn))
656         {
657           qctx->conn_state = QUIC_CONN_STATE_READY;
658           if (quicly_is_client (qctx->conn))
659             {
660               quic_on_client_connected (qctx);
661               /* ctx might be invalidated */
662               qctx = quic_get_conn_ctx (stream->conn);
663             }
664         }
665     }
666
667   stream_session = session_alloc (qctx->c_thread_index);
668   QUIC_DBG (2, "ACCEPTED stream_session 0x%lx ctx %u",
669             session_handle (stream_session), sctx_id);
670   sctx = quic_ctx_get (sctx_id, qctx->c_thread_index);
671   sctx->parent_app_wrk_id = qctx->parent_app_wrk_id;
672   sctx->parent_app_id = qctx->parent_app_id;
673   sctx->quic_connection_ctx_id = qctx->c_c_index;
674   sctx->c_c_index = sctx_id;
675   sctx->c_s_index = stream_session->session_index;
676   sctx->stream = stream;
677   sctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
678   sctx->flags |= QUIC_F_IS_STREAM;
679
680   stream_data = (quic_stream_data_t *) stream->data;
681   stream_data->ctx_id = sctx_id;
682   stream_data->thread_index = sctx->c_thread_index;
683   stream_data->app_rx_data_len = 0;
684
685   sctx->c_s_index = stream_session->session_index;
686   stream_session->session_state = SESSION_STATE_CREATED;
687   stream_session->app_wrk_index = sctx->parent_app_wrk_id;
688   stream_session->connection_index = sctx->c_c_index;
689   stream_session->session_type =
690     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, qctx->udp_is_ip4);
691   quic_session = session_get (qctx->c_s_index, qctx->c_thread_index);
692   stream_session->listener_handle = listen_session_get_handle (quic_session);
693
694   app_wrk = app_worker_get (stream_session->app_wrk_index);
695   if ((rv = app_worker_init_connected (app_wrk, stream_session)))
696     {
697       QUIC_DBG (1, "failed to allocate fifos");
698       session_free (stream_session);
699       quicly_reset_stream (stream, QUIC_APP_ALLOCATION_ERROR);
700       return;
701     }
702   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
703                              SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
704                              SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
705
706   if ((rv = app_worker_accept_notify (app_wrk, stream_session)))
707     {
708       QUIC_DBG (1, "failed to notify accept worker app");
709       session_free_w_fifos (stream_session);
710       quicly_reset_stream (stream, QUIC_APP_ACCEPT_NOTIFY_ERROR);
711       return;
712     }
713 }
714
715 static int
716 quic_on_stream_open (quicly_stream_open_t * self, quicly_stream_t * stream)
717 {
718   QUIC_DBG (2, "on_stream_open called");
719   stream->data = malloc (sizeof (quic_stream_data_t));
720   stream->callbacks = &quic_stream_callbacks;
721   /* Notify accept on parent qsession, but only if this is not a locally
722    * initiated stream */
723   if (!quicly_stream_is_self_initiated (stream))
724     quic_accept_stream (stream);
725   return 0;
726 }
727
728 static void
729 quic_on_closed_by_peer (quicly_closed_by_peer_t * self, quicly_conn_t * conn,
730                         int code, uint64_t frame_type,
731                         const char *reason, size_t reason_len)
732 {
733   quic_ctx_t *ctx = quic_get_conn_ctx (conn);
734 #if QUIC_DEBUG >= 2
735   session_t *quic_session = session_get (ctx->c_s_index, ctx->c_thread_index);
736   clib_warning ("Session 0x%lx closed by peer (%U) %.*s ",
737                 session_handle (quic_session), quic_format_err, code,
738                 reason_len, reason);
739 #endif
740   ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING;
741   session_transport_closing_notify (&ctx->connection);
742 }
743
744 static quicly_stream_open_t on_stream_open = { &quic_on_stream_open };
745 static quicly_closed_by_peer_t on_closed_by_peer =
746   { &quic_on_closed_by_peer };
747
748
749 /*****************************************************************************
750  *
751  * END QUICLY CALLBACKS
752  *
753  *****************************************************************************/
754
755 /*****************************************************************************
756  *
757  * BEGIN TIMERS HANDLING
758  *
759  *****************************************************************************/
760
761 static int64_t
762 quic_get_thread_time (u8 thread_index)
763 {
764   return quic_main.wrk_ctx[thread_index].time_now;
765 }
766
767 static int64_t
768 quic_get_time (quicly_now_t * self)
769 {
770   u8 thread_index = vlib_get_thread_index ();
771   return quic_get_thread_time (thread_index);
772 }
773
774 static quicly_now_t quicly_vpp_now_cb = { quic_get_time };
775
776 static u32
777 quic_set_time_now (u32 thread_index)
778 {
779   vlib_main_t *vlib_main = vlib_get_main ();
780   f64 time = vlib_time_now (vlib_main);
781   quic_main.wrk_ctx[thread_index].time_now = (int64_t) (time * 1000.f);
782   return quic_main.wrk_ctx[thread_index].time_now;
783 }
784
785 /* Transport proto callback */
786 static void
787 quic_update_time (f64 now, u8 thread_index)
788 {
789   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
790
791   tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
792   quic_set_time_now (thread_index);
793   tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
794 }
795
796 static void
797 quic_timer_expired (u32 conn_index)
798 {
799   quic_ctx_t *ctx;
800   QUIC_DBG (4, "Timer expired for conn %u at %ld", conn_index,
801             quic_get_time (NULL));
802   ctx = quic_ctx_get (conn_index, vlib_get_thread_index ());
803   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
804   quic_send_packets (ctx);
805 }
806
807 static void
808 quic_update_timer (quic_ctx_t * ctx)
809 {
810   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
811   int64_t next_timeout, next_interval;
812   session_t *quic_session;
813
814   /*  This timeout is in ms which is the unit of our timer */
815   next_timeout = quicly_get_first_timeout (ctx->conn);
816   next_interval = next_timeout - quic_get_time (NULL);
817
818   if (next_timeout == 0 || next_interval <= 0)
819     {
820       if (ctx->c_s_index == QUIC_SESSION_INVALID)
821         {
822           next_interval = 1;
823         }
824       else
825         {
826           quic_session = session_get (ctx->c_s_index, ctx->c_thread_index);
827           if (svm_fifo_set_event (quic_session->tx_fifo))
828             session_send_io_evt_to_thread_custom (quic_session,
829                                                   quic_session->thread_index,
830                                                   SESSION_IO_EVT_BUILTIN_TX);
831           return;
832         }
833     }
834
835   tw = &quic_main.wrk_ctx[vlib_get_thread_index ()].timer_wheel;
836
837   QUIC_DBG (4, "Timer set to %ld (int %ld) for ctx %u", next_timeout,
838             next_interval, ctx->c_c_index);
839
840   if (ctx->timer_handle == QUIC_TIMER_HANDLE_INVALID)
841     {
842       if (next_timeout == INT64_MAX)
843         {
844           QUIC_DBG (4, "timer for ctx %u already stopped", ctx->c_c_index);
845           return;
846         }
847       ctx->timer_handle =
848         tw_timer_start_1t_3w_1024sl_ov (tw, ctx->c_c_index, 0, next_interval);
849     }
850   else
851     {
852       if (next_timeout == INT64_MAX)
853         {
854           tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->timer_handle);
855           ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
856           QUIC_DBG (4, "Stopping timer for ctx %u", ctx->c_c_index);
857         }
858       else
859         tw_timer_update_1t_3w_1024sl_ov (tw, ctx->timer_handle,
860                                          next_interval);
861     }
862   return;
863 }
864
865 static void
866 quic_expired_timers_dispatch (u32 * expired_timers)
867 {
868   int i;
869
870   for (i = 0; i < vec_len (expired_timers); i++)
871     {
872       quic_timer_expired (expired_timers[i]);
873     }
874 }
875
876 /*****************************************************************************
877  *
878  * END TIMERS HANDLING
879  *
880  *****************************************************************************/
881
882 static int
883 quic_encrypt_ticket_cb (ptls_encrypt_ticket_t * _self, ptls_t * tls,
884                         int is_encrypt, ptls_buffer_t * dst, ptls_iovec_t src)
885 {
886   quic_session_cache_t *self = (void *) _self;
887   int ret;
888
889   if (is_encrypt)
890     {
891
892       /* replace the cached entry along with a newly generated session id */
893       free (self->data.base);
894       if ((self->data.base = malloc (src.len)) == NULL)
895         return PTLS_ERROR_NO_MEMORY;
896
897       ptls_get_context (tls)->random_bytes (self->id, sizeof (self->id));
898       memcpy (self->data.base, src.base, src.len);
899       self->data.len = src.len;
900
901       /* store the session id in buffer */
902       if ((ret = ptls_buffer_reserve (dst, sizeof (self->id))) != 0)
903         return ret;
904       memcpy (dst->base + dst->off, self->id, sizeof (self->id));
905       dst->off += sizeof (self->id);
906
907     }
908   else
909     {
910
911       /* check if session id is the one stored in cache */
912       if (src.len != sizeof (self->id))
913         return PTLS_ERROR_SESSION_NOT_FOUND;
914       if (memcmp (self->id, src.base, sizeof (self->id)) != 0)
915         return PTLS_ERROR_SESSION_NOT_FOUND;
916
917       /* return the cached value */
918       if ((ret = ptls_buffer_reserve (dst, self->data.len)) != 0)
919         return ret;
920       memcpy (dst->base + dst->off, self->data.base, self->data.len);
921       dst->off += self->data.len;
922     }
923
924   return 0;
925 }
926
927 typedef struct quicly_ctx_data_
928 {
929   quicly_context_t quicly_ctx;
930   char cid_key[17];
931   ptls_context_t ptls_ctx;
932 } quicly_ctx_data_t;
933
934 static void
935 quic_store_quicly_ctx (application_t * app, u8 is_client)
936 {
937   quic_main_t *qm = &quic_main;
938   quicly_context_t *quicly_ctx;
939   ptls_iovec_t key_vec;
940   if (app->quicly_ctx)
941     return;
942
943   quicly_ctx_data_t *quicly_ctx_data =
944     clib_mem_alloc (sizeof (quicly_ctx_data_t));
945   clib_memset (quicly_ctx_data, 0, sizeof (*quicly_ctx_data));  /* picotls depends on this */
946   quicly_ctx = &quicly_ctx_data->quicly_ctx;
947   ptls_context_t *ptls_ctx = &quicly_ctx_data->ptls_ctx;
948   ptls_ctx->random_bytes = ptls_openssl_random_bytes;
949   ptls_ctx->get_time = &ptls_get_time;
950   ptls_ctx->key_exchanges = ptls_openssl_key_exchanges;
951   ptls_ctx->cipher_suites = qm->quic_ciphers[qm->default_cipher];
952   ptls_ctx->certificates.list = NULL;
953   ptls_ctx->certificates.count = 0;
954   ptls_ctx->esni = NULL;
955   ptls_ctx->on_client_hello = NULL;
956   ptls_ctx->emit_certificate = NULL;
957   ptls_ctx->sign_certificate = NULL;
958   ptls_ctx->verify_certificate = NULL;
959   ptls_ctx->ticket_lifetime = 86400;
960   ptls_ctx->max_early_data_size = 8192;
961   ptls_ctx->hkdf_label_prefix__obsolete = NULL;
962   ptls_ctx->require_dhe_on_psk = 1;
963   ptls_ctx->encrypt_ticket = &qm->session_cache.super;
964
965   app->quicly_ctx = (u64 *) quicly_ctx;
966   memcpy (quicly_ctx, &quicly_spec_context, sizeof (quicly_context_t));
967
968   quicly_ctx->max_packet_size = QUIC_MAX_PACKET_SIZE;
969   quicly_ctx->tls = ptls_ctx;
970   quicly_ctx->stream_open = &on_stream_open;
971   quicly_ctx->closed_by_peer = &on_closed_by_peer;
972   quicly_ctx->now = &quicly_vpp_now_cb;
973   quicly_amend_ptls_context (quicly_ctx->tls);
974
975   quicly_ctx->event_log.mask = UINT64_MAX;      /* logs */
976   quicly_ctx->event_log.cb = quic_new_event_logger ();
977
978   quicly_ctx->transport_params.max_data = QUIC_INT_MAX;
979   quicly_ctx->transport_params.max_streams_uni = (uint64_t) 1 << 60;
980   quicly_ctx->transport_params.max_streams_bidi = (uint64_t) 1 << 60;
981   quicly_ctx->transport_params.max_stream_data.bidi_local = (QUIC_FIFO_SIZE - 1);       /* max_enq is SIZE - 1 */
982   quicly_ctx->transport_params.max_stream_data.bidi_remote = (QUIC_FIFO_SIZE - 1);      /* max_enq is SIZE - 1 */
983   quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX;
984
985   quicly_ctx->tls->random_bytes (quicly_ctx_data->cid_key, 16);
986   quicly_ctx_data->cid_key[16] = 0;
987   key_vec =
988     ptls_iovec_init (quicly_ctx_data->cid_key,
989                      strlen (quicly_ctx_data->cid_key));
990   quicly_ctx->cid_encryptor =
991     quicly_new_default_cid_encryptor (&ptls_openssl_bfecb,
992                                       &ptls_openssl_sha256, key_vec);
993   if (is_client)
994     return;
995   if (app->tls_key != NULL && app->tls_cert != NULL)
996     {
997       if (load_bio_private_key (quicly_ctx->tls, (char *) app->tls_key))
998         {
999           QUIC_DBG (1, "failed to read private key from app configuration\n");
1000         }
1001       if (load_bio_certificate_chain (quicly_ctx->tls,
1002                                       (char *) app->tls_cert))
1003         {
1004           QUIC_DBG (1, "failed to load certificate\n");
1005         }
1006     }
1007 }
1008
1009 /*****************************************************************************
1010  *
1011  * BEGIN TRANSPORT PROTO FUNCTIONS
1012  *
1013  *****************************************************************************/
1014
1015 static int
1016 quic_connect_new_stream (session_t * quic_session, u32 opaque)
1017 {
1018   uint64_t quic_session_handle;
1019   session_t *stream_session;
1020   quic_stream_data_t *stream_data;
1021   quicly_stream_t *stream;
1022   quicly_conn_t *conn;
1023   app_worker_t *app_wrk;
1024   quic_ctx_t *qctx, *sctx;
1025   u32 sctx_index;
1026   int rv;
1027
1028   /*  Find base session to which the user want to attach a stream */
1029   quic_session_handle = session_handle (quic_session);
1030   QUIC_DBG (2, "Opening new stream (qsession %u)", quic_session_handle);
1031
1032   if (session_type_transport_proto (quic_session->session_type) !=
1033       TRANSPORT_PROTO_QUIC)
1034     {
1035       QUIC_DBG (1, "received incompatible session");
1036       return -1;
1037     }
1038
1039   app_wrk = app_worker_get_if_valid (quic_session->app_wrk_index);
1040   if (!app_wrk)
1041     {
1042       QUIC_DBG (1, "Invalid app worker :(");
1043       return -1;
1044     }
1045
1046   sctx_index = quic_ctx_alloc (quic_session->thread_index);     /*  Allocate before we get pointers */
1047   sctx = quic_ctx_get (sctx_index, quic_session->thread_index);
1048   qctx =
1049     quic_ctx_get (quic_session->connection_index, quic_session->thread_index);
1050   if (quic_ctx_is_stream (qctx))
1051     {
1052       QUIC_DBG (1, "session is a stream");
1053       quic_ctx_free (sctx);
1054       return -1;
1055     }
1056
1057   sctx->parent_app_wrk_id = qctx->parent_app_wrk_id;
1058   sctx->parent_app_id = qctx->parent_app_id;
1059   sctx->quic_connection_ctx_id = qctx->c_c_index;
1060   sctx->c_c_index = sctx_index;
1061   sctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
1062   sctx->flags |= QUIC_F_IS_STREAM;
1063
1064   conn = qctx->conn;
1065
1066   if (!conn || !quicly_connection_is_ready (conn))
1067     return -1;
1068
1069   if ((rv = quicly_open_stream (conn, &stream, 0 /* uni */ )))
1070     {
1071       QUIC_DBG (2, "Stream open failed with %d", rv);
1072       return -1;
1073     }
1074   sctx->stream = stream;
1075
1076   QUIC_DBG (2, "Opened stream %d, creating session", stream->stream_id);
1077
1078   stream_session = session_alloc (qctx->c_thread_index);
1079   QUIC_DBG (2, "Allocated stream_session 0x%lx ctx %u",
1080             session_handle (stream_session), sctx_index);
1081   stream_session->app_wrk_index = app_wrk->wrk_index;
1082   stream_session->connection_index = sctx_index;
1083   stream_session->listener_handle = quic_session_handle;
1084   stream_session->session_type =
1085     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, qctx->udp_is_ip4);
1086
1087   sctx->c_s_index = stream_session->session_index;
1088
1089   if (app_worker_init_connected (app_wrk, stream_session))
1090     {
1091       QUIC_DBG (1, "failed to app_worker_init_connected");
1092       quicly_reset_stream (stream, QUIC_APP_ALLOCATION_ERROR);
1093       session_free_w_fifos (stream_session);
1094       quic_ctx_free (sctx);
1095       return app_worker_connect_notify (app_wrk, NULL, opaque);
1096     }
1097
1098   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
1099                              SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
1100                              SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
1101
1102   stream_session->session_state = SESSION_STATE_READY;
1103   if (app_worker_connect_notify (app_wrk, stream_session, opaque))
1104     {
1105       QUIC_DBG (1, "failed to notify app");
1106       quicly_reset_stream (stream, QUIC_APP_CONNECT_NOTIFY_ERROR);
1107       session_free_w_fifos (stream_session);
1108       quic_ctx_free (sctx);
1109       return -1;
1110     }
1111   stream_data = (quic_stream_data_t *) stream->data;
1112   stream_data->ctx_id = sctx->c_c_index;
1113   stream_data->thread_index = sctx->c_thread_index;
1114   stream_data->app_rx_data_len = 0;
1115   return 0;
1116 }
1117
1118 static int
1119 quic_connect_new_connection (session_endpoint_cfg_t * sep)
1120 {
1121   vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
1122   quic_main_t *qm = &quic_main;
1123   quic_ctx_t *ctx;
1124   app_worker_t *app_wrk;
1125   application_t *app;
1126   u32 ctx_index;
1127   int error;
1128
1129   ctx_index = quic_ctx_alloc (vlib_get_thread_index ());
1130   ctx = quic_ctx_get (ctx_index, vlib_get_thread_index ());
1131   ctx->parent_app_wrk_id = sep->app_wrk_index;
1132   ctx->c_s_index = QUIC_SESSION_INVALID;
1133   ctx->c_c_index = ctx_index;
1134   ctx->udp_is_ip4 = sep->is_ip4;
1135   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
1136   ctx->conn_state = QUIC_CONN_STATE_HANDSHAKE;
1137   ctx->client_opaque = sep->opaque;
1138   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
1139   if (sep->hostname)
1140     ctx->srv_hostname = format (0, "%v", sep->hostname);
1141   else
1142     /*  needed by quic for crypto + determining client / server */
1143     ctx->srv_hostname =
1144       format (0, "%U", format_ip46_address, &sep->ip, sep->is_ip4);
1145   vec_terminate_c_string (ctx->srv_hostname);
1146
1147   clib_memcpy (&cargs->sep, sep, sizeof (session_endpoint_cfg_t));
1148   cargs->sep.transport_proto = TRANSPORT_PROTO_UDPC;
1149   cargs->app_index = qm->app_index;
1150   cargs->api_context = ctx_index;
1151
1152   app_wrk = app_worker_get (sep->app_wrk_index);
1153   app = application_get (app_wrk->app_index);
1154   ctx->parent_app_id = app_wrk->app_index;
1155   cargs->sep_ext.ns_index = app->ns_index;
1156
1157   quic_store_quicly_ctx (app, 1 /* is client */ );
1158
1159   if ((error = vnet_connect (cargs)))
1160     return error;
1161
1162   return 0;
1163 }
1164
1165 static int
1166 quic_connect (transport_endpoint_cfg_t * tep)
1167 {
1168   QUIC_DBG (2, "Called quic_connect");
1169   session_endpoint_cfg_t *sep = (session_endpoint_cfg_t *) tep;
1170   session_t *quic_session;
1171   sep = (session_endpoint_cfg_t *) tep;
1172
1173   quic_session = session_get_from_handle_if_valid (sep->parent_handle);
1174   if (quic_session)
1175     return quic_connect_new_stream (quic_session, sep->opaque);
1176   else
1177     return quic_connect_new_connection (sep);
1178 }
1179
1180 static void
1181 quic_proto_on_close (u32 ctx_index, u32 thread_index)
1182 {
1183   quic_ctx_t *ctx = quic_ctx_get_if_valid (ctx_index, thread_index);
1184   if (!ctx)
1185     return;
1186 #if QUIC_DEBUG >= 2
1187   session_t *stream_session =
1188     session_get (ctx->c_s_index, ctx->c_thread_index);
1189   clib_warning ("Closing session 0x%lx", session_handle (stream_session));
1190 #endif
1191   if (quic_ctx_is_stream (ctx))
1192     {
1193       quicly_stream_t *stream = ctx->stream;
1194       quicly_reset_stream (stream, QUIC_APP_ERROR_CLOSE_NOTIFY);
1195       quic_send_packets (ctx);
1196       return;
1197     }
1198
1199   switch (ctx->conn_state)
1200     {
1201     case QUIC_CONN_STATE_READY:
1202       ctx->conn_state = QUIC_CONN_STATE_ACTIVE_CLOSING;
1203       quicly_conn_t *conn = ctx->conn;
1204       /* Start connection closing. Keep sending packets until quicly_send
1205          returns QUICLY_ERROR_FREE_CONNECTION */
1206       quicly_close (conn, QUIC_APP_ERROR_CLOSE_NOTIFY, "Closed by peer");
1207       /* This also causes all streams to be closed (and the cb called) */
1208       quic_send_packets (ctx);
1209       break;
1210     case QUIC_CONN_STATE_PASSIVE_CLOSING:
1211       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED;
1212       /* send_packets will eventually return an error, we delete the conn at
1213          that point */
1214       break;
1215     case QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED:
1216       quic_connection_delete (ctx);
1217       break;
1218     default:
1219       QUIC_DBG (0, "BUG");
1220       break;
1221     }
1222 }
1223
1224 static u32
1225 quic_start_listen (u32 quic_listen_session_index, transport_endpoint_t * tep)
1226 {
1227   vnet_listen_args_t _bargs, *args = &_bargs;
1228   quic_main_t *qm = &quic_main;
1229   session_handle_t udp_handle;
1230   session_endpoint_cfg_t *sep;
1231   session_t *udp_listen_session;
1232   app_worker_t *app_wrk;
1233   application_t *app;
1234   quic_ctx_t *lctx;
1235   u32 lctx_index;
1236   app_listener_t *app_listener;
1237
1238   sep = (session_endpoint_cfg_t *) tep;
1239   app_wrk = app_worker_get (sep->app_wrk_index);
1240   /* We need to call this because we call app_worker_init_connected in
1241    * quic_accept_stream, which assumes the connect segment manager exists */
1242   app_worker_alloc_connects_segment_manager (app_wrk);
1243   app = application_get (app_wrk->app_index);
1244   QUIC_DBG (2, "Called quic_start_listen for app %d", app_wrk->app_index);
1245
1246   quic_store_quicly_ctx (app, 0 /* is_client */ );
1247
1248   sep->transport_proto = TRANSPORT_PROTO_UDPC;
1249   memset (args, 0, sizeof (*args));
1250   args->app_index = qm->app_index;
1251   args->sep_ext = *sep;
1252   args->sep_ext.ns_index = app->ns_index;
1253   if (vnet_listen (args))
1254     return -1;
1255
1256   lctx_index = quic_ctx_alloc (0);
1257   udp_handle = args->handle;
1258   app_listener = app_listener_get_w_handle (udp_handle);
1259   udp_listen_session = app_listener_get_session (app_listener);
1260   udp_listen_session->opaque = lctx_index;
1261
1262   lctx = quic_ctx_get (lctx_index, 0);
1263   lctx->flags |= QUIC_F_IS_LISTENER;
1264
1265   clib_memcpy (&lctx->c_rmt_ip, &args->sep.peer.ip, sizeof (ip46_address_t));
1266   clib_memcpy (&lctx->c_lcl_ip, &args->sep.ip, sizeof (ip46_address_t));
1267   lctx->c_rmt_port = args->sep.peer.port;
1268   lctx->c_lcl_port = args->sep.port;
1269   lctx->c_is_ip4 = args->sep.is_ip4;
1270   lctx->c_fib_index = args->sep.fib_index;
1271   lctx->c_proto = TRANSPORT_PROTO_QUIC;
1272   lctx->parent_app_wrk_id = sep->app_wrk_index;
1273   lctx->parent_app_id = app_wrk->app_index;
1274   lctx->udp_session_handle = udp_handle;
1275   lctx->c_s_index = quic_listen_session_index;
1276
1277   QUIC_DBG (2, "Listening UDP session 0x%lx",
1278             session_handle (udp_listen_session));
1279   QUIC_DBG (2, "Listening QUIC session 0x%lx", quic_listen_session_index);
1280   return lctx_index;
1281 }
1282
1283 static u32
1284 quic_stop_listen (u32 lctx_index)
1285 {
1286   QUIC_DBG (2, "Called quic_stop_listen");
1287   quic_ctx_t *lctx;
1288   lctx = quic_ctx_get (lctx_index, 0);
1289   ASSERT (quic_ctx_is_listener (lctx));
1290   vnet_unlisten_args_t a = {
1291     .handle = lctx->udp_session_handle,
1292     .app_index = quic_main.app_index,
1293     .wrk_map_index = 0          /* default wrk */
1294   };
1295   if (vnet_unlisten (&a))
1296     clib_warning ("unlisten errored");
1297
1298   /*  TODO: crypto state cleanup */
1299
1300   quic_ctx_free (lctx);
1301   return 0;
1302 }
1303
1304 static transport_connection_t *
1305 quic_connection_get (u32 ctx_index, u32 thread_index)
1306 {
1307   quic_ctx_t *ctx;
1308   ctx = quic_ctx_get (ctx_index, thread_index);
1309   return &ctx->connection;
1310 }
1311
1312 static transport_connection_t *
1313 quic_listener_get (u32 listener_index)
1314 {
1315   QUIC_DBG (2, "Called quic_listener_get");
1316   quic_ctx_t *ctx;
1317   ctx = quic_ctx_get (listener_index, 0);
1318   return &ctx->connection;
1319 }
1320
1321 static u8 *
1322 format_quic_ctx (u8 * s, va_list * args)
1323 {
1324   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
1325   u32 verbose = va_arg (*args, u32);
1326   u8 *str = 0;
1327
1328   if (!ctx)
1329     return s;
1330   str = format (str, "[#%d][Q] ", ctx->c_thread_index);
1331
1332   if (quic_ctx_is_listener (ctx))
1333     str = format (str, "Listener, UDP %ld", ctx->udp_session_handle);
1334   else if (quic_ctx_is_stream (ctx))
1335     str = format (str, "Stream %ld conn %d",
1336                   ctx->stream->stream_id, ctx->quic_connection_ctx_id);
1337   else                          /* connection */
1338     str = format (str, "Conn %d UDP %d", ctx->c_c_index,
1339                   ctx->udp_session_handle);
1340
1341   str = format (str, " app %d wrk %d", ctx->parent_app_id,
1342                 ctx->parent_app_wrk_id);
1343
1344   if (verbose == 1)
1345     s = format (s, "%-50s%-15d", str, ctx->conn_state);
1346   else
1347     s = format (s, "%s\n", str);
1348   vec_free (str);
1349   return s;
1350 }
1351
1352 static u8 *
1353 format_quic_connection (u8 * s, va_list * args)
1354 {
1355   u32 qc_index = va_arg (*args, u32);
1356   u32 thread_index = va_arg (*args, u32);
1357   u32 verbose = va_arg (*args, u32);
1358   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
1359   s = format (s, "%U", format_quic_ctx, ctx, verbose);
1360   return s;
1361 }
1362
1363 static u8 *
1364 format_quic_half_open (u8 * s, va_list * args)
1365 {
1366   u32 qc_index = va_arg (*args, u32);
1367   u32 thread_index = va_arg (*args, u32);
1368   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
1369   s =
1370     format (s, "[#%d][Q] half-open app %u", thread_index, ctx->parent_app_id);
1371   return s;
1372 }
1373
1374 /*  TODO improve */
1375 static u8 *
1376 format_quic_listener (u8 * s, va_list * args)
1377 {
1378   u32 tci = va_arg (*args, u32);
1379   u32 thread_index = va_arg (*args, u32);
1380   u32 verbose = va_arg (*args, u32);
1381   quic_ctx_t *ctx = quic_ctx_get (tci, thread_index);
1382   s = format (s, "%U", format_quic_ctx, ctx, verbose);
1383   return s;
1384 }
1385
1386 /*****************************************************************************
1387  * END TRANSPORT PROTO FUNCTIONS
1388  *
1389  * START SESSION CALLBACKS
1390  * Called from UDP layer
1391  *****************************************************************************/
1392
1393 static inline void
1394 quic_build_sockaddr (struct sockaddr *sa, socklen_t * salen,
1395                      ip46_address_t * addr, u16 port, u8 is_ip4)
1396 {
1397   if (is_ip4)
1398     {
1399       struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
1400       sa4->sin_family = AF_INET;
1401       sa4->sin_port = port;
1402       sa4->sin_addr.s_addr = addr->ip4.as_u32;
1403       *salen = sizeof (struct sockaddr_in);
1404     }
1405   else
1406     {
1407       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
1408       sa6->sin6_family = AF_INET6;
1409       sa6->sin6_port = port;
1410       clib_memcpy (&sa6->sin6_addr, &addr->ip6, 16);
1411       *salen = sizeof (struct sockaddr_in6);
1412     }
1413 }
1414
1415 static int
1416 quic_on_client_connected (quic_ctx_t * ctx)
1417 {
1418   session_t *quic_session;
1419   app_worker_t *app_wrk;
1420   u32 ctx_id = ctx->c_c_index;
1421   u32 thread_index = ctx->c_thread_index;
1422   int rv;
1423
1424   app_wrk = app_worker_get_if_valid (ctx->parent_app_wrk_id);
1425   if (!app_wrk)
1426     {
1427       quic_disconnect_transport (ctx);
1428       return -1;
1429     }
1430
1431   quic_session = session_alloc (thread_index);
1432
1433   QUIC_DBG (2, "Allocated quic session 0x%lx", session_handle (quic_session));
1434   ctx->c_s_index = quic_session->session_index;
1435   quic_session->app_wrk_index = ctx->parent_app_wrk_id;
1436   quic_session->connection_index = ctx->c_c_index;
1437   quic_session->listener_handle = SESSION_INVALID_HANDLE;
1438   quic_session->session_type =
1439     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, ctx->udp_is_ip4);
1440
1441   if (app_worker_init_connected (app_wrk, quic_session))
1442     {
1443       QUIC_DBG (1, "failed to app_worker_init_connected");
1444       quic_proto_on_close (ctx_id, thread_index);
1445       return app_worker_connect_notify (app_wrk, NULL, ctx->client_opaque);
1446     }
1447
1448   quic_session->session_state = SESSION_STATE_CONNECTING;
1449   if ((rv = app_worker_connect_notify (app_wrk, quic_session,
1450                                        ctx->client_opaque)))
1451     {
1452       QUIC_DBG (1, "failed to notify app %d", rv);
1453       quic_proto_on_close (ctx_id, thread_index);
1454       return -1;
1455     }
1456
1457   /*  If the app opens a stream in its callback it may invalidate ctx */
1458   ctx = quic_ctx_get (ctx_id, thread_index);
1459   /*
1460    * app_worker_connect_notify() might have reallocated pool, reload
1461    * quic_session pointer
1462    */
1463   quic_session = session_get (ctx->c_s_index, thread_index);
1464   quic_session->session_state = SESSION_STATE_LISTENING;
1465
1466   return 0;
1467 }
1468
1469 static void
1470 quic_receive_connection (void *arg)
1471 {
1472   u32 new_ctx_id, thread_index = vlib_get_thread_index ();
1473   quic_ctx_t *temp_ctx, *new_ctx;
1474   clib_bihash_kv_16_8_t kv;
1475   quicly_conn_t *conn;
1476   session_t *udp_session;
1477
1478   temp_ctx = arg;
1479   new_ctx_id = quic_ctx_alloc (thread_index);
1480   new_ctx = quic_ctx_get (new_ctx_id, thread_index);
1481
1482   QUIC_DBG (2, "Received conn %u (now %u)", temp_ctx->c_thread_index,
1483             new_ctx_id);
1484
1485
1486   memcpy (new_ctx, temp_ctx, sizeof (quic_ctx_t));
1487   clib_mem_free (temp_ctx);
1488
1489   new_ctx->c_thread_index = thread_index;
1490   new_ctx->c_c_index = new_ctx_id;
1491
1492   conn = new_ctx->conn;
1493   quic_store_conn_ctx (conn, new_ctx);
1494   quic_make_connection_key (&kv, quicly_get_master_id (conn));
1495   kv.value = ((u64) thread_index) << 32 | (u64) new_ctx_id;
1496   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
1497   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
1498   new_ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
1499   quic_update_timer (new_ctx);
1500
1501   /*  Trigger write on this connection if necessary */
1502   udp_session = session_get_from_handle (new_ctx->udp_session_handle);
1503   if (svm_fifo_max_dequeue (udp_session->tx_fifo))
1504     if (session_send_io_evt_to_thread (udp_session->tx_fifo,
1505                                        SESSION_IO_EVT_TX))
1506       QUIC_DBG (4, "Cannot send TX event");
1507 }
1508
1509 static void
1510 quic_transfer_connection (u32 ctx_index, u32 dest_thread)
1511 {
1512   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
1513   quic_ctx_t *ctx, *temp_ctx;
1514   u32 thread_index = vlib_get_thread_index ();
1515
1516   QUIC_DBG (2, "Transferring conn %u to thread %u", ctx_index, dest_thread);
1517
1518   temp_ctx = clib_mem_alloc (sizeof (quic_ctx_t));
1519   ASSERT (temp_ctx);
1520   ctx = quic_ctx_get (ctx_index, thread_index);
1521
1522   memcpy (temp_ctx, ctx, sizeof (quic_ctx_t));
1523
1524   /*  Remove from timer wheel and thread-local pool */
1525   if (ctx->timer_handle != QUIC_TIMER_HANDLE_INVALID)
1526     {
1527       tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
1528       tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->timer_handle);
1529     }
1530   quic_ctx_free (ctx);
1531
1532   /*  Send connection to destination thread */
1533   session_send_rpc_evt_to_thread (dest_thread, quic_receive_connection,
1534                                   (void *) temp_ctx);
1535 }
1536
1537 static int
1538 quic_session_connected_callback (u32 quic_app_index, u32 ctx_index,
1539                                  session_t * udp_session, u8 is_fail)
1540 {
1541   QUIC_DBG (2, "QSession is now connected (id %u)",
1542             udp_session->session_index);
1543   /* This should always be called before quic_connect returns since UDP always
1544    * connects instantly. */
1545   clib_bihash_kv_16_8_t kv;
1546   struct sockaddr_in6 sa6;
1547   struct sockaddr *sa = (struct sockaddr *) &sa6;
1548   socklen_t salen;
1549   transport_connection_t *tc;
1550   app_worker_t *app_wrk;
1551   quicly_conn_t *conn;
1552   quic_ctx_t *ctx;
1553   u32 thread_index = vlib_get_thread_index ();
1554   int ret;
1555   quicly_context_t *quicly_ctx;
1556
1557
1558   ctx = quic_ctx_get (ctx_index, thread_index);
1559   if (is_fail)
1560     {
1561       u32 api_context;
1562       app_wrk = app_worker_get_if_valid (ctx->parent_app_wrk_id);
1563       if (app_wrk)
1564         {
1565           api_context = ctx->c_s_index;
1566           app_worker_connect_notify (app_wrk, 0, api_context);
1567         }
1568       return 0;
1569     }
1570
1571   ctx->c_thread_index = thread_index;
1572   ctx->c_c_index = ctx_index;
1573
1574   QUIC_DBG (2, "Quic connect returned %u. New ctx [%u]%x",
1575             is_fail, thread_index, (ctx) ? ctx_index : ~0);
1576
1577   ctx->udp_session_handle = session_handle (udp_session);
1578   udp_session->opaque = ctx->parent_app_id;
1579
1580   /* Init QUIC lib connection
1581    * Generate required sockaddr & salen */
1582   tc = session_get_transport (udp_session);
1583   quic_build_sockaddr (sa, &salen, &tc->rmt_ip, tc->rmt_port, tc->is_ip4);
1584
1585   quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
1586   ret = quicly_connect (&ctx->conn, quicly_ctx, (char *) ctx->srv_hostname,
1587                         sa, salen, &quic_main.next_cid,
1588                         &quic_main.hs_properties, NULL);
1589   ++quic_main.next_cid.master_id;
1590   /*  Save context handle in quicly connection */
1591   quic_store_conn_ctx (ctx->conn, ctx);
1592   assert (ret == 0);
1593
1594   /*  Register connection in connections map */
1595   conn = ctx->conn;
1596   quic_make_connection_key (&kv, quicly_get_master_id (conn));
1597   kv.value = ((u64) thread_index) << 32 | (u64) ctx_index;
1598   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
1599   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
1600
1601   /*  UDP stack quirk? preemptively transfer connection if that happens */
1602   if (udp_session->thread_index != thread_index)
1603     quic_transfer_connection (ctx_index, udp_session->thread_index);
1604   else
1605     quic_send_packets (ctx);
1606
1607   return ret;
1608 }
1609
1610 static void
1611 quic_session_disconnect_callback (session_t * s)
1612 {
1613   clib_warning ("UDP session disconnected???");
1614 }
1615
1616 static void
1617 quic_session_reset_callback (session_t * s)
1618 {
1619   clib_warning ("UDP session reset???");
1620 }
1621
1622 static void
1623 quic_session_migrate_callback (session_t * s, session_handle_t new_sh)
1624 {
1625   /*
1626    * TODO we need better way to get the connection from the session
1627    * This will become possible once we stop storing the app id in the UDP
1628    * session opaque
1629    */
1630   u32 thread_index = vlib_get_thread_index ();
1631   u64 old_session_handle = session_handle (s);
1632   u32 new_thread = session_thread_from_handle (new_sh);
1633   quic_ctx_t *ctx;
1634
1635   QUIC_DBG (1, "Session %x migrated to %lx", s->session_index, new_sh);
1636   /* *INDENT-OFF* */
1637   pool_foreach (ctx, quic_main.ctx_pool[thread_index],
1638     ({
1639       if (ctx->udp_session_handle == old_session_handle)
1640         {
1641           /*  Right ctx found, move associated conn */
1642           QUIC_DBG (5, "Found right ctx: %x", ctx->c_c_index);
1643           ctx->udp_session_handle = new_sh;
1644           quic_transfer_connection (ctx->c_c_index, new_thread);
1645           return;
1646         }
1647     }));
1648   /* *INDENT-ON* */
1649   QUIC_DBG (0, "BUG: Connection to migrate not found");
1650 }
1651
1652 int
1653 quic_session_accepted_callback (session_t * udp_session)
1654 {
1655   /* New UDP connection, try to accept it */
1656   u32 ctx_index;
1657   u32 *pool_index;
1658   quic_ctx_t *ctx, *lctx;
1659   session_t *udp_listen_session;
1660   u32 thread_index = vlib_get_thread_index ();
1661
1662   udp_listen_session =
1663     listen_session_get_from_handle (udp_session->listener_handle);
1664
1665   ctx_index = quic_ctx_alloc (thread_index);
1666   ctx = quic_ctx_get (ctx_index, thread_index);
1667   ctx->c_thread_index = udp_session->thread_index;
1668   ctx->c_c_index = ctx_index;
1669   ctx->c_s_index = QUIC_SESSION_INVALID;
1670   ctx->udp_session_handle = session_handle (udp_session);
1671   QUIC_DBG (2, "ACCEPTED UDP 0x%lx", ctx->udp_session_handle);
1672   ctx->listener_ctx_id = udp_listen_session->opaque;
1673   lctx = quic_ctx_get (udp_listen_session->opaque,
1674                        udp_listen_session->thread_index);
1675   ctx->udp_is_ip4 = lctx->c_is_ip4;
1676   ctx->parent_app_id = lctx->parent_app_id;
1677   ctx->parent_app_wrk_id = lctx->parent_app_wrk_id;
1678   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
1679   ctx->conn_state = QUIC_CONN_STATE_OPENED;
1680   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
1681
1682   udp_session->opaque = ctx->parent_app_id;
1683
1684   /* Put this ctx in the "opening" pool */
1685   pool_get (quic_main.wrk_ctx[ctx->c_thread_index].opening_ctx_pool,
1686             pool_index);
1687   *pool_index = ctx_index;
1688
1689   /* TODO timeout to delete these if they never connect */
1690   return 0;
1691 }
1692
1693 static int
1694 quic_add_segment_callback (u32 client_index, u64 seg_handle)
1695 {
1696   QUIC_DBG (2, "Called quic_add_segment_callback");
1697   QUIC_DBG (2, "NOT IMPLEMENTED");
1698   /* No-op for builtin */
1699   return 0;
1700 }
1701
1702 static int
1703 quic_del_segment_callback (u32 client_index, u64 seg_handle)
1704 {
1705   QUIC_DBG (2, "Called quic_del_segment_callback");
1706   QUIC_DBG (2, "NOT IMPLEMENTED");
1707   /* No-op for builtin */
1708   return 0;
1709 }
1710
1711 static int
1712 quic_custom_app_rx_callback (transport_connection_t * tc)
1713 {
1714   quic_ctx_t *ctx;
1715   session_t *stream_session = session_get (tc->s_index, tc->thread_index);
1716   QUIC_DBG (3, "Received app READ notification");
1717   quic_ack_rx_data (stream_session);
1718   svm_fifo_reset_has_deq_ntf (stream_session->rx_fifo);
1719
1720   /* Need to send packets (acks may never be sent otherwise) */
1721   ctx = quic_ctx_get (stream_session->connection_index,
1722                       stream_session->thread_index);
1723   quic_send_packets (ctx);
1724   return 0;
1725 }
1726
1727 static int
1728 quic_custom_tx_callback (void *s, u32 max_burst_size)
1729 {
1730   session_t *stream_session = (session_t *) s;
1731   quicly_stream_t *stream;
1732   quic_ctx_t *ctx;
1733   int rv;
1734
1735   if (PREDICT_FALSE
1736       (stream_session->session_state >= SESSION_STATE_TRANSPORT_CLOSING))
1737     return 0;
1738   ctx =
1739     quic_ctx_get (stream_session->connection_index,
1740                   stream_session->thread_index);
1741   if (PREDICT_FALSE (!quic_ctx_is_stream (ctx)))
1742     {
1743       goto tx_end;              /* Most probably a reschedule */
1744     }
1745
1746   QUIC_DBG (3, "Stream TX event");
1747   quic_ack_rx_data (stream_session);
1748   if (!svm_fifo_max_dequeue (stream_session->tx_fifo))
1749     return 0;
1750
1751   stream = ctx->stream;
1752   if (!quicly_sendstate_is_open (&stream->sendstate))
1753     {
1754       QUIC_DBG (1, "Warning: tried to send on closed stream");
1755       return -1;
1756     }
1757
1758   if ((rv = quicly_stream_sync_sendbuf (stream, 1)) != 0)
1759     return rv;
1760
1761 tx_end:
1762   quic_send_packets (ctx);
1763   return 0;
1764 }
1765
1766
1767 /*
1768  * Returns 0 if a matching connection is found and is on the right thread.
1769  * Otherwise returns -1.
1770  * If a connection is found, even on the wrong thread, ctx_thread and ctx_index
1771  * will be set.
1772  */
1773 static inline int
1774 quic_find_packet_ctx (u32 * ctx_thread, u32 * ctx_index,
1775                       struct sockaddr *sa, socklen_t salen,
1776                       quicly_decoded_packet_t * packet,
1777                       u32 caller_thread_index)
1778 {
1779   quic_ctx_t *ctx_;
1780   quicly_conn_t *conn_;
1781   clib_bihash_kv_16_8_t kv;
1782   clib_bihash_16_8_t *h;
1783
1784   h = &quic_main.connection_hash;
1785   quic_make_connection_key (&kv, &packet->cid.dest.plaintext);
1786   QUIC_DBG (3, "Searching conn with id %lu %lu", kv.key[0], kv.key[1]);
1787
1788   if (clib_bihash_search_16_8 (h, &kv, &kv) == 0)
1789     {
1790       u32 index = kv.value & UINT32_MAX;
1791       u32 thread_id = kv.value >> 32;
1792       /* Check if this connection belongs to this thread, otherwise
1793        * ask for it to be moved */
1794       if (thread_id != caller_thread_index)
1795         {
1796           QUIC_DBG (2, "Connection is on wrong thread");
1797           /* Cannot make full check with quicly_is_destination... */
1798           *ctx_index = index;
1799           *ctx_thread = thread_id;
1800           return -1;
1801         }
1802       ctx_ = quic_ctx_get (index, vlib_get_thread_index ());
1803       conn_ = ctx_->conn;
1804       if (conn_ && quicly_is_destination (conn_, sa, salen, packet))
1805         {
1806           QUIC_DBG (3, "Connection found");
1807           *ctx_index = index;
1808           *ctx_thread = thread_id;
1809           return 0;
1810         }
1811     }
1812   QUIC_DBG (3, "connection not found");
1813   return -1;
1814 }
1815
1816 static int
1817 quic_create_quic_session (quic_ctx_t * ctx)
1818 {
1819   session_t *quic_session;
1820   app_worker_t *app_wrk;
1821   quic_ctx_t *lctx;
1822   int rv;
1823
1824   quic_session = session_alloc (ctx->c_thread_index);
1825   QUIC_DBG (2, "Allocated quic_session, 0x%lx ctx %u",
1826             session_handle (quic_session), ctx->c_c_index);
1827   quic_session->session_state = SESSION_STATE_LISTENING;
1828   ctx->c_s_index = quic_session->session_index;
1829
1830   lctx = quic_ctx_get (ctx->listener_ctx_id, 0);
1831
1832   quic_session->app_wrk_index = lctx->parent_app_wrk_id;
1833   quic_session->connection_index = ctx->c_c_index;
1834   quic_session->session_type =
1835     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, ctx->udp_is_ip4);
1836   quic_session->listener_handle = lctx->c_s_index;
1837
1838   /* TODO: don't alloc fifos when we don't transfer data on this session
1839    * but we still need fifos for the events? */
1840   if ((rv = app_worker_init_accepted (quic_session)))
1841     {
1842       QUIC_DBG (1, "failed to allocate fifos");
1843       session_free (quic_session);
1844       return rv;
1845     }
1846   app_wrk = app_worker_get (quic_session->app_wrk_index);
1847   if ((rv = app_worker_accept_notify (app_wrk, quic_session)))
1848     {
1849       QUIC_DBG (1, "failed to notify accept worker app");
1850       return rv;
1851     }
1852   return 0;
1853 }
1854
1855 static int
1856 quic_create_connection (u32 ctx_index, struct sockaddr *sa,
1857                         socklen_t salen, quicly_decoded_packet_t packet)
1858 {
1859   clib_bihash_kv_16_8_t kv;
1860   quic_ctx_t *ctx;
1861   quicly_conn_t *conn;
1862   u32 thread_index = vlib_get_thread_index ();
1863   quicly_context_t *quicly_ctx;
1864   int rv;
1865
1866   /* new connection, accept and create context if packet is valid
1867    * TODO: check if socket is actually listening? */
1868   ctx = quic_ctx_get (ctx_index, thread_index);
1869   quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
1870   if ((rv = quicly_accept (&conn, quicly_ctx, sa, salen,
1871                            &packet, ptls_iovec_init (NULL, 0),
1872                            &quic_main.next_cid, NULL)))
1873     {
1874       /* Invalid packet, pass */
1875       assert (conn == NULL);
1876       QUIC_DBG (1, "Accept failed with %d", rv);
1877       /* TODO: cleanup created quic ctx and UDP session */
1878       return 0;
1879     }
1880   assert (conn != NULL);
1881
1882   ++quic_main.next_cid.master_id;
1883   /* Save ctx handle in quicly connection */
1884   quic_store_conn_ctx (conn, ctx);
1885   ctx->conn = conn;
1886   ctx->conn_state = QUIC_CONN_STATE_HANDSHAKE;
1887
1888   quic_create_quic_session (ctx);
1889
1890   /* Register connection in connections map */
1891   quic_make_connection_key (&kv, quicly_get_master_id (conn));
1892   kv.value = ((u64) thread_index) << 32 | (u64) ctx_index;
1893   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
1894   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
1895
1896   return quic_send_packets (ctx);
1897 }
1898
1899 static int
1900 quic_reset_connection (u64 udp_session_handle,
1901                        struct sockaddr *sa, socklen_t salen,
1902                        quicly_decoded_packet_t packet)
1903 {
1904   /* short header packet; potentially a dead connection. No need to check the
1905    * length of the incoming packet, because loop is prevented by authenticating
1906    * the CID (by checking node_id and thread_id). If the peer is also sending a
1907    * reset, then the next CID is highly likely to contain a non-authenticating
1908    * CID, ... */
1909   QUIC_DBG (2, "Sending stateless reset");
1910   int rv;
1911   quicly_datagram_t *dgram;
1912   session_t *udp_session;
1913   quicly_context_t *quicly_ctx;
1914   if (packet.cid.dest.plaintext.node_id != 0
1915       || packet.cid.dest.plaintext.thread_id != 0)
1916     return 0;
1917   quicly_ctx = quic_get_quicly_ctx_from_udp (udp_session_handle);
1918   dgram = quicly_send_stateless_reset (quicly_ctx, sa, salen,
1919                                        &packet.cid.dest.plaintext);
1920   if (dgram == NULL)
1921     return 1;
1922   udp_session = session_get_from_handle (udp_session_handle);
1923   rv = quic_send_datagram (udp_session, dgram);
1924   if (svm_fifo_set_event (udp_session->tx_fifo))
1925     session_send_io_evt_to_thread (udp_session->tx_fifo, SESSION_IO_EVT_TX);
1926   return rv;
1927 }
1928
1929 typedef struct quic_rx_packet_ctx_
1930 {
1931   quicly_decoded_packet_t packet;
1932   u8 data[QUIC_MAX_PACKET_SIZE];
1933   u32 ctx_index;
1934   u32 thread_index;
1935 } quic_rx_packet_ctx_t;
1936
1937 static void
1938 check_quic_client_connected (struct quic_rx_packet_ctx_ *quic_rx_ctx)
1939 {
1940   /* ctx pointer may change if a new stream is opened */
1941   quic_ctx_t *ctx = quic_ctx_get (quic_rx_ctx->ctx_index,
1942                                   quic_rx_ctx->thread_index);
1943   /* Conn may be set to null if the connection is terminated */
1944   if (ctx->conn && ctx->conn_state == QUIC_CONN_STATE_HANDSHAKE)
1945     {
1946       if (quicly_connection_is_ready (ctx->conn))
1947         {
1948           ctx->conn_state = QUIC_CONN_STATE_READY;
1949           if (quicly_is_client (ctx->conn))
1950             {
1951               quic_on_client_connected (ctx);
1952             }
1953         }
1954     }
1955
1956 }
1957
1958 static int
1959 quic_process_one_rx_packet (u64 udp_session_handle,
1960                             quicly_context_t * quicly_ctx, svm_fifo_t * f,
1961                             u32 * fifo_offset, u32 * max_packet, u32 packet_n,
1962                             quic_rx_packet_ctx_t * packet_ctx)
1963 {
1964   session_dgram_hdr_t ph;
1965   quic_ctx_t *ctx = NULL;
1966   size_t plen;
1967   struct sockaddr_in6 sa6;
1968   struct sockaddr *sa = (struct sockaddr *) &sa6;
1969   socklen_t salen;
1970   u32 full_len, ret;
1971   int err, rv = 0;
1972   packet_ctx->thread_index = UINT32_MAX;
1973   packet_ctx->ctx_index = UINT32_MAX;
1974   u32 thread_index = vlib_get_thread_index ();
1975   u32 *opening_ctx_pool, *ctx_index_ptr;
1976   u32 cur_deq = svm_fifo_max_dequeue (f) - *fifo_offset;
1977
1978   if (cur_deq == 0)
1979     {
1980       *max_packet = packet_n + 1;
1981       return 0;
1982     }
1983
1984   if (cur_deq < SESSION_CONN_HDR_LEN)
1985     {
1986       QUIC_DBG (1, "Not enough data for even a header in RX");
1987       return 1;
1988     }
1989   ret = svm_fifo_peek (f, *fifo_offset, SESSION_CONN_HDR_LEN, (u8 *) & ph);
1990   if (ret != SESSION_CONN_HDR_LEN)
1991     {
1992       QUIC_DBG (1, "Not enough data for header in RX");
1993       return 1;
1994     }
1995   ASSERT (ph.data_offset == 0);
1996   full_len = ph.data_length + SESSION_CONN_HDR_LEN;
1997   if (full_len > cur_deq)
1998     {
1999       QUIC_DBG (1, "Not enough data in fifo RX");
2000       return 1;
2001     }
2002
2003   /* Quicly can read len bytes from the fifo at offset:
2004    * ph.data_offset + SESSION_CONN_HDR_LEN */
2005   ret =
2006     svm_fifo_peek (f, SESSION_CONN_HDR_LEN + *fifo_offset, ph.data_length,
2007                    packet_ctx->data);
2008   if (ret != ph.data_length)
2009     {
2010       QUIC_DBG (1, "Not enough data peeked in RX");
2011       return 1;
2012     }
2013
2014   quic_increment_counter (QUIC_ERROR_RX_PACKETS, 1);
2015   rv = 0;
2016   quic_build_sockaddr (sa, &salen, &ph.rmt_ip, ph.rmt_port, ph.is_ip4);
2017   quicly_ctx = quic_get_quicly_ctx_from_udp (udp_session_handle);
2018   plen =
2019     quicly_decode_packet (quicly_ctx, &packet_ctx->packet, packet_ctx->data,
2020                           ph.data_length);
2021
2022   if (plen == SIZE_MAX)
2023     {
2024       *fifo_offset += SESSION_CONN_HDR_LEN + ph.data_length;
2025       return 1;
2026     }
2027
2028   err =
2029     quic_find_packet_ctx (&packet_ctx->thread_index, &packet_ctx->ctx_index,
2030                           sa, salen, &packet_ctx->packet, thread_index);
2031   if (err == 0)
2032     {
2033       ctx = quic_ctx_get (packet_ctx->ctx_index, thread_index);
2034       rv = quicly_receive (ctx->conn, &packet_ctx->packet);
2035       if (rv)
2036         QUIC_DBG (1, "quicly_receive return error %d", rv);
2037     }
2038   else if (packet_ctx->ctx_index != UINT32_MAX)
2039     {
2040       /*  Connection found but on wrong thread, ask move */
2041       *max_packet = packet_n + 1;
2042       return 0;
2043     }
2044   else if ((packet_ctx->packet.octets.base[0] & QUICLY_PACKET_TYPE_BITMASK) ==
2045            QUICLY_PACKET_TYPE_INITIAL)
2046     {
2047       /*  Try to find matching "opening" ctx */
2048       opening_ctx_pool = quic_main.wrk_ctx[thread_index].opening_ctx_pool;
2049
2050         /* *INDENT-OFF* */
2051         pool_foreach (ctx_index_ptr, opening_ctx_pool,
2052         ({
2053           ctx = quic_ctx_get (*ctx_index_ptr, thread_index);
2054           if (ctx->udp_session_handle == udp_session_handle)
2055             {
2056               /*  Right ctx found, create conn & remove from pool */
2057               quic_create_connection(*ctx_index_ptr, sa, salen, packet_ctx->packet);
2058               *max_packet = packet_n + 1;
2059               packet_ctx->thread_index = thread_index;
2060               packet_ctx->ctx_index = *ctx_index_ptr;
2061               pool_put (opening_ctx_pool, ctx_index_ptr);
2062               goto updateOffset;
2063             }
2064         }));
2065         /* *INDENT-ON* */
2066     }
2067   else
2068     {
2069       quic_reset_connection (udp_session_handle, sa, salen,
2070                              packet_ctx->packet);
2071     }
2072
2073 updateOffset:
2074   *fifo_offset += SESSION_CONN_HDR_LEN + ph.data_length;
2075   return 0;
2076 }
2077
2078 static int
2079 quic_app_rx_callback (session_t * udp_session)
2080 {
2081   /*  Read data from UDP rx_fifo and pass it to the quicly conn. */
2082   application_t *app;
2083   quic_ctx_t *ctx = NULL;
2084   svm_fifo_t *f;
2085   u32 max_deq;
2086   u32 app_index = udp_session->opaque;
2087   u64 udp_session_handle = session_handle (udp_session);
2088   int rv = 0;
2089   app = application_get_if_valid (app_index);
2090   u32 thread_index = vlib_get_thread_index ();
2091   quic_rx_packet_ctx_t packets_ctx[16];
2092
2093   if (!app)
2094     {
2095       QUIC_DBG (1, "Got RX on detached app");
2096       /*  TODO: close this session, cleanup state? */
2097       return 1;
2098     }
2099
2100   do
2101     {
2102       udp_session = session_get_from_handle (udp_session_handle);       /*  session alloc might have happened */
2103       f = udp_session->rx_fifo;
2104       max_deq = svm_fifo_max_dequeue (f);
2105       if (max_deq == 0)
2106         {
2107           return 0;
2108         }
2109
2110       u32 fifo_offset = 0;
2111       u32 max_packets = 16;
2112       for (int i = 0; i < max_packets; i++)
2113         {
2114           quic_process_one_rx_packet (udp_session_handle,
2115                                       (quicly_context_t *) app->quicly_ctx, f,
2116                                       &fifo_offset, &max_packets, i,
2117                                       &packets_ctx[i]);
2118         }
2119
2120       for (int i = 0; i < max_packets; i++)
2121         {
2122           if (packets_ctx[i].thread_index != thread_index)
2123             continue;
2124
2125           check_quic_client_connected (&packets_ctx[i]);
2126           ctx =
2127             quic_ctx_get (packets_ctx[i].ctx_index,
2128                           packets_ctx[i].thread_index);
2129           quic_send_packets (ctx);
2130         }
2131       svm_fifo_dequeue_drop (f, fifo_offset);
2132     }
2133   while (1);
2134   return rv;
2135 }
2136
2137 always_inline void
2138 quic_common_get_transport_endpoint (quic_ctx_t * ctx,
2139                                     transport_endpoint_t * tep, u8 is_lcl)
2140 {
2141   session_t *udp_session;
2142   if (!quic_ctx_is_stream (ctx))
2143     {
2144       udp_session = session_get_from_handle (ctx->udp_session_handle);
2145       session_get_endpoint (udp_session, tep, is_lcl);
2146     }
2147 }
2148
2149 static void
2150 quic_get_transport_listener_endpoint (u32 listener_index,
2151                                       transport_endpoint_t * tep, u8 is_lcl)
2152 {
2153   quic_ctx_t *ctx;
2154   app_listener_t *app_listener;
2155   session_t *udp_listen_session;
2156   ctx = quic_ctx_get (listener_index, vlib_get_thread_index ());
2157   if (quic_ctx_is_listener (ctx))
2158     {
2159       app_listener = app_listener_get_w_handle (ctx->udp_session_handle);
2160       udp_listen_session = app_listener_get_session (app_listener);
2161       return session_get_endpoint (udp_listen_session, tep, is_lcl);
2162     }
2163   quic_common_get_transport_endpoint (ctx, tep, is_lcl);
2164 }
2165
2166 static void
2167 quic_get_transport_endpoint (u32 ctx_index, u32 thread_index,
2168                              transport_endpoint_t * tep, u8 is_lcl)
2169 {
2170   quic_ctx_t *ctx;
2171   ctx = quic_ctx_get (ctx_index, thread_index);
2172   quic_common_get_transport_endpoint (ctx, tep, is_lcl);
2173 }
2174
2175 /*****************************************************************************
2176  * END TRANSPORT PROTO FUNCTIONS
2177 *****************************************************************************/
2178
2179 /* *INDENT-OFF* */
2180 static session_cb_vft_t quic_app_cb_vft = {
2181   .session_accept_callback = quic_session_accepted_callback,
2182   .session_disconnect_callback = quic_session_disconnect_callback,
2183   .session_connected_callback = quic_session_connected_callback,
2184   .session_reset_callback = quic_session_reset_callback,
2185   .session_migrate_callback = quic_session_migrate_callback,
2186   .add_segment_callback = quic_add_segment_callback,
2187   .del_segment_callback = quic_del_segment_callback,
2188   .builtin_app_rx_callback = quic_app_rx_callback,
2189 };
2190
2191 static const transport_proto_vft_t quic_proto = {
2192   .connect = quic_connect,
2193   .close = quic_proto_on_close,
2194   .start_listen = quic_start_listen,
2195   .stop_listen = quic_stop_listen,
2196   .get_connection = quic_connection_get,
2197   .get_listener = quic_listener_get,
2198   .update_time = quic_update_time,
2199   .app_rx_evt = quic_custom_app_rx_callback,
2200   .custom_tx = quic_custom_tx_callback,
2201   .format_connection = format_quic_connection,
2202   .format_half_open = format_quic_half_open,
2203   .format_listener = format_quic_listener,
2204   .get_transport_endpoint = quic_get_transport_endpoint,
2205   .get_transport_listener_endpoint = quic_get_transport_listener_endpoint,
2206   .transport_options = {
2207     .tx_type = TRANSPORT_TX_INTERNAL,
2208     .service_type = TRANSPORT_SERVICE_APP,
2209   },
2210 };
2211 /* *INDENT-ON* */
2212
2213 static void
2214 quic_register_cipher_suite (quic_crypto_engine_t type,
2215                             ptls_cipher_suite_t ** ciphers)
2216 {
2217   quic_main_t *qm = &quic_main;
2218   vec_validate (qm->quic_ciphers, type);
2219   qm->quic_ciphers[type] = ciphers;
2220 }
2221
2222 static clib_error_t *
2223 quic_init (vlib_main_t * vm)
2224 {
2225   u32 segment_size = 256 << 20;
2226   vlib_thread_main_t *vtm = vlib_get_thread_main ();
2227   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
2228   vnet_app_attach_args_t _a, *a = &_a;
2229   u64 options[APP_OPTIONS_N_OPTIONS];
2230   quic_main_t *qm = &quic_main;
2231   u32 fifo_size = QUIC_FIFO_SIZE;
2232   u32 num_threads, i;
2233
2234   num_threads = 1 /* main thread */  + vtm->n_threads;
2235
2236   memset (a, 0, sizeof (*a));
2237   memset (options, 0, sizeof (options));
2238
2239   a->session_cb_vft = &quic_app_cb_vft;
2240   a->api_client_index = APP_INVALID_INDEX;
2241   a->options = options;
2242   a->name = format (0, "quic");
2243   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
2244   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
2245   a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
2246   a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
2247   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
2248   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
2249   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
2250
2251   if (vnet_application_attach (a))
2252     {
2253       clib_warning ("failed to attach quic app");
2254       return clib_error_return (0, "failed to attach quic app");
2255     }
2256
2257   vec_validate (qm->ctx_pool, num_threads - 1);
2258   vec_validate (qm->wrk_ctx, num_threads - 1);
2259   /*  Timer wheels, one per thread. */
2260   for (i = 0; i < num_threads; i++)
2261     {
2262       tw = &qm->wrk_ctx[i].timer_wheel;
2263       tw_timer_wheel_init_1t_3w_1024sl_ov (tw, quic_expired_timers_dispatch,
2264                                            1e-3 /* timer period 1ms */ , ~0);
2265       tw->last_run_time = vlib_time_now (vlib_get_main ());
2266     }
2267
2268   clib_bihash_init_16_8 (&qm->connection_hash, "quic connections", 1024,
2269                          4 << 20);
2270
2271
2272   qm->app_index = a->app_index;
2273   qm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
2274     / QUIC_TSTAMP_RESOLUTION;
2275   qm->session_cache.super.cb = quic_encrypt_ticket_cb;
2276
2277   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
2278                                FIB_PROTOCOL_IP4, ~0);
2279   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
2280                                FIB_PROTOCOL_IP6, ~0);
2281
2282   quic_register_cipher_suite (CRYPTO_ENGINE_VPP, quic_crypto_cipher_suites);
2283   quic_register_cipher_suite (CRYPTO_ENGINE_PICOTLS,
2284                               ptls_openssl_cipher_suites);
2285   qm->default_cipher = CRYPTO_ENGINE_PICOTLS;
2286   vec_free (a->name);
2287   return 0;
2288 }
2289
2290 VLIB_INIT_FUNCTION (quic_init);
2291
2292 static clib_error_t *
2293 quic_plugin_crypto_command_fn (vlib_main_t * vm,
2294                                unformat_input_t * input,
2295                                vlib_cli_command_t * cmd)
2296 {
2297   quic_main_t *qm = &quic_main;
2298   if (unformat_check_input (input) == UNFORMAT_END_OF_INPUT)
2299     return clib_error_return (0, "unknown input '%U'",
2300                               format_unformat_error, input);
2301   if (unformat (input, "vpp"))
2302     qm->default_cipher = CRYPTO_ENGINE_VPP;
2303   else if (unformat (input, "picotls"))
2304     qm->default_cipher = CRYPTO_ENGINE_PICOTLS;
2305   else
2306     return clib_error_return (0, "unknown input '%U'",
2307                               format_unformat_error, input);
2308   return 0;
2309 }
2310
2311 static u8 *
2312 quic_format_ctx_stat (u8 * s, va_list * args)
2313 {
2314   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
2315   quicly_stats_t quicly_stats;
2316
2317   quicly_get_stats (ctx->conn, &quicly_stats);
2318
2319   s = format (s, "\n\rQUIC conn stats \n\r");
2320
2321   s =
2322     format (s, "RTT: min:%d, smoothed:%d, variance:%d, latest:%d \n\r",
2323             quicly_stats.rtt.minimum, quicly_stats.rtt.smoothed,
2324             quicly_stats.rtt.variance, quicly_stats.rtt.latest);
2325   s = format (s, "Packet loss:%d \n\r", quicly_stats.num_packets.lost);
2326
2327   return s;
2328 }
2329
2330 static clib_error_t *
2331 quic_plugin_showstats_command_fn (vlib_main_t * vm,
2332                                   unformat_input_t * input,
2333                                   vlib_cli_command_t * cmd)
2334 {
2335   quic_main_t *qm = &quic_main;
2336   quic_ctx_t *ctx = NULL;
2337   u32 num_workers = vlib_num_workers ();
2338
2339   for (int i = 0; i < num_workers + 1; i++)
2340     {
2341       /* *INDENT-OFF* */
2342       pool_foreach (ctx, qm->ctx_pool[i],
2343       ({
2344         if(!(ctx->flags & QUIC_F_IS_LISTENER) && !(ctx->flags & QUIC_F_IS_STREAM))
2345           vlib_cli_output (vm, "%U", quic_format_ctx_stat, ctx);
2346       }));
2347       /* *INDENT-ON* */
2348     }
2349   return 0;
2350 }
2351
2352 /* *INDENT-OFF* */
2353 VLIB_CLI_COMMAND(quic_plugin_crypto_command, static)=
2354 {
2355   .path = "quic set crypto api",
2356   .short_help = "quic set crypto api [picotls, vpp]",
2357   .function = quic_plugin_crypto_command_fn,
2358 };
2359 VLIB_CLI_COMMAND(quic_plugin_stats_command, static)=
2360 {
2361   .path = "show quic stats",
2362   .short_help = "show quic stats",
2363   .function = quic_plugin_showstats_command_fn,
2364 };
2365 VLIB_PLUGIN_REGISTER () =
2366 {
2367   .version = VPP_BUILD_VER,
2368   .description = "Quic transport protocol",
2369   .default_disabled = 1,
2370 };
2371
2372 static uword
2373 quic_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
2374               vlib_frame_t * frame)
2375 {
2376   return 0;
2377 }
2378
2379 /* *INDENT-OFF* */
2380 VLIB_REGISTER_NODE (quic_input_node) =
2381 {
2382   .function = quic_node_fn,
2383   .name = "quic-input",
2384   .vector_size = sizeof (u32),
2385   .type = VLIB_NODE_TYPE_INTERNAL,
2386   .n_errors = ARRAY_LEN (quic_error_strings),
2387   .error_strings = quic_error_strings,
2388 };
2389 /* *INDENT-ON* */
2390
2391 /*
2392  * fd.io coding-style-patch-verification: ON
2393  *
2394  * Local Variables:
2395  * eval: (c-set-style "gnu")
2396  * End:
2397  */