nsim: add packet loss simulation, docs
[vpp.git] / src / plugins / nsim / node.c
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 {
25   f64 expires;
26   u32 tx_sw_if_index;
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
51 vlib_node_registration_t nsim_node;
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,
62   foreach_nsim_error
63 #undef _
64     NSIM_N_ERROR,
65 } nsim_error_t;
66
67 #ifndef CLIB_MARCH_VARIANT
68 static char *nsim_error_strings[] = {
69 #define _(sym,string) string,
70   foreach_nsim_error
71 #undef _
72 };
73 #endif /* CLIB_MARCH_VARIANT */
74
75 typedef enum
76 {
77   NSIM_NEXT_DROP,
78   NSIM_N_NEXT,
79 } nsim_next_t;
80
81 always_inline uword
82 nsim_inline (vlib_main_t * vm,
83              vlib_node_runtime_t * node, vlib_frame_t * frame, int is_trace)
84 {
85   nsim_main_t *nsm = &nsim_main;
86   u32 n_left_from, *from;
87   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
88   u16 nexts[VLIB_FRAME_SIZE], *next;
89   u32 my_thread_index = vm->thread_index;
90   nsim_wheel_t *wp = nsm->wheel_by_thread[my_thread_index];
91   f64 now = vlib_time_now (vm);
92   f64 expires = now + nsm->delay;
93   int is_drop0;
94   u32 no_error = node->errors[NSIM_ERROR_BUFFERED];
95   u32 no_buffer_error = node->errors[NSIM_ERROR_DROPPED];
96   u32 loss_error = node->errors[NSIM_ERROR_LOSS];
97   nsim_wheel_entry_t *ep = 0;
98
99   ASSERT (wp);
100
101   from = vlib_frame_vector_args (frame);
102   n_left_from = frame->n_vectors;
103
104   vlib_get_buffers (vm, from, bufs, n_left_from);
105   b = bufs;
106   next = nexts;
107
108   /* There is no point in trying to do more than 1 pkt here */
109   while (n_left_from > 0)
110     {
111       b[0]->error = no_error;
112       next[0] = NSIM_NEXT_DROP;
113       is_drop0 = 0;
114       if (PREDICT_TRUE (wp->cursize < wp->wheel_size))
115         {
116           if (PREDICT_FALSE (nsm->drop_fraction != 0.0))
117             {
118               /* Get a random number on the closed interval [0,1] */
119               f64 rnd = random_f64 (&nsm->seed);
120               /* Drop the pkt? */
121               if (rnd <= nsm->drop_fraction)
122                 {
123                   b[0]->error = loss_error;
124                   is_drop0 = 1;
125                   goto do_trace;
126                 }
127             }
128
129           ep = wp->entries + wp->tail;
130           wp->tail++;
131           if (wp->tail == wp->wheel_size)
132             wp->tail = 0;
133           wp->cursize++;
134
135           ep->tx_time = expires;
136           ep->tx_sw_if_index =
137             (vnet_buffer (b[0])->sw_if_index[VLIB_RX] == nsm->sw_if_index0)
138             ? nsm->sw_if_index1 : nsm->sw_if_index0;
139           ep->current_length = vlib_buffer_length_in_chain (vm, b[0]);
140           ASSERT (ep->current_length <= WHEEL_ENTRY_DATA_SIZE);
141           clib_memcpy_fast (ep->data, vlib_buffer_get_current (b[0]),
142                             ep->current_length);
143         }
144       else                      /* out of wheel space, drop pkt */
145         {
146           b[0]->error = no_buffer_error;
147           is_drop0 = 1;
148         }
149
150     do_trace:
151       if (is_trace)
152         {
153           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
154             {
155               nsim_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
156               t->expires = expires;
157               t->is_drop = is_drop0;
158               t->is_lost = b[0]->error == loss_error;
159               t->tx_sw_if_index = (is_drop0 == 0) ? ep->tx_sw_if_index : 0;
160             }
161         }
162
163       b += 1;
164       next += 1;
165       n_left_from -= 1;
166     }
167   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
168   return frame->n_vectors;
169 }
170
171 VLIB_NODE_FN (nsim_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
172                           vlib_frame_t * frame)
173 {
174   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
175     return nsim_inline (vm, node, frame, 1 /* is_trace */ );
176   else
177     return nsim_inline (vm, node, frame, 0 /* is_trace */ );
178 }
179
180 /* *INDENT-OFF* */
181 #ifndef CLIB_MARCH_VARIANT
182 VLIB_REGISTER_NODE (nsim_node) =
183 {
184   .name = "nsim",
185   .vector_size = sizeof (u32),
186   .format_trace = format_nsim_trace,
187   .type = VLIB_NODE_TYPE_INTERNAL,
188
189   .n_errors = ARRAY_LEN(nsim_error_strings),
190   .error_strings = nsim_error_strings,
191
192   .n_next_nodes = NSIM_N_NEXT,
193
194   /* edit / add dispositions here */
195   .next_nodes = {
196         [NSIM_NEXT_DROP] = "error-drop",
197   },
198 };
199 #endif /* CLIB_MARCH_VARIANT */
200 /* *INDENT-ON* */
201
202 /*
203  * fd.io coding-style-patch-verification: ON
204  *
205  * Local Variables:
206  * eval: (c-set-style "gnu")
207  * End:
208  */