blob: 8e2105bcc82a4c21226ffbf41bdb7e206bd3fe81 [file] [log] [blame]
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +01001/*
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>
Eyal Bari001fd402017-07-16 09:34:53 +030021#include <vnet/l2/l2_input.h>
22#include <vnet/l2/l2_output.h>
23#include <vnet/l2/feat_bitmap.h>
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +010024
25#include <vppinfra/error.h>
26#include <vppinfra/elog.h>
27
28vlib_node_registration_t span_node;
29
30/* packet trace format function */
31u8 *
32format_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
49typedef enum
50{
51#define _(sym,str) SPAN_ERROR_##sym,
52 foreach_span_error
53#undef _
54 SPAN_N_ERROR,
55} span_error_t;
56
57static char *span_error_strings[] = {
58#define _(sym,string) string,
59 foreach_span_error
60#undef _
61};
62
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010063static_always_inline void
Pavel Kotucek077d6ae2017-01-24 08:33:38 +010064span_mirror (vlib_main_t * vm, vlib_node_runtime_t * node, u32 sw_if_index0,
Eyal Bari001fd402017-07-16 09:34:53 +030065 vlib_buffer_t * b0, vlib_frame_t ** mirror_frames,
66 vlib_rx_or_tx_t rxtx, span_feat_t sf)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010067{
68 vlib_buffer_t *c0;
Pavel Kotucek077d6ae2017-01-24 08:33:38 +010069 span_main_t *sm = &span_main;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010070 vnet_main_t *vnm = &vnet_main;
71 u32 *to_mirror_next = 0;
72 u32 i;
73
Eyal Bari001fd402017-07-16 09:34:53 +030074 span_interface_t *si0 = vec_elt_at_index (sm->interfaces, sw_if_index0);
75 span_mirror_t *sm0 = &si0->mirror_rxtx[sf][rxtx];
Pavel Kotucek077d6ae2017-01-24 08:33:38 +010076
Eyal Bari001fd402017-07-16 09:34:53 +030077 if (sm0->num_mirror_ports == 0)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010078 return;
79
80 /* Don't do it again */
Damjan Marion213b5aa2017-07-13 21:19:27 +020081 if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_SPAN_CLONE))
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010082 return;
83
84 /* *INDENT-OFF* */
Eyal Bari001fd402017-07-16 09:34:53 +030085 clib_bitmap_foreach (i, sm0->mirror_ports, (
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010086 {
87 if (mirror_frames[i] == 0)
Eyal Bari001fd402017-07-16 09:34:53 +030088 {
89 if (sf == SPAN_FEAT_L2)
John Loa43ccae2018-02-13 17:15:23 -050090 mirror_frames[i] = vlib_get_frame_to_node (vnm->vlib_main,
91 l2output_node.index);
Eyal Bari001fd402017-07-16 09:34:53 +030092 else
93 mirror_frames[i] = vnet_get_frame_to_sw_interface (vnm, i);
94 }
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010095 to_mirror_next = vlib_frame_vector_args (mirror_frames[i]);
96 to_mirror_next += mirror_frames[i]->n_vectors;
Dave Barach26cd8c12017-02-23 17:11:26 -050097 /* This can fail */
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010098 c0 = vlib_buffer_copy (vm, b0);
Dave Barach26cd8c12017-02-23 17:11:26 -050099 if (PREDICT_TRUE(c0 != 0))
100 {
101 vnet_buffer (c0)->sw_if_index[VLIB_TX] = i;
Damjan Marion213b5aa2017-07-13 21:19:27 +0200102 c0->flags |= VNET_BUFFER_F_SPAN_CLONE;
Eyal Bari001fd402017-07-16 09:34:53 +0300103 if (sf == SPAN_FEAT_L2)
104 vnet_buffer (c0)->l2.feature_bitmap = L2OUTPUT_FEAT_OUTPUT;
Dave Barach26cd8c12017-02-23 17:11:26 -0500105 to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
106 mirror_frames[i]->n_vectors++;
Pavel Kotucek077d6ae2017-01-24 08:33:38 +0100107 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;
John Loa43ccae2018-02-13 17:15:23 -0500112#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 }
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100122 }));
123 /* *INDENT-ON* */
124}
125
126static_always_inline uword
127span_node_inline_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
Eyal Bari001fd402017-07-16 09:34:53 +0300128 vlib_frame_t * frame, vlib_rx_or_tx_t rxtx,
129 span_feat_t sf)
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100130{
131 span_main_t *sm = &span_main;
132 vnet_main_t *vnm = &vnet_main;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100133 u32 n_left_from, *from, *to_next;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100134 u32 n_span_packets = 0;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100135 u32 next_index;
136 u32 sw_if_index;
137 static __thread vlib_frame_t **mirror_frames = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100138
139 from = vlib_frame_vector_args (frame);
140 n_left_from = frame->n_vectors;
141 next_index = node->cached_next_index;
142
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100143 vec_validate_aligned (mirror_frames, sm->max_sw_if_index,
144 CLIB_CACHE_LINE_BYTES);
145
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100146 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;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100156 vlib_buffer_t *b0;
157 vlib_buffer_t *b1;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100158 u32 sw_if_index0;
Pavel Kotuceke2e95ce2016-11-29 11:03:37 +0100159 u32 next0 = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100160 u32 sw_if_index1;
Pavel Kotuceke2e95ce2016-11-29 11:03:37 +0100161 u32 next1 = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100162
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);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100172 b1 = vlib_get_buffer (vm, bi1);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100173 sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
174 sw_if_index1 = vnet_buffer (b1)->sw_if_index[rxtx];
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100175
Eyal Bari001fd402017-07-16 09:34:53 +0300176 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);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100178
Eyal Bari001fd402017-07-16 09:34:53 +0300179 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 }
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100203
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100204 /* 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;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100212 vlib_buffer_t *b0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100213 u32 sw_if_index0;
Pavel Kotuceke2e95ce2016-11-29 11:03:37 +0100214 u32 next0 = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100215
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);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100224 sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
Pavel Kotucek077d6ae2017-01-24 08:33:38 +0100225
Eyal Bari001fd402017-07-16 09:34:53 +0300226 span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100227
Eyal Bari001fd402017-07-16 09:34:53 +0300228 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 }
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100243
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100244 /* 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
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100252
253 for (sw_if_index = 0; sw_if_index < vec_len (mirror_frames); sw_if_index++)
254 {
Eyal Bari001fd402017-07-16 09:34:53 +0300255 vlib_frame_t *f = mirror_frames[sw_if_index];
256 if (f == 0)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100257 continue;
258
Eyal Bari001fd402017-07-16 09:34:53 +0300259 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);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100263 mirror_frames[sw_if_index] = 0;
264 }
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100265 vlib_node_increment_counter (vm, span_node.index, SPAN_ERROR_HITS,
266 n_span_packets);
267
268 return frame->n_vectors;
269}
270
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100271static uword
Eyal Bari001fd402017-07-16 09:34:53 +0300272span_device_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
273 vlib_frame_t * frame)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100274{
Eyal Bari001fd402017-07-16 09:34:53 +0300275 return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_DEVICE);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100276}
277
278static uword
Eyal Bari001fd402017-07-16 09:34:53 +0300279span_device_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
280 vlib_frame_t * frame)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100281{
Eyal Bari001fd402017-07-16 09:34:53 +0300282 return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_DEVICE);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100283}
284
Eyal Bari001fd402017-07-16 09:34:53 +0300285static uword
286span_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
292static uword
293span_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
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100310/* *INDENT-OFF* */
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100311VLIB_REGISTER_NODE (span_input_node) = {
Eyal Bari001fd402017-07-16 09:34:53 +0300312 span_node_defs,
313 .function = span_device_input_node_fn,
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100314 .name = "span-input",
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100315};
316
Eyal Bari001fd402017-07-16 09:34:53 +0300317VLIB_NODE_FUNCTION_MULTIARCH (span_input_node, span_device_input_node_fn)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100318
319VLIB_REGISTER_NODE (span_output_node) = {
Eyal Bari001fd402017-07-16 09:34:53 +0300320 span_node_defs,
321 .function = span_device_output_node_fn,
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100322 .name = "span-output",
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100323};
324
Eyal Bari001fd402017-07-16 09:34:53 +0300325VLIB_NODE_FUNCTION_MULTIARCH (span_output_node, span_device_output_node_fn)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100326
Eyal Bari001fd402017-07-16 09:34:53 +0300327VLIB_REGISTER_NODE (span_l2_input_node) = {
328 span_node_defs,
329 .function = span_l2_input_node_fn,
330 .name = "span-l2-input",
331};
332
333VLIB_NODE_FUNCTION_MULTIARCH (span_l2_input_node, span_l2_input_node_fn)
334
335VLIB_REGISTER_NODE (span_l2_output_node) = {
336 span_node_defs,
337 .function = span_l2_output_node_fn,
338 .name = "span-l2-output",
339};
340
341VLIB_NODE_FUNCTION_MULTIARCH (span_l2_output_node, span_l2_output_node_fn)
342
343clib_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
365VLIB_INIT_FUNCTION (span_init);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100366/* *INDENT-ON* */
367
Eyal Bari001fd402017-07-16 09:34:53 +0300368#undef span_node_defs
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100369/*
370 * fd.io coding-style-patch-verification: ON
371 *
372 * Local Variables:
373 * eval: (c-set-style "gnu")
374 * End:
375 */