vpp_echo_common.c revision f98e59b8
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
21char *echo_fail_code_str[] = {
22#define _(sym, str) str,
23  foreach_echo_fail_code
24#undef _
25};
26
27/*
28 *
29 *  Format functions
30 *
31 */
32
33u8 *
34format_ip4_address (u8 * s, va_list * args)
35{
36  u8 *a = va_arg (*args, u8 *);
37  return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
38}
39
40u8 *
41format_ip6_address (u8 * s, va_list * args)
42{
43  ip6_address_t *a = va_arg (*args, ip6_address_t *);
44  u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
45
46  i_max_n_zero = ARRAY_LEN (a->as_u16);
47  max_n_zeros = 0;
48  i_first_zero = i_max_n_zero;
49  n_zeros = 0;
50  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
51    {
52      u32 is_zero = a->as_u16[i] == 0;
53      if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
54	{
55	  i_first_zero = i;
56	  n_zeros = 0;
57	}
58      n_zeros += is_zero;
59      if ((!is_zero && n_zeros > max_n_zeros)
60	  || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
61	{
62	  i_max_n_zero = i_first_zero;
63	  max_n_zeros = n_zeros;
64	  i_first_zero = ARRAY_LEN (a->as_u16);
65	  n_zeros = 0;
66	}
67    }
68
69  last_double_colon = 0;
70  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
71    {
72      if (i == i_max_n_zero && max_n_zeros > 1)
73	{
74	  s = format (s, "::");
75	  i += max_n_zeros - 1;
76	  last_double_colon = 1;
77	}
78      else
79	{
80	  s = format (s, "%s%x",
81		      (last_double_colon || i == 0) ? "" : ":",
82		      clib_net_to_host_u16 (a->as_u16[i]));
83	  last_double_colon = 0;
84	}
85    }
86
87  return s;
88}
89
90/* Format an IP46 address. */
91u8 *
92format_ip46_address (u8 * s, va_list * args)
93{
94  ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
95  ip46_type_t type = va_arg (*args, ip46_type_t);
96  int is_ip4 = 1;
97
98  switch (type)
99    {
100    case IP46_TYPE_ANY:
101      is_ip4 = ip46_address_is_ip4 (ip46);
102      break;
103    case IP46_TYPE_IP4:
104      is_ip4 = 1;
105      break;
106    case IP46_TYPE_IP6:
107      is_ip4 = 0;
108      break;
109    }
110
111  return is_ip4 ?
112    format (s, "%U", format_ip4_address, &ip46->ip4) :
113    format (s, "%U", format_ip6_address, &ip46->ip6);
114}
115
116u8 *
117format_api_error (u8 * s, va_list * args)
118{
119  echo_main_t *em = &echo_main;
120  i32 error = va_arg (*args, u32);
121  uword *p;
122
123  p = hash_get (em->error_string_by_error_number, -error);
124
125  if (p)
126    s = format (s, "%s", p[0]);
127  else
128    s = format (s, "%d", error);
129  return s;
130}
131
132void
133init_error_string_table ()
134{
135  echo_main_t *em = &echo_main;
136  em->error_string_by_error_number = hash_create (0, sizeof (uword));
137
138#define _(n,v,s) hash_set (em->error_string_by_error_number, -v, s);
139  foreach_vnet_api_error;
140#undef _
141
142  hash_set (em->error_string_by_error_number, 99, "Misc");
143}
144
145u8 *
146echo_format_session (u8 * s, va_list * args)
147{
148  echo_session_t *session = va_arg (*args, echo_session_t *);
149
150  return format (s, "%U 0x%lx S[%u]", echo_format_session_type,
151		 session->session_type, session->vpp_session_handle,
152		 session->session_index);
153}
154
155u8 *
156echo_format_session_type (u8 * s, va_list * args)
157{
158  u32 session_type = va_arg (*args, u32);
159  switch (session_type)
160    {
161    case ECHO_SESSION_TYPE_QUIC:
162      return format (s, "Qsession");
163    case ECHO_SESSION_TYPE_STREAM:
164      return format (s, "Stream");
165    case ECHO_SESSION_TYPE_LISTEN:
166      return format (s, "Lsession");
167    default:
168      break;
169    }
170  return format (s, "BadSession");
171}
172
173u8 *
174echo_format_session_state (u8 * s, va_list * args)
175{
176  u32 session_state = va_arg (*args, u32);
177  switch (session_state)
178    {
179    case ECHO_SESSION_STATE_INITIAL:
180      return format (s, "ECHO_SESSION_STATE_INITIAL (%u)", session_state);
181    case ECHO_SESSION_STATE_READY:
182      return format (s, "ECHO_SESSION_STATE_READY (%u)", session_state);
183    case ECHO_SESSION_STATE_AWAIT_CLOSING:
184      return format (s, "ECHO_SESSION_STATE_AWAIT_CLOSING (%u)",
185		     session_state);
186    case ECHO_SESSION_STATE_AWAIT_DATA:
187      return format (s, "ECHO_SESSION_STATE_AWAIT_DATA (%u)", session_state);
188    case ECHO_SESSION_STATE_CLOSING:
189      return format (s, "ECHO_SESSION_STATE_CLOSING (%u)", session_state);
190    case ECHO_SESSION_STATE_CLOSED:
191      return format (s, "ECHO_SESSION_STATE_CLOSED (%u)", session_state);
192    default:
193      break;
194    }
195  return format (s, "unknown session state (%u)", session_state);
196}
197
198u8 *
199echo_format_app_state (u8 * s, va_list * args)
200{
201  u32 state = va_arg (*args, u32);
202  if (state == STATE_START)
203    return format (s, "STATE_START (%u)", state);
204  if (state == STATE_ATTACHED)
205    return format (s, "STATE_ATTACHED (%u)", state);
206  if (state == STATE_ATTACHED_NO_CERT)
207    return format (s, "STATE_ATTACHED_NO_CERT (%u)", state);
208  if (state == STATE_LISTEN)
209    return format (s, "STATE_LISTEN (%u)", state);
210  if (state == STATE_READY)
211    return format (s, "STATE_READY (%u)", state);
212  if (state == STATE_DATA_DONE)
213    return format (s, "STATE_DATA_DONE (%u)", state);
214  if (state == STATE_DISCONNECTED)
215    return format (s, "STATE_DISCONNECTED (%u)", state);
216  if (state == STATE_DETACHED)
217    return format (s, "STATE_DETACHED (%u)", state);
218  else
219    return format (s, "unknown state (%u)", state);
220}
221
222uword
223echo_unformat_close (unformat_input_t * input, va_list * args)
224{
225  u8 *a = va_arg (*args, u8 *);
226  if (unformat (input, "Y"))
227    *a = ECHO_CLOSE_F_ACTIVE;
228  else if (unformat (input, "N"))
229    *a = ECHO_CLOSE_F_NONE;
230  else if (unformat (input, "W"))
231    *a = ECHO_CLOSE_F_PASSIVE;
232  else
233    return 0;
234  return 1;
235}
236
237uword
238echo_unformat_timing_event (unformat_input_t * input, va_list * args)
239{
240  u8 *a = va_arg (*args, u8 *);
241  if (unformat (input, "start"))
242    *a = ECHO_EVT_START;
243  else if (unformat (input, "qconnected"))
244    *a = ECHO_EVT_LAST_QCONNECTED;
245  else if (unformat (input, "qconnect"))
246    *a = ECHO_EVT_FIRST_QCONNECT;
247  else if (unformat (input, "sconnected"))
248    *a = ECHO_EVT_LAST_SCONNECTED;
249  else if (unformat (input, "sconnect"))
250    *a = ECHO_EVT_FIRST_SCONNECT;
251  else if (unformat (input, "lastbyte"))
252    *a = ECHO_EVT_LAST_BYTE;
253  else if (unformat (input, "exit"))
254    *a = ECHO_EVT_EXIT;
255  else
256    return 0;
257  return 1;
258}
259
260u8 *
261echo_format_timing_event (u8 * s, va_list * args)
262{
263  u32 timing_event = va_arg (*args, u32);
264  if (timing_event == ECHO_EVT_START)
265    return format (s, "start");
266  if (timing_event == ECHO_EVT_FIRST_QCONNECT)
267    return format (s, "qconnect");
268  if (timing_event == ECHO_EVT_LAST_QCONNECTED)
269    return format (s, "qconnected");
270  if (timing_event == ECHO_EVT_FIRST_SCONNECT)
271    return format (s, "sconnect");
272  if (timing_event == ECHO_EVT_LAST_SCONNECTED)
273    return format (s, "sconnected");
274  if (timing_event == ECHO_EVT_LAST_BYTE)
275    return format (s, "lastbyte");
276  if (timing_event == ECHO_EVT_EXIT)
277    return format (s, "exit");
278  else
279    return format (s, "unknown timing event");
280}
281
282uword
283unformat_transport_proto (unformat_input_t * input, va_list * args)
284{
285  u32 *proto = va_arg (*args, u32 *);
286  if (unformat (input, "tcp"))
287    *proto = TRANSPORT_PROTO_TCP;
288  else if (unformat (input, "TCP"))
289    *proto = TRANSPORT_PROTO_TCP;
290  else if (unformat (input, "udpc"))
291    *proto = TRANSPORT_PROTO_UDPC;
292  else if (unformat (input, "UDPC"))
293    *proto = TRANSPORT_PROTO_UDPC;
294  else if (unformat (input, "udp"))
295    *proto = TRANSPORT_PROTO_UDP;
296  else if (unformat (input, "UDP"))
297    *proto = TRANSPORT_PROTO_UDP;
298  else if (unformat (input, "sctp"))
299    *proto = TRANSPORT_PROTO_SCTP;
300  else if (unformat (input, "SCTP"))
301    *proto = TRANSPORT_PROTO_SCTP;
302  else if (unformat (input, "tls"))
303    *proto = TRANSPORT_PROTO_TLS;
304  else if (unformat (input, "TLS"))
305    *proto = TRANSPORT_PROTO_TLS;
306  else if (unformat (input, "quic"))
307    *proto = TRANSPORT_PROTO_QUIC;
308  else if (unformat (input, "QUIC"))
309    *proto = TRANSPORT_PROTO_QUIC;
310  else
311    return 0;
312  return 1;
313}
314
315u8 *
316format_transport_proto (u8 * s, va_list * args)
317{
318  u32 transport_proto = va_arg (*args, u32);
319  switch (transport_proto)
320    {
321    case TRANSPORT_PROTO_TCP:
322      s = format (s, "TCP");
323      break;
324    case TRANSPORT_PROTO_UDP:
325      s = format (s, "UDP");
326      break;
327    case TRANSPORT_PROTO_SCTP:
328      s = format (s, "SCTP");
329      break;
330    case TRANSPORT_PROTO_NONE:
331      s = format (s, "NONE");
332      break;
333    case TRANSPORT_PROTO_TLS:
334      s = format (s, "TLS");
335      break;
336    case TRANSPORT_PROTO_UDPC:
337      s = format (s, "UDPC");
338      break;
339    case TRANSPORT_PROTO_QUIC:
340      s = format (s, "QUIC");
341      break;
342    default:
343      s = format (s, "UNKNOWN");
344      break;
345    }
346  return s;
347}
348
349uword
350unformat_ip4_address (unformat_input_t * input, va_list * args)
351{
352  u8 *result = va_arg (*args, u8 *);
353  unsigned a[4];
354
355  if (!unformat (input, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]))
356    return 0;
357
358  if (a[0] >= 256 || a[1] >= 256 || a[2] >= 256 || a[3] >= 256)
359    return 0;
360
361  result[0] = a[0];
362  result[1] = a[1];
363  result[2] = a[2];
364  result[3] = a[3];
365
366  return 1;
367}
368
369uword
370unformat_ip6_address (unformat_input_t * input, va_list * args)
371{
372  ip6_address_t *result = va_arg (*args, ip6_address_t *);
373  u16 hex_quads[8];
374  uword hex_quad, n_hex_quads, hex_digit, n_hex_digits;
375  uword c, n_colon, double_colon_index;
376
377  n_hex_quads = hex_quad = n_hex_digits = n_colon = 0;
378  double_colon_index = ARRAY_LEN (hex_quads);
379  while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
380    {
381      hex_digit = 16;
382      if (c >= '0' && c <= '9')
383	hex_digit = c - '0';
384      else if (c >= 'a' && c <= 'f')
385	hex_digit = c + 10 - 'a';
386      else if (c >= 'A' && c <= 'F')
387	hex_digit = c + 10 - 'A';
388      else if (c == ':' && n_colon < 2)
389	n_colon++;
390      else
391	{
392	  unformat_put_input (input);
393	  break;
394	}
395
396      /* Too many hex quads. */
397      if (n_hex_quads >= ARRAY_LEN (hex_quads))
398	return 0;
399
400      if (hex_digit < 16)
401	{
402	  hex_quad = (hex_quad << 4) | hex_digit;
403
404	  /* Hex quad must fit in 16 bits. */
405	  if (n_hex_digits >= 4)
406	    return 0;
407
408	  n_colon = 0;
409	  n_hex_digits++;
410	}
411
412      /* Save position of :: */
413      if (n_colon == 2)
414	{
415	  /* More than one :: ? */
416	  if (double_colon_index < ARRAY_LEN (hex_quads))
417	    return 0;
418	  double_colon_index = n_hex_quads;
419	}
420
421      if (n_colon > 0 && n_hex_digits > 0)
422	{
423	  hex_quads[n_hex_quads++] = hex_quad;
424	  hex_quad = 0;
425	  n_hex_digits = 0;
426	}
427    }
428
429  if (n_hex_digits > 0)
430    hex_quads[n_hex_quads++] = hex_quad;
431
432  {
433    word i;
434
435    /* Expand :: to appropriate number of zero hex quads. */
436    if (double_colon_index < ARRAY_LEN (hex_quads))
437      {
438	word n_zero = ARRAY_LEN (hex_quads) - n_hex_quads;
439
440	for (i = n_hex_quads - 1; i >= (signed) double_colon_index; i--)
441	  hex_quads[n_zero + i] = hex_quads[i];
442
443	for (i = 0; i < n_zero; i++)
444	  hex_quads[double_colon_index + i] = 0;
445
446	n_hex_quads = ARRAY_LEN (hex_quads);
447      }
448
449    /* Too few hex quads given. */
450    if (n_hex_quads < ARRAY_LEN (hex_quads))
451      return 0;
452
453    for (i = 0; i < ARRAY_LEN (hex_quads); i++)
454      result->as_u16[i] = clib_host_to_net_u16 (hex_quads[i]);
455
456    return 1;
457  }
458}
459
460uword
461unformat_ip46_address (unformat_input_t * input, va_list * args)
462{
463  ip46_address_t *ip = va_arg (*args, ip46_address_t *);
464
465  if (unformat (input, "%U", unformat_ip4_address, &ip->ip4))
466    ;
467  else if (unformat (input, "%U", unformat_ip6_address, &ip->ip6))
468    ;
469  else
470    return 0;
471  return 1;
472}
473
474u8 *
475echo_format_crypto_engine (u8 * s, va_list * args)
476{
477  u32 state = va_arg (*args, u32);
478  if (state == CRYPTO_ENGINE_MBEDTLS)
479    return format (s, "mbedtls");
480  if (state == CRYPTO_ENGINE_OPENSSL)
481    return format (s, "openssl");
482  if (state == CRYPTO_ENGINE_PICOTLS)
483    return format (s, "picotls");
484  if (state == CRYPTO_ENGINE_VPP)
485    return format (s, "vpp");
486  else
487    return format (s, "unknown crypto engine");
488}
489
490uword
491echo_unformat_crypto_engine (unformat_input_t * input, va_list * args)
492{
493  u8 *a = va_arg (*args, u8 *);
494  if (unformat (input, "mbedtls"))
495    *a = CRYPTO_ENGINE_MBEDTLS;
496  else if (unformat (input, "openssl"))
497    *a = CRYPTO_ENGINE_OPENSSL;
498  else if (unformat (input, "picotls"))
499    *a = CRYPTO_ENGINE_PICOTLS;
500  else if (unformat (input, "vpp"))
501    *a = CRYPTO_ENGINE_VPP;
502  else
503    return 0;
504  return 1;
505}
506
507
508/*
509 *
510 *  End of format functions
511 *
512 */
513
514void
515echo_session_handle_add_del (echo_main_t * em, u64 handle, u32 sid)
516{
517  clib_spinlock_lock (&em->sid_vpp_handles_lock);
518  if (sid == SESSION_INVALID_INDEX)
519    {
520      ECHO_LOG (3, "hash_unset(0x%lx)", handle);
521      hash_unset (em->session_index_by_vpp_handles, handle);
522    }
523  else
524    {
525      ECHO_LOG (3, "hash_set(0x%lx) S[%d]", handle, sid);
526      hash_set (em->session_index_by_vpp_handles, handle, sid);
527    }
528  clib_spinlock_unlock (&em->sid_vpp_handles_lock);
529}
530
531echo_session_t *
532echo_session_new (echo_main_t * em)
533{
534  /* thread safe new prealloced session
535   * see echo_session_prealloc */
536  return pool_elt_at_index (em->sessions,
537			    clib_atomic_fetch_add (&em->nxt_available_sidx,
538						   1));
539}
540
541int
542echo_send_rpc (echo_main_t * em, void *fp, echo_rpc_args_t * args)
543{
544  svm_msg_q_msg_t msg;
545  echo_rpc_msg_t *evt;
546  if (PREDICT_FALSE (svm_msg_q_lock (em->rpc_msq_queue)))
547    {
548      ECHO_FAIL (ECHO_FAIL_RPC_SIZE, "RPC lock failed");
549      return -1;
550    }
551  if (PREDICT_FALSE (svm_msg_q_ring_is_full (em->rpc_msq_queue, 0)))
552    {
553      svm_msg_q_unlock (em->rpc_msq_queue);
554      ECHO_FAIL (ECHO_FAIL_RPC_SIZE, "RPC ring is full");
555      return -2;
556    }
557  msg = svm_msg_q_alloc_msg_w_ring (em->rpc_msq_queue, 0);
558  evt = (echo_rpc_msg_t *) svm_msg_q_msg_data (em->rpc_msq_queue, &msg);
559  evt->fp = fp;
560  clib_memcpy (&evt->args, args, sizeof (evt->args));
561
562  svm_msg_q_add_and_unlock (em->rpc_msq_queue, &msg);
563  return 0;
564}
565
566echo_session_t *
567echo_get_session_from_handle (echo_main_t * em, u64 handle)
568{
569  uword *p;
570  clib_spinlock_lock (&em->sid_vpp_handles_lock);
571  p = hash_get (em->session_index_by_vpp_handles, handle);
572  clib_spinlock_unlock (&em->sid_vpp_handles_lock);
573  if (!p)
574    {
575      ECHO_LOG (2, "unknown handle 0x%lx", handle);
576      return 0;
577    }
578  return pool_elt_at_index (em->sessions, p[0]);
579}
580
581int
582wait_for_segment_allocation (u64 segment_handle)
583{
584  echo_main_t *em = &echo_main;
585  f64 timeout;
586  timeout = clib_time_now (&em->clib_time) + TIMEOUT;
587  uword *segment_present;
588  ECHO_LOG (3, "Waiting for segment 0x%lx...", segment_handle);
589  while (clib_time_now (&em->clib_time) < timeout)
590    {
591      clib_spinlock_lock (&em->segment_handles_lock);
592      segment_present = hash_get (em->shared_segment_handles, segment_handle);
593      clib_spinlock_unlock (&em->segment_handles_lock);
594      if (segment_present != 0)
595	return 0;
596      if (em->time_to_stop == 1)
597	return 0;
598    }
599  ECHO_LOG (2, "timeout wait_for_segment_allocation (0x%lx)", segment_handle);
600  return -1;
601}
602
603int
604wait_for_state_change (echo_main_t * em, connection_state_t state,
605		       f64 timeout)
606{
607  f64 end_time = clib_time_now (&em->clib_time) + timeout;
608  while (!timeout || clib_time_now (&em->clib_time) < end_time)
609    {
610      if (em->state == state)
611	return 0;
612      if (em->time_to_stop)
613	return 1;
614    }
615  ECHO_LOG (2, "timeout waiting for %U", echo_format_app_state, state);
616  return -1;
617}
618
619void
620echo_notify_event (echo_main_t * em, echo_test_evt_t e)
621{
622  if (em->timing.events_sent & e)
623    return;
624  if (em->timing.start_event == e)
625    em->timing.start_time = clib_time_now (&em->clib_time);
626  else if (em->timing.end_event == e)
627    em->timing.end_time = clib_time_now (&em->clib_time);
628  em->timing.events_sent |= e;
629}
630
631void
632echo_session_print_stats (echo_main_t * em, echo_session_t * session)
633{
634  f64 deltat = clib_time_now (&em->clib_time) - session->start;
635  ECHO_LOG (1, "Session 0x%x done in %.6fs RX[%.4f] TX[%.4f] Gbit/s\n",
636	    session->vpp_session_handle, deltat,
637	    (session->bytes_received * 8.0) / deltat / 1e9,
638	    (session->bytes_sent * 8.0) / deltat / 1e9);
639}
640
641/*
642 * fd.io coding-style-patch-verification: ON
643 *
644 * Local Variables:
645 * eval: (c-set-style "gnu")
646 * End:
647 */
648