Add get_endpoint in transport vft
[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 int64_t quic_get_time (quicly_now_cb * self);
37 static void quic_connection_closed (u32 conn_index);
38 static void quic_disconnect (u32 ctx_index, u32 thread_index);
39 static int quic_connect_new_stream (session_endpoint_cfg_t * sep);
40 static int quic_connect_new_connection (session_endpoint_cfg_t * sep);
41
42 #define QUIC_INT_MAX  0x3FFFFFFFFFFFFFFF
43
44 static u32
45 quic_ctx_alloc ()
46 {
47   u8 thread_index = vlib_get_thread_index ();
48   quic_main_t *qm = &quic_main;
49   quic_ctx_t *ctx;
50
51   pool_get (qm->ctx_pool[thread_index], ctx);
52
53   memset (ctx, 0, sizeof (quic_ctx_t));
54   ctx->c_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)
70 {
71   return pool_elt_at_index (quic_main.ctx_pool[vlib_get_thread_index ()],
72                             ctx_index);
73 }
74
75 static quic_ctx_t *
76 quic_ctx_get_w_thread (u32 ctx_index, u8 thread_index)
77 {
78   return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
79 }
80
81 static void
82 quic_disconnect_transport (quic_ctx_t * ctx)
83 {
84   QUIC_DBG (2, "Called quic_disconnect_transport");
85   vnet_disconnect_args_t a = {
86     .handle = ctx->c_quic_ctx_id.udp_session_handle,
87     .app_index = quic_main.app_index,
88   };
89
90   if (vnet_disconnect_session (&a))
91     clib_warning ("UDP session disconnect errored");
92 }
93
94 static int
95 quic_send_datagram (session_t * udp_session, quicly_datagram_t * packet)
96 {
97   // QUIC_DBG (2, "Called quic_send_datagram at %ld", quic_get_time (NULL));
98   u32 max_enqueue;
99   session_dgram_hdr_t hdr;
100   int rv;
101   u32 len;
102   svm_fifo_t *f;
103   transport_connection_t *tc;
104
105   len = packet->data.len;
106   f = udp_session->tx_fifo;
107   tc = session_get_transport (udp_session);
108
109   max_enqueue = svm_fifo_max_enqueue (f);
110   if (max_enqueue <= sizeof (session_dgram_hdr_t))
111     return 1;
112
113   max_enqueue -= sizeof (session_dgram_hdr_t);
114
115   if (max_enqueue < len)
116     return 1;
117
118   // Build packet header for fifo
119   hdr.data_length = len;
120   hdr.data_offset = 0;
121   hdr.is_ip4 = tc->is_ip4;
122   clib_memcpy (&hdr.lcl_ip, &tc->lcl_ip, sizeof (ip46_address_t));
123   hdr.lcl_port = tc->lcl_port;
124
125   // Read dest address from quicly-provided sockaddr
126   if (hdr.is_ip4)
127     {
128       ASSERT (packet->sa.sa_family == AF_INET);
129       struct sockaddr_in *sa4 = (struct sockaddr_in *) &packet->sa;
130       hdr.rmt_port = sa4->sin_port;
131       hdr.rmt_ip.ip4.as_u32 = sa4->sin_addr.s_addr;
132     }
133   else
134     {
135       ASSERT (packet->sa.sa_family == AF_INET6);
136       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &packet->sa;
137       hdr.rmt_port = sa6->sin6_port;
138       clib_memcpy (&hdr.rmt_ip.ip6, &sa6->sin6_addr, 16);
139     }
140
141   rv = svm_fifo_enqueue_nowait (f, sizeof (hdr), (u8 *) & hdr);
142   ASSERT (rv == sizeof (hdr));
143   if (svm_fifo_enqueue_nowait (f, len, packet->data.base) != len)
144     return 1;
145   return 0;
146 }
147
148 static int
149 quic_send_packets (quic_ctx_t * ctx)
150 {
151   //QUIC_DBG (2, "Called quic_send_packets");
152   quicly_datagram_t *packets[16];
153   session_t *udp_session;
154   quicly_conn_t *conn;
155   size_t num_packets, i;
156   int ret;
157
158   if (ctx->c_quic_ctx_id.is_stream)
159     {
160       // We have sctx, get qctx
161       ctx = quic_ctx_get (ctx->c_quic_ctx_id.quic_connection_ctx_id);
162     }
163
164   ASSERT (!ctx->c_quic_ctx_id.is_stream);
165
166   udp_session =
167     session_get_from_handle (ctx->c_quic_ctx_id.udp_session_handle);
168   conn = ctx->c_quic_ctx_id.conn;
169
170   if (!conn)
171     return 0;
172
173   do
174     {
175       num_packets = sizeof (packets) / sizeof (packets[0]);
176       if ((ret = quicly_send (conn, packets, &num_packets)) == 0)
177         {
178           for (i = 0; i != num_packets; ++i)
179             {
180               if (quic_send_datagram (udp_session, packets[i]))
181                 {
182                   QUIC_DBG (2, "quic_send_datagram failed");
183                   goto stop_sending;
184                 }
185               ret = 0;
186               quicly_default_free_packet_cb.cb
187                 (&quicly_default_free_packet_cb, packets[i]);
188             }
189         }
190       else
191         {
192           QUIC_DBG (2, "quicly_send returned %d, closing connection\n", ret);
193           return ret;
194         }
195     }
196   while (ret == 0 && num_packets == sizeof (packets) / sizeof (packets[0]));
197
198 stop_sending:
199   if (svm_fifo_set_event (udp_session->tx_fifo))
200     session_send_io_evt_to_thread (udp_session->tx_fifo, FIFO_EVENT_APP_TX);
201
202   quic_update_timer (ctx);
203   return 0;
204 }
205
206 /*****************************************************************************
207  * START QUICLY CALLBACKS
208  * Called from QUIC lib
209  *****************************************************************************/
210
211 static int
212 quic_on_stop_sending (quicly_stream_t * stream, int error_code)
213 {
214   QUIC_DBG (2, "received STOP_SENDING: %d", error_code);
215   return 0;
216 }
217
218 static int
219 quic_on_receive_reset (quicly_stream_t * stream, int error_code)
220 {
221   QUIC_DBG (2, "received RESET_STREAM: %d", error_code);
222   return 0;
223 }
224
225 static int
226 quic_on_receive (quicly_stream_t * stream, size_t off, const void *src,
227                  size_t len)
228 {
229   QUIC_DBG (2, "received data: %lu bytes", len);
230   u32 to_enqueue, ctx_id;
231   quic_ctx_t *sctx;
232   session_t *stream_session;
233   svm_fifo_t *rx_fifo;
234   app_worker_t *app_wrk;
235
236   ctx_id = ((quic_stream_data_t *) stream->data)->ctx_id;
237   sctx = quic_ctx_get (ctx_id);
238   stream_session = session_get (sctx->c_s_index, vlib_get_thread_index ());
239   rx_fifo = stream_session->rx_fifo;
240   to_enqueue = svm_fifo_max_enqueue (rx_fifo);
241   if (to_enqueue > len)
242     to_enqueue = len;
243   // TODO what happens to the excess bytes?
244
245   svm_fifo_enqueue_nowait (rx_fifo, to_enqueue, src);
246
247   // Notify app
248   app_wrk = app_worker_get_if_valid (stream_session->app_wrk_index);
249   if (PREDICT_TRUE (app_wrk != 0))
250     app_worker_lock_and_send_event (app_wrk, stream_session,
251                                     SESSION_IO_EVT_RX);
252   return 0;
253 }
254
255 static const quicly_stream_callbacks_t quic_stream_callbacks = {
256   .on_destroy = quicly_streambuf_destroy,
257   .on_send_shift = quicly_streambuf_egress_shift,
258   .on_send_emit = quicly_streambuf_egress_emit,
259   .on_send_stop = quic_on_stop_sending,
260   .on_receive = quic_on_receive,
261   .on_receive_reset = quic_on_receive_reset
262 };
263
264 static void
265 quic_accept_stream (void *s)
266 {
267   quicly_stream_t *stream = (quicly_stream_t *) s;
268   session_t *stream_session, *quic_session;
269   quic_stream_data_t *stream_data;
270   app_worker_t *app_wrk;
271   quic_ctx_t *qctx, *sctx;
272   u32 qctx_id, sctx_id;
273   int rv;
274
275   sctx_id = quic_ctx_alloc ();
276
277   qctx_id = (u64) * quicly_get_data (stream->conn);
278   qctx = quic_ctx_get (qctx_id);
279
280   stream_session = session_alloc (qctx->c_thread_index);
281   QUIC_DBG (1, "Created stream_session, id %u ctx %u",
282             stream_session->session_index, sctx_id);
283
284   sctx = quic_ctx_get (sctx_id);
285   sctx->c_quic_ctx_id.parent_app_wrk_id =
286     qctx->c_quic_ctx_id.parent_app_wrk_id;
287   sctx->c_quic_ctx_id.parent_app_id = qctx->c_quic_ctx_id.parent_app_id;
288   sctx->c_quic_ctx_id.quic_connection_ctx_id = qctx->c_c_index;
289   sctx->c_c_index = sctx_id;
290   sctx->c_quic_ctx_id.is_stream = 1;
291   sctx->c_s_index = stream_session->session_index;
292   sctx->c_quic_ctx_id.stream = stream;
293   sctx->c_quic_ctx_id.stream_session_handle = session_handle (stream_session);
294
295   quic_session =
296     session_get_from_handle (qctx->c_quic_ctx_id.quic_session_handle);
297   stream_data = (quic_stream_data_t *) stream->data;
298   stream_data->ctx_id = sctx_id;
299
300   sctx->c_s_index = stream_session->session_index;
301   stream_session->session_state = SESSION_STATE_CREATED;
302   stream_session->app_wrk_index = sctx->c_quic_ctx_id.parent_app_wrk_id;
303   stream_session->connection_index = sctx->c_c_index;
304   stream_session->session_type =
305     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC,
306                                     qctx->c_quic_ctx_id.udp_is_ip4);
307   stream_session->opaque = QUIC_SESSION_TYPE_STREAM;
308   stream_session->listener_index = quic_session->session_index;
309   stream_session->app_index = sctx->c_quic_ctx_id.parent_app_id;
310
311   app_wrk = app_worker_get (stream_session->app_wrk_index);
312   if ((rv = app_worker_init_connected (app_wrk, stream_session)))
313     {
314       QUIC_DBG (1, "failed to allocate fifos");
315       session_free (stream_session);
316       quicly_reset_stream (stream, 0x30001);
317       return;
318     }
319
320   rv = app_worker_accept_notify (app_wrk, stream_session);
321   if (rv)
322     {
323       QUIC_DBG (1, "failed to notify accept worker app");
324       session_free_w_fifos (stream_session);
325       quicly_reset_stream (stream, 0x30002);
326       return;
327     }
328   session_lookup_add_connection (&sctx->connection,
329                                  session_handle (stream_session));
330 }
331
332 static int
333 quic_on_stream_open (quicly_stream_open_cb * self, quicly_stream_t * stream)
334 {
335   QUIC_DBG (2, "on_stream_open called");
336   int ret;
337   if ((ret =
338        quicly_streambuf_create (stream, sizeof (quic_stream_data_t))) != 0)
339     {
340       return ret;
341     }
342   stream->callbacks = &quic_stream_callbacks;
343   // Notify accept on parent qsession, but only if this is not a locally
344   // initiated stream
345   if (!quicly_stream_is_self_initiated (stream))
346     {
347       quic_accept_stream (stream);
348     }
349   return 0;
350 }
351
352 static quicly_stream_open_cb on_stream_open = { &quic_on_stream_open };
353
354 static void
355 quic_on_conn_close (quicly_closed_by_peer_cb * self, quicly_conn_t * conn,
356                     int code, uint64_t frame_type,
357                     const char *reason, size_t reason_len)
358 {
359   QUIC_DBG (2, "connection closed, reason: %s", reason);
360   u32 ctx_index = (u64) * quicly_get_data (conn);
361   quic_ctx_t *ctx = quic_ctx_get (ctx_index);
362   session_transport_closing_notify (&ctx->connection);
363 }
364
365 static quicly_closed_by_peer_cb on_closed_by_peer = { &quic_on_conn_close };
366
367
368 /*****************************************************************************
369  * END QUICLY CALLBACKS
370  *****************************************************************************/
371
372 /* single-entry session cache */
373 struct st_util_session_cache_t
374 {
375   ptls_encrypt_ticket_t super;
376   uint8_t id[32];
377   ptls_iovec_t data;
378 };
379
380 static int
381 encrypt_ticket_cb (ptls_encrypt_ticket_t * _self, ptls_t * tls,
382                    int is_encrypt, ptls_buffer_t * dst, ptls_iovec_t src)
383 {
384   struct st_util_session_cache_t *self = (void *) _self;
385   int ret;
386
387   if (is_encrypt)
388     {
389
390       /* replace the cached entry along with a newly generated session id */
391       free (self->data.base);
392       if ((self->data.base = malloc (src.len)) == NULL)
393         return PTLS_ERROR_NO_MEMORY;
394
395       ptls_get_context (tls)->random_bytes (self->id, sizeof (self->id));
396       memcpy (self->data.base, src.base, src.len);
397       self->data.len = src.len;
398
399       /* store the session id in buffer */
400       if ((ret = ptls_buffer_reserve (dst, sizeof (self->id))) != 0)
401         return ret;
402       memcpy (dst->base + dst->off, self->id, sizeof (self->id));
403       dst->off += sizeof (self->id);
404
405     }
406   else
407     {
408
409       /* check if session id is the one stored in cache */
410       if (src.len != sizeof (self->id))
411         return PTLS_ERROR_SESSION_NOT_FOUND;
412       if (memcmp (self->id, src.base, sizeof (self->id)) != 0)
413         return PTLS_ERROR_SESSION_NOT_FOUND;
414
415       /* return the cached value */
416       if ((ret = ptls_buffer_reserve (dst, self->data.len)) != 0)
417         return ret;
418       memcpy (dst->base + dst->off, self->data.base, self->data.len);
419       dst->off += self->data.len;
420     }
421
422   return 0;
423 }
424
425 /* *INDENT-OFF* */
426 static struct st_util_session_cache_t sc = {
427   .super = {
428     .cb = encrypt_ticket_cb,
429   },
430 };
431
432 static ptls_context_t quic_tlsctx = {
433   .random_bytes = ptls_openssl_random_bytes,
434   .get_time = &ptls_get_time,
435   .key_exchanges = ptls_openssl_key_exchanges,
436   .cipher_suites = ptls_openssl_cipher_suites,
437   .certificates = {
438     .list = NULL,
439     .count = 0
440   },
441   .esni = NULL,
442   .on_client_hello = NULL,
443   .emit_certificate = NULL,
444   .sign_certificate = NULL,
445   .verify_certificate = NULL,
446   .ticket_lifetime = 86400,
447   .max_early_data_size = 8192,
448   .hkdf_label_prefix__obsolete = NULL,
449   .require_dhe_on_psk = 1,
450   .encrypt_ticket = &sc.super,
451 };
452 /* *INDENT-ON* */
453
454 static int
455 ptls_compare_separator_line (const char *line, const char *begin_or_end,
456                              const char *label)
457 {
458   int ret = strncmp (line, "-----", 5);
459   size_t text_index = 5;
460
461   if (ret == 0)
462     {
463       size_t begin_or_end_length = strlen (begin_or_end);
464       ret = strncmp (line + text_index, begin_or_end, begin_or_end_length);
465       text_index += begin_or_end_length;
466     }
467
468   if (ret == 0)
469     {
470       ret = line[text_index] - ' ';
471       text_index++;
472     }
473
474   if (ret == 0)
475     {
476       size_t label_length = strlen (label);
477       ret = strncmp (line + text_index, label, label_length);
478       text_index += label_length;
479     }
480
481   if (ret == 0)
482     {
483       ret = strncmp (line + text_index, "-----", 5);
484     }
485
486   return ret;
487 }
488
489 static int
490 ptls_get_bio_pem_object (BIO * bio, const char *label, ptls_buffer_t * buf)
491 {
492   int ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND;
493   char line[256];
494   ptls_base64_decode_state_t state;
495
496   /* Get the label on a line by itself */
497   while (BIO_gets (bio, line, 256))
498     {
499       if (ptls_compare_separator_line (line, "BEGIN", label) == 0)
500         {
501           ret = 0;
502           ptls_base64_decode_init (&state);
503           break;
504         }
505     }
506   /* Get the data in the buffer */
507   while (ret == 0 && BIO_gets (bio, line, 256))
508     {
509       if (ptls_compare_separator_line (line, "END", label) == 0)
510         {
511           if (state.status == PTLS_BASE64_DECODE_DONE
512               || (state.status == PTLS_BASE64_DECODE_IN_PROGRESS
513                   && state.nbc == 0))
514             {
515               ret = 0;
516             }
517           else
518             {
519               ret = PTLS_ERROR_INCORRECT_BASE64;
520             }
521           break;
522         }
523       else
524         {
525           ret = ptls_base64_decode (line, &state, buf);
526         }
527     }
528
529   return ret;
530 }
531
532 static int
533 ptls_load_bio_pem_objects (BIO * bio, const char *label, ptls_iovec_t * list,
534                            size_t list_max, size_t * nb_objects)
535 {
536   int ret = 0;
537   size_t count = 0;
538
539   *nb_objects = 0;
540
541   if (ret == 0)
542     {
543       while (count < list_max)
544         {
545           ptls_buffer_t buf;
546
547           ptls_buffer_init (&buf, "", 0);
548
549           ret = ptls_get_bio_pem_object (bio, label, &buf);
550
551           if (ret == 0)
552             {
553               if (buf.off > 0 && buf.is_allocated)
554                 {
555                   list[count].base = buf.base;
556                   list[count].len = buf.off;
557                   count++;
558                 }
559               else
560                 {
561                   ptls_buffer_dispose (&buf);
562                 }
563             }
564           else
565             {
566               ptls_buffer_dispose (&buf);
567               break;
568             }
569         }
570     }
571
572   if (ret == PTLS_ERROR_PEM_LABEL_NOT_FOUND && count > 0)
573     {
574       ret = 0;
575     }
576
577   *nb_objects = count;
578
579   return ret;
580 }
581
582 #define PTLS_MAX_CERTS_IN_CONTEXT 16
583
584 static int
585 ptls_load_bio_certificates (ptls_context_t * ctx, BIO * bio)
586 {
587   int ret = 0;
588
589   ctx->certificates.list =
590     (ptls_iovec_t *) malloc (PTLS_MAX_CERTS_IN_CONTEXT *
591                              sizeof (ptls_iovec_t));
592
593   if (ctx->certificates.list == NULL)
594     {
595       ret = PTLS_ERROR_NO_MEMORY;
596     }
597   else
598     {
599       ret =
600         ptls_load_bio_pem_objects (bio, "CERTIFICATE", ctx->certificates.list,
601                                    PTLS_MAX_CERTS_IN_CONTEXT,
602                                    &ctx->certificates.count);
603     }
604
605   return ret;
606 }
607
608 static inline void
609 load_bio_certificate_chain (ptls_context_t * ctx, const char *cert_data)
610 {
611   BIO *cert_bio;
612   cert_bio = BIO_new_mem_buf (cert_data, -1);
613   if (ptls_load_bio_certificates (ctx, cert_bio) != 0)
614     {
615       BIO_free (cert_bio);
616       fprintf (stderr, "failed to load certificate:%s\n", strerror (errno));
617       exit (1);
618     }
619   BIO_free (cert_bio);
620 }
621
622 static inline void
623 load_bio_private_key (ptls_context_t * ctx, const char *pk_data)
624 {
625   static ptls_openssl_sign_certificate_t sc;
626   EVP_PKEY *pkey;
627   BIO *key_bio;
628
629   key_bio = BIO_new_mem_buf (pk_data, -1);
630   pkey = PEM_read_bio_PrivateKey (key_bio, NULL, NULL, NULL);
631   BIO_free (key_bio);
632
633   if (pkey == NULL)
634     {
635       fprintf (stderr, "failed to read private key from app configuration\n");
636       exit (1);
637     }
638
639   ptls_openssl_init_sign_certificate (&sc, pkey);
640   EVP_PKEY_free (pkey);
641
642   ctx->sign_certificate = &sc.super;
643 }
644
645 static void
646 quic_connection_closed (u32 ctx_index)
647 {
648   QUIC_DBG (2, "QUIC connection closed");
649   quic_ctx_t *ctx;
650
651   ctx = quic_ctx_get (ctx_index);
652
653   ASSERT (!ctx->c_quic_ctx_id.is_stream);
654   // TODO if connection is not established, just delete the session?
655
656   // TODO: close all streams? or is the streams closed cb called by quicly?
657
658   session_transport_delete_notify (&ctx->connection);
659   // Do not try to send anything anymore
660   quicly_free (ctx->c_quic_ctx_id.conn);
661   ctx->c_quic_ctx_id.conn = NULL;
662   quic_ctx_free (ctx);
663 }
664
665 static int64_t
666 quic_get_time (quicly_now_cb * self)
667 {
668   // TODO read value set by set_time_now?
669   // (needs to change it not to call this function)
670   vlib_main_t *vlib_main = vlib_get_main ();
671   f64 time = vlib_time_now (vlib_main);
672   return (int64_t) (time * 1000.f);
673 }
674 quicly_now_cb quicly_vpp_now_cb = { quic_get_time };
675
676 static void
677 allocate_quicly_ctx (application_t * app, u8 is_client)
678 {
679   QUIC_DBG (2, "Called allocate_quicly_ctx");
680   struct
681   {
682     quicly_context_t _;
683     char cid_key[17];
684   } *ctx_data;
685   quicly_context_t *quicly_ctx;
686   char *cid_key;
687
688   ctx_data = malloc (sizeof (*ctx_data));
689   quicly_ctx = &ctx_data->_;
690   app->quicly_ctx = (u64 *) quicly_ctx;
691   memcpy (quicly_ctx, &quicly_default_context, sizeof (quicly_context_t));
692
693   quicly_ctx->tls = &quic_tlsctx;
694   quicly_ctx->stream_open = &on_stream_open;
695   quicly_ctx->closed_by_peer = &on_closed_by_peer;
696   quicly_ctx->now = &quicly_vpp_now_cb;
697
698   quicly_amend_ptls_context (quicly_ctx->tls);
699
700   quicly_ctx->event_log.mask = 0;
701   quicly_ctx->event_log.cb = quicly_new_default_event_log_cb (stderr);
702
703   quicly_ctx->transport_params.max_data = QUIC_INT_MAX;
704   quicly_ctx->transport_params.max_streams_uni = QUIC_INT_MAX;
705   quicly_ctx->transport_params.max_streams_bidi = QUIC_INT_MAX;
706   quicly_ctx->transport_params.max_stream_data.bidi_local = QUIC_INT_MAX;
707   quicly_ctx->transport_params.max_stream_data.bidi_remote = QUIC_INT_MAX;
708   quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX;
709
710   if (!is_client)
711     {
712       load_bio_private_key (quicly_ctx->tls, (char *) app->tls_key);
713       load_bio_certificate_chain (quicly_ctx->tls, (char *) app->tls_cert);
714       cid_key = ctx_data->cid_key;
715       quicly_ctx->tls->random_bytes (cid_key, 16);
716       cid_key[16] = 0;
717       quicly_ctx->encrypt_cid =
718         quicly_new_default_encrypt_cid_cb (&ptls_openssl_bfecb,
719                                            &ptls_openssl_sha256,
720                                            ptls_iovec_init (cid_key,
721                                                             strlen
722                                                             (cid_key)));
723       quicly_ctx->decrypt_cid =
724         quicly_new_default_decrypt_cid_cb (&ptls_openssl_bfecb,
725                                            &ptls_openssl_sha256,
726                                            ptls_iovec_init (cid_key,
727                                                             strlen
728                                                             (cid_key)));
729     }
730 }
731
732
733 /*****************************************************************************
734  * BEGIN TIMERS HANDLING
735  *****************************************************************************/
736
737 static u32
738 quic_set_time_now (u32 thread_index)
739 {
740   quic_main.wrk_ctx[thread_index].time_now = quic_get_time (NULL);
741   return quic_main.wrk_ctx[thread_index].time_now;
742 }
743
744 static void
745 quic_timer_expired (u32 conn_index)
746 {
747   quic_ctx_t *ctx;
748   QUIC_DBG (2, "Timer expired for conn %u at %ld", conn_index,
749             quic_get_time (NULL));
750   ctx = quic_ctx_get (conn_index);
751   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
752   if (quic_send_packets (ctx))
753     {
754       quic_connection_closed (conn_index);
755     }
756 }
757
758 static void
759 quic_update_timer (quic_ctx_t * ctx)
760 {
761   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
762   int64_t next_timeout;
763
764   // This timeout is in ms which is the unit of our timer
765   next_timeout = quicly_get_first_timeout (ctx->c_quic_ctx_id.conn);
766   tw = &quic_main.wrk_ctx[vlib_get_thread_index ()].timer_wheel;
767   f64 next_timeout_f = ((f64) next_timeout) / 1000.f;
768
769   // clib_warning ("Timer set to %ld (%lf)", next_timeout, next_timeout_f);
770
771   if (ctx->timer_handle == QUIC_TIMER_HANDLE_INVALID)
772     {
773       if (next_timeout == INT64_MAX)
774         return;
775       ctx->timer_handle =
776         tw_timer_start_1t_3w_1024sl_ov (tw, ctx->c_c_index, 0,
777                                         next_timeout_f);
778     }
779   else
780     {
781       if (next_timeout == INT64_MAX)
782         {
783           tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->timer_handle);
784           ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
785         }
786       else
787         tw_timer_update_1t_3w_1024sl_ov (tw, ctx->timer_handle,
788                                          next_timeout_f);
789     }
790 }
791
792 static void
793 quic_expired_timers_dispatch (u32 * expired_timers)
794 {
795   int i;
796
797   for (i = 0; i < vec_len (expired_timers); i++)
798     {
799       quic_timer_expired (expired_timers[i]);
800     }
801 }
802
803
804 /*****************************************************************************
805  * END TIMERS HANDLING
806  *
807  * BEGIN TRANSPORT PROTO FUNCTIONS
808  *****************************************************************************/
809
810 static int
811 quic_connect (transport_endpoint_cfg_t * tep)
812 {
813   QUIC_DBG (2, "Called quic_connect");
814   session_endpoint_cfg_t *sep;
815   int connect_stream = 0;
816
817   sep = (session_endpoint_cfg_t *) tep;
818
819   if (sep->port == 0)
820     {
821       // TODO: better logic to detect if this is a stream or a connection request
822       connect_stream = 1;
823     }
824
825   if (connect_stream)
826     {
827       return quic_connect_new_stream (sep);
828     }
829   else
830     {
831       return quic_connect_new_connection (sep);
832     }
833 }
834
835 static int
836 quic_connect_new_stream (session_endpoint_cfg_t * sep)
837 {
838   uint64_t quic_session_handle;
839   session_t *quic_session, *stream_session;
840   quic_stream_data_t *stream_data;
841   quicly_stream_t *stream;
842   quicly_conn_t *conn;
843   app_worker_t *app_wrk;
844   quic_ctx_t *qctx, *sctx;
845   u32 sctx_index;
846   int rv;
847
848   // Find base session to which the user want to attach a stream
849   quic_session_handle = sep->transport_opts;
850   QUIC_DBG (2, "Opening new stream (qsession %u)", sep->transport_opts);
851   quic_session = session_get_from_handle (quic_session_handle);
852
853   if (quic_session->session_type !=
854       session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, sep->is_ip4))
855     {
856       QUIC_DBG (1, "received incompatible session");
857       return -1;
858     }
859
860   sctx_index = quic_ctx_alloc ();       // Allocate before we get pointers
861   sctx = quic_ctx_get (sctx_index);
862   qctx = quic_ctx_get (quic_session->connection_index);
863   if (qctx->c_quic_ctx_id.is_stream)
864     {
865       QUIC_DBG (1, "session is a stream");
866       quic_ctx_free (sctx);
867       return -1;
868     }
869
870   sctx->c_quic_ctx_id.parent_app_wrk_id =
871     qctx->c_quic_ctx_id.parent_app_wrk_id;
872   sctx->c_quic_ctx_id.parent_app_id = qctx->c_quic_ctx_id.parent_app_id;
873   sctx->c_quic_ctx_id.quic_connection_ctx_id = qctx->c_c_index;
874   sctx->c_c_index = sctx_index;
875   sctx->c_quic_ctx_id.is_stream = 1;
876
877   conn = qctx->c_quic_ctx_id.conn;
878
879   if (!conn || !quicly_connection_is_ready (conn))
880     return -1;
881
882   if ((rv = quicly_open_stream (conn, &stream, 0)))
883     {
884       QUIC_DBG (2, "Stream open failed with %d", rv);
885       return -1;
886     }
887   sctx->c_quic_ctx_id.stream = stream;
888
889   QUIC_DBG (2, "Opened stream %d, creating session", stream->stream_id);
890
891   app_wrk = app_worker_get_if_valid (quic_session->app_wrk_index);
892
893   stream_session = session_alloc (qctx->c_thread_index);
894   QUIC_DBG (1, "Created stream_session, id %u ctx %u",
895             stream_session->session_index, sctx_index);
896   stream_session->app_wrk_index = quic_session->app_wrk_index;
897   stream_session->connection_index = sctx_index;
898   stream_session->session_type =
899     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC,
900                                     qctx->c_quic_ctx_id.udp_is_ip4);
901   stream_session->opaque = QUIC_SESSION_TYPE_STREAM;
902   sctx->c_s_index = stream_session->session_index;
903   sctx->c_quic_ctx_id.stream_session_handle = session_handle (stream_session);
904
905   if (app_worker_init_connected (app_wrk, stream_session))
906     {
907       QUIC_DBG (1, "failed to app_worker_init_connected");
908       quicly_reset_stream (stream, 0x30003);
909       session_free_w_fifos (stream_session);
910       quic_ctx_free (sctx);
911       return app_worker_connect_notify (app_wrk, NULL, sep->opaque);
912     }
913
914   stream_session->session_state = SESSION_STATE_READY;
915   if (app_worker_connect_notify (app_wrk, stream_session, sep->opaque))
916     {
917       QUIC_DBG (1, "failed to notify app");
918       quicly_reset_stream (stream, 0x30004);
919       session_free_w_fifos (stream_session);
920       quic_ctx_free (sctx);
921       return -1;
922     }
923   session_lookup_add_connection (&sctx->connection,
924                                  session_handle (stream_session));
925   stream_data = (quic_stream_data_t *) stream->data;
926   stream_data->ctx_id = sctx->c_c_index;
927   return 0;
928 }
929
930 static int
931 quic_connect_new_connection (session_endpoint_cfg_t * sep)
932 {
933   vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
934   quic_main_t *qm = &quic_main;
935   quic_ctx_t *ctx;
936   app_worker_t *app_wrk;
937   application_t *app;
938   u32 ctx_index;
939   int error;
940
941   ctx_index = quic_ctx_alloc ();
942   ctx = quic_ctx_get (ctx_index);
943   ctx->c_quic_ctx_id.parent_app_wrk_id = sep->app_wrk_index;
944   ctx->c_s_index = 0xFAFAFAFA;
945   ctx->c_quic_ctx_id.udp_is_ip4 = sep->is_ip4;
946   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
947   ctx->conn_state = QUIC_CONN_STATE_HANDSHAKE;
948   ctx->client_opaque = sep->opaque;
949   if (sep->hostname)
950     {
951       ctx->srv_hostname = format (0, "%v", sep->hostname);
952       vec_terminate_c_string (ctx->srv_hostname);
953     }
954   else
955     {
956       // needed by quic for crypto + determining client / server
957       ctx->srv_hostname =
958         format (0, "%U", format_ip46_address, &sep->ip, sep->is_ip4);
959     }
960
961   clib_memcpy (&cargs->sep, sep, sizeof (session_endpoint_cfg_t));
962   cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
963   cargs->app_index = qm->app_index;
964   cargs->api_context = ctx_index;
965
966   app_wrk = app_worker_get (sep->app_wrk_index);
967   app = application_get (app_wrk->app_index);
968   ctx->c_quic_ctx_id.parent_app_id = app_wrk->app_index;
969   cargs->sep_ext.ns_index = app->ns_index;
970
971   allocate_quicly_ctx (app, 1 /* is client */ );
972
973   if ((error = vnet_connect (cargs)))
974     return error;
975
976   QUIC_DBG (1, "New connect request %u", ctx_index);
977   return 0;
978 }
979
980 static void
981 quic_disconnect (u32 ctx_index, u32 thread_index)
982 {
983   QUIC_DBG (2, "Called quic_disconnect");
984   //tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
985   quic_ctx_t *ctx;
986
987   QUIC_DBG (1, "Closing connection %x", ctx_index);
988
989   ctx = quic_ctx_get (ctx_index);
990   if (ctx->c_quic_ctx_id.is_stream)
991     {
992       quicly_stream_t *stream = ctx->c_quic_ctx_id.stream;
993       quicly_reset_stream (stream, 0x30000);
994       session_transport_delete_notify (&ctx->connection);
995       quic_ctx_free (ctx);
996     }
997   else
998     {
999       quicly_conn_t *conn = ctx->c_quic_ctx_id.conn;
1000       // Start connection closing. Keep sending packets until quicly_send
1001       // returns QUICLY_ERROR_FREE_CONNECTION
1002       quicly_close (conn, 0, "");
1003       quic_send_packets (ctx);
1004     }
1005 }
1006
1007 static u32
1008 quic_start_listen (u32 quic_listen_session_index, transport_endpoint_t * tep)
1009 {
1010   vnet_listen_args_t _bargs, *args = &_bargs;
1011   quic_main_t *qm = &quic_main;
1012   session_handle_t udp_handle;
1013   session_endpoint_cfg_t *sep;
1014   session_t *udp_listen_session, *quic_listen_session;
1015   app_worker_t *app_wrk;
1016   application_t *app;
1017   quic_ctx_t *lctx;
1018   u32 lctx_index;
1019   app_listener_t *app_listener;
1020
1021   sep = (session_endpoint_cfg_t *) tep;
1022   app_wrk = app_worker_get (sep->app_wrk_index);
1023   // We need to call this because we call app_worker_init_connected in
1024   // quic_accept_stream, which assumes the connect segment manager exists
1025   app_worker_alloc_connects_segment_manager (app_wrk);
1026   app = application_get (app_wrk->app_index);
1027   QUIC_DBG (2, "Called quic_start_listen for app %d", app_wrk->app_index);
1028
1029   allocate_quicly_ctx (app, 0 /* is_client */ );
1030
1031   sep->transport_proto = TRANSPORT_PROTO_UDP;
1032   memset (args, 0, sizeof (*args));
1033   args->app_index = qm->app_index;
1034   args->sep_ext = *sep;
1035   args->sep_ext.ns_index = app->ns_index;
1036   if (vnet_listen (args))
1037     return -1;
1038
1039   lctx_index = quic_ctx_alloc ();       // listener
1040   udp_handle = args->handle;
1041   app_listener = app_listener_get_w_handle (udp_handle);
1042   udp_listen_session = app_listener_get_session (app_listener);
1043   udp_listen_session->opaque = lctx_index;
1044
1045   quic_listen_session = listen_session_get (quic_listen_session_index);
1046   quic_listen_session->opaque = QUIC_SESSION_TYPE_LISTEN;
1047
1048   lctx = quic_ctx_get (lctx_index);     // listener
1049   lctx->is_listener = 1;
1050   lctx->c_quic_ctx_id.parent_app_wrk_id = sep->app_wrk_index;
1051   lctx->c_quic_ctx_id.parent_app_id = app_wrk->app_index;
1052   lctx->c_quic_ctx_id.udp_session_handle = udp_handle;
1053   lctx->c_quic_ctx_id.quic_session_handle =
1054     listen_session_get_handle (quic_listen_session);
1055   lctx->c_quic_ctx_id.udp_is_ip4 = sep->is_ip4;
1056
1057   QUIC_DBG (1, "Started listening %d", lctx_index);
1058   return lctx_index;
1059 }
1060
1061 static u32
1062 quic_stop_listen (u32 lctx_index)
1063 {
1064   QUIC_DBG (2, "Called quic_stop_listen");
1065   quic_ctx_t *lctx;
1066
1067   lctx = quic_ctx_get (lctx_index);     // listener
1068   vnet_unlisten_args_t a = {
1069     .handle = lctx->c_quic_ctx_id.udp_session_handle,
1070     .app_index = quic_main.app_index,
1071     .wrk_map_index = 0          /* default wrk */
1072   };
1073   if (vnet_unlisten (&a))
1074     clib_warning ("unlisten errored");
1075
1076   // TODO: crypto state cleanup
1077
1078   quic_ctx_free (lctx);         // listener
1079   return 0;
1080 }
1081
1082 static transport_connection_t *
1083 quic_connection_get (u32 ctx_index, u32 thread_index)
1084 {
1085   QUIC_DBG (2, "Called quic_connection_get");
1086   quic_ctx_t *ctx;
1087   ctx = quic_ctx_get_w_thread (ctx_index, thread_index);
1088   return &ctx->connection;
1089 }
1090
1091 static transport_connection_t *
1092 quic_listener_get (u32 listener_index)
1093 {
1094   QUIC_DBG (2, "Called quic_listener_get");
1095   quic_ctx_t *ctx;
1096   ctx = quic_ctx_get (listener_index);
1097   return &ctx->connection;
1098 }
1099
1100 static void
1101 quic_update_time (f64 now, u8 thread_index)
1102 {
1103   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
1104
1105   tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
1106   quic_set_time_now (thread_index);
1107   tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
1108 }
1109
1110 static u8 *
1111 format_quic_connection (u8 * s, va_list * args)
1112 {
1113   s = format (s, "[QUIC] connection");  //TODO
1114   return s;
1115 }
1116
1117 static u8 *
1118 format_quic_half_open (u8 * s, va_list * args)
1119 {
1120   u32 qc_index = va_arg (*args, u32);
1121   quic_ctx_t *ctx = quic_ctx_get (qc_index);
1122   s = format (s, "[QUIC] half-open app %u", ctx->c_quic_ctx_id.parent_app_id);
1123   return s;
1124 }
1125
1126 // TODO improve
1127 static u8 *
1128 format_quic_listener (u8 * s, va_list * args)
1129 {
1130   s = format (s, "[QUIC] listener");    // TODO
1131   return s;
1132 }
1133
1134 /*****************************************************************************
1135  * END TRANSPORT PROTO FUNCTIONS
1136  *
1137  * START SESSION CALLBACKS
1138  * Called from UDP layer
1139  *****************************************************************************/
1140
1141 static inline void
1142 quic_build_sockaddr (struct sockaddr *sa, socklen_t * salen,
1143                      ip46_address_t * addr, u16 port, u8 is_ip4)
1144 {
1145   if (is_ip4)
1146     {
1147       struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
1148       sa4->sin_family = AF_INET;
1149       sa4->sin_port = port;
1150       sa4->sin_addr.s_addr = addr->ip4.as_u32;
1151       *salen = sizeof (struct sockaddr_in);
1152     }
1153   else
1154     {
1155       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
1156       sa6->sin6_family = AF_INET6;
1157       sa6->sin6_port = port;
1158       clib_memcpy (&sa6->sin6_addr, &addr->ip6, 16);
1159       *salen = sizeof (struct sockaddr_in6);
1160     }
1161 }
1162
1163 static int
1164 quic_notify_app_connected (quic_ctx_t * ctx)
1165 {
1166   QUIC_DBG (1, "quic_notify_app_connected");
1167   session_t *quic_session;
1168   app_worker_t *app_wrk;
1169   u32 ctx_id = ctx->c_c_index;
1170
1171   app_wrk = app_worker_get_if_valid (ctx->c_quic_ctx_id.parent_app_wrk_id);
1172   if (!app_wrk)
1173     {
1174       quic_disconnect_transport (ctx);
1175       return -1;
1176     }
1177
1178   quic_session = session_alloc (ctx->c_thread_index);
1179
1180   QUIC_DBG (1, "Created quic_session, id %u", quic_session->session_index);
1181   ctx->c_s_index = quic_session->session_index;
1182   quic_session->app_wrk_index = ctx->c_quic_ctx_id.parent_app_wrk_id;
1183   quic_session->connection_index = ctx->c_c_index;
1184   quic_session->opaque = QUIC_SESSION_TYPE_QUIC;
1185   quic_session->session_type =
1186     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC,
1187                                     ctx->c_quic_ctx_id.udp_is_ip4);
1188
1189   if (app_worker_init_connected (app_wrk, quic_session))
1190     {
1191       QUIC_DBG (1, "failed to app_worker_init_connected");
1192       quic_disconnect (ctx->c_c_index, vlib_get_thread_index ());
1193       return app_worker_connect_notify (app_wrk, NULL, ctx->client_opaque);
1194     }
1195
1196   quic_session->session_state = SESSION_STATE_CONNECTING;
1197   if (app_worker_connect_notify (app_wrk, quic_session, ctx->client_opaque))
1198     {
1199       QUIC_DBG (1, "failed to notify app");
1200       quic_disconnect (ctx->c_c_index, vlib_get_thread_index ());
1201       return -1;
1202     }
1203
1204   // If the app opens a stream in its callback it may invalidate ctx
1205   ctx = quic_ctx_get (ctx_id);
1206   ctx->c_quic_ctx_id.quic_session_handle = session_handle (quic_session);
1207   quic_session->session_state = SESSION_STATE_LISTENING;
1208   session_lookup_add_connection (&ctx->connection,
1209                                  session_handle (quic_session));
1210
1211   return 0;
1212 }
1213
1214 static int
1215 quic_session_connected_callback (u32 quic_app_index, u32 ctx_index,
1216                                  session_t * udp_session, u8 is_fail)
1217 {
1218   QUIC_DBG (2, "QSession is now connected (id %u)",
1219             udp_session->session_index);
1220   // This should always be called before quic_connect returns since UDP always
1221   // connects instantly.
1222   struct sockaddr_in6 sa6;
1223   struct sockaddr *sa = (struct sockaddr *) &sa6;
1224   socklen_t salen;
1225   transport_connection_t *tc;
1226   quic_ctx_t *ctx;
1227   int ret;
1228   application_t *app;
1229   app_worker_t *app_wrk;
1230
1231   ctx = quic_ctx_get (ctx_index);
1232   if (is_fail)
1233     {
1234       u32 api_context;
1235       int rv = 0;
1236
1237       app_wrk =
1238         app_worker_get_if_valid (ctx->c_quic_ctx_id.parent_app_wrk_id);
1239       if (app_wrk)
1240         {
1241           api_context = ctx->c_s_index;
1242           app_worker_connect_notify (app_wrk, 0, api_context);
1243         }
1244       return rv;
1245     }
1246
1247   app_wrk = app_worker_get_if_valid (ctx->c_quic_ctx_id.parent_app_wrk_id);
1248   if (!app_wrk)
1249     {
1250       QUIC_DBG (1, "Appwrk not found");
1251       return -1;
1252     }
1253   app = application_get (app_wrk->app_index);
1254
1255   ctx->c_thread_index = vlib_get_thread_index ();
1256   ctx->c_c_index = ctx_index;
1257
1258   QUIC_DBG (1, "Quic connect returned %u. New ctx [%u]%x",
1259             is_fail, vlib_get_thread_index (), (ctx) ? ctx_index : ~0);
1260
1261   ctx->c_quic_ctx_id.udp_session_handle = session_handle (udp_session);
1262   udp_session->opaque = ctx_index;
1263   udp_session->session_state = SESSION_STATE_READY;
1264
1265   // Init QUIC lib connection
1266   // Generate required sockaddr & salen
1267   tc = session_get_transport (udp_session);
1268   quic_build_sockaddr (sa, &salen, &tc->rmt_ip, tc->rmt_port, tc->is_ip4);
1269
1270   ret =
1271     quicly_connect (&ctx->c_quic_ctx_id.conn,
1272                     (quicly_context_t *) app->quicly_ctx,
1273                     (char *) ctx->srv_hostname, sa, salen,
1274                     &quic_main.next_cid, &quic_main.hs_properties, NULL);
1275   ++quic_main.next_cid.master_id;
1276   // Save context handle in quicly connection
1277   *quicly_get_data (ctx->c_quic_ctx_id.conn) = (void *) (u64) ctx_index;
1278   assert (ret == 0);
1279
1280   int rv = quic_send_packets (ctx);
1281   if (rv)
1282     {
1283       QUIC_DBG (1, "Error sending packets %d, closing connection", rv);
1284       quic_connection_closed (ctx_index);
1285     }
1286   return ret;
1287 }
1288
1289 static void
1290 quic_session_disconnect_callback (session_t * s)
1291 {
1292   clib_warning ("UDP session disconnected???");
1293 }
1294
1295 static void
1296 quic_session_reset_callback (session_t * s)
1297 {
1298   clib_warning ("UDP session reset???");
1299 }
1300
1301 int
1302 quic_session_accepted_callback (session_t * s)
1303 {
1304   /* never called */
1305   return 0;
1306 }
1307
1308 static int
1309 quic_add_segment_callback (u32 client_index, u64 seg_handle)
1310 {
1311   QUIC_DBG (2, "Called quic_add_segment_callback");
1312   QUIC_DBG (2, "NOT IMPLEMENTED");
1313   /* No-op for builtin */
1314   return 0;
1315 }
1316
1317 static int
1318 quic_del_segment_callback (u32 client_index, u64 seg_handle)
1319 {
1320   QUIC_DBG (2, "Called quic_del_segment_callback");
1321   QUIC_DBG (2, "NOT IMPLEMENTED");
1322   /* No-op for builtin */
1323   return 0;
1324 }
1325
1326 static int
1327 quic_custom_tx_callback (void *s)
1328 {
1329   QUIC_DBG (2, "Called quic_custom_tx_callback");
1330   session_t *stream_session = (session_t *) s;
1331   quic_ctx_t *ctx;
1332   svm_fifo_t *f;
1333   quicly_stream_t *stream;
1334   u32 deq_max;
1335   u8 *data;
1336
1337   if (PREDICT_FALSE
1338       (stream_session->session_state >= SESSION_STATE_TRANSPORT_CLOSING))
1339     return 0;
1340   ctx = quic_ctx_get (stream_session->connection_index);
1341   if (PREDICT_FALSE (!ctx->c_quic_ctx_id.is_stream))
1342     {
1343       QUIC_DBG (1, "Error: trying to send on quic session not stream");
1344       return -1;
1345     }
1346
1347   stream = ctx->c_quic_ctx_id.stream;
1348
1349   f = stream_session->tx_fifo;
1350   deq_max = svm_fifo_max_dequeue (f);
1351   if (!deq_max)
1352     return 0;
1353
1354   data = svm_fifo_head (f);
1355   if (quicly_streambuf_egress_write (stream, data, deq_max))
1356     {
1357       assert (0);
1358       return 0;
1359     }
1360   QUIC_DBG (2, "Sent %u bytes", deq_max);
1361   svm_fifo_dequeue_drop (f, deq_max);
1362   int rv = quic_send_packets (ctx);
1363   if (rv)
1364     {
1365       QUIC_DBG (1, "TX error sending packets %d, closing connection", rv);
1366       quic_connection_closed (ctx->c_quic_ctx_id.quic_connection_ctx_id);
1367     }
1368   return 0;
1369 }
1370
1371 static inline int
1372 quic_find_packet_ctx (quic_ctx_t ** ctx, quicly_conn_t ** conn,
1373                       struct sockaddr *sa, socklen_t salen,
1374                       quicly_decoded_packet_t packet)
1375 {
1376   quic_ctx_t *ctx_;
1377   quicly_conn_t *conn_;
1378   /* *INDENT-OFF* */
1379   pool_foreach (ctx_, quic_main.ctx_pool[vlib_get_thread_index()],
1380   ({
1381     conn_ = ctx_->c_quic_ctx_id.conn;
1382     if (!ctx_->c_quic_ctx_id.is_stream && conn_ && !ctx_->is_listener)
1383       {
1384         if (quicly_is_destination(conn_, sa, salen, &packet))
1385           {
1386             *conn = conn_;
1387             *ctx = ctx_;
1388             // QUIC_DBG (2, "connection_found");
1389             return 0;
1390           }
1391       }
1392   }));
1393   /* *INDENT-ON* */
1394   return 0;
1395 }
1396
1397 static int
1398 quic_receive (quic_ctx_t * ctx, quicly_conn_t * conn,
1399               quicly_decoded_packet_t packet)
1400 {
1401   u32 ctx_id = ctx->c_c_index;
1402   quicly_receive (conn, &packet);
1403   // ctx pointer may change if a new stream is opened
1404   ctx = quic_ctx_get (ctx_id);
1405   // Conn may be set to null if the connection is terminated
1406   if (ctx->c_quic_ctx_id.conn && ctx->conn_state == QUIC_CONN_STATE_HANDSHAKE)
1407     {
1408       if (quicly_connection_is_ready (conn))
1409         {
1410           ctx->conn_state = QUIC_CONN_STATE_READY;
1411           if (quicly_is_client (conn))
1412             {
1413               quic_notify_app_connected (ctx);
1414               ctx = quic_ctx_get (ctx_id);
1415             }
1416         }
1417     }
1418   if (quic_send_packets (ctx))
1419     {
1420       quic_connection_closed (ctx->c_c_index);
1421     }
1422   return 0;
1423 }
1424
1425 static int
1426 quic_create_quic_session (quic_ctx_t * ctx)
1427 {
1428   session_t *quic_session, *quic_listen_session;
1429   app_worker_t *app_wrk;
1430   quic_ctx_t *lctx;
1431   int rv;
1432
1433   quic_session = session_alloc (ctx->c_thread_index);
1434   QUIC_DBG (1, "Created quic session, id %u ctx %u",
1435             quic_session->session_index, ctx->c_c_index);
1436   quic_session->session_state = SESSION_STATE_LISTENING;
1437   ctx->c_s_index = quic_session->session_index;
1438
1439   lctx = quic_ctx_get (ctx->c_quic_ctx_id.listener_ctx_id);
1440
1441   quic_listen_session =
1442     listen_session_get_from_handle (lctx->c_quic_ctx_id.quic_session_handle);
1443   quic_session->app_wrk_index = lctx->c_quic_ctx_id.parent_app_wrk_id;
1444   quic_session->connection_index = ctx->c_c_index;
1445   quic_session->session_type =
1446     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC,
1447                                     ctx->c_quic_ctx_id.udp_is_ip4);
1448   quic_session->listener_index = quic_listen_session->session_index;
1449   quic_session->app_index = quic_main.app_index;
1450   quic_session->opaque = QUIC_SESSION_TYPE_QUIC;
1451
1452   // TODO: don't alloc fifos when we don't transfer data on this session
1453   // but we still need fifos for the events?
1454   if ((rv = app_worker_init_accepted (quic_session)))
1455     {
1456       QUIC_DBG (1, "failed to allocate fifos");
1457       session_free (quic_session);
1458       return rv;
1459     }
1460   ctx->c_quic_ctx_id.quic_session_handle = session_handle (quic_session);
1461   ctx->c_quic_ctx_id.parent_app_id = lctx->c_quic_ctx_id.parent_app_id;
1462   ctx->c_quic_ctx_id.udp_is_ip4 = lctx->c_quic_ctx_id.udp_is_ip4;
1463   ctx->c_quic_ctx_id.parent_app_wrk_id = quic_session->app_wrk_index;
1464   session_lookup_add_connection (&ctx->connection,
1465                                  session_handle (quic_session));
1466   app_wrk = app_worker_get (quic_session->app_wrk_index);
1467   rv = app_worker_accept_notify (app_wrk, quic_session);
1468   if (rv)
1469     {
1470       QUIC_DBG (1, "failed to notify accept worker app");
1471       return rv;
1472     }
1473   return 0;
1474 }
1475
1476 static int
1477 quic_create_connection (quicly_context_t * quicly_ctx,
1478                         u64 udp_session_handle, u32 lctx_index,
1479                         struct sockaddr *sa,
1480                         socklen_t salen, quicly_decoded_packet_t packet)
1481 {
1482   quic_ctx_t *ctx;
1483   u32 ctx_index;
1484   quicly_conn_t *conn;
1485   int rv;
1486
1487   /* new connection, accept and create context if packet is valid */
1488   // TODO: check if socket is actually listening?
1489   if ((rv = quicly_accept (&conn, quicly_ctx, sa, salen,
1490                            &packet, ptls_iovec_init (NULL, 0),
1491                            &quic_main.next_cid, NULL)))
1492     {
1493       // Invalid packet, pass
1494       assert (conn == NULL);
1495       QUIC_DBG (2, "Accept failed with %d", rv);
1496       return 0;
1497     }
1498   assert (conn != NULL);
1499
1500   ++quic_main.next_cid.master_id;
1501   // Create context
1502   ctx_index = quic_ctx_alloc ();
1503   ctx = quic_ctx_get (ctx_index);
1504   // Save ctx handle in quicly connection
1505   *quicly_get_data (conn) = (void *) (u64) ctx_index;
1506
1507   ctx->c_thread_index = vlib_get_thread_index ();
1508   ctx->c_c_index = ctx_index;
1509   ctx->c_quic_ctx_id.udp_session_handle = udp_session_handle;
1510   ctx->c_quic_ctx_id.listener_ctx_id = lctx_index;
1511   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
1512   ctx->c_quic_ctx_id.conn = conn;
1513
1514   quic_create_quic_session (ctx);
1515
1516   if (quic_send_packets (ctx))
1517     {
1518       quic_connection_closed (ctx_index);
1519     }
1520   return 0;
1521 }
1522
1523 static int
1524 quic_reset_connection (quicly_context_t * quicly_ctx, u64 udp_session_handle,
1525                        struct sockaddr *sa, socklen_t salen,
1526                        quicly_decoded_packet_t packet)
1527 {
1528   /* short header packet; potentially a dead connection. No need to check the
1529    * length of the incoming packet, because loop is prevented by authenticating
1530    * the CID (by checking node_id and thread_id). If the peer is also sending a
1531    * reset, then the next CID is highly likely to contain a non-authenticating
1532    * CID, ... */
1533   QUIC_DBG (2, "Sending stateless reset");
1534   quicly_datagram_t *dgram;
1535   session_t *udp_session;
1536   if (packet.cid.dest.plaintext.node_id == 0
1537       && packet.cid.dest.plaintext.thread_id == 0)
1538     {
1539       dgram = quicly_send_stateless_reset (quicly_ctx, sa, salen,
1540                                            &packet.cid.dest.plaintext);
1541       udp_session = session_get_from_handle (udp_session_handle);
1542       if (quic_send_datagram (udp_session, dgram))      // TODO : set event on fifo
1543         QUIC_DBG (2, "Send reset failed");
1544     }
1545   return 0;
1546 }
1547
1548 static int
1549 quic_app_rx_callback (session_t * udp_session)
1550 {
1551   // Read data from UDP rx_fifo and pass it to the quicly conn.
1552   quicly_decoded_packet_t packet;
1553   session_dgram_hdr_t ph;
1554   application_t *app;
1555   quicly_conn_t *conn = NULL;
1556   quic_ctx_t *lctx, *ctx = NULL;
1557   svm_fifo_t *f;
1558   size_t plen;
1559   struct sockaddr_in6 sa6;
1560   struct sockaddr *sa = (struct sockaddr *) &sa6;
1561   socklen_t salen;
1562   u32 max_deq, len;
1563   u8 *data;
1564   u32 lctx_index = udp_session->opaque;
1565   u64 udp_session_handle = session_handle (udp_session);
1566
1567   // DEBUG
1568   // lctx = quic_ctx_get (lctx_index);
1569   // QUIC_DBG (2, "Got RX data on session %d",
1570   //           lctx->c_quic_ctx_id.udp_session_handle);
1571
1572   f = udp_session->rx_fifo;
1573
1574   do
1575     {
1576       conn = NULL;
1577       max_deq = svm_fifo_max_dequeue (f);
1578       if (max_deq < sizeof (session_dgram_hdr_t))
1579         {
1580           svm_fifo_unset_event (f);
1581           return 0;
1582         }
1583       // QUIC_DBG (2, "Processing one packet at %ld", quic_get_time (NULL));
1584
1585       svm_fifo_unset_event (f);
1586       svm_fifo_peek (f, 0, sizeof (ph), (u8 *) & ph);
1587       ASSERT (ph.data_length >= ph.data_offset);
1588       len = ph.data_length - ph.data_offset;
1589
1590       quic_build_sockaddr (sa, &salen, &ph.rmt_ip, ph.rmt_port, ph.is_ip4);
1591
1592       // Quicly can read len bytes from the fifo at offset:
1593       // ph.data_offset + SESSION_CONN_HDR_LEN
1594       data = svm_fifo_head (f) + ph.data_offset + SESSION_CONN_HDR_LEN;
1595
1596       lctx = quic_ctx_get (lctx_index);
1597       app = application_get (lctx->c_quic_ctx_id.parent_app_id);
1598
1599       plen =
1600         quicly_decode_packet ((quicly_context_t *) app->quicly_ctx, &packet,
1601                               data, len);
1602       if (plen != SIZE_MAX)
1603         {
1604           quic_find_packet_ctx (&ctx, &conn, sa, salen, packet);
1605           if (conn != NULL)
1606             quic_receive (ctx, conn, packet);
1607           else if (QUICLY_PACKET_IS_LONG_HEADER (packet.octets.base[0]))
1608             quic_create_connection ((quicly_context_t *) app->quicly_ctx,
1609                                     udp_session_handle, lctx_index,
1610                                     sa, salen, packet);
1611           else if (((quicly_context_t *) app->quicly_ctx)->encrypt_cid)
1612             quic_reset_connection ((quicly_context_t *) app->quicly_ctx,
1613                                    udp_session_handle, sa, salen, packet);
1614         }
1615       svm_fifo_dequeue_drop (f,
1616                              ph.data_length + ph.data_offset +
1617                              SESSION_CONN_HDR_LEN);
1618     }
1619   while (1);
1620   return 0;
1621 }
1622
1623 always_inline void
1624 quic_common_get_transport_endpoint (quic_ctx_t * ctx, ip46_address_t * ip,
1625                                     u16 * port, u8 * is_ip4, u8 is_lcl)
1626 {
1627   session_t *udp_session;
1628   QUIC_DBG (2, "Called quic_get_transport_endpoint");
1629   if (ctx->c_quic_ctx_id.is_stream)
1630     *is_ip4 = 255;              /* well this is ugly */
1631   else
1632     {
1633       udp_session =
1634         session_get_from_handle (ctx->c_quic_ctx_id.udp_session_handle);
1635       session_get_endpoint (udp_session, ip, port, is_ip4, is_lcl);
1636     }
1637 }
1638
1639 static void
1640 quic_get_transport_listener_endpoint (u32 listener_index, ip46_address_t * ip,
1641                                       u16 * port, u8 * is_ip4, u8 is_lcl)
1642 {
1643   quic_ctx_t *ctx;
1644   ctx = quic_ctx_get (listener_index);
1645   quic_common_get_transport_endpoint (ctx, ip, port, is_ip4, is_lcl);
1646 }
1647
1648 static void
1649 quic_get_transport_endpoint (u32 ctx_index, u32 thread_index,
1650                              ip46_address_t * ip, u16 * port, u8 * is_ip4,
1651                              u8 is_lcl)
1652 {
1653   quic_ctx_t *ctx;
1654   ctx = quic_ctx_get_w_thread (ctx_index, thread_index);
1655   quic_common_get_transport_endpoint (ctx, ip, port, is_ip4, is_lcl);
1656 }
1657
1658 /*****************************************************************************
1659  * END TRANSPORT PROTO FUNCTIONS
1660 *****************************************************************************/
1661
1662 /* *INDENT-OFF* */
1663 static session_cb_vft_t quic_app_cb_vft = {
1664   .session_accept_callback = quic_session_accepted_callback,
1665   .session_disconnect_callback = quic_session_disconnect_callback,
1666   .session_connected_callback = quic_session_connected_callback,
1667   .session_reset_callback = quic_session_reset_callback,
1668   .add_segment_callback = quic_add_segment_callback,
1669   .del_segment_callback = quic_del_segment_callback,
1670   .builtin_app_rx_callback = quic_app_rx_callback,
1671 };
1672
1673 const static transport_proto_vft_t quic_proto = {
1674   .connect = quic_connect,
1675   .close = quic_disconnect,
1676   .start_listen = quic_start_listen,
1677   .stop_listen = quic_stop_listen,
1678   .get_connection = quic_connection_get,
1679   .get_listener = quic_listener_get,
1680   .update_time = quic_update_time,
1681   .custom_tx = quic_custom_tx_callback,
1682   .tx_type = TRANSPORT_TX_INTERNAL,
1683   .service_type = TRANSPORT_SERVICE_APP,
1684   .format_connection = format_quic_connection,
1685   .format_half_open = format_quic_half_open,
1686   .format_listener = format_quic_listener,
1687   .get_transport_endpoint = quic_get_transport_endpoint,
1688   .get_transport_listener_endpoint = quic_get_transport_listener_endpoint,
1689 };
1690 /* *INDENT-ON* */
1691
1692 static clib_error_t *
1693 quic_init (vlib_main_t * vm)
1694 {
1695   QUIC_DBG (2, "Called quic_init");
1696   u32 add_segment_size = (4096ULL << 20) - 1, segment_size = 512 << 20;
1697   vlib_thread_main_t *vtm = vlib_get_thread_main ();
1698   vnet_app_attach_args_t _a, *a = &_a;
1699   u64 options[APP_OPTIONS_N_OPTIONS];
1700   quic_main_t *qm = &quic_main;
1701   u32 fifo_size = 64 << 10;
1702   u32 num_threads;
1703
1704   num_threads = 1 /* main thread */  + vtm->n_threads;
1705
1706   memset (a, 0, sizeof (*a));
1707   memset (options, 0, sizeof (options));
1708
1709   a->session_cb_vft = &quic_app_cb_vft;
1710   a->api_client_index = APP_INVALID_INDEX;
1711   a->options = options;
1712   a->name = format (0, "quic");
1713   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
1714   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = add_segment_size;
1715   a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
1716   a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
1717   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1718   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
1719   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
1720
1721   if (vnet_application_attach (a))
1722     {
1723       clib_warning ("failed to attach quic app");
1724       return clib_error_return (0, "failed to attach quic app");
1725     }
1726
1727   vec_validate (qm->ctx_pool, num_threads - 1);
1728   vec_validate (qm->wrk_ctx, num_threads - 1);
1729   // Timers, one per thread.
1730   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
1731   /* *INDENT-OFF* */
1732   foreach_vlib_main (({
1733     tw = &qm->wrk_ctx[ii].timer_wheel;
1734     tw_timer_wheel_init_1t_3w_1024sl_ov (tw, quic_expired_timers_dispatch,
1735                                          10e-3 /* timer period 1ms */ , ~0);
1736     tw->last_run_time = vlib_time_now (this_vlib_main);
1737   }));
1738   /* *INDENT-ON* */
1739
1740   if (!qm->ca_cert_path)
1741     qm->ca_cert_path = QUIC_DEFAULT_CA_CERT_PATH;
1742
1743   qm->app_index = a->app_index;
1744   qm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
1745     / QUIC_TSTAMP_RESOLUTION;
1746
1747   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
1748                                FIB_PROTOCOL_IP4, ~0);
1749   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
1750                                FIB_PROTOCOL_IP6, ~0);
1751
1752   vec_free (a->name);
1753   return 0;
1754 }
1755
1756 VLIB_INIT_FUNCTION (quic_init);
1757
1758 /* *INDENT-OFF* */
1759 VLIB_PLUGIN_REGISTER () =
1760 {
1761   .version = VPP_BUILD_VER,
1762   .description = "Quic transport protocol",
1763 };
1764 /* *INDENT-ON* */
1765
1766 /*
1767  * fd.io coding-style-patch-verification: ON
1768  *
1769  * Local Variables:
1770  * eval: (c-set-style "gnu")
1771  * End:
1772  */