Line data Source code
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 <hs_apps/sapi/vpp_echo_common.h>
20 :
21 : typedef struct _quic_echo_cb_vft
22 : {
23 : void (*quic_connected_cb) (session_connected_msg_t * mp, u32 session_index);
24 : void (*client_stream_connected_cb) (session_connected_msg_t * mp,
25 : u32 session_index);
26 : void (*server_stream_connected_cb) (session_connected_msg_t * mp,
27 : u32 session_index);
28 : void (*quic_accepted_cb) (session_accepted_msg_t * mp, u32 session_index);
29 : void (*client_stream_accepted_cb) (session_accepted_msg_t * mp,
30 : u32 session_index);
31 : void (*server_stream_accepted_cb) (session_accepted_msg_t * mp,
32 : u32 session_index);
33 : } quic_echo_cb_vft_t;
34 :
35 : typedef struct
36 : {
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;
42 :
43 : quic_echo_proto_main_t quic_echo_proto_main;
44 :
45 : /*
46 : *
47 : * ECHO Callback definitions
48 : *
49 : */
50 :
51 : static void
52 1 : quic_echo_on_connected_connect (session_connected_msg_t * mp,
53 : u32 session_index)
54 : {
55 1 : echo_main_t *em = &echo_main;
56 1 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
57 1 : echo_connect_args_t _a, *a = &_a;
58 : u64 i;
59 :
60 1 : a->parent_session_handle = mp->handle;
61 1 : a->context = session_index;
62 1 : clib_memcpy_fast (&a->lcl_ip, &em->lcl_ip, sizeof (ip46_address_t));
63 1 : clib_memcpy_fast (&a->ip, &em->uri_elts.ip, sizeof (ip46_address_t));
64 :
65 1 : echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
66 2 : for (i = 0; i < eqm->n_stream_clients; i++)
67 1 : echo_send_rpc (em, echo_send_connect, (echo_rpc_args_t *) a);
68 :
69 1 : ECHO_LOG (1, "Qsession 0x%llx S[%d] connected to %U:%d",
70 : mp->handle, session_index, format_ip46_address, &mp->lcl.ip,
71 : mp->lcl.is_ip4, clib_net_to_host_u16 (mp->lcl.port));
72 1 : }
73 :
74 : static void
75 2 : quic_echo_on_connected_send (session_connected_msg_t * mp, u32 session_index)
76 : {
77 : static u32 client_index = 0;
78 2 : echo_main_t *em = &echo_main;
79 : echo_session_t *session;
80 :
81 2 : session = pool_elt_at_index (em->sessions, session_index);
82 2 : session->bytes_to_send = em->bytes_to_send;
83 2 : session->bytes_to_receive = em->bytes_to_receive;
84 2 : session->session_state = ECHO_SESSION_STATE_READY;
85 2 : em->data_thread_args[client_index++] = session->session_index;
86 2 : }
87 :
88 : static void
89 0 : quic_echo_on_connected_error (session_connected_msg_t * mp, u32 session_index)
90 : {
91 0 : ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_CONNECT,
92 : "Got a wrong connected on session %u [%lx]", session_index,
93 : mp->handle);
94 0 : }
95 :
96 : static void
97 2 : quic_echo_on_accept_recv (session_accepted_msg_t * mp, u32 session_index)
98 : {
99 : static u32 client_index = 0;
100 2 : echo_main_t *em = &echo_main;
101 : echo_session_t *session;
102 :
103 2 : session = pool_elt_at_index (em->sessions, session_index);
104 2 : session->bytes_to_send = em->bytes_to_send;
105 2 : session->bytes_to_receive = em->bytes_to_receive;
106 2 : em->data_thread_args[client_index++] = session->session_index;
107 2 : session->session_state = ECHO_SESSION_STATE_READY;
108 2 : }
109 :
110 : static void
111 1 : quic_echo_on_accept_connect (session_accepted_msg_t * mp, u32 session_index)
112 : {
113 1 : echo_main_t *em = &echo_main;
114 1 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
115 1 : ECHO_LOG (2, "Accept on QSession 0x%lx S[%u]", mp->handle, session_index);
116 1 : echo_connect_args_t _a, *a = &_a;
117 : u32 i;
118 :
119 1 : a->parent_session_handle = mp->handle;
120 1 : a->context = session_index;
121 1 : clib_memcpy_fast (&a->lcl_ip, &em->lcl_ip, sizeof (ip46_address_t));
122 1 : clib_memcpy_fast (&a->ip, &em->uri_elts.ip, sizeof (ip46_address_t));
123 :
124 1 : echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
125 2 : for (i = 0; i < eqm->n_stream_clients; i++)
126 1 : echo_send_rpc (em, echo_send_connect, (echo_rpc_args_t *) a);
127 1 : }
128 :
129 : static void
130 0 : quic_echo_on_accept_error (session_accepted_msg_t * mp, u32 session_index)
131 : {
132 0 : ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_ACCEPT,
133 : "Got a wrong accept on session 0x%lx S[%u]", mp->handle,
134 : session_index);
135 0 : }
136 :
137 : static void
138 1 : quic_echo_on_accept_log_ip (session_accepted_msg_t * mp, u32 session_index)
139 : {
140 : u8 *ip_str;
141 1 : ip_str = format (0, "%U", format_ip46_address, &mp->rmt.ip, mp->rmt.is_ip4);
142 1 : ECHO_LOG (1, "Accepted session from: %s:%d", ip_str,
143 : clib_net_to_host_u16 (mp->rmt.port));
144 :
145 1 : }
146 :
147 : static const quic_echo_cb_vft_t default_cb_vft = {
148 : /* Qsessions */
149 : .quic_accepted_cb = quic_echo_on_accept_log_ip,
150 : .quic_connected_cb = quic_echo_on_connected_connect,
151 : /* client initiated streams */
152 : .server_stream_accepted_cb = quic_echo_on_accept_recv,
153 : .client_stream_connected_cb = quic_echo_on_connected_send,
154 : /* server initiated streams */
155 : .client_stream_accepted_cb = quic_echo_on_accept_error,
156 : .server_stream_connected_cb = quic_echo_on_connected_error,
157 : };
158 :
159 : static const quic_echo_cb_vft_t server_stream_cb_vft = {
160 : /* Qsessions */
161 : .quic_accepted_cb = quic_echo_on_accept_connect,
162 : .quic_connected_cb = NULL,
163 : /* client initiated streams */
164 : .server_stream_accepted_cb = quic_echo_on_accept_error,
165 : .client_stream_connected_cb = quic_echo_on_connected_error,
166 : /* server initiated streams */
167 : .client_stream_accepted_cb = quic_echo_on_accept_recv,
168 : .server_stream_connected_cb = quic_echo_on_connected_send,
169 : };
170 :
171 : static void quic_echo_cleanup_cb (echo_session_t * s, u8 parent_died);
172 :
173 : static inline void
174 2 : quic_echo_cleanup_listener (u32 listener_index, echo_main_t * em,
175 : quic_echo_proto_main_t * eqm)
176 : {
177 : echo_session_t *ls;
178 2 : ls = pool_elt_at_index (em->sessions, listener_index);
179 2 : if (ls->session_type != ECHO_SESSION_TYPE_QUIC)
180 : {
181 0 : ECHO_LOG (2, "%U: Invalid listener session type",
182 : echo_format_session, ls);
183 0 : return;
184 : }
185 2 : if (!clib_atomic_sub_fetch (&ls->accepted_session_count, 1))
186 : {
187 2 : if (eqm->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
188 : {
189 2 : echo_send_rpc (em, echo_send_disconnect_session,
190 2 : (echo_rpc_args_t *) & ls->vpp_session_handle);
191 2 : clib_atomic_fetch_add (&em->stats.active_count.q, 1);
192 : }
193 0 : else if (eqm->send_quic_disconnects == ECHO_CLOSE_F_NONE)
194 : {
195 0 : quic_echo_cleanup_cb (ls, 0 /* parent_died */ );
196 0 : clib_atomic_fetch_add (&em->stats.clean_count.q, 1);
197 : }
198 : }
199 : }
200 :
201 : static void
202 10 : quic_echo_cleanup_cb (echo_session_t * s, u8 parent_died)
203 : {
204 10 : echo_main_t *em = &echo_main;
205 10 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
206 10 : if ((em->state == STATE_DATA_DONE) ||
207 8 : !(s->session_state < ECHO_SESSION_STATE_CLOSED))
208 2 : return;
209 8 : ECHO_LOG (3, "%U cleanup (parent_died %d)", echo_format_session, s,
210 : parent_died);
211 8 : s->session_state = ECHO_SESSION_STATE_CLOSED;
212 8 : if (s->session_type == ECHO_SESSION_TYPE_QUIC)
213 : {
214 4 : if (parent_died)
215 0 : clib_atomic_fetch_add (&em->stats.clean_count.q, 1);
216 : /* Don't cleanup listener as it's handled by main() */
217 4 : clib_atomic_sub_fetch (&eqm->n_quic_clients_connected, 1);
218 : }
219 4 : else if (s->session_type == ECHO_SESSION_TYPE_STREAM)
220 : {
221 4 : if (parent_died)
222 2 : clib_atomic_fetch_add (&em->stats.clean_count.s, 1);
223 : else
224 2 : quic_echo_cleanup_listener (s->listener_index, em, eqm);
225 4 : clib_atomic_sub_fetch (&em->n_clients_connected, 1);
226 : }
227 8 : if (!em->n_clients_connected && !eqm->n_quic_clients_connected)
228 4 : em->state = STATE_DATA_DONE;
229 8 : ECHO_LOG (2, "Cleanup sessions (still %uQ %uS): app %U",
230 : eqm->n_quic_clients_connected, em->n_clients_connected,
231 : echo_format_app_state, em->state);
232 : }
233 :
234 : static void
235 0 : quic_echo_initiate_qsession_close_no_stream (echo_main_t * em)
236 : {
237 0 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
238 0 : ECHO_LOG (2, "Closing Qsessions");
239 : /* Close Quic session without streams */
240 : echo_session_t *s;
241 :
242 : /* *INDENT-OFF* */
243 0 : pool_foreach (s, em->sessions)
244 : {
245 0 : if (s->session_type == ECHO_SESSION_TYPE_QUIC)
246 : {
247 0 : if (eqm->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
248 : {
249 0 : ECHO_LOG (2,"%U: ACTIVE close", echo_format_session, s);
250 0 : echo_send_rpc (em, echo_send_disconnect_session,
251 0 : (echo_rpc_args_t *) &s->vpp_session_handle);
252 0 : clib_atomic_fetch_add (&em->stats.active_count.q, 1);
253 : }
254 0 : else if (eqm->send_quic_disconnects == ECHO_CLOSE_F_NONE)
255 : {
256 0 : ECHO_LOG (2,"%U: CLEAN close", echo_format_session, s);
257 0 : quic_echo_cleanup_cb (s, 0 /* parent_died */);
258 0 : clib_atomic_fetch_add (&em->stats.clean_count.q, 1);
259 : }
260 : else
261 0 : ECHO_LOG (2,"%U: PASSIVE close", echo_format_session, s);
262 : }
263 : }
264 : /* *INDENT-ON* */
265 0 : }
266 :
267 : static void
268 4 : quic_echo_on_connected (session_connected_msg_t * mp, u32 session_index)
269 : {
270 4 : echo_main_t *em = &echo_main;
271 4 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
272 : echo_session_t *listen_session;
273 4 : echo_session_t *session = pool_elt_at_index (em->sessions, session_index);
274 :
275 4 : if (session->listener_index == SESSION_INVALID_INDEX)
276 : {
277 2 : clib_atomic_fetch_add (&em->stats.connected_count.q, 1);
278 2 : session->session_type = ECHO_SESSION_TYPE_QUIC;
279 2 : ECHO_LOG (2, "Connected %U -> URI", echo_format_session, session);
280 2 : session->accepted_session_count = 0;
281 2 : if (eqm->cb_vft.quic_connected_cb)
282 1 : eqm->cb_vft.quic_connected_cb (mp, session->session_index);
283 2 : clib_atomic_fetch_add (&eqm->n_quic_clients_connected, 1);
284 :
285 2 : if (em->stats.connected_count.q % LOGGING_BATCH == 0)
286 0 : ECHO_LOG (0, "Connected Q %d / %d", em->stats.connected_count.q,
287 : em->n_connects);
288 : }
289 : else
290 : {
291 2 : clib_atomic_fetch_add (&em->stats.connected_count.s, 1);
292 2 : listen_session =
293 2 : pool_elt_at_index (em->sessions, session->listener_index);
294 2 : session->session_type = ECHO_SESSION_TYPE_STREAM;
295 2 : clib_atomic_fetch_add (&listen_session->accepted_session_count, 1);
296 2 : ECHO_LOG (2, "Connected %U -> %U", echo_format_session, session,
297 : echo_format_session, listen_session);
298 2 : if (em->i_am_master && eqm->cb_vft.server_stream_connected_cb)
299 1 : eqm->cb_vft.server_stream_connected_cb (mp, session->session_index);
300 2 : if (!em->i_am_master && eqm->cb_vft.client_stream_connected_cb)
301 1 : eqm->cb_vft.client_stream_connected_cb (mp, session->session_index);
302 2 : clib_atomic_fetch_add (&em->n_clients_connected, 1);
303 :
304 2 : if (em->stats.connected_count.s % LOGGING_BATCH == 0)
305 0 : ECHO_LOG (0, "Connected S %d / %d", em->stats.connected_count.s,
306 : em->n_clients);
307 : }
308 :
309 :
310 4 : if (em->n_clients_connected == em->n_clients
311 2 : && em->n_clients_connected != 0)
312 2 : echo_notify_event (em, ECHO_EVT_LAST_SCONNECTED);
313 :
314 4 : if (eqm->n_quic_clients_connected == em->n_connects
315 4 : && em->state < STATE_READY)
316 : {
317 2 : echo_notify_event (em, ECHO_EVT_LAST_QCONNECTED);
318 2 : em->state = STATE_READY;
319 2 : if (eqm->n_stream_clients == 0)
320 0 : quic_echo_initiate_qsession_close_no_stream (em);
321 : }
322 4 : }
323 :
324 : static void
325 4 : quic_echo_connected_cb (session_connected_bundled_msg_t * mp,
326 : u32 session_index, u8 is_failed)
327 : {
328 4 : if (is_failed)
329 : {
330 0 : ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_CONNECT, "Echo connect failed");
331 0 : return;
332 : }
333 4 : return quic_echo_on_connected ((session_connected_msg_t *) mp,
334 : session_index);
335 : }
336 :
337 : static void
338 4 : quic_echo_accepted_cb (session_accepted_msg_t * mp, echo_session_t * session)
339 : {
340 4 : echo_main_t *em = &echo_main;
341 4 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
342 : echo_session_t *ls;
343 4 : ls = pool_elt_at_index (em->sessions, session->listener_index);
344 4 : if (ls->session_type == ECHO_SESSION_TYPE_LISTEN)
345 : {
346 2 : clib_atomic_fetch_add (&em->stats.accepted_count.q, 1);
347 2 : echo_notify_event (em, ECHO_EVT_FIRST_QCONNECT);
348 2 : session->session_type = ECHO_SESSION_TYPE_QUIC;
349 2 : session->accepted_session_count = 0;
350 2 : if (eqm->cb_vft.quic_accepted_cb)
351 2 : eqm->cb_vft.quic_accepted_cb (mp, session->session_index);
352 2 : clib_atomic_fetch_add (&eqm->n_quic_clients_connected, 1);
353 :
354 2 : if (em->stats.accepted_count.q % LOGGING_BATCH == 0)
355 0 : ECHO_LOG (0, "Accepted Q %d / %d", em->stats.accepted_count.q,
356 : em->n_connects);
357 : }
358 : else
359 : {
360 2 : clib_atomic_fetch_add (&em->stats.accepted_count.s, 1);
361 2 : session->session_type = ECHO_SESSION_TYPE_STREAM;
362 2 : echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
363 2 : clib_atomic_fetch_add (&ls->accepted_session_count, 1);
364 2 : if (em->i_am_master && eqm->cb_vft.server_stream_accepted_cb)
365 1 : eqm->cb_vft.server_stream_accepted_cb (mp, session->session_index);
366 2 : if (!em->i_am_master && eqm->cb_vft.client_stream_accepted_cb)
367 1 : eqm->cb_vft.client_stream_accepted_cb (mp, session->session_index);
368 2 : clib_atomic_fetch_add (&em->n_clients_connected, 1);
369 :
370 2 : if (em->stats.accepted_count.s % LOGGING_BATCH == 0)
371 0 : ECHO_LOG (0, "Accepted S %d / %d", em->stats.accepted_count.s,
372 : em->n_clients);
373 :
374 2 : if (em->connect_flag && !(mp->flags & em->connect_flag))
375 : {
376 0 : ECHO_FAIL (ECHO_FAIL_UNIDIRECTIONAL,
377 : "expected unidirectional streams");
378 : }
379 : }
380 :
381 4 : if (em->n_clients_connected == em->n_clients
382 2 : && em->n_clients_connected != 0)
383 2 : echo_notify_event (em, ECHO_EVT_LAST_SCONNECTED);
384 :
385 4 : if (eqm->n_quic_clients_connected == em->n_connects
386 4 : && em->state < STATE_READY)
387 : {
388 2 : echo_notify_event (em, ECHO_EVT_LAST_QCONNECTED);
389 2 : em->state = STATE_READY;
390 2 : if (eqm->n_stream_clients == 0)
391 0 : quic_echo_initiate_qsession_close_no_stream (em);
392 : }
393 4 : }
394 :
395 : static void
396 4 : quic_echo_sent_disconnect_cb (echo_session_t * s)
397 : {
398 4 : if (s->session_type == ECHO_SESSION_TYPE_STREAM)
399 2 : s->session_state = ECHO_SESSION_STATE_CLOSING;
400 : else
401 2 : quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
402 4 : }
403 :
404 : static void
405 2 : quic_echo_disconnected_cb (session_disconnected_msg_t * mp,
406 : echo_session_t * s)
407 : {
408 2 : echo_main_t *em = &echo_main;
409 2 : if (s->session_type == ECHO_SESSION_TYPE_STREAM)
410 : {
411 0 : echo_session_print_stats (em, s);
412 0 : if (s->bytes_to_receive || s->bytes_to_send)
413 0 : s->session_state = ECHO_SESSION_STATE_AWAIT_DATA;
414 : else
415 0 : s->session_state = ECHO_SESSION_STATE_CLOSING;
416 0 : clib_atomic_fetch_add (&em->stats.close_count.s, 1);
417 : }
418 : else
419 : {
420 2 : quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
421 2 : clib_atomic_fetch_add (&em->stats.close_count.q, 1);
422 : }
423 2 : }
424 :
425 : static void
426 0 : quic_echo_reset_cb (session_reset_msg_t * mp, echo_session_t * s)
427 : {
428 0 : echo_main_t *em = &echo_main;
429 0 : if (s->session_type == ECHO_SESSION_TYPE_STREAM)
430 : {
431 0 : clib_atomic_fetch_add (&em->stats.reset_count.s, 1);
432 0 : s->session_state = ECHO_SESSION_STATE_CLOSING;
433 : }
434 : else
435 : {
436 0 : clib_atomic_fetch_add (&em->stats.reset_count.q, 1);
437 0 : quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
438 : }
439 0 : }
440 :
441 : static uword
442 4 : quic_echo_unformat_setup_vft (unformat_input_t * input, va_list * args)
443 : {
444 4 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
445 4 : if (unformat (input, "serverstream"))
446 2 : eqm->cb_vft = server_stream_cb_vft;
447 2 : else if (unformat (input, "default"))
448 : ;
449 : else
450 0 : return 0;
451 4 : return 1;
452 : }
453 :
454 : static int
455 48 : quic_echo_process_opts_cb (unformat_input_t * a)
456 : {
457 48 : echo_main_t *em = &echo_main;
458 48 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
459 48 : if (unformat (a, "quic-streams %d", &eqm->n_stream_clients))
460 : ;
461 48 : else if (unformat (a, "quic-setup %U", quic_echo_unformat_setup_vft))
462 : ;
463 44 : else if (unformat (a, "uni"))
464 0 : em->connect_flag = TRANSPORT_CFG_F_UNIDIRECTIONAL;
465 44 : else if (unformat (a, "qclose=%U",
466 : echo_unformat_close, &eqm->send_quic_disconnects))
467 : ;
468 : else
469 44 : return 0;
470 4 : return 1;
471 : }
472 :
473 : static void
474 4 : quic_echo_set_defaults_before_opts_cb ()
475 : {
476 4 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
477 4 : eqm->cb_vft = default_cb_vft;
478 4 : eqm->n_stream_clients = 1;
479 4 : }
480 :
481 : static void
482 4 : quic_echo_set_defaults_after_opts_cb ()
483 : {
484 4 : quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
485 4 : echo_main_t *em = &echo_main;
486 : u8 default_f_active;
487 :
488 4 : em->n_connects = em->n_clients;
489 4 : em->n_sessions =
490 4 : clib_max (1,
491 4 : eqm->n_stream_clients) * em->n_clients + em->n_clients +
492 4 : em->n_uris;
493 4 : em->n_clients = eqm->n_stream_clients * em->n_clients;
494 :
495 4 : if (em->i_am_master)
496 2 : default_f_active =
497 2 : em->bytes_to_send == 0 ? ECHO_CLOSE_F_ACTIVE : ECHO_CLOSE_F_PASSIVE;
498 : else
499 2 : default_f_active =
500 2 : em->bytes_to_receive == 0 ? ECHO_CLOSE_F_PASSIVE : ECHO_CLOSE_F_ACTIVE;
501 4 : if (eqm->send_quic_disconnects == ECHO_CLOSE_F_INVALID)
502 4 : eqm->send_quic_disconnects = default_f_active;
503 4 : }
504 :
505 : static void
506 0 : quic_echo_print_usage_cb ()
507 : {
508 0 : fprintf (stderr,
509 : "-- QUIC specific options -- \n"
510 : " quic-setup OPT OPT=serverstream : Client open N connections. \n"
511 : " On each one server opens M streams\n"
512 : " OPT=default : Client open N connections.\n"
513 : " On each one client opens M streams\n"
514 : " qclose=[Y|N|W] When connection is done send[Y]|nop[N]|wait[W] for close\n"
515 : " uni Use unidirectional streams\n"
516 : "\n"
517 : " quic-streams N Open N QUIC streams (defaults to 1)\n");
518 0 : }
519 :
520 : echo_proto_cb_vft_t quic_echo_proto_cb_vft = {
521 : .disconnected_cb = quic_echo_disconnected_cb,
522 : .connected_cb = quic_echo_connected_cb,
523 : .accepted_cb = quic_echo_accepted_cb,
524 : .reset_cb = quic_echo_reset_cb,
525 : .sent_disconnect_cb = quic_echo_sent_disconnect_cb,
526 : .cleanup_cb = quic_echo_cleanup_cb,
527 : .process_opts_cb = quic_echo_process_opts_cb,
528 : .print_usage_cb = quic_echo_print_usage_cb,
529 : .set_defaults_before_opts_cb = quic_echo_set_defaults_before_opts_cb,
530 : .set_defaults_after_opts_cb = quic_echo_set_defaults_after_opts_cb,
531 : };
532 :
533 4 : ECHO_REGISTER_PROTO (TRANSPORT_PROTO_QUIC, quic_echo_proto_cb_vft);
534 :
535 : /*
536 : * fd.io coding-style-patch-verification: ON
537 : *
538 : * Local Variables:
539 : * eval: (c-set-style "gnu")
540 : * End:
541 : */
|