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 <hs_apps/sapi/vpp_echo_common.h>
21 typedef struct _quic_echo_cb_vft
23 void (*quic_connected_cb) (session_connected_msg_t * mp, u32 session_index);
24 void (*client_stream_connected_cb) (session_connected_msg_t * mp,
26 void (*server_stream_connected_cb) (session_connected_msg_t * mp,
28 void (*quic_accepted_cb) (session_accepted_msg_t * mp, u32 session_index);
29 void (*client_stream_accepted_cb) (session_accepted_msg_t * mp,
31 void (*server_stream_accepted_cb) (session_accepted_msg_t * mp,
37 quic_echo_cb_vft_t cb_vft; /* cb vft for QUIC scenarios */
38 u8 send_quic_disconnects; /* actively send disconnect */
39 u32 n_stream_clients; /* Target Number of STREAM sessions per QUIC session */
40 volatile u32 n_quic_clients_connected; /* Number of connected QUIC sessions */
41 } quic_echo_proto_main_t;
43 quic_echo_proto_main_t quic_echo_proto_main;
47 * ECHO Callback definitions
52 quic_echo_on_connected_connect (session_connected_msg_t * mp,
55 echo_main_t *em = &echo_main;
56 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
57 u8 *uri = format (0, "quic://session/%lu", mp->handle);
60 echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
61 for (i = 0; i < eqm->n_stream_clients; i++)
62 echo_send_rpc (em, echo_send_connect, (void *) uri, session_index);
64 ECHO_LOG (0, "Qsession 0x%llx connected to %U:%d",
65 mp->handle, format_ip46_address, &mp->lcl.ip,
66 mp->lcl.is_ip4, clib_net_to_host_u16 (mp->lcl.port));
70 quic_echo_on_connected_send (session_connected_msg_t * mp, u32 session_index)
72 static u32 client_index = 0;
73 echo_main_t *em = &echo_main;
74 echo_session_t *session;
76 session = pool_elt_at_index (em->sessions, session_index);
77 session->bytes_to_send = em->bytes_to_send;
78 session->bytes_to_receive = em->bytes_to_receive;
79 session->session_state = ECHO_SESSION_STATE_READY;
80 em->data_thread_args[client_index++] = session->session_index;
84 quic_echo_on_connected_error (session_connected_msg_t * mp, u32 session_index)
86 ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_CONNECT,
87 "Got a wrong connected on session %u [%lx]", session_index,
92 quic_echo_on_accept_recv (session_accepted_msg_t * mp, u32 session_index)
94 static u32 client_index = 0;
95 echo_main_t *em = &echo_main;
96 echo_session_t *session;
98 session = pool_elt_at_index (em->sessions, session_index);
99 session->bytes_to_send = em->bytes_to_send;
100 session->bytes_to_receive = em->bytes_to_receive;
101 em->data_thread_args[client_index++] = session->session_index;
102 session->session_state = ECHO_SESSION_STATE_READY;
106 quic_echo_on_accept_connect (session_accepted_msg_t * mp, u32 session_index)
108 echo_main_t *em = &echo_main;
109 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
110 ECHO_LOG (1, "Accept on QSession 0x%lx %u", mp->handle);
111 u8 *uri = format (0, "quic://session/%lu", mp->handle);
114 echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
115 for (i = 0; i < eqm->n_stream_clients; i++)
116 echo_send_rpc (em, echo_send_connect, (void *) uri, session_index);
120 quic_echo_on_accept_error (session_accepted_msg_t * mp, u32 session_index)
122 ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_ACCEPT,
123 "Got a wrong accept on session %u [%lx]", session_index,
128 quic_echo_on_accept_log_ip (session_accepted_msg_t * mp, u32 session_index)
131 ip_str = format (0, "%U", format_ip46_address, &mp->rmt.ip, mp->rmt.is_ip4);
132 ECHO_LOG (0, "Accepted session from: %s:%d", ip_str,
133 clib_net_to_host_u16 (mp->rmt.port));
137 static const quic_echo_cb_vft_t default_cb_vft = {
139 .quic_accepted_cb = quic_echo_on_accept_log_ip,
140 .quic_connected_cb = quic_echo_on_connected_connect,
141 /* client initiated streams */
142 .server_stream_accepted_cb = quic_echo_on_accept_recv,
143 .client_stream_connected_cb = quic_echo_on_connected_send,
144 /* server initiated streams */
145 .client_stream_accepted_cb = quic_echo_on_accept_error,
146 .server_stream_connected_cb = quic_echo_on_connected_error,
149 static const quic_echo_cb_vft_t server_stream_cb_vft = {
151 .quic_accepted_cb = quic_echo_on_accept_connect,
152 .quic_connected_cb = NULL,
153 /* client initiated streams */
154 .server_stream_accepted_cb = quic_echo_on_accept_error,
155 .client_stream_connected_cb = quic_echo_on_connected_error,
156 /* server initiated streams */
157 .client_stream_accepted_cb = quic_echo_on_accept_recv,
158 .server_stream_connected_cb = quic_echo_on_connected_send,
161 static void quic_echo_cleanup_cb (echo_session_t * s, u8 parent_died);
164 quic_echo_cleanup_listener (u32 listener_index, echo_main_t * em,
165 quic_echo_proto_main_t * eqm)
168 ls = pool_elt_at_index (em->sessions, listener_index);
169 ASSERT (ls->session_type == ECHO_SESSION_TYPE_QUIC);
170 if (!clib_atomic_sub_fetch (&ls->accepted_session_count, 1))
172 if (eqm->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
174 echo_send_rpc (em, echo_send_disconnect_session,
175 (void *) ls->vpp_session_handle, 0);
176 clib_atomic_fetch_add (&em->stats.active_count.q, 1);
178 else if (eqm->send_quic_disconnects == ECHO_CLOSE_F_NONE)
180 quic_echo_cleanup_cb (ls, 0 /* parent_died */ );
181 clib_atomic_fetch_add (&em->stats.clean_count.q, 1);
187 quic_echo_cleanup_cb (echo_session_t * s, u8 parent_died)
189 echo_main_t *em = &echo_main;
190 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
191 ASSERT (s->session_state < ECHO_SESSION_STATE_CLOSED);
192 if (s->session_type == ECHO_SESSION_TYPE_QUIC)
195 clib_atomic_fetch_add (&em->stats.clean_count.q, 1);
196 /* Don't cleanup listener as it's handled by main() */
197 clib_atomic_sub_fetch (&eqm->n_quic_clients_connected, 1);
199 else if (s->session_type == ECHO_SESSION_TYPE_STREAM)
202 clib_atomic_fetch_add (&em->stats.clean_count.s, 1);
204 quic_echo_cleanup_listener (s->listener_index, em, eqm);
205 clib_atomic_sub_fetch (&em->n_clients_connected, 1);
208 ECHO_LOG (1, "Cleanup sessions (still %uQ %uS)",
209 eqm->n_quic_clients_connected, em->n_clients_connected);
210 s->session_state = ECHO_SESSION_STATE_CLOSED;
211 if (!em->n_clients_connected && !eqm->n_quic_clients_connected)
212 em->state = STATE_DATA_DONE;
216 quic_echo_initiate_qsession_close_no_stream (echo_main_t * em)
218 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
219 ECHO_LOG (1, "Closing Qsessions");
220 /* Close Quic session without streams */
224 pool_foreach (s, em->sessions,
226 if (s->session_type == ECHO_SESSION_TYPE_QUIC)
228 if (eqm->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
230 ECHO_LOG (1,"ACTIVE close 0x%lx", s->vpp_session_handle);
231 echo_send_rpc (em, echo_send_disconnect_session, (void *) s->vpp_session_handle, 0);
232 clib_atomic_fetch_add (&em->stats.active_count.q, 1);
234 else if (eqm->send_quic_disconnects == ECHO_CLOSE_F_NONE)
236 ECHO_LOG (1,"Discard close 0x%lx", s->vpp_session_handle);
237 quic_echo_cleanup_cb (s, 0 /* parent_died */);
238 clib_atomic_fetch_add (&em->stats.clean_count.q, 1);
241 ECHO_LOG (1,"Passive close 0x%lx", s->vpp_session_handle);
248 quic_echo_on_connected (session_connected_msg_t * mp, u32 session_index)
250 echo_main_t *em = &echo_main;
251 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
252 echo_session_t *listen_session;
253 echo_session_t *session = pool_elt_at_index (em->sessions, session_index);
254 if (session->listener_index == SESSION_INVALID_INDEX)
256 ECHO_LOG (1, "Connected session 0x%lx -> URI", mp->handle);
257 session->session_type = ECHO_SESSION_TYPE_QUIC;
258 session->accepted_session_count = 0;
259 if (eqm->cb_vft.quic_connected_cb)
260 eqm->cb_vft.quic_connected_cb (mp, session->session_index);
261 clib_atomic_fetch_add (&eqm->n_quic_clients_connected, 1);
266 pool_elt_at_index (em->sessions, session->listener_index);
267 ECHO_LOG (1, "Connected session 0x%lx -> 0x%lx", mp->handle,
268 listen_session->vpp_session_handle);
269 session->session_type = ECHO_SESSION_TYPE_STREAM;
270 clib_atomic_fetch_add (&listen_session->accepted_session_count, 1);
271 if (em->i_am_master && eqm->cb_vft.server_stream_connected_cb)
272 eqm->cb_vft.server_stream_connected_cb (mp, session->session_index);
273 if (!em->i_am_master && eqm->cb_vft.client_stream_connected_cb)
274 eqm->cb_vft.client_stream_connected_cb (mp, session->session_index);
275 clib_atomic_fetch_add (&em->n_clients_connected, 1);
278 if (em->n_clients_connected == em->n_clients
279 && em->n_clients_connected != 0)
280 echo_notify_event (em, ECHO_EVT_LAST_SCONNECTED);
282 if (eqm->n_quic_clients_connected == em->n_connects
283 && em->state < STATE_READY)
285 echo_notify_event (em, ECHO_EVT_LAST_QCONNECTED);
286 em->state = STATE_READY;
287 if (eqm->n_stream_clients == 0)
288 quic_echo_initiate_qsession_close_no_stream (em);
293 quic_echo_retry_connect (u32 session_index)
296 echo_session_t *session;
297 echo_main_t *em = &echo_main;
299 if (session_index == SESSION_INVALID_INDEX)
301 ECHO_LOG (1, "Retrying connect %s", em->uri);
302 echo_send_rpc (em, echo_send_connect, (void *) em->uri,
303 SESSION_INVALID_INDEX);
307 session = pool_elt_at_index (em->sessions, session_index);
308 uri = format (0, "quic://session/%lu", session->vpp_session_handle);
309 ECHO_LOG (1, "Retrying connect %s", uri);
310 echo_send_rpc (em, echo_send_connect, (void *) uri, session_index);
315 quic_echo_connected_cb (session_connected_bundled_msg_t * mp,
316 u32 session_index, u8 is_failed)
319 return quic_echo_retry_connect (session_index);
320 return quic_echo_on_connected ((session_connected_msg_t *) mp,
325 quic_echo_accepted_cb (session_accepted_msg_t * mp, echo_session_t * session)
327 echo_main_t *em = &echo_main;
328 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
330 ls = pool_elt_at_index (em->sessions, session->listener_index);
331 if (ls->session_type == ECHO_SESSION_TYPE_LISTEN)
333 echo_notify_event (em, ECHO_EVT_FIRST_QCONNECT);
334 session->session_type = ECHO_SESSION_TYPE_QUIC;
335 session->accepted_session_count = 0;
336 if (eqm->cb_vft.quic_accepted_cb)
337 eqm->cb_vft.quic_accepted_cb (mp, session->session_index);
338 clib_atomic_fetch_add (&eqm->n_quic_clients_connected, 1);
342 session->session_type = ECHO_SESSION_TYPE_STREAM;
343 echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
344 clib_atomic_fetch_add (&ls->accepted_session_count, 1);
345 if (em->i_am_master && eqm->cb_vft.server_stream_accepted_cb)
346 eqm->cb_vft.server_stream_accepted_cb (mp, session->session_index);
347 if (!em->i_am_master && eqm->cb_vft.client_stream_accepted_cb)
348 eqm->cb_vft.client_stream_accepted_cb (mp, session->session_index);
349 clib_atomic_fetch_add (&em->n_clients_connected, 1);
352 if (em->n_clients_connected == em->n_clients
353 && em->n_clients_connected != 0)
354 echo_notify_event (em, ECHO_EVT_LAST_SCONNECTED);
356 if (eqm->n_quic_clients_connected == em->n_connects
357 && em->state < STATE_READY)
359 echo_notify_event (em, ECHO_EVT_LAST_QCONNECTED);
360 em->state = STATE_READY;
361 if (eqm->n_stream_clients == 0)
362 quic_echo_initiate_qsession_close_no_stream (em);
367 quic_echo_disconnected_reply_cb (echo_session_t * s)
369 if (s->session_type == ECHO_SESSION_TYPE_STREAM)
370 s->session_state = ECHO_SESSION_STATE_CLOSING;
372 quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
376 quic_echo_disconnected_cb (session_disconnected_msg_t * mp,
379 echo_main_t *em = &echo_main;
380 if (s->session_type == ECHO_SESSION_TYPE_STREAM)
382 echo_session_print_stats (em, s);
383 if (s->bytes_to_receive || s->bytes_to_send)
384 s->session_state = ECHO_SESSION_STATE_AWAIT_DATA;
386 s->session_state = ECHO_SESSION_STATE_CLOSING;
387 clib_atomic_fetch_add (&em->stats.close_count.s, 1);
391 quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
392 clib_atomic_fetch_add (&em->stats.close_count.q, 1);
397 quic_echo_reset_cb (session_reset_msg_t * mp, echo_session_t * s)
399 echo_main_t *em = &echo_main;
400 if (s->session_type == ECHO_SESSION_TYPE_STREAM)
402 clib_atomic_fetch_add (&em->stats.reset_count.s, 1);
403 s->session_state = ECHO_SESSION_STATE_CLOSING;
407 clib_atomic_fetch_add (&em->stats.reset_count.q, 1);
408 quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
413 quic_echo_unformat_setup_vft (unformat_input_t * input, va_list * args)
415 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
416 if (unformat (input, "serverstream"))
417 eqm->cb_vft = server_stream_cb_vft;
418 else if (unformat (input, "default"))
426 quic_echo_process_opts_cb (unformat_input_t * a)
428 echo_main_t *em = &echo_main;
429 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
430 if (unformat (a, "nclients %d/%d", &em->n_clients, &eqm->n_stream_clients))
432 else if (unformat (a, "quic-setup %U", quic_echo_unformat_setup_vft))
434 else if (unformat (a, "qclose=%U",
435 echo_unformat_close, &eqm->send_quic_disconnects))
443 quic_echo_set_defaults_before_opts_cb ()
445 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
446 eqm->cb_vft = default_cb_vft;
447 eqm->n_stream_clients = 1;
451 quic_echo_set_defaults_after_opts_cb ()
453 quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
454 echo_main_t *em = &echo_main;
457 em->n_connects = em->n_clients;
459 clib_max (1, eqm->n_stream_clients) * em->n_clients + em->n_clients + 1;
460 em->n_clients = eqm->n_stream_clients * em->n_clients;
464 em->bytes_to_send == 0 ? ECHO_CLOSE_F_ACTIVE : ECHO_CLOSE_F_PASSIVE;
467 em->bytes_to_receive == 0 ? ECHO_CLOSE_F_PASSIVE : ECHO_CLOSE_F_ACTIVE;
468 if (eqm->send_quic_disconnects == ECHO_CLOSE_F_INVALID)
469 eqm->send_quic_disconnects = default_f_active;
473 quic_echo_print_usage_cb ()
476 "-- QUIC specific options -- \n"
477 " quic-setup OPT OPT=serverstream : Client open N connections. \n"
478 " On each one server opens M streams\n"
479 " OPT=default : Client open N connections.\n"
480 " On each one client opens M streams\n"
481 " qclose=[Y|N|W] When a connection is done pass[N] send[Y] or wait[W] for close\n"
483 " nclients N[/M] Open N QUIC connections, each one with M streams (M defaults to 1)\n");
486 echo_proto_cb_vft_t quic_echo_proto_cb_vft = {
487 .disconnected_cb = quic_echo_disconnected_cb,
488 .connected_cb = quic_echo_connected_cb,
489 .accepted_cb = quic_echo_accepted_cb,
490 .reset_cb = quic_echo_reset_cb,
491 .disconnected_reply_cb = quic_echo_disconnected_reply_cb,
492 .cleanup_cb = quic_echo_cleanup_cb,
493 .process_opts_cb = quic_echo_process_opts_cb,
494 .print_usage_cb = quic_echo_print_usage_cb,
495 .set_defaults_before_opts_cb = quic_echo_set_defaults_before_opts_cb,
496 .set_defaults_after_opts_cb = quic_echo_set_defaults_after_opts_cb,
499 ECHO_REGISTER_PROTO (TRANSPORT_PROTO_QUIC, quic_echo_proto_cb_vft);
502 * fd.io coding-style-patch-verification: ON
505 * eval: (c-set-style "gnu")