FD.io VPP  v19.08.1-401-g8e4ed521a
Vector Packet Processing
vpp_echo_common.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 char *echo_fail_code_str[] = {
22 #define _(sym, str) str,
24 #undef _
25 };
26 
27 /*
28  *
29  * Format functions
30  *
31  */
32 
33 u8 *
34 format_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 
40 u8 *
41 format_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. */
91 u8 *
92 format_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 
116 uword
117 unformat_data (unformat_input_t * input, va_list * args)
118 {
119  u64 _a;
120  u64 *a = va_arg (*args, u64 *);
121  if (unformat (input, "%lluGb", &_a))
122  *a = _a << 30;
123  else if (unformat (input, "%lluG", &_a))
124  *a = _a << 30;
125  else if (unformat (input, "%lluMb", &_a))
126  *a = _a << 20;
127  else if (unformat (input, "%lluM", &_a))
128  *a = _a << 20;
129  else if (unformat (input, "%lluKb", &_a))
130  *a = _a << 10;
131  else if (unformat (input, "%lluK", &_a))
132  *a = _a << 10;
133  else if (unformat (input, "%llu", a))
134  ;
135  else
136  return 0;
137  return 1;
138 }
139 
140 u8 *
141 format_api_error (u8 * s, va_list * args)
142 {
143  echo_main_t *em = &echo_main;
144  i32 error = va_arg (*args, u32);
145  uword *p;
146 
147  p = hash_get (em->error_string_by_error_number, -error);
148 
149  if (p)
150  s = format (s, "%s", p[0]);
151  else
152  s = format (s, "%d", error);
153  return s;
154 }
155 
156 void
158 {
159  echo_main_t *em = &echo_main;
160  em->error_string_by_error_number = hash_create (0, sizeof (uword));
161 
162 #define _(n,v,s) hash_set (em->error_string_by_error_number, -v, s);
164 #undef _
165 
166  hash_set (em->error_string_by_error_number, 99, "Misc");
167 }
168 
169 u8 *
170 echo_format_app_state (u8 * s, va_list * args)
171 {
172  u32 state = va_arg (*args, u32);
173  if (state == STATE_START)
174  return format (s, "STATE_START");
175  if (state == STATE_ATTACHED)
176  return format (s, "STATE_ATTACHED");
177  if (state == STATE_LISTEN)
178  return format (s, "STATE_LISTEN");
179  if (state == STATE_READY)
180  return format (s, "STATE_READY");
181  if (state == STATE_DATA_DONE)
182  return format (s, "STATE_DATA_DONE");
183  if (state == STATE_DISCONNECTED)
184  return format (s, "STATE_DISCONNECTED");
185  if (state == STATE_DETACHED)
186  return format (s, "STATE_DETACHED");
187  else
188  return format (s, "unknown state");
189 }
190 
191 uword
192 echo_unformat_close (unformat_input_t * input, va_list * args)
193 {
194  u8 *a = va_arg (*args, u8 *);
195  if (unformat (input, "Y"))
196  *a = ECHO_CLOSE_F_ACTIVE;
197  else if (unformat (input, "N"))
198  *a = ECHO_CLOSE_F_NONE;
199  else if (unformat (input, "W"))
201  else
202  return 0;
203  return 1;
204 }
205 
206 uword
208 {
209  u8 *a = va_arg (*args, u8 *);
210  if (unformat (input, "start"))
211  *a = ECHO_EVT_START;
212  else if (unformat (input, "qconnected"))
214  else if (unformat (input, "qconnect"))
216  else if (unformat (input, "sconnected"))
218  else if (unformat (input, "sconnect"))
220  else if (unformat (input, "lastbyte"))
221  *a = ECHO_EVT_LAST_BYTE;
222  else if (unformat (input, "exit"))
223  *a = ECHO_EVT_EXIT;
224  else
225  return 0;
226  return 1;
227 }
228 
229 u8 *
230 echo_format_timing_event (u8 * s, va_list * args)
231 {
232  u32 timing_event = va_arg (*args, u32);
233  if (timing_event == ECHO_EVT_START)
234  return format (s, "start");
235  if (timing_event == ECHO_EVT_FIRST_QCONNECT)
236  return format (s, "qconnect");
237  if (timing_event == ECHO_EVT_LAST_QCONNECTED)
238  return format (s, "qconnected");
239  if (timing_event == ECHO_EVT_FIRST_SCONNECT)
240  return format (s, "sconnect");
241  if (timing_event == ECHO_EVT_LAST_SCONNECTED)
242  return format (s, "sconnected");
243  if (timing_event == ECHO_EVT_LAST_BYTE)
244  return format (s, "lastbyte");
245  if (timing_event == ECHO_EVT_EXIT)
246  return format (s, "exit");
247  else
248  return format (s, "unknown timing event");
249 }
250 
251 uword
253 {
254  u32 *proto = va_arg (*args, u32 *);
255  if (unformat (input, "tcp"))
256  *proto = TRANSPORT_PROTO_TCP;
257  else if (unformat (input, "TCP"))
258  *proto = TRANSPORT_PROTO_TCP;
259  else if (unformat (input, "udpc"))
260  *proto = TRANSPORT_PROTO_UDPC;
261  else if (unformat (input, "UDPC"))
262  *proto = TRANSPORT_PROTO_UDPC;
263  else if (unformat (input, "udp"))
264  *proto = TRANSPORT_PROTO_UDP;
265  else if (unformat (input, "UDP"))
266  *proto = TRANSPORT_PROTO_UDP;
267  else if (unformat (input, "sctp"))
268  *proto = TRANSPORT_PROTO_SCTP;
269  else if (unformat (input, "SCTP"))
270  *proto = TRANSPORT_PROTO_SCTP;
271  else if (unformat (input, "tls"))
272  *proto = TRANSPORT_PROTO_TLS;
273  else if (unformat (input, "TLS"))
274  *proto = TRANSPORT_PROTO_TLS;
275  else if (unformat (input, "quic"))
276  *proto = TRANSPORT_PROTO_QUIC;
277  else if (unformat (input, "QUIC"))
278  *proto = TRANSPORT_PROTO_QUIC;
279  else
280  return 0;
281  return 1;
282 }
283 
284 u8 *
285 format_transport_proto (u8 * s, va_list * args)
286 {
287  u32 transport_proto = va_arg (*args, u32);
288  switch (transport_proto)
289  {
290  case TRANSPORT_PROTO_TCP:
291  s = format (s, "TCP");
292  break;
293  case TRANSPORT_PROTO_UDP:
294  s = format (s, "UDP");
295  break;
296  case TRANSPORT_PROTO_SCTP:
297  s = format (s, "SCTP");
298  break;
299  case TRANSPORT_PROTO_NONE:
300  s = format (s, "NONE");
301  break;
302  case TRANSPORT_PROTO_TLS:
303  s = format (s, "TLS");
304  break;
305  case TRANSPORT_PROTO_UDPC:
306  s = format (s, "UDPC");
307  break;
308  case TRANSPORT_PROTO_QUIC:
309  s = format (s, "QUIC");
310  break;
311  default:
312  s = format (s, "UNKNOWN");
313  break;
314  }
315  return s;
316 }
317 
318 uword
319 unformat_ip4_address (unformat_input_t * input, va_list * args)
320 {
321  u8 *result = va_arg (*args, u8 *);
322  unsigned a[4];
323 
324  if (!unformat (input, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]))
325  return 0;
326 
327  if (a[0] >= 256 || a[1] >= 256 || a[2] >= 256 || a[3] >= 256)
328  return 0;
329 
330  result[0] = a[0];
331  result[1] = a[1];
332  result[2] = a[2];
333  result[3] = a[3];
334 
335  return 1;
336 }
337 
338 uword
339 unformat_ip6_address (unformat_input_t * input, va_list * args)
340 {
341  ip6_address_t *result = va_arg (*args, ip6_address_t *);
342  u16 hex_quads[8];
343  uword hex_quad, n_hex_quads, hex_digit, n_hex_digits;
344  uword c, n_colon, double_colon_index;
345 
346  n_hex_quads = hex_quad = n_hex_digits = n_colon = 0;
347  double_colon_index = ARRAY_LEN (hex_quads);
348  while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
349  {
350  hex_digit = 16;
351  if (c >= '0' && c <= '9')
352  hex_digit = c - '0';
353  else if (c >= 'a' && c <= 'f')
354  hex_digit = c + 10 - 'a';
355  else if (c >= 'A' && c <= 'F')
356  hex_digit = c + 10 - 'A';
357  else if (c == ':' && n_colon < 2)
358  n_colon++;
359  else
360  {
361  unformat_put_input (input);
362  break;
363  }
364 
365  /* Too many hex quads. */
366  if (n_hex_quads >= ARRAY_LEN (hex_quads))
367  return 0;
368 
369  if (hex_digit < 16)
370  {
371  hex_quad = (hex_quad << 4) | hex_digit;
372 
373  /* Hex quad must fit in 16 bits. */
374  if (n_hex_digits >= 4)
375  return 0;
376 
377  n_colon = 0;
378  n_hex_digits++;
379  }
380 
381  /* Save position of :: */
382  if (n_colon == 2)
383  {
384  /* More than one :: ? */
385  if (double_colon_index < ARRAY_LEN (hex_quads))
386  return 0;
387  double_colon_index = n_hex_quads;
388  }
389 
390  if (n_colon > 0 && n_hex_digits > 0)
391  {
392  hex_quads[n_hex_quads++] = hex_quad;
393  hex_quad = 0;
394  n_hex_digits = 0;
395  }
396  }
397 
398  if (n_hex_digits > 0)
399  hex_quads[n_hex_quads++] = hex_quad;
400 
401  {
402  word i;
403 
404  /* Expand :: to appropriate number of zero hex quads. */
405  if (double_colon_index < ARRAY_LEN (hex_quads))
406  {
407  word n_zero = ARRAY_LEN (hex_quads) - n_hex_quads;
408 
409  for (i = n_hex_quads - 1; i >= (signed) double_colon_index; i--)
410  hex_quads[n_zero + i] = hex_quads[i];
411 
412  for (i = 0; i < n_zero; i++)
413  hex_quads[double_colon_index + i] = 0;
414 
415  n_hex_quads = ARRAY_LEN (hex_quads);
416  }
417 
418  /* Too few hex quads given. */
419  if (n_hex_quads < ARRAY_LEN (hex_quads))
420  return 0;
421 
422  for (i = 0; i < ARRAY_LEN (hex_quads); i++)
423  result->as_u16[i] = clib_host_to_net_u16 (hex_quads[i]);
424 
425  return 1;
426  }
427 }
428 
429 /*
430  *
431  * End of format functions
432  *
433  */
434 
435 void
437 {
439  if (sid == SESSION_INVALID_INDEX)
441  else
442  hash_set (em->session_index_by_vpp_handles, handle, sid);
444 }
445 
448 {
449  /* thread safe new prealloced session */
450  return pool_elt_at_index (em->sessions,
452  1));
453 }
454 
455 int
456 echo_send_rpc (echo_main_t * em, void *fp, void *arg, u32 opaque)
457 {
458  svm_msg_q_msg_t msg;
459  echo_rpc_msg_t *evt;
461  {
462  ECHO_LOG (1, "RPC lock failed");
463  return -1;
464  }
466  {
468  ECHO_LOG (1, "RPC ring is full");
469  return -2;
470  }
472  evt = (echo_rpc_msg_t *) svm_msg_q_msg_data (em->rpc_msq_queue, &msg);
473  evt->arg = arg;
474  evt->opaque = opaque;
475  evt->fp = fp;
476 
478  return 0;
479 }
480 
483 {
484  uword *p;
486  p = hash_get (em->session_index_by_vpp_handles, handle);
488  if (!p)
489  {
490  ECHO_FAIL (ECHO_FAIL_GET_SESSION_FROM_HANDLE,
491  "unknown handle 0x%lx", handle);
492  return 0;
493  }
494  return pool_elt_at_index (em->sessions, p[0]);
495 }
496 
497 int
499 {
500  echo_main_t *em = &echo_main;
501  f64 timeout;
502  timeout = clib_time_now (&em->clib_time) + TIMEOUT;
503  uword *segment_present;
504  ECHO_LOG (1, "Waiting for segment 0x%lx...", segment_handle);
505  while (clib_time_now (&em->clib_time) < timeout)
506  {
508  segment_present = hash_get (em->shared_segment_handles, segment_handle);
510  if (segment_present != 0)
511  return 0;
512  if (em->time_to_stop == 1)
513  return 0;
514  }
515  ECHO_LOG (1, "timeout wait_for_segment_allocation (0x%lx)", segment_handle);
516  return -1;
517 }
518 
519 int
521  f64 timeout)
522 {
523  f64 end_time = clib_time_now (&em->clib_time) + timeout;
524  while (!timeout || clib_time_now (&em->clib_time) < end_time)
525  {
526  if (em->state == state)
527  return 0;
528  if (em->time_to_stop)
529  return 1;
530  }
531  ECHO_LOG (1, "timeout waiting for %U", echo_format_app_state, state);
532  return -1;
533 }
534 
535 void
537 {
538  if (em->timing.events_sent & e)
539  return;
540  if (em->timing.start_event == e)
542  else if (em->timing.end_event == e)
543  em->timing.end_time = clib_time_now (&em->clib_time);
544  em->timing.events_sent |= e;
545 }
546 
547 void
549 {
550  f64 deltat = clib_time_now (&em->clib_time) - session->start;
551  ECHO_LOG (0, "Session 0x%x done in %.6fs RX[%.4f] TX[%.4f] Gbit/s\n",
552  session->vpp_session_handle, deltat,
553  (session->bytes_received * 8.0) / deltat / 1e9,
554  (session->bytes_sent * 8.0) / deltat / 1e9);
555 }
556 
557 /*
558  * fd.io coding-style-patch-verification: ON
559  *
560  * Local Variables:
561  * eval: (c-set-style "gnu")
562  * End:
563  */
uword unformat_data(unformat_input_t *input, va_list *args)
clib_time_t clib_time
#define hash_set(h, key, value)
Definition: hash.h:255
void * svm_msg_q_msg_data(svm_msg_q_t *mq, svm_msg_q_msg_t *msg)
Get data for message in queue.
static u8 svm_msg_q_ring_is_full(svm_msg_q_t *mq, u32 ring_index)
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:102
static_always_inline void clib_spinlock_lock(clib_spinlock_t *p)
Definition: lock.h:80
u8 * echo_format_timing_event(u8 *s, va_list *args)
#define hash_unset(h, key)
Definition: hash.h:261
a
Definition: bitmap.h:538
uword unformat_ip6_address(unformat_input_t *input, va_list *args)
echo_session_t * sessions
u8 * format_ip6_address(u8 *s, va_list *args)
clib_spinlock_t sid_vpp_handles_lock
static uword unformat_get_input(unformat_input_t *input)
Definition: format.h:192
unsigned long u64
Definition: types.h:89
static f64 clib_time_now(clib_time_t *c)
Definition: time.h:230
uword echo_unformat_timing_event(unformat_input_t *input, va_list *args)
uword unformat_ip4_address(unformat_input_t *input, va_list *args)
int i
echo_main_t echo_main
Definition: vpp_echo.c:24
#define TIMEOUT
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
volatile connection_state_t state
char * echo_fail_code_str[]
unsigned char u8
Definition: types.h:56
int wait_for_state_change(echo_main_t *em, connection_state_t state, f64 timeout)
foreach_app_session_field u64 vpp_session_handle
double f64
Definition: types.h:142
echo_session_t * echo_session_new(echo_main_t *em)
i64 word
Definition: types.h:111
connection_state_t
volatile u64 bytes_received
void echo_session_handle_add_del(echo_main_t *em, u64 handle, u32 sid)
clib_spinlock_t segment_handles_lock
vhost_vring_state_t state
Definition: vhost_user.h:146
#define ECHO_FAIL(fail, _fmt, _args...)
unsigned int u32
Definition: types.h:88
vl_api_fib_path_type_t type
Definition: fib_types.api:123
#define hash_get(h, key)
Definition: hash.h:249
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
void init_error_string_table()
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
#define PREDICT_FALSE(x)
Definition: clib.h:111
u8 * echo_format_app_state(u8 *s, va_list *args)
uword * error_string_by_error_number
static void unformat_put_input(unformat_input_t *input)
Definition: format.h:205
#define SESSION_INVALID_INDEX
Definition: session_types.h:22
#define ip46_address_is_ip4(ip46)
Definition: ip6_packet.h:88
u8 * format_ip46_address(u8 *s, va_list *args)
enum echo_test_evt_ echo_test_evt_t
#define UNFORMAT_END_OF_INPUT
Definition: format.h:145
svmdb_client_t * c
static void svm_msg_q_unlock(svm_msg_q_t *mq)
Unlock message queue.
u8 * format_ip4_address(u8 *s, va_list *args)
uword * shared_segment_handles
#define ECHO_LOG(lvl, _fmt, _args...)
void echo_notify_event(echo_main_t *em, echo_test_evt_t e)
volatile u8 time_to_stop
#define ARRAY_LEN(x)
Definition: clib.h:62
uword unformat_transport_proto(unformat_input_t *input, va_list *args)
#define foreach_vnet_api_error
Definition: api_errno.h:22
struct echo_main_t::@482 timing
signed int i32
Definition: types.h:77
#define hash_create(elts, value_bytes)
Definition: hash.h:696
void svm_msg_q_add_and_unlock(svm_msg_q_t *mq, svm_msg_q_msg_t *msg)
Producer enqueue one message to queue with mutex held.
volatile u32 nxt_available_sidx
ip46_type_t
Definition: ip6_packet.h:70
echo_session_t * echo_get_session_from_handle(echo_main_t *em, u64 handle)
#define clib_atomic_fetch_add(a, b)
Definition: atomics.h:23
u64 uword
Definition: types.h:112
u8 * format_api_error(u8 *s, va_list *args)
uword * session_index_by_vpp_handles
int wait_for_segment_allocation(u64 segment_handle)
svm_msg_q_t * rpc_msq_queue
int echo_send_rpc(echo_main_t *em, void *fp, void *arg, u32 opaque)
u16 as_u16[8]
Definition: ip6_packet.h:49
static int svm_msg_q_lock(svm_msg_q_t *mq)
Lock, or block trying, the message queue.
void echo_session_print_stats(echo_main_t *em, echo_session_t *session)
svm_msg_q_msg_t svm_msg_q_alloc_msg_w_ring(svm_msg_q_t *mq, u32 ring_index)
Allocate message buffer on ring.
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
vl_api_fib_path_nh_proto_t proto
Definition: fib_types.api:125
uword echo_unformat_close(unformat_input_t *input, va_list *args)
u8 * format_transport_proto(u8 *s, va_list *args)