17a9e113a00296b0bf168b20c513fbcccf91f57d
[vpp.git] / src / plugins / hs_apps / sapi / quic_echo.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 <stdio.h>
17 #include <signal.h>
18
19 #include <vnet/session/application_interface.h>
20 #include <vlibmemory/api.h>
21
22 #include <vpp/api/vpe_msg_enum.h>
23 #include <svm/fifo_segment.h>
24
25 #define vl_typedefs             /* define message structures */
26 #include <vpp/api/vpe_all_api_h.h>
27 #undef vl_typedefs
28
29 /* declare message handlers for each api */
30
31 #define vl_endianfun            /* define message structures */
32 #include <vpp/api/vpe_all_api_h.h>
33 #undef vl_endianfun
34
35 /* instantiate all the print functions we know about */
36 #define vl_print(handle, ...)
37 #define vl_printfun
38 #include <vpp/api/vpe_all_api_h.h>
39 #undef vl_printfun
40
41 #define CHECK(expected, result, _fmt, _args...)         \
42     if (expected != result)                             \
43       ECHO_FAIL ("expected %d, got %d : " _fmt, expected, result, ##_args);
44
45 #define ECHO_FAIL(_fmt,_args...)        \
46   {                                     \
47     echo_main_t *em = &echo_main;       \
48     em->has_failed = 1;         \
49     em->time_to_stop = 1;               \
50     if (em->log_lvl > 1)                \
51       clib_warning ("ECHO-ERROR: "_fmt, ##_args);       \
52   }
53
54 #define ECHO_LOG(lvl, _fmt,_args...)    \
55   {                                     \
56     echo_main_t *em = &echo_main;       \
57     if (em->log_lvl > lvl)              \
58       clib_warning (_fmt, ##_args);     \
59   }
60
61 typedef struct
62 {
63   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
64 #define _(type, name) type name;
65   foreach_app_session_field
66 #undef _
67   u64 vpp_session_handle;
68   u64 bytes_sent;
69   u64 bytes_to_send;
70   volatile u64 bytes_received;
71   volatile u64 bytes_to_receive;
72   f64 start;
73   u32 listener_index;           /* listener index in echo session pool */
74   u32 idle_cycles;              /* consecutive enq/deq with no data */
75   volatile u64 accepted_session_count;  /* sessions we accepted */
76 } echo_session_t;
77
78 typedef enum
79 {
80   ECHO_NO_DATA_SOURCE,
81   ECHO_TEST_DATA_SOURCE,
82   ECHO_RX_DATA_SOURCE,
83   ECHO_INVALID_DATA_SOURCE
84 } data_source_t;
85
86 enum echo_close_f_t
87 {
88   ECHO_CLOSE_F_INVALID = 0,
89   ECHO_CLOSE_F_PASSIVE,         /* wait for close msg */
90   ECHO_CLOSE_F_ACTIVE,          /* send close msg */
91   ECHO_CLOSE_F_NONE,            /* don't bother sending close msg */
92 };
93
94 enum quic_session_type_t
95 {
96   QUIC_SESSION_TYPE_QUIC,
97   QUIC_SESSION_TYPE_STREAM,
98   QUIC_SESSION_TYPE_LISTEN,
99 };
100
101 enum quic_session_state_t
102 {
103   QUIC_SESSION_STATE_INITIAL,
104   QUIC_SESSION_STATE_AWAIT_CLOSING,     /* Data transfer is done, wait for close evt */
105   QUIC_SESSION_STATE_AWAIT_DATA,        /* Peer closed, wait for outstanding data */
106   QUIC_SESSION_STATE_CLOSING,   /* told vpp to close */
107   QUIC_SESSION_STATE_CLOSED,    /* closed in vpp */
108 };
109
110 typedef enum
111 {
112   STATE_START,
113   STATE_ATTACHED,
114   STATE_LISTEN,
115   STATE_READY,
116   STATE_DISCONNECTED,
117   STATE_DETACHED
118 } connection_state_t;
119
120 typedef enum echo_test_evt_
121 {
122   ECHO_EVT_START = 1,           /* app starts */
123   ECHO_EVT_FIRST_QCONNECT = (1 << 1),   /* First connect Quic session sent */
124   ECHO_EVT_LAST_QCONNECTED = (1 << 2),  /* All Quic session are connected */
125   ECHO_EVT_FIRST_SCONNECT = (1 << 3),   /* First connect Stream session sent */
126   ECHO_EVT_LAST_SCONNECTED = (1 << 4),  /* All Stream session are connected */
127   ECHO_EVT_LAST_BYTE = (1 << 5),        /* Last byte received */
128   ECHO_EVT_EXIT = (1 << 6),     /* app exits */
129 } echo_test_evt_t;
130
131 typedef struct _quic_echo_cb_vft
132 {
133   void (*quic_connected_cb) (session_connected_msg_t * mp, u32 session_index);
134   void (*client_stream_connected_cb) (session_connected_msg_t * mp,
135                                       u32 session_index);
136   void (*server_stream_connected_cb) (session_connected_msg_t * mp,
137                                       u32 session_index);
138   void (*quic_accepted_cb) (session_accepted_msg_t * mp, u32 session_index);
139   void (*client_stream_accepted_cb) (session_accepted_msg_t * mp,
140                                      u32 session_index);
141   void (*server_stream_accepted_cb) (session_accepted_msg_t * mp,
142                                      u32 session_index);
143 } quic_echo_cb_vft_t;
144
145
146 typedef enum
147 {
148   RETURN_PACKETS_NOTEST,
149   RETURN_PACKETS_LOG_WRONG,
150   RETURN_PACKETS_ASSERT,
151 } test_return_packets_t;
152
153 typedef struct
154 {
155   /* vpe input queue */
156   svm_queue_t *vl_input_queue;
157
158   /* API client handle */
159   u32 my_client_index;
160
161   /* The URI we're playing with */
162   u8 *uri;
163
164   /* Session pool */
165   echo_session_t *sessions;
166
167   /* Hash table for disconnect processing */
168   uword *session_index_by_vpp_handles;
169   /* Index of vpp listener session */
170   u32 listen_session_index;
171
172   /* Hash table for shared segment_names */
173   uword *shared_segment_handles;
174   clib_spinlock_t segment_handles_lock;
175
176   int i_am_master;
177
178   /* Our event queue */
179   svm_msg_q_t *our_event_queue;
180
181   u8 *socket_name;
182
183   pid_t my_pid;
184
185   /* For deadman timers */
186   clib_time_t clib_time;
187
188   /* State of the connection, shared between msg RX thread and main thread */
189   volatile connection_state_t state;
190
191   /* Signal variables */
192   volatile u8 time_to_stop;
193   u8 has_failed;
194
195   /* VNET_API_ERROR_FOO -> "Foo" hash table */
196   uword *error_string_by_error_number;
197
198   u8 *connect_test_data;
199   u8 test_return_packets;
200   u64 bytes_to_send;
201   u64 bytes_to_receive;
202   u32 fifo_size;
203   u32 rx_buf_size;
204   u32 tx_buf_size;
205   data_source_t data_source;
206   u8 send_quic_disconnects;     /* actively send disconnect */
207   u8 send_stream_disconnects;   /* actively send disconnect */
208   u8 output_json;
209   u8 log_lvl;
210
211   u8 *appns_id;
212   u64 appns_flags;
213   u64 appns_secret;
214
215   pthread_t *client_thread_handles;
216   u32 *thread_args;
217   u32 n_clients;                /* Target number of QUIC sessions */
218   u32 n_stream_clients;         /* Target Number of STREAM sessions per QUIC session */
219   volatile u32 n_quic_clients_connected;        /* Number of connected QUIC sessions */
220   volatile u32 n_clients_connected;     /* Number of STREAM sessions connected */
221   u32 n_rx_threads;             /* Number of data threads */
222
223   u64 tx_total;
224   u64 rx_total;
225
226   /* Event based timing : start & end depend on CLI specified events */
227   u8 events_sent;
228   f64 start_time;
229   f64 end_time;
230   u8 timing_start_event;
231   u8 timing_end_event;
232
233   /* cb vft for QUIC scenarios */
234   quic_echo_cb_vft_t cb_vft;
235
236   /** Flag that decides if socket, instead of svm, api is used to connect to
237    * vpp. If sock api is used, shm binary api is subsequently bootstrapped
238    * and all other messages are exchanged using shm IPC. */
239   u8 use_sock_api;
240
241   /* Limit the number of incorrect data messages */
242   int max_test_msg;
243
244   fifo_segment_main_t segment_main;
245 } echo_main_t;
246
247 echo_main_t echo_main;
248
249 #if CLIB_DEBUG > 0
250 #define NITER 10000
251 #else
252 #define NITER 4000000
253 #endif
254
255 #if CLIB_DEBUG > 0
256 #define TIMEOUT 10.0
257 #else
258 #define TIMEOUT 10.0
259 #endif
260
261 /*
262  *
263  *  Format functions
264  *
265  */
266
267 u8 *
268 format_ip4_address (u8 * s, va_list * args)
269 {
270   u8 *a = va_arg (*args, u8 *);
271   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
272 }
273
274 u8 *
275 format_ip6_address (u8 * s, va_list * args)
276 {
277   ip6_address_t *a = va_arg (*args, ip6_address_t *);
278   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
279
280   i_max_n_zero = ARRAY_LEN (a->as_u16);
281   max_n_zeros = 0;
282   i_first_zero = i_max_n_zero;
283   n_zeros = 0;
284   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
285     {
286       u32 is_zero = a->as_u16[i] == 0;
287       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
288         {
289           i_first_zero = i;
290           n_zeros = 0;
291         }
292       n_zeros += is_zero;
293       if ((!is_zero && n_zeros > max_n_zeros)
294           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
295         {
296           i_max_n_zero = i_first_zero;
297           max_n_zeros = n_zeros;
298           i_first_zero = ARRAY_LEN (a->as_u16);
299           n_zeros = 0;
300         }
301     }
302
303   last_double_colon = 0;
304   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
305     {
306       if (i == i_max_n_zero && max_n_zeros > 1)
307         {
308           s = format (s, "::");
309           i += max_n_zeros - 1;
310           last_double_colon = 1;
311         }
312       else
313         {
314           s = format (s, "%s%x",
315                       (last_double_colon || i == 0) ? "" : ":",
316                       clib_net_to_host_u16 (a->as_u16[i]));
317           last_double_colon = 0;
318         }
319     }
320
321   return s;
322 }
323
324 /* Format an IP46 address. */
325 u8 *
326 format_ip46_address (u8 * s, va_list * args)
327 {
328   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
329   ip46_type_t type = va_arg (*args, ip46_type_t);
330   int is_ip4 = 1;
331
332   switch (type)
333     {
334     case IP46_TYPE_ANY:
335       is_ip4 = ip46_address_is_ip4 (ip46);
336       break;
337     case IP46_TYPE_IP4:
338       is_ip4 = 1;
339       break;
340     case IP46_TYPE_IP6:
341       is_ip4 = 0;
342       break;
343     }
344
345   return is_ip4 ?
346     format (s, "%U", format_ip4_address, &ip46->ip4) :
347     format (s, "%U", format_ip6_address, &ip46->ip6);
348 }
349
350 u8 *
351 format_quic_echo_state (u8 * s, va_list * args)
352 {
353   u32 state = va_arg (*args, u32);
354   if (state == STATE_START)
355     return format (s, "STATE_START");
356   if (state == STATE_ATTACHED)
357     return format (s, "STATE_ATTACHED");
358   if (state == STATE_LISTEN)
359     return format (s, "STATE_LISTEN");
360   if (state == STATE_READY)
361     return format (s, "STATE_READY");
362   if (state == STATE_DISCONNECTED)
363     return format (s, "STATE_DISCONNECTED");
364   if (state == STATE_DETACHED)
365     return format (s, "STATE_DETACHED");
366   else
367     return format (s, "unknown state");
368 }
369
370 static u8 *
371 format_api_error (u8 * s, va_list * args)
372 {
373   echo_main_t *em = &echo_main;
374   i32 error = va_arg (*args, u32);
375   uword *p;
376
377   p = hash_get (em->error_string_by_error_number, -error);
378
379   if (p)
380     s = format (s, "%s", p[0]);
381   else
382     s = format (s, "%d", error);
383   return s;
384 }
385
386 static uword
387 unformat_close (unformat_input_t * input, va_list * args)
388 {
389   u8 *a = va_arg (*args, u8 *);
390   if (unformat (input, "Y"))
391     *a = ECHO_CLOSE_F_ACTIVE;
392   else if (unformat (input, "N"))
393     *a = ECHO_CLOSE_F_NONE;
394   else if (unformat (input, "W"))
395     *a = ECHO_CLOSE_F_PASSIVE;
396   else
397     return 0;
398   return 1;
399 }
400
401 static uword
402 unformat_data (unformat_input_t * input, va_list * args)
403 {
404   u64 _a;
405   u64 *a = va_arg (*args, u64 *);
406   if (unformat (input, "%lluGb", &_a))
407     {
408       *a = _a << 30;
409       return 1;
410     }
411   else if (unformat (input, "%lluMb", &_a))
412     {
413       *a = _a << 20;
414       return 1;
415     }
416   else if (unformat (input, "%lluKb", &_a))
417     {
418       *a = _a << 10;
419       return 1;
420     }
421   else if (unformat (input, "%llu", a))
422     return 1;
423   return 0;
424 }
425
426 static uword
427 echo_unformat_timing_event (unformat_input_t * input, va_list * args)
428 {
429   echo_test_evt_t *a = va_arg (*args, echo_test_evt_t *);
430   if (unformat (input, "start"))
431     *a = ECHO_EVT_START;
432   else if (unformat (input, "qconnected"))
433     *a = ECHO_EVT_LAST_QCONNECTED;
434   else if (unformat (input, "qconnect"))
435     *a = ECHO_EVT_FIRST_QCONNECT;
436   else if (unformat (input, "sconnected"))
437     *a = ECHO_EVT_LAST_SCONNECTED;
438   else if (unformat (input, "sconnect"))
439     *a = ECHO_EVT_FIRST_SCONNECT;
440   else if (unformat (input, "lastbyte"))
441     *a = ECHO_EVT_LAST_BYTE;
442   else if (unformat (input, "exit"))
443     *a = ECHO_EVT_EXIT;
444   else
445     return 0;
446   return 1;
447 }
448
449 u8 *
450 echo_format_timing_event (u8 * s, va_list * args)
451 {
452   u32 timing_event = va_arg (*args, u32);
453   if (timing_event == ECHO_EVT_START)
454     return format (s, "start");
455   if (timing_event == ECHO_EVT_FIRST_QCONNECT)
456     return format (s, "qconnect");
457   if (timing_event == ECHO_EVT_LAST_QCONNECTED)
458     return format (s, "qconnected");
459   if (timing_event == ECHO_EVT_FIRST_SCONNECT)
460     return format (s, "sconnect");
461   if (timing_event == ECHO_EVT_LAST_SCONNECTED)
462     return format (s, "sconnected");
463   if (timing_event == ECHO_EVT_LAST_BYTE)
464     return format (s, "lastbyte");
465   if (timing_event == ECHO_EVT_EXIT)
466     return format (s, "exit");
467   else
468     return format (s, "unknown timing event");
469 }
470
471 static void
472 init_error_string_table (echo_main_t * em)
473 {
474   em->error_string_by_error_number = hash_create (0, sizeof (uword));
475
476 #define _(n,v,s) hash_set (em->error_string_by_error_number, -v, s);
477   foreach_vnet_api_error;
478 #undef _
479
480   hash_set (em->error_string_by_error_number, 99, "Misc");
481 }
482
483 /*
484  *
485  *  End of format functions
486  *
487  */
488
489 static echo_session_t *
490 echo_session_alloc (echo_main_t * em)
491 {
492   echo_session_t *session;
493   pool_get (em->sessions, session);
494   clib_memset (session, 0, sizeof (*session));
495   session->session_index = session - em->sessions;
496   session->listener_index = SESSION_INVALID_INDEX;
497   session->session_state = QUIC_SESSION_STATE_INITIAL;
498   return session;
499 }
500
501 /*
502  *
503  *  Session API Calls
504  *
505  */
506
507 void
508 application_send_attach (echo_main_t * em)
509 {
510   vl_api_application_attach_t *bmp;
511   vl_api_application_tls_cert_add_t *cert_mp;
512   vl_api_application_tls_key_add_t *key_mp;
513
514   bmp = vl_msg_api_alloc (sizeof (*bmp));
515   clib_memset (bmp, 0, sizeof (*bmp));
516
517   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_ATTACH);
518   bmp->client_index = em->my_client_index;
519   bmp->context = ntohl (0xfeedface);
520   bmp->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_ACCEPT_REDIRECT;
521   bmp->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ADD_SEGMENT;
522   bmp->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 16;
523   bmp->options[APP_OPTIONS_RX_FIFO_SIZE] = em->fifo_size;
524   bmp->options[APP_OPTIONS_TX_FIFO_SIZE] = em->fifo_size;
525   bmp->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = 128 << 20;
526   bmp->options[APP_OPTIONS_SEGMENT_SIZE] = 256 << 20;
527   bmp->options[APP_OPTIONS_EVT_QUEUE_SIZE] = 256;
528   if (em->appns_id)
529     {
530       bmp->namespace_id_len = vec_len (em->appns_id);
531       clib_memcpy_fast (bmp->namespace_id, em->appns_id,
532                         bmp->namespace_id_len);
533       bmp->options[APP_OPTIONS_FLAGS] |= em->appns_flags;
534       bmp->options[APP_OPTIONS_NAMESPACE_SECRET] = em->appns_secret;
535     }
536   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
537
538   cert_mp = vl_msg_api_alloc (sizeof (*cert_mp) + test_srv_crt_rsa_len);
539   clib_memset (cert_mp, 0, sizeof (*cert_mp));
540   cert_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_CERT_ADD);
541   cert_mp->client_index = em->my_client_index;
542   cert_mp->context = ntohl (0xfeedface);
543   cert_mp->cert_len = clib_host_to_net_u16 (test_srv_crt_rsa_len);
544   clib_memcpy_fast (cert_mp->cert, test_srv_crt_rsa, test_srv_crt_rsa_len);
545   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & cert_mp);
546
547   key_mp = vl_msg_api_alloc (sizeof (*key_mp) + test_srv_key_rsa_len);
548   clib_memset (key_mp, 0, sizeof (*key_mp) + test_srv_key_rsa_len);
549   key_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_KEY_ADD);
550   key_mp->client_index = em->my_client_index;
551   key_mp->context = ntohl (0xfeedface);
552   key_mp->key_len = clib_host_to_net_u16 (test_srv_key_rsa_len);
553   clib_memcpy_fast (key_mp->key, test_srv_key_rsa, test_srv_key_rsa_len);
554   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & key_mp);
555 }
556
557 void
558 application_detach (echo_main_t * em)
559 {
560   vl_api_application_detach_t *bmp;
561   bmp = vl_msg_api_alloc (sizeof (*bmp));
562   clib_memset (bmp, 0, sizeof (*bmp));
563
564   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
565   bmp->client_index = em->my_client_index;
566   bmp->context = ntohl (0xfeedface);
567   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
568 }
569
570 static void
571 server_send_listen (echo_main_t * em)
572 {
573   vl_api_bind_uri_t *bmp;
574   bmp = vl_msg_api_alloc (sizeof (*bmp));
575   clib_memset (bmp, 0, sizeof (*bmp));
576
577   bmp->_vl_msg_id = ntohs (VL_API_BIND_URI);
578   bmp->client_index = em->my_client_index;
579   bmp->context = ntohl (0xfeedface);
580   memcpy (bmp->uri, em->uri, vec_len (em->uri));
581   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
582 }
583
584 static void
585 server_send_unbind (echo_main_t * em)
586 {
587   vl_api_unbind_uri_t *ump;
588
589   ump = vl_msg_api_alloc (sizeof (*ump));
590   clib_memset (ump, 0, sizeof (*ump));
591
592   ump->_vl_msg_id = ntohs (VL_API_UNBIND_URI);
593   ump->client_index = em->my_client_index;
594   memcpy (ump->uri, em->uri, vec_len (em->uri));
595   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & ump);
596 }
597
598 static void
599 echo_send_connect (echo_main_t * em, u8 * uri, u32 opaque)
600 {
601   vl_api_connect_uri_t *cmp;
602   cmp = vl_msg_api_alloc (sizeof (*cmp));
603   clib_memset (cmp, 0, sizeof (*cmp));
604
605   cmp->_vl_msg_id = ntohs (VL_API_CONNECT_URI);
606   cmp->client_index = em->my_client_index;
607   cmp->context = ntohl (opaque);
608   memcpy (cmp->uri, uri, vec_len (uri));
609   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & cmp);
610 }
611
612 static void
613 echo_disconnect_session (echo_main_t * em, echo_session_t * s)
614 {
615   vl_api_disconnect_session_t *dmp;
616   dmp = vl_msg_api_alloc (sizeof (*dmp));
617   clib_memset (dmp, 0, sizeof (*dmp));
618   dmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION);
619   dmp->client_index = em->my_client_index;
620   dmp->handle = s->vpp_session_handle;
621   ECHO_LOG (1, "Disconnect session 0x%lx", dmp->handle);
622   vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & dmp);
623 }
624
625 /*
626  *
627  *  End Session API Calls
628  *
629  */
630
631 static int
632 wait_for_segment_allocation (u64 segment_handle)
633 {
634   echo_main_t *em = &echo_main;
635   f64 timeout;
636   timeout = clib_time_now (&em->clib_time) + TIMEOUT;
637   uword *segment_present;
638   ECHO_LOG (1, "Waiting for segment 0x%lx...", segment_handle);
639   while (clib_time_now (&em->clib_time) < timeout)
640     {
641       clib_spinlock_lock (&em->segment_handles_lock);
642       segment_present = hash_get (em->shared_segment_handles, segment_handle);
643       clib_spinlock_unlock (&em->segment_handles_lock);
644       if (segment_present != 0)
645         return 0;
646       if (em->time_to_stop == 1)
647         return 0;
648     }
649   ECHO_LOG (1, "timeout wait_for_segment_allocation (0x%lx)", segment_handle);
650   return -1;
651 }
652
653 static void
654 quic_echo_notify_event (echo_main_t * em, echo_test_evt_t e)
655 {
656   if (em->events_sent & e)
657     return;
658   if (em->timing_start_event == e)
659     em->start_time = clib_time_now (&em->clib_time);
660   else if (em->timing_end_event == e)
661     em->end_time = clib_time_now (&em->clib_time);
662   em->events_sent |= e;
663 }
664
665 static void
666 echo_assert_test_suceeded (echo_main_t * em)
667 {
668   CHECK (em->rx_total,
669          em->n_stream_clients * em->n_clients * em->bytes_to_receive,
670          "Not enough data received");
671   CHECK (em->tx_total,
672          em->n_stream_clients * em->n_clients * em->bytes_to_send,
673          "Not enough data sent");
674   CHECK (0, hash_elts (em->session_index_by_vpp_handles),
675          "Some sessions are still open");
676 }
677
678 always_inline void
679 echo_session_dequeue_notify (echo_session_t * s)
680 {
681   int rv;
682   rv = app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo->master_session_index,
683                                SESSION_IO_EVT_RX, SVM_Q_WAIT);
684   svm_fifo_clear_deq_ntf (s->rx_fifo);
685   if (rv)
686     ECHO_FAIL ("app_send_io_evt_to_vpp errored %d", rv);
687 }
688
689 static int
690 ssvm_segment_attach (char *name, ssvm_segment_type_t type, int fd)
691 {
692   fifo_segment_create_args_t _a, *a = &_a;
693   fifo_segment_main_t *sm = &echo_main.segment_main;
694   int rv;
695
696   clib_memset (a, 0, sizeof (*a));
697   a->segment_name = (char *) name;
698   a->segment_type = type;
699
700   if (type == SSVM_SEGMENT_MEMFD)
701     a->memfd_fd = fd;
702
703   if ((rv = fifo_segment_attach (sm, a)))
704     return rv;
705   vec_reset_length (a->new_segment_indices);
706   return 0;
707 }
708
709 static void
710 stop_signal (int signum)
711 {
712   echo_main_t *um = &echo_main;
713   um->time_to_stop = 1;
714 }
715
716 static clib_error_t *
717 setup_signal_handlers (void)
718 {
719   signal (SIGINT, stop_signal);
720   signal (SIGQUIT, stop_signal);
721   signal (SIGTERM, stop_signal);
722   return 0;
723 }
724
725 int
726 connect_to_vpp (char *name)
727 {
728   echo_main_t *em = &echo_main;
729   api_main_t *am = &api_main;
730
731   if (em->use_sock_api)
732     {
733       if (vl_socket_client_connect ((char *) em->socket_name, name,
734                                     0 /* default rx, tx buffer */ ))
735         {
736           ECHO_FAIL ("socket connect failed");
737           return -1;
738         }
739
740       if (vl_socket_client_init_shm (0, 1 /* want_pthread */ ))
741         {
742           ECHO_FAIL ("init shm api failed");
743           return -1;
744         }
745     }
746   else
747     {
748       if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
749         {
750           ECHO_FAIL ("shmem connect failed");
751           return -1;
752         }
753     }
754   em->vl_input_queue = am->shmem_hdr->vl_input_queue;
755   em->my_client_index = am->my_client_index;
756   return 0;
757 }
758
759 static void
760 session_print_stats (echo_main_t * em, echo_session_t * session)
761 {
762   f64 deltat = clib_time_now (&em->clib_time) - session->start;
763   ECHO_LOG (0, "Session 0x%x done in %.6fs RX[%.4f] TX[%.4f] Gbit/s\n",
764             session->vpp_session_handle, deltat,
765             (session->bytes_received * 8.0) / deltat / 1e9,
766             (session->bytes_sent * 8.0) / deltat / 1e9);
767 }
768
769 static void
770 echo_event_didnt_happen (u8 e)
771 {
772   echo_main_t *em = &echo_main;
773   u8 *s = format (0, "%U", echo_format_timing_event, e);
774   ECHO_LOG (0, "Expected event %s to happend, which did not", s);
775   em->has_failed = 1;
776 }
777
778 static void
779 print_global_json_stats (echo_main_t * em)
780 {
781   if (!(em->events_sent & em->timing_start_event))
782     return echo_event_didnt_happen (em->timing_start_event);
783   if (!(em->events_sent & em->timing_end_event))
784     return echo_event_didnt_happen (em->timing_end_event);
785   f64 deltat = em->end_time - em->start_time;
786   u8 *start_evt =
787     format (0, "%U", echo_format_timing_event, em->timing_start_event);
788   u8 *end_evt =
789     format (0, "%U", echo_format_timing_event, em->timing_end_event);
790   fformat (stdout, "{\n");
791   fformat (stdout, "\"time\": \"%.9f\",\n", deltat);
792   fformat (stdout, "\"start_evt\": \"%s\",\n", start_evt);
793   fformat (stdout, "\"end_evt\": \"%s\",\n", end_evt);
794   fformat (stdout, "\"rx_data\": %lld,\n", em->rx_total);
795   fformat (stdout, "\"tx_rx\": %lld,\n", em->tx_total);
796   fformat (stdout, "}\n");
797 }
798
799 static void
800 print_global_stats (echo_main_t * em)
801 {
802   u8 *s;
803   if (!(em->events_sent & em->timing_start_event))
804     return echo_event_didnt_happen (em->timing_start_event);
805   if (!(em->events_sent & em->timing_end_event))
806     return echo_event_didnt_happen (em->timing_end_event);
807   f64 deltat = em->end_time - em->start_time;
808   s = format (0, "%U:%U",
809               echo_format_timing_event, em->timing_start_event,
810               echo_format_timing_event, em->timing_end_event);
811   fformat (stdout, "Timing %s\n", s);
812   fformat (stdout, "-------- TX --------\n");
813   fformat (stdout, "%lld bytes (%lld mbytes, %lld gbytes) in %.6f seconds\n",
814            em->tx_total, em->tx_total / (1ULL << 20),
815            em->tx_total / (1ULL << 30), deltat);
816   fformat (stdout, "%.4f Gbit/second\n", (em->tx_total * 8.0) / deltat / 1e9);
817   fformat (stdout, "-------- RX --------\n");
818   fformat (stdout, "%lld bytes (%lld mbytes, %lld gbytes) in %.6f seconds\n",
819            em->rx_total, em->rx_total / (1ULL << 20),
820            em->rx_total / (1ULL << 30), deltat);
821   fformat (stdout, "%.4f Gbit/second\n", (em->rx_total * 8.0) / deltat / 1e9);
822   fformat (stdout, "--------------------\n");
823 }
824
825 static void
826 echo_free_sessions (echo_main_t * em)
827 {
828   /* Free marked sessions */
829   echo_session_t *s;
830   u32 *session_indexes = 0, *session_index;
831
832   /* *INDENT-OFF* */
833   pool_foreach (s, em->sessions,
834   ({
835     if (s->session_state == QUIC_SESSION_STATE_CLOSED)
836       vec_add1 (session_indexes, s->session_index);}
837   ));
838   /* *INDENT-ON* */
839   vec_foreach (session_index, session_indexes)
840   {
841     /* Free session */
842     s = pool_elt_at_index (em->sessions, *session_index);
843     ECHO_LOG (1, "Freeing session 0x%lx", s->vpp_session_handle);
844     pool_put (em->sessions, s);
845     clib_memset (s, 0xfe, sizeof (*s));
846   }
847 }
848
849 static void
850 echo_cleanup_session (echo_main_t * em, echo_session_t * s)
851 {
852   u64 c;
853   echo_session_t *ls;
854   if (s->listener_index != SESSION_INVALID_INDEX)
855     {
856       ls = pool_elt_at_index (em->sessions, s->listener_index);
857       c = clib_atomic_sub_fetch (&ls->accepted_session_count, 1);
858       if (c == 0 && ls->session_type == QUIC_SESSION_TYPE_QUIC)
859         {
860           if (em->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
861             echo_disconnect_session (em, ls);
862           else if (em->send_quic_disconnects == ECHO_CLOSE_F_NONE)
863             echo_cleanup_session (em, ls);
864         }
865     }
866   if (s->session_type == QUIC_SESSION_TYPE_QUIC)
867     clib_atomic_sub_fetch (&em->n_quic_clients_connected, 1);
868   else if (s->session_type == QUIC_SESSION_TYPE_STREAM)
869     clib_atomic_sub_fetch (&em->n_clients_connected, 1);
870
871   ECHO_LOG (1, "Cleanup sessions (still %uQ %uS)",
872             em->n_quic_clients_connected, em->n_clients_connected);
873   hash_unset (em->session_index_by_vpp_handles, s->vpp_session_handle);
874   s->session_state = QUIC_SESSION_STATE_CLOSED;
875 }
876
877 static void
878 echo_initiate_session_close (echo_main_t * em, echo_session_t * s, u8 active)
879 {
880   if (s->session_type == QUIC_SESSION_TYPE_STREAM)
881     {
882       if (!active && s->bytes_to_receive)
883         s->session_state = QUIC_SESSION_STATE_AWAIT_DATA;
884       else
885         s->session_state = QUIC_SESSION_STATE_CLOSING;
886     }
887   else
888     echo_cleanup_session (em, s);       /* We can clean Q/Lsessions right away */
889 }
890
891 static void
892 echo_initiate_qsession_close_no_stream (echo_main_t * em)
893 {
894   ECHO_LOG (1, "Closing Qsessions");
895   /* Close Quic session without streams */
896   echo_session_t *s;
897
898   /* *INDENT-OFF* */
899   pool_foreach (s, em->sessions,
900   ({
901     if (s->session_type == QUIC_SESSION_TYPE_QUIC && s->accepted_session_count == 0)
902       {
903         ECHO_LOG (1,"ACTIVE close 0x%lx", s->vpp_session_handle);
904         if (em->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
905           echo_disconnect_session (em, s);
906         else if (em->send_quic_disconnects == ECHO_CLOSE_F_NONE)
907           echo_cleanup_session (em, s);
908       }
909   }));
910   /* *INDENT-ON* */
911 }
912
913 static void
914 test_recv_bytes (echo_main_t * em, echo_session_t * s, u8 * rx_buf,
915                  u32 n_read)
916 {
917   u32 i;
918   u8 expected;
919   for (i = 0; i < n_read; i++)
920     {
921       expected = (s->bytes_received + i) & 0xff;
922       if (rx_buf[i] == expected || em->max_test_msg > 0)
923         continue;
924       ECHO_LOG (0, "Session 0x%lx byte %lld was 0x%x expected 0x%x",
925                 s->vpp_session_handle, s->bytes_received + i, rx_buf[i],
926                 expected);
927       em->max_test_msg--;
928       if (em->max_test_msg == 0)
929         ECHO_LOG (0, "Too many errors, hiding next ones");
930       if (em->test_return_packets == RETURN_PACKETS_ASSERT)
931         ECHO_FAIL ("test-bytes errored");
932     }
933 }
934
935 static int
936 recv_data_chunk (echo_main_t * em, echo_session_t * s, u8 * rx_buf)
937 {
938   int n_read;
939   n_read = app_recv_stream ((app_session_t *) s, rx_buf, vec_len (rx_buf));
940   if (n_read <= 0)
941     return 0;
942   if (svm_fifo_needs_deq_ntf (s->rx_fifo, n_read))
943     echo_session_dequeue_notify (s);
944
945   if (em->test_return_packets)
946     test_recv_bytes (em, s, rx_buf, n_read);
947
948   s->bytes_received += n_read;
949   s->bytes_to_receive -= n_read;
950   return n_read;
951 }
952
953 static int
954 send_data_chunk (echo_session_t * s, u8 * tx_buf, int offset, int len)
955 {
956   int n_sent;
957   int bytes_this_chunk = clib_min (s->bytes_to_send, len - offset);
958   if (!bytes_this_chunk)
959     return 0;
960   n_sent = app_send_stream ((app_session_t *) s, tx_buf + offset,
961                             bytes_this_chunk, 0);
962   if (n_sent < 0)
963     return 0;
964   s->bytes_to_send -= n_sent;
965   s->bytes_sent += n_sent;
966   return n_sent;
967 }
968
969 static int
970 mirror_data_chunk (echo_main_t * em, echo_session_t * s, u8 * tx_buf, u64 len)
971 {
972   u64 n_sent = 0;
973   while (n_sent < len && !em->time_to_stop)
974     n_sent += send_data_chunk (s, tx_buf, n_sent, len);
975   return n_sent;
976 }
977
978 /*
979  * Rx/Tx polling thread per connection
980  */
981 static void
982 echo_thread_handle_data (echo_main_t * em, echo_session_t * s, u8 * rx_buf)
983 {
984   int n_read, n_sent;
985
986   n_read = recv_data_chunk (em, s, rx_buf);
987   if (em->data_source == ECHO_TEST_DATA_SOURCE)
988     n_sent =
989       send_data_chunk (s, em->connect_test_data,
990                        s->bytes_sent % em->tx_buf_size, em->tx_buf_size);
991   else if (em->data_source == ECHO_RX_DATA_SOURCE)
992     n_sent = mirror_data_chunk (em, s, rx_buf, n_read);
993   else
994     n_sent = 0;
995   if (!s->bytes_to_send && !s->bytes_to_receive)
996     {
997       /* Session is done, need to close */
998       if (s->session_state == QUIC_SESSION_STATE_AWAIT_DATA)
999         s->session_state = QUIC_SESSION_STATE_CLOSING;
1000       else
1001         {
1002           s->session_state = QUIC_SESSION_STATE_AWAIT_CLOSING;
1003           if (em->send_stream_disconnects == ECHO_CLOSE_F_ACTIVE)
1004             echo_disconnect_session (em, s);
1005           else if (em->send_stream_disconnects == ECHO_CLOSE_F_NONE)
1006             s->session_state = QUIC_SESSION_STATE_CLOSING;
1007         }
1008       return;
1009     }
1010
1011   if (em->log_lvl > 1)
1012     {
1013       /* check idle clients */
1014       if (n_sent || n_read)
1015         s->idle_cycles = 0;
1016       else if (s->idle_cycles++ == 1e7)
1017         {
1018           s->idle_cycles = 0;
1019           ECHO_LOG (1, "Idle client TX:%dB RX:%dB", s->bytes_to_send,
1020                     s->bytes_to_receive);
1021           ECHO_LOG (1, "Idle FIFOs  TX:%dB RX:%dB",
1022                     svm_fifo_max_dequeue (s->tx_fifo),
1023                     svm_fifo_max_dequeue (s->rx_fifo));
1024         }
1025     }
1026 }
1027
1028 static void
1029 echo_thread_handle_closing (echo_main_t * em, echo_session_t * s)
1030 {
1031
1032   ECHO_LOG (1, "[%lu/%lu] -> S(%x) -> [%lu/%lu]",
1033             s->bytes_received, s->bytes_received + s->bytes_to_receive,
1034             s->session_index, s->bytes_sent,
1035             s->bytes_sent + s->bytes_to_send);
1036   clib_atomic_fetch_add (&em->tx_total, s->bytes_sent);
1037   clib_atomic_fetch_add (&em->rx_total, s->bytes_received);
1038
1039   if (PREDICT_FALSE (em->rx_total ==
1040                      em->n_clients * em->n_stream_clients *
1041                      em->bytes_to_receive))
1042     quic_echo_notify_event (em, ECHO_EVT_LAST_BYTE);
1043   echo_cleanup_session (em, s);
1044 }
1045
1046 static void *
1047 echo_thread_fn (void *arg)
1048 {
1049   echo_main_t *em = &echo_main;
1050   u32 thread_n_sessions = (u64) arg & 0xFFFFFFFF;
1051   u32 session_first_idx = (u64) arg >> 32;
1052
1053   u32 i = 0;
1054   u32 n_closed_sessions = 0;
1055   u32 session_index;
1056   u8 *rx_buf = 0;
1057   echo_session_t *s;
1058   vec_validate (rx_buf, em->rx_buf_size);
1059
1060   while (!em->time_to_stop && em->state != STATE_READY)
1061     ;
1062
1063   for (i = 0; !em->time_to_stop; i++)
1064     {
1065       if (i % thread_n_sessions == 0)
1066         n_closed_sessions = 0;
1067       session_index =
1068         em->thread_args[session_first_idx + i % thread_n_sessions];
1069       s = pool_elt_at_index (em->sessions, session_index);
1070       if (s->session_state == QUIC_SESSION_STATE_INITIAL
1071           || s->session_state == QUIC_SESSION_STATE_AWAIT_DATA)
1072         echo_thread_handle_data (em, s, rx_buf);
1073       else if (s->session_state == QUIC_SESSION_STATE_CLOSING)
1074         echo_thread_handle_closing (em, s);
1075       else if (s->session_state == QUIC_SESSION_STATE_CLOSED)
1076         n_closed_sessions++;
1077       if (n_closed_sessions == thread_n_sessions)
1078         break;
1079     }
1080   pthread_exit (0);
1081 }
1082
1083 static int
1084 echo_start_rx_thread (u32 session_index)
1085 {
1086   /* Each thread owns n consecutive sessions of the total N */
1087
1088   echo_main_t *em = &echo_main;
1089   u32 N = em->n_clients * em->n_stream_clients;
1090   u32 nc, n, first_idx, thread_sessions;
1091
1092   n = (N + em->n_rx_threads - 1) / em->n_rx_threads;
1093   nc = em->n_clients_connected;
1094
1095   ASSERT (nc + 1 <= N);
1096   em->thread_args[nc] = session_index;
1097
1098   if ((nc + 1) % n == 0 || nc + 1 == N)
1099     {
1100       first_idx = n * (nc / n);
1101       thread_sessions = (nc + 1) % n == 0 ? n : (nc + 1) % n;
1102       ECHO_LOG (1, "Start thread %u [%u -> %u]", nc / n, first_idx,
1103                 first_idx + thread_sessions - 1);
1104       return pthread_create (&em->client_thread_handles[nc / n],
1105                              NULL /*attr */ , echo_thread_fn,
1106                              (void *) ((u64) first_idx << 32 | (u64)
1107                                        thread_sessions));
1108     }
1109
1110   return 0;
1111 }
1112
1113 static void
1114 session_bound_handler (session_bound_msg_t * mp)
1115 {
1116   echo_main_t *em = &echo_main;
1117   echo_session_t *listen_session;
1118   if (mp->retval)
1119     {
1120       ECHO_FAIL ("bind failed: %U", format_api_error,
1121                  clib_net_to_host_u32 (mp->retval));
1122       return;
1123     }
1124   ECHO_LOG (0, "listening on %U:%u", format_ip46_address, mp->lcl_ip,
1125             mp->lcl_is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1126             clib_net_to_host_u16 (mp->lcl_port));
1127
1128   /* Allocate local session and set it up */
1129   listen_session = echo_session_alloc (em);
1130   listen_session->session_type = QUIC_SESSION_TYPE_LISTEN;
1131   listen_session->accepted_session_count = 0;
1132   hash_set (em->session_index_by_vpp_handles, mp->handle,
1133             listen_session->session_index);
1134   em->state = STATE_LISTEN;
1135   em->listen_session_index = listen_session->session_index;
1136 }
1137
1138 static void
1139 session_accepted_handler (session_accepted_msg_t * mp)
1140 {
1141   app_session_evt_t _app_evt, *app_evt = &_app_evt;
1142   session_accepted_reply_msg_t *rmp;
1143   svm_fifo_t *rx_fifo, *tx_fifo;
1144   echo_main_t *em = &echo_main;
1145   echo_session_t *session, *listen_session;
1146   uword *p;
1147   /* Allocate local session and set it up */
1148   session = echo_session_alloc (em);
1149
1150   if (wait_for_segment_allocation (mp->segment_handle))
1151     {
1152       ECHO_FAIL ("wait_for_segment_allocation errored");
1153       return;
1154     }
1155
1156   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
1157   rx_fifo->client_session_index = session->session_index;
1158   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
1159   tx_fifo->client_session_index = session->session_index;
1160
1161   session->rx_fifo = rx_fifo;
1162   session->tx_fifo = tx_fifo;
1163   session->vpp_session_handle = mp->handle;
1164   session->start = clib_time_now (&em->clib_time);
1165   session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
1166                                          svm_msg_q_t *);
1167
1168   session->accepted_session_count = 0;
1169   p = hash_get (em->session_index_by_vpp_handles, mp->listener_handle);
1170   if (!p)
1171     {
1172       ECHO_FAIL ("unknown handle 0x%lx", mp->listener_handle);
1173       return;
1174     }
1175   session->listener_index = p[0];
1176   listen_session = pool_elt_at_index (em->sessions, p[0]);
1177   clib_atomic_fetch_add (&listen_session->accepted_session_count, 1);
1178
1179   /* Add it to lookup table */
1180   ECHO_LOG (1, "Accepted session 0x%lx -> 0x%lx", mp->handle,
1181             mp->listener_handle);
1182   hash_set (em->session_index_by_vpp_handles, mp->handle,
1183             session->session_index);
1184
1185   app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
1186                              SESSION_CTRL_EVT_ACCEPTED_REPLY);
1187   rmp = (session_accepted_reply_msg_t *) app_evt->evt->data;
1188   rmp->handle = mp->handle;
1189   rmp->context = mp->context;
1190   app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
1191
1192   if (listen_session->session_type == QUIC_SESSION_TYPE_LISTEN)
1193     {
1194       quic_echo_notify_event (em, ECHO_EVT_FIRST_QCONNECT);
1195       session->session_type = QUIC_SESSION_TYPE_QUIC;
1196       if (em->cb_vft.quic_accepted_cb)
1197         em->cb_vft.quic_accepted_cb (mp, session->session_index);
1198       clib_atomic_fetch_add (&em->n_quic_clients_connected, 1);
1199     }
1200   else if (em->i_am_master)
1201     {
1202       session->session_type = QUIC_SESSION_TYPE_STREAM;
1203       quic_echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
1204       if (em->cb_vft.server_stream_accepted_cb)
1205         em->cb_vft.server_stream_accepted_cb (mp, session->session_index);
1206       clib_atomic_fetch_add (&em->n_clients_connected, 1);
1207     }
1208   else
1209     {
1210       session->session_type = QUIC_SESSION_TYPE_STREAM;
1211       quic_echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
1212       if (em->cb_vft.client_stream_accepted_cb)
1213         em->cb_vft.client_stream_accepted_cb (mp, session->session_index);
1214       clib_atomic_fetch_add (&em->n_clients_connected, 1);
1215     }
1216
1217   if (em->n_clients_connected == em->n_clients * em->n_stream_clients
1218       && em->n_clients_connected != 0)
1219     {
1220       ECHO_LOG (1, "App is ready");
1221       em->state = STATE_READY;
1222       quic_echo_notify_event (em, ECHO_EVT_LAST_SCONNECTED);
1223     }
1224   if (em->n_quic_clients_connected == em->n_clients)
1225     {
1226       quic_echo_notify_event (em, ECHO_EVT_LAST_QCONNECTED);
1227       if (em->n_stream_clients == 0)
1228         {
1229           em->state = STATE_READY;
1230           echo_initiate_qsession_close_no_stream (em);
1231         }
1232     }
1233 }
1234
1235 static void
1236 session_connected_handler (session_connected_msg_t * mp)
1237 {
1238   echo_main_t *em = &echo_main;
1239   echo_session_t *session, *listen_session;
1240   u32 listener_index = htonl (mp->context);
1241   svm_fifo_t *rx_fifo, *tx_fifo;
1242
1243   if (mp->retval)
1244     {
1245       ECHO_FAIL ("connection failed with code: %U", format_api_error,
1246                  clib_net_to_host_u32 (mp->retval));
1247       return;
1248     }
1249
1250   session = echo_session_alloc (em);
1251   if (wait_for_segment_allocation (mp->segment_handle))
1252     {
1253       ECHO_FAIL ("wait_for_segment_allocation errored");
1254       return;
1255     }
1256
1257   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
1258   rx_fifo->client_session_index = session->session_index;
1259   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
1260   tx_fifo->client_session_index = session->session_index;
1261
1262   session->rx_fifo = rx_fifo;
1263   session->tx_fifo = tx_fifo;
1264   session->vpp_session_handle = mp->handle;
1265   session->start = clib_time_now (&em->clib_time);
1266   session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
1267                                          svm_msg_q_t *);
1268
1269   session->accepted_session_count = 0;
1270   if (listener_index != SESSION_INVALID_INDEX)
1271     {
1272       listen_session = pool_elt_at_index (em->sessions, listener_index);
1273       clib_atomic_fetch_add (&listen_session->accepted_session_count, 1);
1274       session->listener_index = listen_session->session_index;
1275     }
1276
1277   ECHO_LOG (1, "Connected session 0x%lx -> 0x%lx", mp->handle,
1278             listener_index !=
1279             SESSION_INVALID_INDEX ? listen_session->vpp_session_handle : (u64)
1280             ~ 0);
1281   hash_set (em->session_index_by_vpp_handles, mp->handle,
1282             session->session_index);
1283
1284   if (listener_index == SESSION_INVALID_INDEX)
1285     {
1286       session->session_type = QUIC_SESSION_TYPE_QUIC;
1287       if (em->cb_vft.quic_connected_cb)
1288         em->cb_vft.quic_connected_cb (mp, session->session_index);
1289       clib_atomic_fetch_add (&em->n_quic_clients_connected, 1);
1290     }
1291   else if (em->i_am_master)
1292     {
1293       session->session_type = QUIC_SESSION_TYPE_STREAM;
1294       if (em->cb_vft.server_stream_connected_cb)
1295         em->cb_vft.server_stream_connected_cb (mp, session->session_index);
1296       clib_atomic_fetch_add (&em->n_clients_connected, 1);
1297     }
1298   else
1299     {
1300       session->session_type = QUIC_SESSION_TYPE_STREAM;
1301       if (em->cb_vft.client_stream_connected_cb)
1302         em->cb_vft.client_stream_connected_cb (mp, session->session_index);
1303       clib_atomic_fetch_add (&em->n_clients_connected, 1);
1304     }
1305
1306   if (em->n_clients_connected == em->n_clients * em->n_stream_clients
1307       && em->n_clients_connected != 0)
1308     {
1309       ECHO_LOG (1, "App is ready");
1310       em->state = STATE_READY;
1311       quic_echo_notify_event (em, ECHO_EVT_LAST_SCONNECTED);
1312     }
1313   if (em->n_quic_clients_connected == em->n_clients)
1314     {
1315       quic_echo_notify_event (em, ECHO_EVT_LAST_QCONNECTED);
1316       if (em->n_stream_clients == 0)
1317         {
1318           em->state = STATE_READY;
1319           echo_initiate_qsession_close_no_stream (em);
1320         }
1321     }
1322 }
1323
1324 /*
1325  *
1326  *  ECHO Callback definitions
1327  *
1328  */
1329
1330
1331 static void
1332 echo_on_connected_connect (session_connected_msg_t * mp, u32 session_index)
1333 {
1334   echo_main_t *em = &echo_main;
1335   u8 *uri = format (0, "QUIC://session/%lu", mp->handle);
1336   u64 i;
1337
1338   quic_echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
1339   for (i = 0; i < em->n_stream_clients; i++)
1340     echo_send_connect (em, uri, session_index);
1341
1342   ECHO_LOG (0, "Qsession 0x%llx connected to %U:%d",
1343             mp->handle, format_ip46_address, &mp->lcl.ip,
1344             mp->lcl.is_ip4, clib_net_to_host_u16 (mp->lcl.port));
1345 }
1346
1347 static void
1348 echo_on_connected_send (session_connected_msg_t * mp, u32 session_index)
1349 {
1350   echo_main_t *em = &echo_main;
1351   int rv;
1352   echo_session_t *session;
1353
1354   session = pool_elt_at_index (em->sessions, session_index);
1355   session->bytes_to_send = em->bytes_to_send;
1356   session->bytes_to_receive = em->bytes_to_receive;
1357
1358   if ((rv = echo_start_rx_thread (session_index)))
1359     {
1360       ECHO_FAIL ("pthread_create returned %d", rv);
1361       return;
1362     }
1363 }
1364
1365 static void
1366 echo_on_connected_error (session_connected_msg_t * mp, u32 session_index)
1367 {
1368   ECHO_FAIL ("Got a wrong connected on session %u [%lx]", session_index,
1369              mp->handle);
1370 }
1371
1372 static void
1373 echo_on_accept_recv (session_accepted_msg_t * mp, u32 session_index)
1374 {
1375   echo_main_t *em = &echo_main;
1376   int rv;
1377   echo_session_t *session;
1378
1379   session = pool_elt_at_index (em->sessions, session_index);
1380   session->bytes_to_send = em->bytes_to_send;
1381   session->bytes_to_receive = em->bytes_to_receive;
1382
1383   if ((rv = echo_start_rx_thread (session_index)))
1384     {
1385       ECHO_FAIL ("pthread_create returned %d", rv);
1386       return;
1387     }
1388 }
1389
1390 static void
1391 echo_on_accept_connect (session_accepted_msg_t * mp, u32 session_index)
1392 {
1393   echo_main_t *em = &echo_main;
1394   ECHO_LOG (1, "Accept on QSession 0x%lx %u", mp->handle);
1395   u8 *uri = format (0, "QUIC://session/%lu", mp->handle);
1396   u32 i;
1397
1398   quic_echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
1399   for (i = 0; i < em->n_stream_clients; i++)
1400     echo_send_connect (em, uri, session_index);
1401 }
1402
1403 static void
1404 echo_on_accept_error (session_accepted_msg_t * mp, u32 session_index)
1405 {
1406   ECHO_FAIL ("Got a wrong accept on session %u [%lx]", session_index,
1407              mp->handle);
1408 }
1409
1410 static void
1411 echo_on_accept_log_ip (session_accepted_msg_t * mp, u32 session_index)
1412 {
1413   u8 *ip_str;
1414   ip_str = format (0, "%U", format_ip46_address, &mp->rmt.ip, mp->rmt.is_ip4);
1415   ECHO_LOG (0, "Accepted session from: %s:%d", ip_str,
1416             clib_net_to_host_u16 (mp->rmt.port));
1417
1418 }
1419
1420 static const quic_echo_cb_vft_t default_cb_vft = {
1421   /* Qsessions */
1422   .quic_accepted_cb = echo_on_accept_log_ip,
1423   .quic_connected_cb = echo_on_connected_connect,
1424   /* client initiated streams */
1425   .server_stream_accepted_cb = echo_on_accept_recv,
1426   .client_stream_connected_cb = echo_on_connected_send,
1427   /* server initiated streams */
1428   .client_stream_accepted_cb = echo_on_accept_error,
1429   .server_stream_connected_cb = echo_on_connected_error,
1430 };
1431
1432 static const quic_echo_cb_vft_t server_stream_cb_vft = {
1433   /* Qsessions */
1434   .quic_accepted_cb = echo_on_accept_connect,
1435   .quic_connected_cb = NULL,
1436   /* client initiated streams */
1437   .server_stream_accepted_cb = echo_on_accept_error,
1438   .client_stream_connected_cb = echo_on_connected_error,
1439   /* server initiated streams */
1440   .client_stream_accepted_cb = echo_on_accept_recv,
1441   .server_stream_connected_cb = echo_on_connected_send,
1442 };
1443
1444 static uword
1445 echo_unformat_quic_setup_vft (unformat_input_t * input, va_list * args)
1446 {
1447   echo_main_t *em = &echo_main;
1448   if (unformat (input, "serverstream"))
1449     em->cb_vft = server_stream_cb_vft;
1450   else if (unformat (input, "default"))
1451     ;
1452   else
1453     return 0;
1454   return 1;
1455 }
1456
1457 /*
1458  *
1459  *  End of ECHO callback definitions
1460  *
1461  */
1462
1463 static void
1464 session_disconnected_handler (session_disconnected_msg_t * mp)
1465 {
1466   app_session_evt_t _app_evt, *app_evt = &_app_evt;
1467   session_disconnected_reply_msg_t *rmp;
1468   echo_main_t *em = &echo_main;
1469   echo_session_t *s;
1470   uword *p;
1471   int rv = 0;
1472   ECHO_LOG (1, "passive close session 0x%lx", mp->handle);
1473   p = hash_get (em->session_index_by_vpp_handles, mp->handle);
1474   if (!p)
1475     {
1476       ECHO_FAIL ("couldn't find session key %llx", mp->handle);
1477       return;
1478     }
1479
1480   s = pool_elt_at_index (em->sessions, p[0]);
1481   app_alloc_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt,
1482                              SESSION_CTRL_EVT_DISCONNECTED_REPLY);
1483   rmp = (session_disconnected_reply_msg_t *) app_evt->evt->data;
1484   rmp->retval = rv;
1485   rmp->handle = mp->handle;
1486   rmp->context = mp->context;
1487   app_send_ctrl_evt_to_vpp (s->vpp_evt_q, app_evt);
1488
1489   echo_initiate_session_close (em, s, 0 /* active */ );
1490   if (s->session_type == QUIC_SESSION_TYPE_STREAM)
1491     session_print_stats (em, s);
1492 }
1493
1494 static void
1495 session_reset_handler (session_reset_msg_t * mp)
1496 {
1497   app_session_evt_t _app_evt, *app_evt = &_app_evt;
1498   echo_main_t *em = &echo_main;
1499   session_reset_reply_msg_t *rmp;
1500   echo_session_t *session = 0;
1501   uword *p;
1502   int rv = 0;
1503
1504   ECHO_LOG (1, "Reset session 0x%lx", mp->handle);
1505   p = hash_get (em->session_index_by_vpp_handles, mp->handle);
1506
1507   if (!p)
1508     {
1509       ECHO_FAIL ("couldn't find session key %llx", mp->handle);
1510       return;
1511     }
1512
1513   session = pool_elt_at_index (em->sessions, p[0]);
1514   /* Cleanup later */
1515   em->time_to_stop = 1;
1516   app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
1517                              SESSION_CTRL_EVT_RESET_REPLY);
1518   rmp = (session_reset_reply_msg_t *) app_evt->evt->data;
1519   rmp->retval = rv;
1520   rmp->handle = mp->handle;
1521   app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
1522 }
1523
1524 static void
1525 handle_mq_event (session_event_t * e)
1526 {
1527   switch (e->event_type)
1528     {
1529     case SESSION_CTRL_EVT_BOUND:
1530       session_bound_handler ((session_bound_msg_t *) e->data);
1531       break;
1532     case SESSION_CTRL_EVT_ACCEPTED:
1533       session_accepted_handler ((session_accepted_msg_t *) e->data);
1534       break;
1535     case SESSION_CTRL_EVT_CONNECTED:
1536       session_connected_handler ((session_connected_msg_t *) e->data);
1537       break;
1538     case SESSION_CTRL_EVT_DISCONNECTED:
1539       session_disconnected_handler ((session_disconnected_msg_t *) e->data);
1540       break;
1541     case SESSION_CTRL_EVT_RESET:
1542       session_reset_handler ((session_reset_msg_t *) e->data);
1543       break;
1544     case SESSION_IO_EVT_RX:
1545       break;
1546     default:
1547       ECHO_LOG (0, "unhandled event %u", e->event_type);
1548     }
1549 }
1550
1551 static int
1552 wait_for_state_change (echo_main_t * em, connection_state_t state,
1553                        f64 timeout)
1554 {
1555   svm_msg_q_msg_t msg;
1556   session_event_t *e;
1557   f64 end_time = clib_time_now (&em->clib_time) + timeout;
1558
1559   while (!timeout || clib_time_now (&em->clib_time) < end_time)
1560     {
1561       if (em->state == state)
1562         return 0;
1563       if (em->time_to_stop == 1)
1564         return 0;
1565       if (!em->our_event_queue || em->state < STATE_ATTACHED)
1566         continue;
1567
1568       if (svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_NOWAIT, 0))
1569         continue;
1570       e = svm_msg_q_msg_data (em->our_event_queue, &msg);
1571       handle_mq_event (e);
1572       svm_msg_q_free_msg (em->our_event_queue, &msg);
1573     }
1574   ECHO_LOG (1, "timeout waiting for %U", format_quic_echo_state, state);
1575   return -1;
1576 }
1577
1578 static void
1579 echo_event_loop (echo_main_t * em)
1580 {
1581   svm_msg_q_msg_t msg;
1582   session_event_t *e;
1583
1584   ECHO_LOG (1, "Waiting for data on %u clients", em->n_clients_connected);
1585   while (em->n_clients_connected | em->n_quic_clients_connected)
1586     {
1587       int rc = svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_TIMEDWAIT, 1);
1588       if (PREDICT_FALSE (rc == ETIMEDOUT && em->time_to_stop))
1589         break;
1590       if (rc == ETIMEDOUT)
1591         continue;
1592       e = svm_msg_q_msg_data (em->our_event_queue, &msg);
1593       handle_mq_event (e);
1594       svm_msg_q_free_msg (em->our_event_queue, &msg);
1595     }
1596 }
1597
1598 static void
1599 clients_run (echo_main_t * em)
1600 {
1601   u64 i;
1602   quic_echo_notify_event (em, ECHO_EVT_FIRST_QCONNECT);
1603   for (i = 0; i < em->n_clients; i++)
1604     echo_send_connect (em, em->uri, SESSION_INVALID_INDEX);
1605
1606   if (wait_for_state_change (em, STATE_READY, TIMEOUT))
1607     {
1608       ECHO_FAIL ("Timeout waiting for state ready");
1609       return;
1610     }
1611
1612   echo_event_loop (em);
1613 }
1614
1615 static void
1616 server_run (echo_main_t * em)
1617 {
1618   server_send_listen (em);
1619   if (wait_for_state_change (em, STATE_READY, 0))
1620     {
1621       ECHO_FAIL ("Timeout waiting for state ready");
1622       return;
1623     }
1624   echo_event_loop (em);
1625
1626   /* Cleanup */
1627   server_send_unbind (em);
1628   if (wait_for_state_change (em, STATE_DISCONNECTED, TIMEOUT))
1629     {
1630       ECHO_FAIL ("Timeout waiting for state disconnected");
1631       return;
1632     }
1633 }
1634
1635 /*
1636  *
1637  *  Session API handlers
1638  *
1639  */
1640
1641
1642 static void
1643 vl_api_application_attach_reply_t_handler (vl_api_application_attach_reply_t *
1644                                            mp)
1645 {
1646   echo_main_t *em = &echo_main;
1647   int *fds = 0, i;
1648   u32 n_fds = 0;
1649   u64 segment_handle;
1650   segment_handle = clib_net_to_host_u64 (mp->segment_handle);
1651   ECHO_LOG (1, "Attached returned app %u", htons (mp->app_index));
1652
1653   if (mp->retval)
1654     {
1655       ECHO_FAIL ("attach failed: %U", format_api_error,
1656                  clib_net_to_host_u32 (mp->retval));
1657       return;
1658     }
1659
1660   if (mp->segment_name_length == 0)
1661     {
1662       ECHO_FAIL ("segment_name_length zero");
1663       return;
1664     }
1665
1666   ASSERT (mp->app_event_queue_address);
1667   em->our_event_queue = uword_to_pointer (mp->app_event_queue_address,
1668                                           svm_msg_q_t *);
1669
1670   if (mp->n_fds)
1671     {
1672       vec_validate (fds, mp->n_fds);
1673       if (vl_socket_client_recv_fd_msg (fds, mp->n_fds, 5))
1674         {
1675           ECHO_FAIL ("vl_socket_client_recv_fd_msg failed");
1676           goto failed;
1677         }
1678
1679       if (mp->fd_flags & SESSION_FD_F_VPP_MQ_SEGMENT)
1680         if (ssvm_segment_attach (0, SSVM_SEGMENT_MEMFD, fds[n_fds++]))
1681           {
1682             ECHO_FAIL ("svm_fifo_segment_attach failed");
1683             goto failed;
1684           }
1685
1686       if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
1687         if (ssvm_segment_attach ((char *) mp->segment_name,
1688                                  SSVM_SEGMENT_MEMFD, fds[n_fds++]))
1689           {
1690             ECHO_FAIL ("svm_fifo_segment_attach ('%s') failed",
1691                        mp->segment_name);
1692             goto failed;
1693           }
1694       if (mp->fd_flags & SESSION_FD_F_MQ_EVENTFD)
1695         svm_msg_q_set_consumer_eventfd (em->our_event_queue, fds[n_fds++]);
1696
1697       vec_free (fds);
1698     }
1699   else
1700     {
1701       if (ssvm_segment_attach ((char *) mp->segment_name, SSVM_SEGMENT_SHM,
1702                                -1))
1703         {
1704           ECHO_FAIL ("svm_fifo_segment_attach ('%s') failed",
1705                      mp->segment_name);
1706           return;
1707         }
1708     }
1709   clib_spinlock_lock (&em->segment_handles_lock);
1710   hash_set (em->shared_segment_handles, segment_handle, 1);
1711   clib_spinlock_unlock (&em->segment_handles_lock);
1712   ECHO_LOG (1, "Mapped segment 0x%lx", segment_handle);
1713
1714   em->state = STATE_ATTACHED;
1715   return;
1716 failed:
1717   for (i = clib_max (n_fds - 1, 0); i < vec_len (fds); i++)
1718     close (fds[i]);
1719   vec_free (fds);
1720 }
1721
1722 static void
1723 vl_api_application_detach_reply_t_handler (vl_api_application_detach_reply_t *
1724                                            mp)
1725 {
1726   if (mp->retval)
1727     {
1728       ECHO_FAIL ("detach returned with err: %d", mp->retval);
1729       return;
1730     }
1731   echo_main.state = STATE_DETACHED;
1732 }
1733
1734
1735 static void
1736 vl_api_unmap_segment_t_handler (vl_api_unmap_segment_t * mp)
1737 {
1738   echo_main_t *em = &echo_main;
1739   u64 segment_handle = clib_net_to_host_u64 (mp->segment_handle);
1740
1741   clib_spinlock_lock (&em->segment_handles_lock);
1742   hash_unset (em->shared_segment_handles, segment_handle);
1743   clib_spinlock_unlock (&em->segment_handles_lock);
1744   ECHO_LOG (1, "Unmaped segment 0x%lx", segment_handle);
1745 }
1746
1747 static void
1748 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
1749 {
1750   fifo_segment_main_t *sm = &echo_main.segment_main;
1751   fifo_segment_create_args_t _a, *a = &_a;
1752   echo_main_t *em = &echo_main;
1753   int *fds = 0, i;
1754   char *seg_name = (char *) mp->segment_name;
1755   u64 segment_handle = clib_net_to_host_u64 (mp->segment_handle);
1756
1757   if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
1758     {
1759       vec_validate (fds, 1);
1760       if (vl_socket_client_recv_fd_msg (fds, 1, 5))
1761         {
1762           ECHO_FAIL ("vl_socket_client_recv_fd_msg failed");
1763           goto failed;
1764         }
1765
1766       if (ssvm_segment_attach (seg_name, SSVM_SEGMENT_MEMFD, fds[0]))
1767         {
1768           ECHO_FAIL ("svm_fifo_segment_attach ('%s')"
1769                      "failed on SSVM_SEGMENT_MEMFD", seg_name);
1770           goto failed;
1771         }
1772       vec_free (fds);
1773     }
1774   else
1775     {
1776       clib_memset (a, 0, sizeof (*a));
1777       a->segment_name = seg_name;
1778       a->segment_size = mp->segment_size;
1779       /* Attach to the segment vpp created */
1780       if (fifo_segment_attach (sm, a))
1781         {
1782           ECHO_FAIL ("svm_fifo_segment_attach ('%s') failed", seg_name);
1783           goto failed;
1784         }
1785     }
1786   clib_spinlock_lock (&em->segment_handles_lock);
1787   hash_set (em->shared_segment_handles, segment_handle, 1);
1788   clib_spinlock_unlock (&em->segment_handles_lock);
1789   ECHO_LOG (1, "Mapped segment 0x%lx", segment_handle);
1790   return;
1791
1792 failed:
1793   for (i = 0; i < vec_len (fds); i++)
1794     close (fds[i]);
1795   vec_free (fds);
1796 }
1797
1798 static void
1799 vl_api_bind_uri_reply_t_handler (vl_api_bind_uri_reply_t * mp)
1800 {
1801   echo_main_t *em = &echo_main;
1802   if (mp->retval)
1803     {
1804       ECHO_FAIL ("bind failed: %U", format_api_error,
1805                  clib_net_to_host_u32 (mp->retval));
1806       return;
1807     }
1808
1809   em->state = STATE_LISTEN;
1810 }
1811
1812 static void
1813 vl_api_unbind_uri_reply_t_handler (vl_api_unbind_uri_reply_t * mp)
1814 {
1815   echo_session_t *listen_session;
1816   echo_main_t *em = &echo_main;
1817   if (mp->retval != 0)
1818     {
1819       ECHO_FAIL ("returned %d", ntohl (mp->retval));
1820       return;
1821     }
1822   em->state = STATE_DISCONNECTED;
1823   listen_session = pool_elt_at_index (em->sessions, em->listen_session_index);
1824   echo_initiate_session_close (em, listen_session, 1 /* active */ );
1825 }
1826
1827 static void
1828 vl_api_disconnect_session_reply_t_handler (vl_api_disconnect_session_reply_t *
1829                                            mp)
1830 {
1831   echo_main_t *em = &echo_main;
1832   echo_session_t *s;
1833   uword *p;
1834
1835   if (mp->retval)
1836     {
1837       ECHO_FAIL ("vpp complained about disconnect: %d", ntohl (mp->retval));
1838       return;
1839     }
1840
1841   ECHO_LOG (1, "Got disonnected reply for session 0x%lx", mp->handle);
1842   p = hash_get (em->session_index_by_vpp_handles, mp->handle);
1843   if (!p)
1844     {
1845       ECHO_FAIL ("couldn't find session key %llx", mp->handle);
1846       return;
1847     }
1848
1849   s = pool_elt_at_index (em->sessions, p[0]);
1850   echo_initiate_session_close (em, s, 1 /* active */ );
1851 }
1852
1853 static void
1854   vl_api_application_tls_cert_add_reply_t_handler
1855   (vl_api_application_tls_cert_add_reply_t * mp)
1856 {
1857   if (mp->retval)
1858     ECHO_FAIL ("failed to add tls cert");
1859 }
1860
1861 static void
1862   vl_api_application_tls_key_add_reply_t_handler
1863   (vl_api_application_tls_key_add_reply_t * mp)
1864 {
1865   if (mp->retval)
1866     ECHO_FAIL ("failed to add tls key");
1867 }
1868
1869 static void
1870 vl_api_connect_uri_reply_t_handler (vl_api_connect_uri_reply_t * mp)
1871 {
1872   echo_session_t *session;
1873   echo_main_t *em = &echo_main;
1874   u8 *uri;
1875   if (!mp->retval)
1876     return;
1877   /* retry connect */
1878   if (mp->context == SESSION_INVALID_INDEX)
1879     {
1880       ECHO_LOG (1, "Retrying connect %s", em->uri);
1881       echo_send_connect (em, em->uri, SESSION_INVALID_INDEX);
1882     }
1883   else
1884     {
1885       session = pool_elt_at_index (em->sessions, mp->context);
1886       uri = format (0, "QUIC://session/%lu", session->vpp_session_handle);
1887       ECHO_LOG (1, "Retrying connect %s", uri);
1888       echo_send_connect (em, uri, mp->context);
1889     }
1890 }
1891
1892 #define foreach_quic_echo_msg                                           \
1893 _(BIND_URI_REPLY, bind_uri_reply)                                       \
1894 _(UNBIND_URI_REPLY, unbind_uri_reply)                                   \
1895 _(DISCONNECT_SESSION_REPLY, disconnect_session_reply)                   \
1896 _(APPLICATION_ATTACH_REPLY, application_attach_reply)                   \
1897 _(APPLICATION_DETACH_REPLY, application_detach_reply)                   \
1898 _(MAP_ANOTHER_SEGMENT, map_another_segment)                             \
1899 _(UNMAP_SEGMENT, unmap_segment)                                         \
1900 _(APPLICATION_TLS_CERT_ADD_REPLY, application_tls_cert_add_reply)       \
1901 _(APPLICATION_TLS_KEY_ADD_REPLY, application_tls_key_add_reply)         \
1902 _(CONNECT_URI_REPLY, connect_uri_reply)         \
1903
1904 void
1905 quic_echo_api_hookup (echo_main_t * em)
1906 {
1907 #define _(N,n)                                                  \
1908     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1909                            vl_api_##n##_t_handler,              \
1910                            vl_noop_handler,                     \
1911                            vl_api_##n##_t_endian,               \
1912                            vl_api_##n##_t_print,                \
1913                            sizeof(vl_api_##n##_t), 1);
1914   foreach_quic_echo_msg;
1915 #undef _
1916 }
1917
1918 /*
1919  *
1920  *  End Session API handlers
1921  *
1922  */
1923
1924 static void
1925 print_usage_and_exit (void)
1926 {
1927   fprintf (stderr,
1928            "Usage: quic_echo [socket-name SOCKET] [client|server] [uri URI] [OPTIONS]\n"
1929            "Generates traffic and assert correct teardown of the QUIC hoststack\n"
1930            "\n"
1931            "  socket-name PATH    Specify the binary socket path to connect to VPP\n"
1932            "  use-svm-api         Use SVM API to connect to VPP\n"
1933            "  test-bytes[:assert] Check data correctness when receiving (assert fails on first error)\n"
1934            "  fifo-size N         Use N Kb fifos\n"
1935            "  rx-buf N            Use N Kb RX buffer\n"
1936            "  tx-buf N            Use N Kb TX test buffer\n"
1937            "  appns NAMESPACE     Use the namespace NAMESPACE\n"
1938            "  all-scope           all-scope option\n"
1939            "  local-scope         local-scope option\n"
1940            "  global-scope        global-scope option\n"
1941            "  secret SECRET       set namespace secret\n"
1942            "  chroot prefix PATH  Use PATH as memory root path\n"
1943            "  quic-setup OPT      OPT=serverstream : Client open N connections. \n"
1944            "                       On each one server opens M streams\n"
1945            "                      OPT=default : Client open N connections.\n"
1946            "                       On each one client opens M streams\n"
1947            "  sclose=[Y|N|W]      When a stream is done,    pass[N] send[Y] or wait[W] for close\n"
1948            "  qclose=[Y|N|W]      When a connection is done pass[N] send[Y] or wait[W] for close\n"
1949            "\n"
1950            "  time START:END      Time between evts START & END, events being :\n"
1951            "                       start - Start of the app\n"
1952            "                       qconnect    - first Connection connect sent\n"
1953            "                       qconnected  - last Connection connected\n"
1954            "                       sconnect    - first Stream connect sent\n"
1955            "                       sconnected  - last Stream got connected\n"
1956            "                       lastbyte    - Last expected byte received\n"
1957            "                       exit        - Exiting of the app\n"
1958            "  json                Output global stats in json\n"
1959            "  log=N               Set the log level to [0: no output, 1:errors, 2:log]\n"
1960            "\n"
1961            "  nclients N[/M]      Open N QUIC connections, each one with M streams (M defaults to 1)\n"
1962            "  nthreads N          Use N busy loop threads for data [in addition to main & msg queue]\n"
1963            "  TX=1337[Kb|Mb|GB]   Send 1337 [K|M|G]bytes, use TX=RX to reflect the data\n"
1964            "  RX=1337[Kb|Mb|GB]   Expect 1337 [K|M|G]bytes\n"
1965            "\n"
1966            "Default configuration is :\n"
1967            " server nclients 1/1 RX=64Kb TX=RX\n"
1968            " client nclients 1/1 RX=64Kb TX=64Kb\n");
1969   exit (1);
1970 }
1971
1972
1973 void
1974 quic_echo_process_opts (int argc, char **argv)
1975 {
1976   echo_main_t *em = &echo_main;
1977   unformat_input_t _argv, *a = &_argv;
1978   u32 tmp;
1979   u8 *chroot_prefix;
1980   u8 *uri = 0;
1981   u8 default_f_active;
1982
1983   unformat_init_command_line (a, argv);
1984   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
1985     {
1986       if (unformat (a, "chroot prefix %s", &chroot_prefix))
1987         {
1988           vl_set_memory_root_path ((char *) chroot_prefix);
1989         }
1990       else if (unformat (a, "uri %s", &uri))
1991         em->uri = format (0, "%s%c", uri, 0);
1992       else if (unformat (a, "server"))
1993         em->i_am_master = 1;
1994       else if (unformat (a, "client"))
1995         em->i_am_master = 0;
1996       else if (unformat (a, "test-bytes:assert"))
1997         em->test_return_packets = RETURN_PACKETS_ASSERT;
1998       else if (unformat (a, "test-bytes"))
1999         em->test_return_packets = RETURN_PACKETS_LOG_WRONG;
2000       else if (unformat (a, "socket-name %s", &em->socket_name))
2001         ;
2002       else if (unformat (a, "use-svm-api"))
2003         em->use_sock_api = 0;
2004       else if (unformat (a, "fifo-size %d", &tmp))
2005         em->fifo_size = tmp << 10;
2006       else if (unformat (a, "rx-buf %d", &tmp))
2007         em->rx_buf_size = tmp << 10;
2008       else if (unformat (a, "tx-buf %d", &tmp))
2009         em->rx_buf_size = tmp << 10;
2010       else
2011         if (unformat
2012             (a, "nclients %d/%d", &em->n_clients, &em->n_stream_clients))
2013         ;
2014       else if (unformat (a, "nclients %d", &em->n_clients))
2015         ;
2016       else if (unformat (a, "nthreads %d", &em->n_rx_threads))
2017         ;
2018       else if (unformat (a, "appns %_%v%_", &em->appns_id))
2019         ;
2020       else if (unformat (a, "all-scope"))
2021         em->appns_flags |= (APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE
2022                             | APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE);
2023       else if (unformat (a, "local-scope"))
2024         em->appns_flags = APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
2025       else if (unformat (a, "global-scope"))
2026         em->appns_flags = APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
2027       else if (unformat (a, "secret %lu", &em->appns_secret))
2028         ;
2029       else if (unformat (a, "quic-setup %U", echo_unformat_quic_setup_vft))
2030         ;
2031       else if (unformat (a, "TX=RX"))
2032         em->data_source = ECHO_RX_DATA_SOURCE;
2033       else if (unformat (a, "TX=%U", unformat_data, &em->bytes_to_send))
2034         ;
2035       else if (unformat (a, "RX=%U", unformat_data, &em->bytes_to_receive))
2036         ;
2037       else if (unformat (a, "json"))
2038         em->output_json = 1;
2039       else if (unformat (a, "log=%d", &em->log_lvl))
2040         ;
2041       else
2042         if (unformat
2043             (a, "sclose=%U", unformat_close, &em->send_stream_disconnects))
2044         ;
2045       else
2046         if (unformat
2047             (a, "qclose=%U", unformat_close, &em->send_quic_disconnects))
2048         ;
2049       else if (unformat (a, "time %U:%U",
2050                          echo_unformat_timing_event, &em->timing_start_event,
2051                          echo_unformat_timing_event, &em->timing_end_event))
2052         ;
2053       else
2054         print_usage_and_exit ();
2055     }
2056
2057   /* setting default for unset values
2058    *
2059    * bytes_to_send / bytes_to_receive & data_source  */
2060   if (em->bytes_to_receive == (u64) ~ 0)
2061     em->bytes_to_receive = 64 << 10;    /* default */
2062   if (em->bytes_to_send == (u64) ~ 0)
2063     em->bytes_to_send = 64 << 10;       /* default */
2064   else if (em->bytes_to_send == 0)
2065     em->data_source = ECHO_NO_DATA_SOURCE;
2066   else
2067     em->data_source = ECHO_TEST_DATA_SOURCE;
2068
2069   if (em->data_source == ECHO_INVALID_DATA_SOURCE)
2070     em->data_source =
2071       em->i_am_master ? ECHO_RX_DATA_SOURCE : ECHO_TEST_DATA_SOURCE;
2072   if (em->data_source == ECHO_RX_DATA_SOURCE)
2073     em->bytes_to_send = em->bytes_to_receive;
2074
2075   /* disconnect flags  */
2076   if (em->i_am_master)
2077     default_f_active =
2078       em->bytes_to_send == 0 ? ECHO_CLOSE_F_ACTIVE : ECHO_CLOSE_F_PASSIVE;
2079   else
2080     default_f_active =
2081       em->bytes_to_receive == 0 ? ECHO_CLOSE_F_PASSIVE : ECHO_CLOSE_F_ACTIVE;
2082   if (em->send_stream_disconnects == ECHO_CLOSE_F_INVALID)
2083     em->send_stream_disconnects = default_f_active;
2084   if (em->send_quic_disconnects == ECHO_CLOSE_F_INVALID)
2085     em->send_quic_disconnects = default_f_active;
2086 }
2087
2088 int
2089 main (int argc, char **argv)
2090 {
2091   echo_main_t *em = &echo_main;
2092   fifo_segment_main_t *sm = &em->segment_main;
2093   char *app_name;
2094   u32 n_clients, i;
2095
2096   clib_mem_init_thread_safe (0, 256 << 20);
2097   clib_memset (em, 0, sizeof (*em));
2098   em->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
2099   em->shared_segment_handles = hash_create (0, sizeof (uword));
2100   em->my_pid = getpid ();
2101   em->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0);
2102   em->use_sock_api = 1;
2103   em->fifo_size = 64 << 10;
2104   em->n_clients = 1;
2105   em->n_stream_clients = 1;
2106   em->max_test_msg = 50;
2107   em->time_to_stop = 0;
2108   em->i_am_master = 1;
2109   em->n_rx_threads = 4;
2110   em->test_return_packets = RETURN_PACKETS_NOTEST;
2111   em->timing_start_event = ECHO_EVT_FIRST_QCONNECT;
2112   em->timing_end_event = ECHO_EVT_LAST_BYTE;
2113   em->bytes_to_receive = ~0;    /* defaulted when we know if server/client */
2114   em->bytes_to_send = ~0;       /* defaulted when we know if server/client */
2115   em->rx_buf_size = 1 << 20;
2116   em->tx_buf_size = 1 << 20;
2117   em->data_source = ECHO_INVALID_DATA_SOURCE;
2118   em->uri = format (0, "%s%c", "quic://0.0.0.0/1234", 0);
2119   em->cb_vft = default_cb_vft;
2120   quic_echo_process_opts (argc, argv);
2121
2122   n_clients = em->n_clients * em->n_stream_clients;
2123   vec_validate (em->client_thread_handles, em->n_rx_threads - 1);
2124   vec_validate (em->thread_args, n_clients - 1);
2125   clib_time_init (&em->clib_time);
2126   init_error_string_table (em);
2127   fifo_segment_main_init (sm, HIGH_SEGMENT_BASEVA, 20);
2128   clib_spinlock_init (&em->segment_handles_lock);
2129   vec_validate (em->connect_test_data, em->tx_buf_size);
2130   for (i = 0; i < em->tx_buf_size; i++)
2131     em->connect_test_data[i] = i & 0xff;
2132
2133   setup_signal_handlers ();
2134   quic_echo_api_hookup (em);
2135
2136   app_name = em->i_am_master ? "quic_echo_server" : "quic_echo_client";
2137   if (connect_to_vpp (app_name) < 0)
2138     {
2139       svm_region_exit ();
2140       ECHO_FAIL ("ECHO-ERROR: Couldn't connect to vpe, exiting...\n");
2141       exit (1);
2142     }
2143
2144   quic_echo_notify_event (em, ECHO_EVT_START);
2145
2146   application_send_attach (em);
2147   if (wait_for_state_change (em, STATE_ATTACHED, TIMEOUT))
2148     {
2149       ECHO_FAIL ("ECHO-ERROR: Couldn't attach to vpp, exiting...\n");
2150       exit (1);
2151     }
2152   if (em->i_am_master)
2153     server_run (em);
2154   else
2155     clients_run (em);
2156   quic_echo_notify_event (em, ECHO_EVT_EXIT);
2157   if (em->output_json)
2158     print_global_json_stats (em);
2159   else
2160     print_global_stats (em);
2161   echo_free_sessions (em);
2162   echo_assert_test_suceeded (em);
2163   application_detach (em);
2164   if (wait_for_state_change (em, STATE_DETACHED, TIMEOUT))
2165     {
2166       ECHO_FAIL ("ECHO-ERROR: Couldn't detach from vpp, exiting...\n");
2167       exit (1);
2168     }
2169   if (em->use_sock_api)
2170     vl_socket_client_disconnect ();
2171   else
2172     vl_client_disconnect_from_vlib ();
2173   ECHO_LOG (0, "Test complete !\n");
2174   exit (em->has_failed);
2175 }
2176
2177 /*
2178  * fd.io coding-style-patch-verification: ON
2179  *
2180  * Local Variables:
2181  * eval: (c-set-style "gnu")
2182  * End:
2183  */