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