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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <vnet/session/application_interface.h>
20 #include <vlibmemory/api.h>
22 #include <vpp/api/vpe_msg_enum.h>
23 #include <svm/fifo_segment.h>
25 #define vl_typedefs /* define message structures */
26 #include <vpp/api/vpe_all_api_h.h>
29 /* declare message handlers for each api */
31 #define vl_endianfun /* define message structures */
32 #include <vpp/api/vpe_all_api_h.h>
35 /* instantiate all the print functions we know about */
36 #define vl_print(handle, ...)
38 #include <vpp/api/vpe_all_api_h.h>
41 #define QUIC_ECHO_DBG 0
42 #define DBG(_fmt, _args...) \
44 clib_warning (_fmt, ##_args)
48 CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
49 #define _(type, name) type name;
50 foreach_app_session_field
52 u64 vpp_session_handle;
55 volatile u64 bytes_received;
56 volatile u64 bytes_to_receive;
71 enum quic_session_type_t
73 QUIC_SESSION_TYPE_QUIC = 0,
74 QUIC_SESSION_TYPE_STREAM = 1,
75 QUIC_SESSION_TYPE_LISTEN = INT32_MAX,
81 svm_queue_t *vl_input_queue;
83 /* API client handle */
86 /* The URI we're playing with */
90 echo_session_t *sessions;
92 /* Hash table for disconnect processing */
93 uword *session_index_by_vpp_handles;
95 /* Hash table for shared segment_names */
96 uword *shared_segment_names;
97 clib_spinlock_t segment_names_lock;
99 /* intermediate rx buffer */
102 /* URI for slave's connect */
105 u32 connected_session_index;
109 /* drop all packets */
112 /* Our event queue */
113 svm_msg_q_t *our_event_queue;
119 /* For deadman timers */
120 clib_time_t clib_time;
122 /* State of the connection, shared between msg RX thread and main thread */
123 volatile connection_state_t state;
125 /* Signal variables */
126 volatile int time_to_stop;
127 volatile int time_to_print_stats;
129 u32 configured_segment_size;
131 /* VNET_API_ERROR_FOO -> "Foo" hash table */
132 uword *error_string_by_error_number;
134 u8 *connect_test_data;
135 pthread_t *client_thread_handles;
137 u32 client_bytes_received;
138 u8 test_return_packets;
146 volatile u32 n_clients_connected;
147 volatile u32 n_active_clients;
150 /** Flag that decides if socket, instead of svm, api is used to connect to
151 * vpp. If sock api is used, shm binary api is subsequently bootstrapped
152 * and all other messages are exchanged using shm IPC. */
156 fifo_segment_main_t segment_main;
159 echo_main_t echo_main;
164 #define NITER 4000000
168 format_api_error (u8 * s, va_list * args)
170 echo_main_t *em = &echo_main;
171 i32 error = va_arg (*args, u32);
174 p = hash_get (em->error_string_by_error_number, -error);
177 s = format (s, "%s", p[0]);
179 s = format (s, "%d", error);
184 init_error_string_table (echo_main_t * em)
186 em->error_string_by_error_number = hash_create (0, sizeof (uword));
188 #define _(n,v,s) hash_set (em->error_string_by_error_number, -v, s);
189 foreach_vnet_api_error;
192 hash_set (em->error_string_by_error_number, 99, "Misc");
195 static void handle_mq_event (session_event_t * e);
204 wait_for_segment_allocation (u64 segment_handle)
206 echo_main_t *em = &echo_main;
208 timeout = clib_time_now (&em->clib_time) + TIMEOUT;
209 uword *segment_present;
210 DBG ("ASKING for %lu", segment_handle);
211 while (clib_time_now (&em->clib_time) < timeout)
213 clib_spinlock_lock (&em->segment_names_lock);
214 segment_present = hash_get (em->shared_segment_names, segment_handle);
215 clib_spinlock_unlock (&em->segment_names_lock);
216 if (segment_present != 0)
218 if (em->time_to_stop == 1)
221 DBG ("timeout waiting for segment_allocation %lu", segment_handle);
226 wait_for_disconnected_sessions (echo_main_t * em)
229 timeout = clib_time_now (&em->clib_time) + TIMEOUT;
230 while (clib_time_now (&em->clib_time) < timeout)
232 if (hash_elts (em->session_index_by_vpp_handles) == 0)
235 DBG ("timeout waiting for disconnected_sessions");
240 wait_for_state_change (echo_main_t * em, connection_state_t state)
245 timeout = clib_time_now (&em->clib_time) + TIMEOUT;
247 while (clib_time_now (&em->clib_time) < timeout)
249 if (em->state == state)
251 if (em->state == STATE_FAILED)
253 if (em->time_to_stop == 1)
255 if (!em->our_event_queue || em->state < STATE_ATTACHED)
258 if (svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_NOWAIT, 0))
260 e = svm_msg_q_msg_data (em->our_event_queue, &msg);
262 svm_msg_q_free_msg (em->our_event_queue, &msg);
264 clib_warning ("timeout waiting for state %d", state);
269 application_send_attach (echo_main_t * em)
271 vl_api_application_attach_t *bmp;
272 vl_api_application_tls_cert_add_t *cert_mp;
273 vl_api_application_tls_key_add_t *key_mp;
275 bmp = vl_msg_api_alloc (sizeof (*bmp));
276 clib_memset (bmp, 0, sizeof (*bmp));
278 bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_ATTACH);
279 bmp->client_index = em->my_client_index;
280 bmp->context = ntohl (0xfeedface);
281 bmp->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_ACCEPT_REDIRECT;
282 bmp->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ADD_SEGMENT;
283 bmp->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 16;
284 bmp->options[APP_OPTIONS_RX_FIFO_SIZE] = em->fifo_size;
285 bmp->options[APP_OPTIONS_TX_FIFO_SIZE] = em->fifo_size;
286 bmp->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = 128 << 20;
287 bmp->options[APP_OPTIONS_SEGMENT_SIZE] = 256 << 20;
288 bmp->options[APP_OPTIONS_EVT_QUEUE_SIZE] = 256;
289 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
291 cert_mp = vl_msg_api_alloc (sizeof (*cert_mp) + test_srv_crt_rsa_len);
292 clib_memset (cert_mp, 0, sizeof (*cert_mp));
293 cert_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_CERT_ADD);
294 cert_mp->client_index = em->my_client_index;
295 cert_mp->context = ntohl (0xfeedface);
296 cert_mp->cert_len = clib_host_to_net_u16 (test_srv_crt_rsa_len);
297 clib_memcpy_fast (cert_mp->cert, test_srv_crt_rsa, test_srv_crt_rsa_len);
298 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & cert_mp);
300 key_mp = vl_msg_api_alloc (sizeof (*key_mp) + test_srv_key_rsa_len);
301 clib_memset (key_mp, 0, sizeof (*key_mp) + test_srv_key_rsa_len);
302 key_mp->_vl_msg_id = ntohs (VL_API_APPLICATION_TLS_KEY_ADD);
303 key_mp->client_index = em->my_client_index;
304 key_mp->context = ntohl (0xfeedface);
305 key_mp->key_len = clib_host_to_net_u16 (test_srv_key_rsa_len);
306 clib_memcpy_fast (key_mp->key, test_srv_key_rsa, test_srv_key_rsa_len);
307 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & key_mp);
311 application_attach (echo_main_t * em)
313 application_send_attach (em);
314 if (wait_for_state_change (em, STATE_ATTACHED))
316 clib_warning ("timeout waiting for STATE_ATTACHED");
323 application_detach (echo_main_t * em)
325 vl_api_application_detach_t *bmp;
326 bmp = vl_msg_api_alloc (sizeof (*bmp));
327 clib_memset (bmp, 0, sizeof (*bmp));
329 bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
330 bmp->client_index = em->my_client_index;
331 bmp->context = ntohl (0xfeedface);
332 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
334 DBG ("%s", "Sent detach");
338 ssvm_segment_attach (char *name, ssvm_segment_type_t type, int fd)
340 fifo_segment_create_args_t _a, *a = &_a;
341 fifo_segment_main_t *sm = &echo_main.segment_main;
344 clib_memset (a, 0, sizeof (*a));
345 a->segment_name = (char *) name;
346 a->segment_type = type;
348 if (type == SSVM_SEGMENT_MEMFD)
351 if ((rv = fifo_segment_attach (sm, a)))
353 clib_warning ("svm_fifo_segment_attach ('%s') failed", name);
356 vec_reset_length (a->new_segment_indices);
361 vl_api_application_attach_reply_t_handler (vl_api_application_attach_reply_t *
364 echo_main_t *em = &echo_main;
368 segment_handle = clib_net_to_host_u64 (mp->segment_handle);
369 DBG ("Attached returned app %u", htons (mp->app_index));
373 clib_warning ("attach failed: %U", format_api_error,
374 clib_net_to_host_u32 (mp->retval));
378 if (mp->segment_name_length == 0)
380 clib_warning ("segment_name_length zero");
384 ASSERT (mp->app_event_queue_address);
385 em->our_event_queue = uword_to_pointer (mp->app_event_queue_address,
390 vec_validate (fds, mp->n_fds);
391 vl_socket_client_recv_fd_msg (fds, mp->n_fds, 5);
393 if (mp->fd_flags & SESSION_FD_F_VPP_MQ_SEGMENT)
394 if (ssvm_segment_attach (0, SSVM_SEGMENT_MEMFD, fds[n_fds++]))
397 if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
398 if (ssvm_segment_attach ((char *) mp->segment_name,
399 SSVM_SEGMENT_MEMFD, fds[n_fds++]))
402 if (mp->fd_flags & SESSION_FD_F_MQ_EVENTFD)
403 svm_msg_q_set_consumer_eventfd (em->our_event_queue, fds[n_fds++]);
409 if (ssvm_segment_attach ((char *) mp->segment_name, SSVM_SEGMENT_SHM,
413 DBG ("SETTING for %lu", segment_handle);
414 clib_spinlock_lock (&em->segment_names_lock);
415 hash_set (em->shared_segment_names, segment_handle, 1);
416 clib_spinlock_unlock (&em->segment_names_lock);
418 em->state = STATE_ATTACHED;
421 em->state = STATE_FAILED;
426 vl_api_application_detach_reply_t_handler (vl_api_application_detach_reply_t *
430 clib_warning ("detach returned with err: %d", mp->retval);
431 echo_main.state = STATE_DETACHED;
435 stop_signal (int signum)
437 echo_main_t *um = &echo_main;
438 um->time_to_stop = 1;
442 stats_signal (int signum)
444 echo_main_t *um = &echo_main;
445 um->time_to_print_stats = 1;
448 static clib_error_t *
449 setup_signal_handlers (void)
451 signal (SIGUSR2, stats_signal);
452 signal (SIGINT, stop_signal);
453 signal (SIGQUIT, stop_signal);
454 signal (SIGTERM, stop_signal);
459 vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...)
461 clib_warning ("BUG");
465 connect_to_vpp (char *name)
467 echo_main_t *em = &echo_main;
468 api_main_t *am = &api_main;
470 if (em->use_sock_api)
472 if (vl_socket_client_connect ((char *) em->socket_name, name,
473 0 /* default rx, tx buffer */ ))
475 clib_warning ("socket connect failed");
479 if (vl_socket_client_init_shm (0, 1 /* want_pthread */ ))
481 clib_warning ("init shm api failed");
487 if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
489 clib_warning ("shmem connect failed");
493 em->vl_input_queue = am->shmem_hdr->vl_input_queue;
494 em->my_client_index = am->my_client_index;
499 disconnect_from_vpp (echo_main_t * em)
501 if (em->use_sock_api)
502 vl_socket_client_disconnect ();
504 vl_client_disconnect_from_vlib ();
508 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
510 fifo_segment_main_t *sm = &echo_main.segment_main;
511 fifo_segment_create_args_t _a, *a = &_a;
512 echo_main_t *em = &echo_main;
516 if (mp->fd_flags & SESSION_FD_F_MEMFD_SEGMENT)
518 vec_validate (fds, 1);
519 vl_socket_client_recv_fd_msg (fds, 1, 5);
520 if (ssvm_segment_attach
521 ((char *) mp->segment_name, SSVM_SEGMENT_MEMFD, fds[0]))
523 ("svm_fifo_segment_attach ('%s') failed on SSVM_SEGMENT_MEMFD",
525 DBG ("SETTING for %lu", mp->segment_name);
526 clib_spinlock_lock (&em->segment_names_lock);
527 hash_set (em->shared_segment_names, mp->segment_name, 1);
528 clib_spinlock_unlock (&em->segment_names_lock);
533 clib_memset (a, 0, sizeof (*a));
534 a->segment_name = (char *) mp->segment_name;
535 a->segment_size = mp->segment_size;
536 /* Attach to the segment vpp created */
537 rv = fifo_segment_attach (sm, a);
540 clib_warning ("svm_fifo_segment_attach ('%s') failed",
544 clib_warning ("Mapped new segment '%s' size %d", mp->segment_name,
546 clib_spinlock_lock (&em->segment_names_lock);
547 hash_set (em->shared_segment_names, mp->segment_name, 1);
548 clib_spinlock_unlock (&em->segment_names_lock);
552 session_print_stats (echo_main_t * em, echo_session_t * session)
557 deltat = clib_time_now (&em->clib_time) - session->start;
558 bytes = em->i_am_master ? session->bytes_received : em->bytes_to_send;
559 fformat (stdout, "Finished in %.6f\n", deltat);
560 fformat (stdout, "%.4f Gbit/second\n", (bytes * 8.0) / deltat / 1e9);
564 test_recv_bytes (echo_main_t * em, echo_session_t * s, u8 * rx_buf,
568 for (i = 0; i < n_read; i++)
570 if (rx_buf[i] != ((s->bytes_received + i) & 0xff)
571 && em->max_test_msg > 0)
573 clib_warning ("error at byte %lld, 0x%x not 0x%x",
574 s->bytes_received + i, rx_buf[i],
575 ((s->bytes_received + i) & 0xff));
577 if (em->max_test_msg == 0)
578 clib_warning ("Too many errors, hiding next ones");
584 recv_data_chunk (echo_main_t * em, echo_session_t * s, u8 * rx_buf)
586 int n_to_read, n_read;
588 n_to_read = svm_fifo_max_dequeue (s->rx_fifo);
594 n_read = app_recv_stream ((app_session_t *) s, rx_buf,
599 if (em->test_return_packets)
600 test_recv_bytes (em, s, rx_buf, n_read);
604 s->bytes_received += n_read;
605 s->bytes_to_receive -= n_read;
606 ASSERT (s->bytes_to_receive >= 0);
611 while (n_to_read > 0);
615 send_data_chunk (echo_main_t * em, echo_session_t * s)
617 u64 test_buf_len, bytes_this_chunk, test_buf_offset;
618 u8 *test_data = em->connect_test_data;
621 test_buf_len = vec_len (test_data);
622 test_buf_offset = s->bytes_sent % test_buf_len;
623 bytes_this_chunk = clib_min (test_buf_len - test_buf_offset,
626 n_sent = app_send_stream ((app_session_t *) s, test_data + test_buf_offset,
627 bytes_this_chunk, 0);
631 s->bytes_to_send -= n_sent;
632 s->bytes_sent += n_sent;
637 * Rx/Tx polling thread per connection
640 client_thread_fn (void *arg)
642 echo_main_t *em = &echo_main;
643 static u8 *rx_buf = 0;
644 u32 session_index = *(u32 *) arg;
647 vec_validate (rx_buf, 1 << 20);
649 while (!em->time_to_stop && em->state != STATE_READY)
652 s = pool_elt_at_index (em->sessions, session_index);
653 while (!em->time_to_stop)
655 send_data_chunk (em, s);
656 recv_data_chunk (em, s, rx_buf);
657 if (!s->bytes_to_send && !s->bytes_to_receive)
661 DBG ("session %d done send %lu to do, %lu done || recv %lu to do, %lu done",
662 session_index, s->bytes_to_send, s->bytes_sent, s->bytes_to_receive,
664 em->tx_total += s->bytes_sent;
665 em->rx_total += s->bytes_received;
666 em->n_active_clients--;
672 client_send_connect (echo_main_t * em, u8 * uri, u32 opaque)
674 vl_api_connect_uri_t *cmp;
675 cmp = vl_msg_api_alloc (sizeof (*cmp));
676 clib_memset (cmp, 0, sizeof (*cmp));
678 cmp->_vl_msg_id = ntohs (VL_API_CONNECT_URI);
679 cmp->client_index = em->my_client_index;
680 cmp->context = ntohl (opaque);
681 memcpy (cmp->uri, uri, vec_len (uri));
682 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & cmp);
686 client_send_disconnect (echo_main_t * em, echo_session_t * s)
688 vl_api_disconnect_session_t *dmp;
689 dmp = vl_msg_api_alloc (sizeof (*dmp));
690 clib_memset (dmp, 0, sizeof (*dmp));
691 dmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION);
692 dmp->client_index = em->my_client_index;
693 dmp->handle = s->vpp_session_handle;
694 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & dmp);
698 client_disconnect (echo_main_t * em, echo_session_t * s)
700 client_send_disconnect (em, s);
701 pool_put (em->sessions, s);
702 clib_memset (s, 0xfe, sizeof (*s));
707 session_bound_handler (session_bound_msg_t * mp)
709 echo_main_t *em = &echo_main;
713 clib_warning ("bind failed: %U", format_api_error,
714 clib_net_to_host_u32 (mp->retval));
715 em->state = STATE_FAILED;
719 clib_warning ("listening on %U:%u", format_ip46_address, mp->lcl_ip,
720 mp->lcl_is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
721 clib_net_to_host_u16 (mp->lcl_port));
722 em->state = STATE_READY;
726 quic_qsession_accepted_handler (session_accepted_msg_t * mp)
728 DBG ("Accept on QSession index %u", mp->handle);
733 session_accepted_handler (session_accepted_msg_t * mp)
735 app_session_evt_t _app_evt, *app_evt = &_app_evt;
736 session_accepted_reply_msg_t *rmp;
737 svm_fifo_t *rx_fifo, *tx_fifo;
738 echo_main_t *em = &echo_main;
739 echo_session_t *session;
740 static f64 start_time;
745 segment_handle = mp->segment_handle;
747 if (start_time == 0.0)
748 start_time = clib_time_now (&em->clib_time);
750 ip_str = format (0, "%U", format_ip46_address, &mp->rmt.ip, mp->rmt.is_ip4);
751 clib_warning ("Accepted session from: %s:%d", ip_str,
752 clib_net_to_host_u16 (mp->rmt.port));
754 /* Allocate local session and set it up */
755 pool_get (em->sessions, session);
756 session_index = session - em->sessions;
757 DBG ("Setting session_index %lu", session_index);
759 if (wait_for_segment_allocation (segment_handle))
761 clib_warning ("timeout waiting for segment allocation %lu",
765 rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
766 rx_fifo->client_session_index = session_index;
767 tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
768 tx_fifo->client_session_index = session_index;
770 session->rx_fifo = rx_fifo;
771 session->tx_fifo = tx_fifo;
772 session->vpp_session_handle = mp->handle;
773 session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
776 /* Add it to lookup table */
777 hash_set (em->session_index_by_vpp_handles, mp->handle, session_index);
780 * Send accept reply to vpp
782 app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
783 SESSION_CTRL_EVT_ACCEPTED_REPLY);
784 rmp = (session_accepted_reply_msg_t *) app_evt->evt->data;
785 rmp->handle = mp->handle;
786 rmp->context = mp->context;
787 app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
789 /* TODO : this is very ugly */
790 if (mp->rmt.is_ip4 != 255)
791 return quic_qsession_accepted_handler (mp);
792 DBG ("SSession handle is %lu", mp->handle);
794 em->state = STATE_READY;
797 if (pool_elts (em->sessions) && (pool_elts (em->sessions) % 20000) == 0)
799 f64 now = clib_time_now (&em->clib_time);
800 fformat (stdout, "%d active sessions in %.2f seconds, %.2f/sec...\n",
801 pool_elts (em->sessions), now - start_time,
802 (f64) pool_elts (em->sessions) / (now - start_time));
805 session->bytes_received = 0;
806 session->start = clib_time_now (&em->clib_time);
810 quic_session_connected_handler (session_connected_msg_t * mp)
812 echo_main_t *em = &echo_main;
813 u8 *uri = format (0, "QUIC://session/%lu", mp->handle);
814 DBG ("QSession Connect : %s", uri);
815 client_send_connect (em, uri, QUIC_SESSION_TYPE_STREAM);
819 session_connected_handler (session_connected_msg_t * mp)
821 echo_main_t *em = &echo_main;
822 echo_session_t *session;
824 svm_fifo_t *rx_fifo, *tx_fifo;
827 segment_handle = mp->segment_handle;
831 clib_warning ("connection failed with code: %U", format_api_error,
832 clib_net_to_host_u32 (mp->retval));
833 em->state = STATE_FAILED;
841 pool_get (em->sessions, session);
842 clib_memset (session, 0, sizeof (*session));
843 session_index = session - em->sessions;
844 DBG ("Setting session_index %lu", session_index);
846 if (wait_for_segment_allocation (segment_handle))
848 clib_warning ("timeout waiting for segment allocation %lu",
852 rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
853 rx_fifo->client_session_index = session_index;
854 tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
855 tx_fifo->client_session_index = session_index;
857 session->rx_fifo = rx_fifo;
858 session->tx_fifo = tx_fifo;
859 session->vpp_session_handle = mp->handle;
860 session->start = clib_time_now (&em->clib_time);
861 session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
864 hash_set (em->session_index_by_vpp_handles, mp->handle, session_index);
866 if (mp->context == QUIC_SESSION_TYPE_QUIC)
867 return quic_session_connected_handler (mp);
869 DBG ("SSession Connected");
874 em->thread_args[em->n_clients_connected] = session_index;
875 rv = pthread_create (&em->client_thread_handles[em->n_clients_connected],
876 NULL /*attr */ , client_thread_fn,
877 (void *) &em->thread_args[em->n_clients_connected]);
880 clib_warning ("pthread_create returned %d", rv);
884 em->n_clients_connected += 1;
885 clib_warning ("session %u (0x%llx) connected with local ip %U port %d",
886 session_index, mp->handle, format_ip46_address, &mp->lcl.ip,
887 mp->lcl.is_ip4, clib_net_to_host_u16 (mp->lcl.port));
891 session_disconnected_handler (session_disconnected_msg_t * mp)
893 app_session_evt_t _app_evt, *app_evt = &_app_evt;
894 session_disconnected_reply_msg_t *rmp;
895 echo_main_t *em = &echo_main;
896 echo_session_t *session = 0;
899 DBG ("Got a SESSION_CTRL_EVT_DISCONNECTED for session %lu", mp->handle);
901 p = hash_get (em->session_index_by_vpp_handles, mp->handle);
904 clib_warning ("couldn't find session key %llx", mp->handle);
908 session = pool_elt_at_index (em->sessions, p[0]);
909 hash_unset (em->session_index_by_vpp_handles, mp->handle);
911 pool_put (em->sessions, session);
913 app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
914 SESSION_CTRL_EVT_DISCONNECTED_REPLY);
915 rmp = (session_disconnected_reply_msg_t *) app_evt->evt->data;
917 rmp->handle = mp->handle;
918 rmp->context = mp->context;
919 app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
921 session_print_stats (em, session);
925 session_reset_handler (session_reset_msg_t * mp)
927 app_session_evt_t _app_evt, *app_evt = &_app_evt;
928 echo_main_t *em = &echo_main;
929 session_reset_reply_msg_t *rmp;
930 echo_session_t *session = 0;
934 p = hash_get (em->session_index_by_vpp_handles, mp->handle);
938 session = pool_elt_at_index (em->sessions, p[0]);
939 clib_warning ("got reset");
941 em->time_to_stop = 1;
945 clib_warning ("couldn't find session key %llx", mp->handle);
949 app_alloc_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt,
950 SESSION_CTRL_EVT_RESET_REPLY);
951 rmp = (session_reset_reply_msg_t *) app_evt->evt->data;
953 rmp->handle = mp->handle;
954 app_send_ctrl_evt_to_vpp (session->vpp_evt_q, app_evt);
958 handle_mq_event (session_event_t * e)
960 switch (e->event_type)
962 case SESSION_CTRL_EVT_BOUND:
963 DBG ("SESSION_CTRL_EVT_BOUND");
964 session_bound_handler ((session_bound_msg_t *) e->data);
966 case SESSION_CTRL_EVT_ACCEPTED:
967 DBG ("SESSION_CTRL_EVT_ACCEPTED");
968 session_accepted_handler ((session_accepted_msg_t *) e->data);
970 case SESSION_CTRL_EVT_CONNECTED:
971 DBG ("SESSION_CTRL_EVT_CONNECTED");
972 session_connected_handler ((session_connected_msg_t *) e->data);
974 case SESSION_CTRL_EVT_DISCONNECTED:
975 DBG ("SESSION_CTRL_EVT_DISCONNECTED");
976 session_disconnected_handler ((session_disconnected_msg_t *) e->data);
978 case SESSION_CTRL_EVT_RESET:
979 DBG ("SESSION_CTRL_EVT_RESET");
980 session_reset_handler ((session_reset_msg_t *) e->data);
983 clib_warning ("unhandled %u", e->event_type);
988 clients_run (echo_main_t * em)
990 f64 start_time, deltat, timeout = 100.0;
998 vec_validate (em->connect_test_data, 1024 * 1024 - 1);
999 for (i = 0; i < vec_len (em->connect_test_data); i++)
1000 em->connect_test_data[i] = i & 0xff;
1003 * Attach and connect the clients
1005 if (application_attach (em))
1008 for (i = 0; i < em->n_clients; i++)
1009 client_send_connect (em, em->connect_uri, QUIC_SESSION_TYPE_QUIC);
1011 start_time = clib_time_now (&em->clib_time);
1012 while (em->n_clients_connected < em->n_clients
1013 && (clib_time_now (&em->clib_time) - start_time < timeout)
1014 && em->state != STATE_FAILED && em->time_to_stop != 1)
1017 int rc = svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_TIMEDWAIT, 1);
1018 if (rc == ETIMEDOUT && em->time_to_stop)
1020 if (rc == ETIMEDOUT)
1022 e = svm_msg_q_msg_data (em->our_event_queue, &msg);
1023 handle_mq_event (e);
1024 svm_msg_q_free_msg (em->our_event_queue, &msg);
1027 if (em->n_clients_connected != em->n_clients)
1029 clib_warning ("failed to initialize all connections");
1034 * Initialize connections
1036 DBG ("Initialize connections on %u clients", em->n_clients);
1039 hash_foreach_pair (p, em->session_index_by_vpp_handles,
1041 s = pool_elt_at_index (em->sessions, p->value[0]);
1042 s->bytes_to_send = em->bytes_to_send;
1044 s->bytes_to_receive = em->bytes_to_send;
1047 em->n_active_clients = em->n_clients_connected;
1050 * Wait for client threads to send the data
1052 DBG ("Waiting for data on %u clients", em->n_active_clients);
1053 start_time = clib_time_now (&em->clib_time);
1054 em->state = STATE_READY;
1055 while (em->n_active_clients)
1056 if (!svm_msg_q_is_empty (em->our_event_queue))
1058 if (svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_TIMEDWAIT, 0))
1060 clib_warning ("svm msg q returned");
1063 e = svm_msg_q_msg_data (em->our_event_queue, &msg);
1064 if (e->event_type != FIFO_EVENT_APP_RX)
1065 handle_mq_event (e);
1066 svm_msg_q_free_msg (em->our_event_queue, &msg);
1070 hash_foreach_pair (p, em->session_index_by_vpp_handles,
1072 s = pool_elt_at_index (em->sessions, p->value[0]);
1073 DBG ("Sending disconnect on session %lu", p->key);
1074 client_disconnect (em, s);
1081 deltat = clib_time_now (&em->clib_time) - start_time;
1082 fformat (stdout, "%lld bytes (%lld mbytes, %lld gbytes) in %.2f seconds\n",
1083 em->tx_total, em->tx_total / (1ULL << 20),
1084 em->tx_total / (1ULL << 30), deltat);
1085 fformat (stdout, "%.4f Gbit/second\n", (em->tx_total * 8.0) / deltat / 1e9);
1087 wait_for_disconnected_sessions (em);
1088 application_detach (em);
1092 vl_api_bind_uri_reply_t_handler (vl_api_bind_uri_reply_t * mp)
1094 echo_main_t *em = &echo_main;
1098 clib_warning ("bind failed: %U", format_api_error,
1099 clib_net_to_host_u32 (mp->retval));
1100 em->state = STATE_FAILED;
1104 em->state = STATE_READY;
1108 vl_api_unbind_uri_reply_t_handler (vl_api_unbind_uri_reply_t * mp)
1110 echo_main_t *em = &echo_main;
1112 if (mp->retval != 0)
1113 clib_warning ("returned %d", ntohl (mp->retval));
1115 em->state = STATE_START;
1119 format_ip4_address (u8 * s, va_list * args)
1121 u8 *a = va_arg (*args, u8 *);
1122 return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
1126 format_ip6_address (u8 * s, va_list * args)
1128 ip6_address_t *a = va_arg (*args, ip6_address_t *);
1129 u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
1131 i_max_n_zero = ARRAY_LEN (a->as_u16);
1133 i_first_zero = i_max_n_zero;
1135 for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
1137 u32 is_zero = a->as_u16[i] == 0;
1138 if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
1144 if ((!is_zero && n_zeros > max_n_zeros)
1145 || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
1147 i_max_n_zero = i_first_zero;
1148 max_n_zeros = n_zeros;
1149 i_first_zero = ARRAY_LEN (a->as_u16);
1154 last_double_colon = 0;
1155 for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
1157 if (i == i_max_n_zero && max_n_zeros > 1)
1159 s = format (s, "::");
1160 i += max_n_zeros - 1;
1161 last_double_colon = 1;
1165 s = format (s, "%s%x",
1166 (last_double_colon || i == 0) ? "" : ":",
1167 clib_net_to_host_u16 (a->as_u16[i]));
1168 last_double_colon = 0;
1175 /* Format an IP46 address. */
1177 format_ip46_address (u8 * s, va_list * args)
1179 ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
1180 ip46_type_t type = va_arg (*args, ip46_type_t);
1186 is_ip4 = ip46_address_is_ip4 (ip46);
1197 format (s, "%U", format_ip4_address, &ip46->ip4) :
1198 format (s, "%U", format_ip6_address, &ip46->ip6);
1202 server_handle_rx (echo_main_t * em, session_event_t * e)
1204 int n_read, max_dequeue, n_sent;
1205 u32 offset, to_dequeue;
1207 s = pool_elt_at_index (em->sessions, e->session_index);
1209 /* Clear event only once. Otherwise, if we do it in the loop by calling
1210 * app_recv_stream, we may end up with a lot of unhandled rx events on the
1212 svm_fifo_unset_event (s->rx_fifo);
1214 max_dequeue = svm_fifo_max_dequeue (s->rx_fifo);
1215 if (PREDICT_FALSE (!max_dequeue))
1219 /* The options here are to limit ourselves to max_dequeue or read
1220 * even the data that was enqueued while we were dequeueing and which
1221 * now has an rx event in the mq. Either of the two work. */
1222 to_dequeue = clib_min (max_dequeue, vec_len (em->rx_buf));
1223 n_read = app_recv_stream_raw (s->rx_fifo, em->rx_buf, to_dequeue,
1224 0 /* clear evt */ , 0 /* peek */ );
1228 if (em->test_return_packets)
1229 test_recv_bytes (em, s, em->rx_buf, n_read);
1231 max_dequeue -= n_read;
1232 s->bytes_received += n_read;
1237 /* Reflect if a non-drop session */
1238 if (!em->no_return && n_read > 0)
1243 n_sent = app_send_stream ((app_session_t *) s,
1244 &em->rx_buf[offset],
1245 n_read, SVM_Q_WAIT);
1252 while ((n_sent <= 0 || n_read > 0) && !em->time_to_stop);
1255 while (max_dequeue > 0 && !em->time_to_stop);
1259 server_handle_mq (echo_main_t * em)
1261 svm_msg_q_msg_t msg;
1266 int rc = svm_msg_q_sub (em->our_event_queue, &msg, SVM_Q_TIMEDWAIT, 1);
1267 if (PREDICT_FALSE (rc == ETIMEDOUT && em->time_to_stop))
1269 if (PREDICT_FALSE (em->time_to_print_stats == 1))
1271 em->time_to_print_stats = 0;
1272 fformat (stdout, "%d connections\n", pool_elts (em->sessions));
1274 if (rc == ETIMEDOUT)
1276 e = svm_msg_q_msg_data (em->our_event_queue, &msg);
1277 switch (e->event_type)
1279 case SESSION_IO_EVT_RX:
1280 DBG ("SESSION_IO_EVT_RX");
1281 server_handle_rx (em, e);
1284 handle_mq_event (e);
1287 svm_msg_q_free_msg (em->our_event_queue, &msg);
1292 server_send_listen (echo_main_t * em)
1294 vl_api_bind_uri_t *bmp;
1295 bmp = vl_msg_api_alloc (sizeof (*bmp));
1296 clib_memset (bmp, 0, sizeof (*bmp));
1298 bmp->_vl_msg_id = ntohs (VL_API_BIND_URI);
1299 bmp->client_index = em->my_client_index;
1300 bmp->context = ntohl (0xfeedface);
1301 memcpy (bmp->uri, em->uri, vec_len (em->uri));
1302 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & bmp);
1306 server_listen (echo_main_t * em)
1308 server_send_listen (em);
1309 if (wait_for_state_change (em, STATE_READY))
1311 clib_warning ("timeout waiting for STATE_READY");
1318 server_send_unbind (echo_main_t * em)
1320 vl_api_unbind_uri_t *ump;
1322 ump = vl_msg_api_alloc (sizeof (*ump));
1323 clib_memset (ump, 0, sizeof (*ump));
1325 ump->_vl_msg_id = ntohs (VL_API_UNBIND_URI);
1326 ump->client_index = em->my_client_index;
1327 memcpy (ump->uri, em->uri, vec_len (em->uri));
1328 vl_msg_api_send_shmem (em->vl_input_queue, (u8 *) & ump);
1332 server_run (echo_main_t * em)
1334 echo_session_t *session;
1337 /* $$$$ hack preallocation */
1338 for (i = 0; i < 200000; i++)
1340 pool_get (em->sessions, session);
1341 clib_memset (session, 0, sizeof (*session));
1343 for (i = 0; i < 200000; i++)
1344 pool_put_index (em->sessions, i);
1346 if (application_attach (em))
1350 if (server_listen (em))
1353 /* Enter handle event loop */
1354 server_handle_mq (em);
1357 server_send_unbind (em);
1359 application_detach (em);
1361 fformat (stdout, "Test complete...\n");
1365 vl_api_disconnect_session_reply_t_handler (vl_api_disconnect_session_reply_t *
1368 echo_main_t *em = &echo_main;
1370 DBG ("Got disonnected reply for session %lu", mp->handle);
1374 clib_warning ("vpp complained about disconnect: %d",
1375 ntohl (mp->retval));
1379 em->state = STATE_START;
1381 p = hash_get (em->session_index_by_vpp_handles, mp->handle);
1384 hash_unset (em->session_index_by_vpp_handles, mp->handle);
1388 clib_warning ("couldn't find session key %llx", mp->handle);
1393 vl_api_application_tls_cert_add_reply_t_handler
1394 (vl_api_application_tls_cert_add_reply_t * mp)
1397 clib_warning ("failed to add tls cert");
1401 vl_api_application_tls_key_add_reply_t_handler
1402 (vl_api_application_tls_key_add_reply_t * mp)
1405 clib_warning ("failed to add tls key");
1408 #define foreach_quic_echo_msg \
1409 _(BIND_URI_REPLY, bind_uri_reply) \
1410 _(UNBIND_URI_REPLY, unbind_uri_reply) \
1411 _(DISCONNECT_SESSION_REPLY, disconnect_session_reply) \
1412 _(APPLICATION_ATTACH_REPLY, application_attach_reply) \
1413 _(APPLICATION_DETACH_REPLY, application_detach_reply) \
1414 _(MAP_ANOTHER_SEGMENT, map_another_segment) \
1415 _(APPLICATION_TLS_CERT_ADD_REPLY, application_tls_cert_add_reply) \
1416 _(APPLICATION_TLS_KEY_ADD_REPLY, application_tls_key_add_reply) \
1419 quic_echo_api_hookup (echo_main_t * em)
1422 vl_msg_api_set_handlers(VL_API_##N, #n, \
1423 vl_api_##n##_t_handler, \
1425 vl_api_##n##_t_endian, \
1426 vl_api_##n##_t_print, \
1427 sizeof(vl_api_##n##_t), 1);
1428 foreach_quic_echo_msg;
1433 main (int argc, char **argv)
1435 int i_am_server = 1, test_return_packets = 0;
1436 echo_main_t *em = &echo_main;
1437 fifo_segment_main_t *sm = &em->segment_main;
1438 unformat_input_t _argv, *a = &_argv;
1441 u8 *bind_uri = (u8 *) "quic://0.0.0.0/1234";
1442 u8 *connect_uri = (u8 *) "quic://6.0.1.1/1234";
1443 u64 bytes_to_send = 64 << 10, mbytes;
1447 clib_mem_init_thread_safe (0, 256 << 20);
1449 clib_memset (em, 0, sizeof (*em));
1450 em->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
1451 em->shared_segment_names = hash_create (0, sizeof (uword));
1452 clib_spinlock_init (&em->segment_names_lock);
1453 em->my_pid = getpid ();
1454 em->socket_name = 0;
1455 em->use_sock_api = 1;
1456 em->fifo_size = 64 << 10;
1458 em->max_test_msg = 50;
1460 clib_time_init (&em->clib_time);
1461 init_error_string_table (em);
1462 fifo_segment_main_init (sm, HIGH_SEGMENT_BASEVA, 20);
1463 unformat_init_command_line (a, argv);
1465 while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
1467 if (unformat (a, "chroot prefix %s", &chroot_prefix))
1469 vl_set_memory_root_path ((char *) chroot_prefix);
1471 else if (unformat (a, "uri %s", &uri))
1473 else if (unformat (a, "server"))
1475 else if (unformat (a, "client"))
1477 else if (unformat (a, "no-return"))
1479 else if (unformat (a, "test"))
1480 test_return_packets = 1;
1481 else if (unformat (a, "bytes %lld", &mbytes))
1483 bytes_to_send = mbytes;
1485 else if (unformat (a, "mbytes %lld", &mbytes))
1487 bytes_to_send = mbytes << 20;
1489 else if (unformat (a, "gbytes %lld", &mbytes))
1491 bytes_to_send = mbytes << 30;
1493 else if (unformat (a, "socket-name %s", &em->socket_name))
1495 else if (unformat (a, "use-svm-api"))
1496 em->use_sock_api = 0;
1497 else if (unformat (a, "fifo-size %d", &tmp))
1498 em->fifo_size = tmp << 10;
1499 else if (unformat (a, "nclients %d", &em->n_clients))
1503 fformat (stderr, "%s: usage [master|slave]\n", argv[0]);
1508 if (!em->socket_name)
1509 em->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0);
1513 em->uri = format (0, "%s%c", uri, 0);
1514 em->connect_uri = format (0, "%s%c", uri, 0);
1518 em->uri = format (0, "%s%c", bind_uri, 0);
1519 em->connect_uri = format (0, "%s%c", connect_uri, 0);
1522 em->i_am_master = i_am_server;
1523 em->test_return_packets = test_return_packets;
1524 em->bytes_to_send = bytes_to_send;
1525 em->time_to_stop = 0;
1526 vec_validate (em->rx_buf, 4 << 20);
1527 vec_validate (em->client_thread_handles, em->n_clients - 1);
1528 vec_validate (em->thread_args, em->n_clients - 1);
1530 setup_signal_handlers ();
1531 quic_echo_api_hookup (em);
1533 app_name = i_am_server ? "quic_echo_server" : "quic_echo_client";
1534 if (connect_to_vpp (app_name) < 0)
1537 fformat (stderr, "Couldn't connect to vpe, exiting...\n");
1541 if (i_am_server == 0)
1546 /* Make sure detach finishes */
1547 wait_for_state_change (em, STATE_DETACHED);
1549 disconnect_from_vpp (em);
1554 * fd.io coding-style-patch-verification: ON
1557 * eval: (c-set-style "gnu")