FD.io VPP  v20.05-21-gb1500e9ff
Vector Packet Processing
node.c
Go to the documentation of this file.
1 /*
2  * node.c - skeleton vpp engine plug-in dual-loop node skeleton
3  *
4  * Copyright (c) <current-year> <your-organization>
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vppinfra/error.h>
21 #include <nsim/nsim.h>
22 
23 typedef struct
24 {
27  int is_drop;
28  int is_lost;
29 } nsim_trace_t;
30 
31 #ifndef CLIB_MARCH_VARIANT
32 
33 /* packet trace format function */
34 static u8 *
35 format_nsim_trace (u8 * s, va_list * args)
36 {
37  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
38  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
39  nsim_trace_t *t = va_arg (*args, nsim_trace_t *);
40 
41  if (t->is_drop)
42  s = format (s, "NSIM: dropped, %s", t->is_lost ?
43  "simulated network loss" : "no space in ring");
44  else
45  s = format (s, "NSIM: tx time %.6f sw_if_index %d",
46  t->expires, t->tx_sw_if_index);
47 
48  return s;
49 }
50 
52 #endif /* CLIB_MARCH_VARIANT */
53 
54 #define foreach_nsim_error \
55 _(BUFFERED, "Packets buffered") \
56 _(DROPPED, "Packets dropped due to lack of space") \
57 _(LOSS, "Network loss simulation drop packets")
58 
59 typedef enum
60 {
61 #define _(sym,str) NSIM_ERROR_##sym,
63 #undef _
65 } nsim_error_t;
66 
67 #ifndef CLIB_MARCH_VARIANT
68 static char *nsim_error_strings[] = {
69 #define _(sym,string) string,
71 #undef _
72 };
73 #endif /* CLIB_MARCH_VARIANT */
74 
75 typedef enum
76 {
79 } nsim_next_t;
80 
83  vlib_node_runtime_t * node, vlib_frame_t * frame, int is_trace,
84  int is_cross_connect)
85 {
86  nsim_main_t *nsm = &nsim_main;
87  u32 n_left_from, *from;
88  u32 *to_next, n_left_to_next;
89  u32 drops[VLIB_FRAME_SIZE], *drop;
90  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
91  u8 is_drop[4];
92  u16 nexts[VLIB_FRAME_SIZE], *next;
93  u32 my_thread_index = vm->thread_index;
94  nsim_wheel_t *wp = nsm->wheel_by_thread[my_thread_index];
95  f64 now = vlib_time_now (vm);
96  f64 expires = now + nsm->delay;
97  f64 rnd[4];
98  u32 no_buffer_error = node->errors[NSIM_ERROR_DROPPED];
99  u32 loss_error = node->errors[NSIM_ERROR_LOSS];
100  u32 buffered = 0;
101  nsim_wheel_entry_t *ep = 0;
102 
103  ASSERT (wp);
104 
105  from = vlib_frame_vector_args (frame);
106  n_left_from = frame->n_vectors;
107 
108  vlib_get_buffers (vm, from, bufs, n_left_from);
109  b = bufs;
110  next = nexts;
111  drop = drops;
112 
113  while (n_left_from >= 8)
114  {
115  vlib_prefetch_buffer_header (b[4], STORE);
116  vlib_prefetch_buffer_header (b[5], STORE);
117  vlib_prefetch_buffer_header (b[6], STORE);
118  vlib_prefetch_buffer_header (b[7], STORE);
119 
120  memset (&is_drop, 0, sizeof (is_drop));
121  next[0] = next[1] = next[2] = next[3] = NSIM_NEXT_DROP;
122  if (PREDICT_FALSE (wp->cursize + 4 >= wp->wheel_size))
123  goto slow_path;
124  if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
125  {
126  rnd[0] = random_f64 (&nsm->seed);
127  rnd[1] = random_f64 (&nsm->seed);
128  rnd[2] = random_f64 (&nsm->seed);
129  rnd[3] = random_f64 (&nsm->seed);
130 
131  if (rnd[0] <= nsm->drop_fraction)
132  {
133  b[0]->error = loss_error;
134  is_drop[0] = 1;
135  }
136  if (rnd[1] <= nsm->drop_fraction)
137  {
138  b[1]->error = loss_error;
139  is_drop[1] = 1;
140  }
141  if (rnd[2] <= nsm->drop_fraction)
142  {
143  b[2]->error = loss_error;
144  is_drop[2] = 1;
145  }
146  if (rnd[3] <= nsm->drop_fraction)
147  {
148  b[3]->error = loss_error;
149  is_drop[3] = 1;
150  }
151  }
152 
153  if (PREDICT_TRUE (is_drop[0] == 0))
154  {
155  ep = wp->entries + wp->tail;
156  wp->tail++;
157  if (wp->tail == wp->wheel_size)
158  wp->tail = 0;
159  wp->cursize++;
160 
161  ep->tx_time = expires;
162  ep->rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
163  if (is_cross_connect)
164  {
165  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
166  (vnet_buffer (b[0])->sw_if_index[VLIB_RX] ==
167  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
168  ep->output_next_index =
169  (ep->tx_sw_if_index ==
170  nsm->sw_if_index0) ? nsm->
171  output_next_index0 : nsm->output_next_index1;
172  }
173  else /* output feature, even easier... */
174  {
175  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
176  ep->output_next_index =
178  }
179  ep->buffer_index = from[0];
180  buffered++;
181  }
182  if (is_trace)
183  {
184  if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
185  {
186  nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
187  t->expires = expires;
188  t->is_drop = is_drop[0];
189  t->is_lost = b[0]->error == loss_error;
190  t->tx_sw_if_index = (is_drop[0] == 0) ? ep->tx_sw_if_index : 0;
191  }
192  }
193 
194  if (PREDICT_TRUE (is_drop[1] == 0))
195  {
196  ep = wp->entries + wp->tail;
197  wp->tail++;
198  if (wp->tail == wp->wheel_size)
199  wp->tail = 0;
200  wp->cursize++;
201 
202  ep->tx_time = expires;
203  ep->rx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
204  if (is_cross_connect)
205  {
206  ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX] =
207  (vnet_buffer (b[1])->sw_if_index[VLIB_RX] ==
208  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
209  ep->output_next_index =
210  (ep->tx_sw_if_index ==
211  nsm->sw_if_index0) ? nsm->
212  output_next_index0 : nsm->output_next_index1;
213  }
214  else /* output feature, even easier... */
215  {
216  ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
217  ep->output_next_index =
219  }
220  ep->buffer_index = from[1];
221  buffered++;
222  }
223 
224  if (is_trace)
225  {
226  if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
227  {
228  nsim_trace_t *t = vlib_add_trace (vm, node, b[1], sizeof (*t));
229  t->expires = expires;
230  t->is_drop = is_drop[1];
231  t->is_lost = b[1]->error == loss_error;
232  t->tx_sw_if_index = (is_drop[1] == 0) ? ep->tx_sw_if_index : 0;
233  }
234  }
235 
236  if (PREDICT_TRUE (is_drop[2] == 0))
237  {
238  ep = wp->entries + wp->tail;
239  wp->tail++;
240  if (wp->tail == wp->wheel_size)
241  wp->tail = 0;
242  wp->cursize++;
243 
244  ep->tx_time = expires;
245  ep->rx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
246  if (is_cross_connect)
247  {
248  ep->tx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_TX] =
249  (vnet_buffer (b[2])->sw_if_index[VLIB_RX] ==
250  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
251  ep->output_next_index =
252  (ep->tx_sw_if_index ==
253  nsm->sw_if_index0) ? nsm->
254  output_next_index0 : nsm->output_next_index1;
255  }
256  else /* output feature, even easier... */
257  {
258  ep->tx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
259  ep->output_next_index =
261  }
262  ep->buffer_index = from[2];
263  buffered++;
264  }
265 
266  if (is_trace)
267  {
268  if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
269  {
270  nsim_trace_t *t = vlib_add_trace (vm, node, b[2], sizeof (*t));
271  t->expires = expires;
272  t->is_drop = is_drop[2];
273  t->is_lost = b[2]->error == loss_error;
274  t->tx_sw_if_index = (is_drop[2] == 0) ? ep->tx_sw_if_index : 0;
275  }
276  }
277 
278  if (PREDICT_TRUE (is_drop[3] == 0))
279  {
280  ep = wp->entries + wp->tail;
281  wp->tail++;
282  if (wp->tail == wp->wheel_size)
283  wp->tail = 0;
284  wp->cursize++;
285 
286  ep->tx_time = expires;
287  ep->rx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
288  if (is_cross_connect)
289  {
290  ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX] =
291  (vnet_buffer (b[3])->sw_if_index[VLIB_RX] ==
292  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
293  ep->output_next_index =
294  (ep->tx_sw_if_index ==
295  nsm->sw_if_index0) ? nsm->
296  output_next_index0 : nsm->output_next_index1;
297  }
298  else /* output feature, even easier... */
299  {
300  ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
301  ep->output_next_index =
303  }
304  ep->buffer_index = from[3];
305  buffered++;
306  }
307 
308  if (is_trace)
309  {
310  if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
311  {
312  nsim_trace_t *t = vlib_add_trace (vm, node, b[3], sizeof (*t));
313  t->expires = expires;
314  t->is_drop = is_drop[3];
315  t->is_lost = b[3]->error == loss_error;
316  t->tx_sw_if_index = (is_drop[3] == 0) ? ep->tx_sw_if_index : 0;
317  }
318  }
319 
320  if (PREDICT_FALSE (is_drop[0]))
321  *drop++ = from[0];
322  if (PREDICT_FALSE (is_drop[1]))
323  *drop++ = from[1];
324  if (PREDICT_FALSE (is_drop[2]))
325  *drop++ = from[2];
326  if (PREDICT_FALSE (is_drop[3]))
327  *drop++ = from[3];
328 
329  b += 4;
330  next += 4;
331  from += 4;
332  n_left_from -= 4;
333  }
334 
335 slow_path:
336 
337  while (n_left_from > 0)
338  {
339  next[0] = NSIM_NEXT_DROP;
340  is_drop[0] = 0;
341  if (PREDICT_TRUE (wp->cursize < wp->wheel_size))
342  {
343  if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
344  {
345  /* Get a random number on the closed interval [0,1] */
346  rnd[0] = random_f64 (&nsm->seed);
347  /* Drop the pkt? */
348  if (rnd[0] <= nsm->drop_fraction)
349  {
350  b[0]->error = loss_error;
351  is_drop[0] = 1;
352  goto do_trace;
353  }
354  }
355 
356  ep = wp->entries + wp->tail;
357  wp->tail++;
358  if (wp->tail == wp->wheel_size)
359  wp->tail = 0;
360  wp->cursize++;
361 
362  ep->tx_time = expires;
363  ep->rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
364  if (is_cross_connect)
365  {
366  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
367  (vnet_buffer (b[0])->sw_if_index[VLIB_RX] ==
368  nsm->sw_if_index0) ? nsm->sw_if_index1 : nsm->sw_if_index0;
369  ep->output_next_index =
370  (ep->tx_sw_if_index ==
371  nsm->sw_if_index0) ? nsm->
372  output_next_index0 : nsm->output_next_index1;
373  }
374  else /* output feature, even easier... */
375  {
376  ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
377  ep->output_next_index =
379  }
380  ep->buffer_index = from[0];
381  buffered++;
382  }
383  else /* out of wheel space, drop pkt */
384  {
385  b[0]->error = no_buffer_error;
386  is_drop[0] = 1;
387  }
388 
389  do_trace:
390  if (is_trace)
391  {
392  if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
393  {
394  nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
395  t->expires = expires;
396  t->is_drop = is_drop[0];
397  t->is_lost = b[0]->error == loss_error;
398  t->tx_sw_if_index = (is_drop[0] == 0) ? ep->tx_sw_if_index : 0;
399  }
400  }
401 
402  b += 1;
403  next += 1;
404  if (PREDICT_FALSE (is_drop[0]))
405  {
406  drop[0] = from[0];
407  drop++;
408  }
409  from++;
410  n_left_from -= 1;
411  }
412  if (PREDICT_FALSE (drop > drops))
413  {
414  u32 n_left_to_drop = drop - drops;
415  drop = drops;
416 
417  while (n_left_to_drop > 0)
418  {
419  u32 this_copy_size;
420  vlib_get_next_frame (vm, node, NSIM_NEXT_DROP, to_next,
421  n_left_to_next);
422  this_copy_size = clib_min (n_left_to_drop, n_left_to_next);
423  clib_memcpy_fast (to_next, drop, this_copy_size * sizeof (u32));
424  n_left_to_next -= this_copy_size;
425  vlib_put_next_frame (vm, node, NSIM_NEXT_DROP, n_left_to_next);
426  drop += this_copy_size;
427  n_left_to_drop -= this_copy_size;
428  }
429  }
431  NSIM_ERROR_BUFFERED, buffered);
432  return frame->n_vectors;
433 }
434 
437 {
438  if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
439  return nsim_inline (vm, node, frame,
440  1 /* is_trace */ , 1 /* is_cross_connect */ );
441  else
442  return nsim_inline (vm, node, frame,
443  0 /* is_trace */ , 1 /* is_cross_connect */ );
444 }
445 
446 /* *INDENT-OFF* */
447 #ifndef CLIB_MARCH_VARIANT
449 {
450  .name = "nsim",
451  .vector_size = sizeof (u32),
452  .format_trace = format_nsim_trace,
454 
455  .n_errors = ARRAY_LEN(nsim_error_strings),
456  .error_strings = nsim_error_strings,
457 
458  .n_next_nodes = NSIM_N_NEXT,
459 
460  /* edit / add dispositions here */
461  .next_nodes = {
462  [NSIM_NEXT_DROP] = "error-drop",
463  },
464 };
465 #endif /* CLIB_MARCH_VARIANT */
466 /* *INDENT-ON* */
467 
471 {
472  if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
473  return nsim_inline (vm, node, frame,
474  1 /* is_trace */ , 0 /* is_cross_connect */ );
475  else
476  return nsim_inline (vm, node, frame,
477  0 /* is_trace */ , 0 /* is_cross_connect */ );
478 }
479 
480 /* *INDENT-OFF* */
481 #ifndef CLIB_MARCH_VARIANT
483 {
484  .name = "nsim-output-feature",
485  .vector_size = sizeof (u32),
486  .format_trace = format_nsim_trace,
488 
489  .n_errors = ARRAY_LEN(nsim_error_strings),
490  .error_strings = nsim_error_strings,
491 
492  .n_next_nodes = NSIM_N_NEXT,
493 
494  /* edit / add dispositions here */
495  .next_nodes = {
496  [NSIM_NEXT_DROP] = "error-drop",
497  },
498 };
499 #endif /* CLIB_MARCH_VARIANT */
500 /* *INDENT-ON* */
501 
502 /*
503  * fd.io coding-style-patch-verification: ON
504  *
505  * Local Variables:
506  * eval: (c-set-style "gnu")
507  * End:
508  */
u32 * output_next_index_by_sw_if_index
Definition: nsim.h:58
static char * nsim_error_strings[]
Definition: node.c:68
#define clib_min(x, y)
Definition: clib.h:319
#define CLIB_UNUSED(x)
Definition: clib.h:86
nsim_error_t
Definition: node.c:59
#define PREDICT_TRUE(x)
Definition: clib.h:119
Definition: nsim.h:28
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:291
vlib_node_registration_t nsim_node
(constructor) VLIB_REGISTER_NODE (nsim_node)
Definition: node.c:51
u32 thread_index
Definition: main.h:218
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
#define VLIB_NODE_FN(node)
Definition: node.h:202
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:472
vlib_node_registration_t nsim_feature_node
(constructor) VLIB_REGISTER_NODE (nsim_feature_node)
Definition: node.c:482
unsigned char u8
Definition: types.h:56
#define foreach_nsim_error
Definition: node.c:54
u32 tx_sw_if_index
Definition: node.c:26
double f64
Definition: types.h:142
u32 tx_sw_if_index
Definition: nsim.h:32
u32 cursize
Definition: nsim.h:41
static u32 slow_path(dslite_main_t *dm, dslite_session_key_t *in2out_key, dslite_session_t **sp, u32 next, u8 *error, u32 thread_index)
Definition: dslite_in2out.c:34
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:203
unsigned int u32
Definition: types.h:88
f64 expires
Definition: node.c:25
#define VLIB_FRAME_SIZE
Definition: node.h:380
int is_drop
Definition: node.c:27
vl_api_fib_path_type_t type
Definition: fib_types.api:123
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:136
unsigned short u16
Definition: types.h:57
nsim_next_t
Definition: node.c:75
#define PREDICT_FALSE(x)
Definition: clib.h:118
#define always_inline
Definition: ipsec.h:28
u32 tail
Definition: nsim.h:43
u32 node_index
Node index.
Definition: node.h:498
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Get pointer to next frame vector data by (vlib_node_runtime_t, next_index).
Definition: node_funcs.h:338
vlib_main_t * vm
Definition: in2out_ed.c:1599
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1150
u32 sw_if_index1
Definition: nsim.h:54
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
u32 flags
Definition: vhost_user.h:248
u16 n_vectors
Definition: node.h:399
nsim_wheel_entry_t * entries
Definition: nsim.h:44
#define ARRAY_LEN(x)
Definition: clib.h:66
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Release pointer to next frame vector data.
Definition: main.c:483
vlib_main_t vlib_node_runtime_t * node
Definition: in2out_ed.c:1599
#define ASSERT(truth)
static uword nsim_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, int is_trace, int is_cross_connect)
Definition: node.c:82
nsim_wheel_t ** wheel_by_thread
Definition: nsim.h:64
static u8 * format_nsim_trace(u8 *s, va_list *args)
Definition: node.c:35
static f64 random_f64(u32 *seed)
Generate f64 random number in the interval [0,1].
Definition: random.h:145
static void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:55
struct _vlib_node_registration vlib_node_registration_t
Definition: defs.h:47
u32 seed
Definition: nsim.h:61
u32 sw_if_index0
Definition: nsim.h:54
f64 tx_time
Definition: nsim.h:30
u32 buffer_index
Definition: nsim.h:34
VLIB buffer representation.
Definition: buffer.h:102
u64 uword
Definition: types.h:112
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:244
int is_lost
Definition: node.c:28
f64 delay
Definition: nsim.h:67
#define vnet_buffer(b)
Definition: buffer.h:417
vlib_main_t vlib_node_runtime_t vlib_frame_t * frame
Definition: in2out_ed.c:1600
static_always_inline void vlib_get_buffers(vlib_main_t *vm, u32 *bi, vlib_buffer_t **b, int count)
Translate array of buffer indices into buffer pointers.
Definition: buffer_funcs.h:280
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:304
u32 output_next_index
Definition: nsim.h:33
f64 drop_fraction
Definition: nsim.h:70
u32 wheel_size
Definition: nsim.h:40
u32 output_next_index1
Definition: nsim.h:55
nsim_main_t nsim_main
Definition: nsim.c:39
u32 rx_sw_if_index
Definition: nsim.h:31
Definition: defs.h:46