Optimize GRE Tunnel and add support for ERSPAN encap
[vpp.git] / src / vnet / span / node.c
1 /*
2  * Copyright (c) 2016 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 <vlib/vlib.h>
17 #include <vnet/vnet.h>
18 #include <vppinfra/error.h>
19
20 #include <vnet/span/span.h>
21 #include <vnet/l2/l2_input.h>
22 #include <vnet/l2/l2_output.h>
23 #include <vnet/l2/feat_bitmap.h>
24
25 #include <vppinfra/error.h>
26 #include <vppinfra/elog.h>
27
28 vlib_node_registration_t span_node;
29
30 /* packet trace format function */
31 u8 *
32 format_span_trace (u8 * s, va_list * args)
33 {
34   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
35   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
36   span_trace_t *t = va_arg (*args, span_trace_t *);
37
38   vnet_main_t *vnm = &vnet_main;
39   s = format (s, "SPAN: mirrored %U -> %U",
40               format_vnet_sw_if_index_name, vnm, t->src_sw_if_index,
41               format_vnet_sw_if_index_name, vnm, t->mirror_sw_if_index);
42
43   return s;
44 }
45
46 #define foreach_span_error                      \
47 _(HITS, "SPAN incomming packets processed")
48
49 typedef enum
50 {
51 #define _(sym,str) SPAN_ERROR_##sym,
52   foreach_span_error
53 #undef _
54     SPAN_N_ERROR,
55 } span_error_t;
56
57 static char *span_error_strings[] = {
58 #define _(sym,string) string,
59   foreach_span_error
60 #undef _
61 };
62
63 static_always_inline void
64 span_mirror (vlib_main_t * vm, vlib_node_runtime_t * node, u32 sw_if_index0,
65              vlib_buffer_t * b0, vlib_frame_t ** mirror_frames,
66              vlib_rx_or_tx_t rxtx, span_feat_t sf)
67 {
68   vlib_buffer_t *c0;
69   span_main_t *sm = &span_main;
70   vnet_main_t *vnm = &vnet_main;
71   u32 *to_mirror_next = 0;
72   u32 i;
73
74   span_interface_t *si0 = vec_elt_at_index (sm->interfaces, sw_if_index0);
75   span_mirror_t *sm0 = &si0->mirror_rxtx[sf][rxtx];
76
77   if (sm0->num_mirror_ports == 0)
78     return;
79
80   /* Don't do it again */
81   if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_SPAN_CLONE))
82     return;
83
84   /* *INDENT-OFF* */
85   clib_bitmap_foreach (i, sm0->mirror_ports, (
86     {
87       if (mirror_frames[i] == 0)
88         {
89           if (sf == SPAN_FEAT_L2)
90             mirror_frames[i] = vlib_get_frame_to_node (vnm->vlib_main,
91                                                        l2output_node.index);
92           else
93             mirror_frames[i] = vnet_get_frame_to_sw_interface (vnm, i);
94         }
95       to_mirror_next = vlib_frame_vector_args (mirror_frames[i]);
96       to_mirror_next += mirror_frames[i]->n_vectors;
97       /* This can fail */
98       c0 = vlib_buffer_copy (vm, b0);
99       if (PREDICT_TRUE(c0 != 0))
100         {
101           vnet_buffer (c0)->sw_if_index[VLIB_TX] = i;
102           c0->flags |= VNET_BUFFER_F_SPAN_CLONE;
103           if (sf == SPAN_FEAT_L2)
104             vnet_buffer (c0)->l2.feature_bitmap = L2OUTPUT_FEAT_OUTPUT;
105           to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
106           mirror_frames[i]->n_vectors++;
107           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
108             {
109               span_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
110               t->src_sw_if_index = sw_if_index0;
111               t->mirror_sw_if_index = i;
112 #if 0
113               /* Enable this path to allow packet trace of SPAN packets.
114                  Note that all SPAN packets will show up on the trace output
115                  with the first SPAN packet (since they are in the same frame)
116                  thus making trace output of the original packet confusing */
117               mirror_frames[i]->flags |= VLIB_FRAME_TRACE;
118               c0->flags |= VLIB_BUFFER_IS_TRACED;
119 #endif
120             }
121         }
122     }));
123   /* *INDENT-ON* */
124 }
125
126 static_always_inline uword
127 span_node_inline_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
128                      vlib_frame_t * frame, vlib_rx_or_tx_t rxtx,
129                      span_feat_t sf)
130 {
131   span_main_t *sm = &span_main;
132   vnet_main_t *vnm = &vnet_main;
133   u32 n_left_from, *from, *to_next;
134   u32 n_span_packets = 0;
135   u32 next_index;
136   u32 sw_if_index;
137   static __thread vlib_frame_t **mirror_frames = 0;
138
139   from = vlib_frame_vector_args (frame);
140   n_left_from = frame->n_vectors;
141   next_index = node->cached_next_index;
142
143   vec_validate_aligned (mirror_frames, sm->max_sw_if_index,
144                         CLIB_CACHE_LINE_BYTES);
145
146   while (n_left_from > 0)
147     {
148       u32 n_left_to_next;
149
150       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
151
152       while (n_left_from >= 4 && n_left_to_next >= 2)
153         {
154           u32 bi0;
155           u32 bi1;
156           vlib_buffer_t *b0;
157           vlib_buffer_t *b1;
158           u32 sw_if_index0;
159           u32 next0 = 0;
160           u32 sw_if_index1;
161           u32 next1 = 0;
162
163           /* speculatively enqueue b0, b1 to the current next frame */
164           to_next[0] = bi0 = from[0];
165           to_next[1] = bi1 = from[1];
166           to_next += 2;
167           n_left_to_next -= 2;
168           from += 2;
169           n_left_from -= 2;
170
171           b0 = vlib_get_buffer (vm, bi0);
172           b1 = vlib_get_buffer (vm, bi1);
173           sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
174           sw_if_index1 = vnet_buffer (b1)->sw_if_index[rxtx];
175
176           span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
177           span_mirror (vm, node, sw_if_index1, b1, mirror_frames, rxtx, sf);
178
179           switch (sf)
180             {
181             case SPAN_FEAT_L2:
182               if (rxtx == VLIB_RX)
183                 {
184                   next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
185                                                 L2INPUT_FEAT_SPAN);
186                   next1 = vnet_l2_feature_next (b1, sm->l2_input_next,
187                                                 L2INPUT_FEAT_SPAN);
188                 }
189               else
190                 {
191                   next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
192                                                 L2OUTPUT_FEAT_SPAN);
193                   next1 = vnet_l2_feature_next (b1, sm->l2_output_next,
194                                                 L2OUTPUT_FEAT_SPAN);
195                 }
196               break;
197             case SPAN_FEAT_DEVICE:
198             default:
199               vnet_feature_next (sw_if_index0, &next0, b0);
200               vnet_feature_next (sw_if_index1, &next1, b1);
201               break;
202             }
203
204           /* verify speculative enqueue, maybe switch current next frame */
205           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
206                                            to_next, n_left_to_next,
207                                            bi0, bi1, next0, next1);
208         }
209       while (n_left_from > 0 && n_left_to_next > 0)
210         {
211           u32 bi0;
212           vlib_buffer_t *b0;
213           u32 sw_if_index0;
214           u32 next0 = 0;
215
216           /* speculatively enqueue b0 to the current next frame */
217           to_next[0] = bi0 = from[0];
218           to_next += 1;
219           n_left_to_next -= 1;
220           from += 1;
221           n_left_from -= 1;
222
223           b0 = vlib_get_buffer (vm, bi0);
224           sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
225
226           span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
227
228           switch (sf)
229             {
230             case SPAN_FEAT_L2:
231               if (rxtx == VLIB_RX)
232                 next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
233                                               L2INPUT_FEAT_SPAN);
234               else
235                 next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
236                                               L2OUTPUT_FEAT_SPAN);
237               break;
238             case SPAN_FEAT_DEVICE:
239             default:
240               vnet_feature_next (sw_if_index0, &next0, b0);
241               break;
242             }
243
244           /* verify speculative enqueue, maybe switch current next frame */
245           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
246                                            n_left_to_next, bi0, next0);
247         }
248
249       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
250     }
251
252
253   for (sw_if_index = 0; sw_if_index < vec_len (mirror_frames); sw_if_index++)
254     {
255       vlib_frame_t *f = mirror_frames[sw_if_index];
256       if (f == 0)
257         continue;
258
259       if (sf == SPAN_FEAT_L2)
260         vlib_put_frame_to_node (vnm->vlib_main, l2output_node.index, f);
261       else
262         vnet_put_frame_to_sw_interface (vnm, sw_if_index, f);
263       mirror_frames[sw_if_index] = 0;
264     }
265   vlib_node_increment_counter (vm, span_node.index, SPAN_ERROR_HITS,
266                                n_span_packets);
267
268   return frame->n_vectors;
269 }
270
271 static uword
272 span_device_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
273                            vlib_frame_t * frame)
274 {
275   return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_DEVICE);
276 }
277
278 static uword
279 span_device_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
280                             vlib_frame_t * frame)
281 {
282   return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_DEVICE);
283 }
284
285 static uword
286 span_l2_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
287                        vlib_frame_t * frame)
288 {
289   return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_L2);
290 }
291
292 static uword
293 span_l2_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
294                         vlib_frame_t * frame)
295 {
296   return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_L2);
297 }
298
299 #define span_node_defs                           \
300   .vector_size = sizeof (u32),                   \
301   .format_trace = format_span_trace,             \
302   .type = VLIB_NODE_TYPE_INTERNAL,               \
303   .n_errors = ARRAY_LEN(span_error_strings),     \
304   .error_strings = span_error_strings,           \
305   .n_next_nodes = 0,                             \
306   .next_nodes = {                                \
307     [0] = "error-drop"                           \
308   }
309
310 /* *INDENT-OFF* */
311 VLIB_REGISTER_NODE (span_input_node) = {
312   span_node_defs,
313   .function = span_device_input_node_fn,
314   .name = "span-input",
315 };
316
317 VLIB_NODE_FUNCTION_MULTIARCH (span_input_node, span_device_input_node_fn)
318
319 VLIB_REGISTER_NODE (span_output_node) = {
320   span_node_defs,
321   .function = span_device_output_node_fn,
322   .name = "span-output",
323 };
324
325 VLIB_NODE_FUNCTION_MULTIARCH (span_output_node, span_device_output_node_fn)
326
327 VLIB_REGISTER_NODE (span_l2_input_node) = {
328   span_node_defs,
329   .function = span_l2_input_node_fn,
330   .name = "span-l2-input",
331 };
332
333 VLIB_NODE_FUNCTION_MULTIARCH (span_l2_input_node, span_l2_input_node_fn)
334
335 VLIB_REGISTER_NODE (span_l2_output_node) = {
336   span_node_defs,
337   .function = span_l2_output_node_fn,
338   .name = "span-l2-output",
339 };
340
341 VLIB_NODE_FUNCTION_MULTIARCH (span_l2_output_node, span_l2_output_node_fn)
342
343 clib_error_t *span_init (vlib_main_t * vm)
344 {
345   span_main_t *sm = &span_main;
346
347   sm->vlib_main = vm;
348   sm->vnet_main = vnet_get_main ();
349
350   /* Initialize the feature next-node indexes */
351   feat_bitmap_init_next_nodes (vm,
352                                span_l2_input_node.index,
353                                L2INPUT_N_FEAT,
354                                l2input_get_feat_names (),
355                                sm->l2_input_next);
356
357   feat_bitmap_init_next_nodes (vm,
358                                span_l2_output_node.index,
359                                L2OUTPUT_N_FEAT,
360                                l2output_get_feat_names (),
361                                sm->l2_output_next);
362   return 0;
363 }
364
365 VLIB_INIT_FUNCTION (span_init);
366 /* *INDENT-ON* */
367
368 #undef span_node_defs
369 /*
370  * fd.io coding-style-patch-verification: ON
371  *
372  * Local Variables:
373  * eval: (c-set-style "gnu")
374  * End:
375  */