2 * node.c - skeleton vpp engine plug-in dual-loop node skeleton
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
31 #ifndef CLIB_MARCH_VARIANT
33 /* packet trace format function */
35 format_nsim_trace (u8 * s, va_list * args)
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 *);
42 s = format (s, "NSIM: dropped, %s", t->is_lost ?
43 "simulated network loss" : "no space in ring");
45 s = format (s, "NSIM: tx time %.6f sw_if_index %d",
46 t->expires, t->tx_sw_if_index);
51 vlib_node_registration_t nsim_node;
52 #endif /* CLIB_MARCH_VARIANT */
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")
61 #define _(sym,str) NSIM_ERROR_##sym,
67 #ifndef CLIB_MARCH_VARIANT
68 static char *nsim_error_strings[] = {
69 #define _(sym,string) string,
73 #endif /* CLIB_MARCH_VARIANT */
82 nsim_inline (vlib_main_t * vm,
83 vlib_node_runtime_t * node, vlib_frame_t * frame, int is_trace,
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;
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;
98 u32 no_buffer_error = node->errors[NSIM_ERROR_DROPPED];
99 u32 loss_error = node->errors[NSIM_ERROR_LOSS];
101 nsim_wheel_entry_t *ep = 0;
105 from = vlib_frame_vector_args (frame);
106 n_left_from = frame->n_vectors;
108 vlib_get_buffers (vm, from, bufs, n_left_from);
113 while (n_left_from >= 8)
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);
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))
124 if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
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);
131 if (rnd[0] <= nsm->drop_fraction)
133 b[0]->error = loss_error;
136 if (rnd[1] <= nsm->drop_fraction)
138 b[1]->error = loss_error;
141 if (rnd[2] <= nsm->drop_fraction)
143 b[2]->error = loss_error;
146 if (rnd[3] <= nsm->drop_fraction)
148 b[3]->error = loss_error;
153 if (PREDICT_TRUE (is_drop[0] == 0))
155 ep = wp->entries + wp->tail;
157 if (wp->tail == wp->wheel_size)
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)
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;
173 else /* output feature, even easier... */
175 ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
176 ep->output_next_index =
177 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
179 ep->buffer_index = from[0];
184 if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
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;
194 if (PREDICT_TRUE (is_drop[1] == 0))
196 ep = wp->entries + wp->tail;
198 if (wp->tail == wp->wheel_size)
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)
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;
214 else /* output feature, even easier... */
216 ep->tx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
217 ep->output_next_index =
218 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
220 ep->buffer_index = from[1];
226 if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
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;
236 if (PREDICT_TRUE (is_drop[2] == 0))
238 ep = wp->entries + wp->tail;
240 if (wp->tail == wp->wheel_size)
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)
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;
256 else /* output feature, even easier... */
258 ep->tx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
259 ep->output_next_index =
260 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
262 ep->buffer_index = from[2];
268 if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
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;
278 if (PREDICT_TRUE (is_drop[3] == 0))
280 ep = wp->entries + wp->tail;
282 if (wp->tail == wp->wheel_size)
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)
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;
298 else /* output feature, even easier... */
300 ep->tx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
301 ep->output_next_index =
302 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
304 ep->buffer_index = from[3];
310 if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
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;
320 if (PREDICT_FALSE (is_drop[0]))
322 if (PREDICT_FALSE (is_drop[1]))
324 if (PREDICT_FALSE (is_drop[2]))
326 if (PREDICT_FALSE (is_drop[3]))
337 while (n_left_from > 0)
339 next[0] = NSIM_NEXT_DROP;
341 if (PREDICT_TRUE (wp->cursize < wp->wheel_size))
343 if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
345 /* Get a random number on the closed interval [0,1] */
346 rnd[0] = random_f64 (&nsm->seed);
348 if (rnd[0] <= nsm->drop_fraction)
350 b[0]->error = loss_error;
356 ep = wp->entries + wp->tail;
358 if (wp->tail == wp->wheel_size)
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)
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;
374 else /* output feature, even easier... */
376 ep->tx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
377 ep->output_next_index =
378 nsm->output_next_index_by_sw_if_index[ep->tx_sw_if_index];
380 ep->buffer_index = from[0];
383 else /* out of wheel space, drop pkt */
385 b[0]->error = no_buffer_error;
392 if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
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;
404 if (PREDICT_FALSE (is_drop[0]))
412 if (PREDICT_FALSE (drop > drops))
414 u32 n_left_to_drop = drop - drops;
417 while (n_left_to_drop > 0)
420 vlib_get_next_frame (vm, node, NSIM_NEXT_DROP, 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;
430 vlib_node_increment_counter (vm, node->node_index,
431 NSIM_ERROR_BUFFERED, buffered);
432 return frame->n_vectors;
435 VLIB_NODE_FN (nsim_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
436 vlib_frame_t * frame)
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 */ );
442 return nsim_inline (vm, node, frame,
443 0 /* is_trace */ , 1 /* is_cross_connect */ );
447 #ifndef CLIB_MARCH_VARIANT
448 VLIB_REGISTER_NODE (nsim_node) =
451 .vector_size = sizeof (u32),
452 .format_trace = format_nsim_trace,
453 .type = VLIB_NODE_TYPE_INTERNAL,
455 .n_errors = ARRAY_LEN(nsim_error_strings),
456 .error_strings = nsim_error_strings,
458 .n_next_nodes = NSIM_N_NEXT,
460 /* edit / add dispositions here */
462 [NSIM_NEXT_DROP] = "error-drop",
465 #endif /* CLIB_MARCH_VARIANT */
468 VLIB_NODE_FN (nsim_feature_node) (vlib_main_t * vm,
469 vlib_node_runtime_t * node,
470 vlib_frame_t * frame)
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 */ );
476 return nsim_inline (vm, node, frame,
477 0 /* is_trace */ , 0 /* is_cross_connect */ );
481 #ifndef CLIB_MARCH_VARIANT
482 VLIB_REGISTER_NODE (nsim_feature_node) =
484 .name = "nsim-output-feature",
485 .vector_size = sizeof (u32),
486 .format_trace = format_nsim_trace,
487 .type = VLIB_NODE_TYPE_INTERNAL,
489 .n_errors = ARRAY_LEN(nsim_error_strings),
490 .error_strings = nsim_error_strings,
492 .n_next_nodes = NSIM_N_NEXT,
494 /* edit / add dispositions here */
496 [NSIM_NEXT_DROP] = "error-drop",
499 #endif /* CLIB_MARCH_VARIANT */
503 * fd.io coding-style-patch-verification: ON
506 * eval: (c-set-style "gnu")