FD.io VPP  v19.08.1-401-g8e4ed521a
Vector Packet Processing
vpp_echo_proto_quic.c
Go to the documentation of this file.
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 
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);
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 */
42 
44 
45 /*
46  *
47  * ECHO Callback definitions
48  *
49  */
50 
51 static void
53  u32 session_index)
54 {
55  echo_main_t *em = &echo_main;
57  u8 *uri = format (0, "quic://session/%lu", mp->handle);
58  u64 i;
59 
61  for (i = 0; i < eqm->n_stream_clients; i++)
62  echo_send_rpc (em, echo_send_connect, (void *) uri, session_index);
63 
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));
67 }
68 
69 static void
71 {
72  static u32 client_index = 0;
73  echo_main_t *em = &echo_main;
74  echo_session_t *session;
75 
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;
81 }
82 
83 static void
85 {
86  ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_CONNECT,
87  "Got a wrong connected on session %u [%lx]", session_index,
88  mp->handle);
89 }
90 
91 static void
93 {
94  static u32 client_index = 0;
95  echo_main_t *em = &echo_main;
96  echo_session_t *session;
97 
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;
103 }
104 
105 static void
107 {
108  echo_main_t *em = &echo_main;
110  ECHO_LOG (1, "Accept on QSession 0x%lx %u", mp->handle);
111  u8 *uri = format (0, "quic://session/%lu", mp->handle);
112  u32 i;
113 
115  for (i = 0; i < eqm->n_stream_clients; i++)
116  echo_send_rpc (em, echo_send_connect, (void *) uri, session_index);
117 }
118 
119 static void
121 {
122  ECHO_FAIL (ECHO_FAIL_QUIC_WRONG_ACCEPT,
123  "Got a wrong accept on session %u [%lx]", session_index,
124  mp->handle);
125 }
126 
127 static void
129 {
130  u8 *ip_str;
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));
134 
135 }
136 
138  /* Qsessions */
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,
147 };
148 
150  /* Qsessions */
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,
159 };
160 
161 static void quic_echo_cleanup_cb (echo_session_t * s, u8 parent_died);
162 
163 static inline void
166 {
167  echo_session_t *ls;
168  ls = pool_elt_at_index (em->sessions, listener_index);
169  ASSERT (ls->session_type == ECHO_SESSION_TYPE_QUIC);
171  {
173  {
175  (void *) ls->vpp_session_handle, 0);
177  }
178  else if (eqm->send_quic_disconnects == ECHO_CLOSE_F_NONE)
179  {
180  quic_echo_cleanup_cb (ls, 0 /* parent_died */ );
182  }
183  }
184 }
185 
186 static void
188 {
189  echo_main_t *em = &echo_main;
191  ASSERT (s->session_state < ECHO_SESSION_STATE_CLOSED);
192  if (s->session_type == ECHO_SESSION_TYPE_QUIC)
193  {
194  if (parent_died)
196  /* Don't cleanup listener as it's handled by main() */
198  }
199  else if (s->session_type == ECHO_SESSION_TYPE_STREAM)
200  {
201  if (parent_died)
203  else
206  }
207 
208  ECHO_LOG (1, "Cleanup sessions (still %uQ %uS)",
210  s->session_state = ECHO_SESSION_STATE_CLOSED;
212  em->state = STATE_DATA_DONE;
213 }
214 
215 static void
217 {
219  ECHO_LOG (1, "Closing Qsessions");
220  /* Close Quic session without streams */
221  echo_session_t *s;
222 
223  /* *INDENT-OFF* */
224  pool_foreach (s, em->sessions,
225  ({
226  if (s->session_type == ECHO_SESSION_TYPE_QUIC)
227  {
228  if (eqm->send_quic_disconnects == ECHO_CLOSE_F_ACTIVE)
229  {
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);
233  }
234  else if (eqm->send_quic_disconnects == ECHO_CLOSE_F_NONE)
235  {
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);
239  }
240  else
241  ECHO_LOG (1,"Passive close 0x%lx", s->vpp_session_handle);
242  }
243  }));
244  /* *INDENT-ON* */
245 }
246 
247 static void
249 {
250  echo_main_t *em = &echo_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)
255  {
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);
262  }
263  else
264  {
265  listen_session =
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);
276  }
277 
278  if (em->n_clients_connected == em->n_clients
279  && em->n_clients_connected != 0)
281 
282  if (eqm->n_quic_clients_connected == em->n_connects
283  && em->state < STATE_READY)
284  {
286  em->state = STATE_READY;
287  if (eqm->n_stream_clients == 0)
289  }
290 }
291 
292 static void
294 {
295  /* retry connect */
296  echo_session_t *session;
297  echo_main_t *em = &echo_main;
298  u8 *uri;
299  if (session_index == SESSION_INVALID_INDEX)
300  {
301  ECHO_LOG (1, "Retrying connect %s", em->uri);
302  echo_send_rpc (em, echo_send_connect, (void *) em->uri,
304  }
305  else
306  {
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);
311  }
312 }
313 
314 static void
316  u32 session_index, u8 is_failed)
317 {
318  if (is_failed)
319  return quic_echo_retry_connect (session_index);
321  session_index);
322 }
323 
324 static void
326 {
327  echo_main_t *em = &echo_main;
329  echo_session_t *ls;
330  ls = pool_elt_at_index (em->sessions, session->listener_index);
331  if (ls->session_type == ECHO_SESSION_TYPE_LISTEN)
332  {
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);
339  }
340  else
341  {
342  session->session_type = ECHO_SESSION_TYPE_STREAM;
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);
350  }
351 
352  if (em->n_clients_connected == em->n_clients
353  && em->n_clients_connected != 0)
355 
356  if (eqm->n_quic_clients_connected == em->n_connects
357  && em->state < STATE_READY)
358  {
360  em->state = STATE_READY;
361  if (eqm->n_stream_clients == 0)
363  }
364 }
365 
366 static void
368 {
369  if (s->session_type == ECHO_SESSION_TYPE_STREAM)
370  s->session_state = ECHO_SESSION_STATE_CLOSING;
371  else
372  quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
373 }
374 
375 static void
377  echo_session_t * s)
378 {
379  echo_main_t *em = &echo_main;
380  if (s->session_type == ECHO_SESSION_TYPE_STREAM)
381  {
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;
385  else
386  s->session_state = ECHO_SESSION_STATE_CLOSING;
388  }
389  else
390  {
391  quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
393  }
394 }
395 
396 static void
398 {
399  echo_main_t *em = &echo_main;
400  if (s->session_type == ECHO_SESSION_TYPE_STREAM)
401  {
403  s->session_state = ECHO_SESSION_STATE_CLOSING;
404  }
405  else
406  {
408  quic_echo_cleanup_cb (s, 0 /* parent_died */ ); /* We can clean Q/Lsessions right away */
409  }
410 }
411 
412 static uword
414 {
416  if (unformat (input, "serverstream"))
418  else if (unformat (input, "default"))
419  ;
420  else
421  return 0;
422  return 1;
423 }
424 
425 static int
427 {
428  echo_main_t *em = &echo_main;
430  if (unformat (a, "nclients %d/%d", &em->n_clients, &eqm->n_stream_clients))
431  ;
432  else if (unformat (a, "quic-setup %U", quic_echo_unformat_setup_vft))
433  ;
434  else if (unformat (a, "qclose=%U",
436  ;
437  else
438  return 0;
439  return 1;
440 }
441 
442 static void
444 {
446  eqm->cb_vft = default_cb_vft;
447  eqm->n_stream_clients = 1;
448 }
449 
450 static void
452 {
454  echo_main_t *em = &echo_main;
455  u8 default_f_active;
456 
457  em->n_connects = em->n_clients;
458  em->n_sessions =
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;
461 
462  if (em->i_am_master)
463  default_f_active =
465  else
466  default_f_active =
469  eqm->send_quic_disconnects = default_f_active;
470 }
471 
472 static void
474 {
475  fprintf (stderr,
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"
482  "\n"
483  " nclients N[/M] Open N QUIC connections, each one with M streams (M defaults to 1)\n");
484 }
485 
486 echo_proto_cb_vft_t quic_echo_proto_cb_vft = {
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,
497 };
498 
499 ECHO_REGISTER_PROTO (TRANSPORT_PROTO_QUIC, quic_echo_proto_cb_vft);
500 
501 /*
502  * fd.io coding-style-patch-verification: ON
503  *
504  * Local Variables:
505  * eval: (c-set-style "gnu")
506  * End:
507  */
volatile u64 accepted_session_count
static void quic_echo_disconnected_reply_cb(echo_session_t *s)
static void quic_echo_on_accept_connect(session_accepted_msg_t *mp, u32 session_index)
static void quic_echo_on_connected(session_connected_msg_t *mp, u32 session_index)
teardown_stat_t clean_count
transport_endpoint_t rmt
a
Definition: bitmap.h:538
echo_session_t * sessions
unsigned long u64
Definition: types.h:89
static void quic_echo_set_defaults_after_opts_cb()
static const quic_echo_cb_vft_t server_stream_cb_vft
#define NULL
Definition: clib.h:58
static void quic_echo_disconnected_cb(session_disconnected_msg_t *mp, echo_session_t *s)
static void quic_echo_connected_cb(session_connected_bundled_msg_t *mp, u32 session_index, u8 is_failed)
int i
echo_main_t echo_main
Definition: vpp_echo.c:24
format_function_t format_ip46_address
Definition: format.h:61
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
volatile connection_state_t state
quic_echo_cb_vft_t cb_vft
ECHO_REGISTER_PROTO(TRANSPORT_PROTO_QUIC, quic_echo_proto_cb_vft)
unsigned char u8
Definition: types.h:56
void echo_send_disconnect_session(u64 handle, u32 opaque)
foreach_app_session_field u64 vpp_session_handle
volatile u32 n_clients_connected
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:493
quic_echo_proto_main_t quic_echo_proto_main
#define ECHO_FAIL(fail, _fmt, _args...)
unsigned int u32
Definition: types.h:88
static void quic_echo_reset_cb(session_reset_msg_t *mp, echo_session_t *s)
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
static const quic_echo_cb_vft_t default_cb_vft
struct _unformat_input_t unformat_input_t
static void quic_echo_accepted_cb(session_accepted_msg_t *mp, echo_session_t *session)
static uword quic_echo_unformat_setup_vft(unformat_input_t *input, va_list *args)
static void quic_echo_initiate_qsession_close_no_stream(echo_main_t *em)
volatile u32 n_quic_clients_connected
static void quic_echo_on_accept_error(session_accepted_msg_t *mp, u32 session_index)
#define SESSION_INVALID_INDEX
Definition: session_types.h:22
#define clib_atomic_sub_fetch(a, b)
Definition: atomics.h:31
void echo_send_connect(u8 *uri, u32 opaque)
static void quic_echo_on_connected_error(session_connected_msg_t *mp, u32 session_index)
static void quic_echo_cleanup_cb(echo_session_t *s, u8 parent_died)
static void quic_echo_print_usage_cb()
#define ECHO_LOG(lvl, _fmt, _args...)
void echo_notify_event(echo_main_t *em, echo_test_evt_t e)
u32 *volatile data_thread_args
struct _quic_echo_cb_vft quic_echo_cb_vft_t
teardown_stat_t reset_count
#define ASSERT(truth)
static void quic_echo_on_connected_connect(session_connected_msg_t *mp, u32 session_index)
static int quic_echo_process_opts_cb(unformat_input_t *a)
static void quic_echo_retry_connect(u32 session_index)
#define clib_max(x, y)
Definition: clib.h:288
#define clib_atomic_fetch_add(a, b)
Definition: atomics.h:23
static void quic_echo_on_accept_log_ip(session_accepted_msg_t *mp, u32 session_index)
teardown_stat_t close_count
volatile u64 bytes_to_receive
u64 uword
Definition: types.h:112
teardown_stat_t active_count
static void quic_echo_set_defaults_before_opts_cb()
transport_endpoint_t lcl
static void quic_echo_on_accept_recv(session_accepted_msg_t *mp, u32 session_index)
int echo_send_rpc(echo_main_t *em, void *fp, void *arg, u32 opaque)
void echo_session_print_stats(echo_main_t *em, echo_session_t *session)
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
struct echo_main_t::@481 stats
static void quic_echo_cleanup_listener(u32 listener_index, echo_main_t *em, quic_echo_proto_main_t *eqm)
uword echo_unformat_close(unformat_input_t *input, va_list *args)
static void quic_echo_on_connected_send(session_connected_msg_t *mp, u32 session_index)
void(* disconnected_cb)(session_disconnected_msg_t *mp, echo_session_t *s)