blob: 4ac3a1db49649e25167531c642de05ff6fc6d687 [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;
Steven074883a2018-04-24 22:43:07 -070073 span_interface_t *si0;
74 span_mirror_t *sm0;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010075
Steven074883a2018-04-24 22:43:07 -070076 if (sw_if_index0 >= vec_len (sm->interfaces))
77 return;
78
79 si0 = vec_elt_at_index (sm->interfaces, sw_if_index0);
80 sm0 = &si0->mirror_rxtx[sf][rxtx];
Pavel Kotucek077d6ae2017-01-24 08:33:38 +010081
Eyal Bari001fd402017-07-16 09:34:53 +030082 if (sm0->num_mirror_ports == 0)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010083 return;
84
85 /* Don't do it again */
Damjan Marion213b5aa2017-07-13 21:19:27 +020086 if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_SPAN_CLONE))
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010087 return;
88
89 /* *INDENT-OFF* */
Eyal Bari001fd402017-07-16 09:34:53 +030090 clib_bitmap_foreach (i, sm0->mirror_ports, (
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +010091 {
92 if (mirror_frames[i] == 0)
Eyal Bari001fd402017-07-16 09:34:53 +030093 {
94 if (sf == SPAN_FEAT_L2)
John Loa43ccae2018-02-13 17:15:23 -050095 mirror_frames[i] = vlib_get_frame_to_node (vnm->vlib_main,
96 l2output_node.index);
Eyal Bari001fd402017-07-16 09:34:53 +030097 else
98 mirror_frames[i] = vnet_get_frame_to_sw_interface (vnm, i);
99 }
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100100 to_mirror_next = vlib_frame_vector_args (mirror_frames[i]);
101 to_mirror_next += mirror_frames[i]->n_vectors;
Dave Barach26cd8c12017-02-23 17:11:26 -0500102 /* This can fail */
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100103 c0 = vlib_buffer_copy (vm, b0);
Dave Barach26cd8c12017-02-23 17:11:26 -0500104 if (PREDICT_TRUE(c0 != 0))
105 {
106 vnet_buffer (c0)->sw_if_index[VLIB_TX] = i;
Damjan Marion213b5aa2017-07-13 21:19:27 +0200107 c0->flags |= VNET_BUFFER_F_SPAN_CLONE;
Eyal Bari001fd402017-07-16 09:34:53 +0300108 if (sf == SPAN_FEAT_L2)
109 vnet_buffer (c0)->l2.feature_bitmap = L2OUTPUT_FEAT_OUTPUT;
Dave Barach26cd8c12017-02-23 17:11:26 -0500110 to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
111 mirror_frames[i]->n_vectors++;
Pavel Kotucek077d6ae2017-01-24 08:33:38 +0100112 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
113 {
114 span_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
115 t->src_sw_if_index = sw_if_index0;
116 t->mirror_sw_if_index = i;
John Loa43ccae2018-02-13 17:15:23 -0500117#if 0
118 /* Enable this path to allow packet trace of SPAN packets.
119 Note that all SPAN packets will show up on the trace output
120 with the first SPAN packet (since they are in the same frame)
121 thus making trace output of the original packet confusing */
122 mirror_frames[i]->flags |= VLIB_FRAME_TRACE;
123 c0->flags |= VLIB_BUFFER_IS_TRACED;
124#endif
125 }
126 }
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100127 }));
128 /* *INDENT-ON* */
129}
130
131static_always_inline uword
132span_node_inline_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
Eyal Bari001fd402017-07-16 09:34:53 +0300133 vlib_frame_t * frame, vlib_rx_or_tx_t rxtx,
134 span_feat_t sf)
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100135{
136 span_main_t *sm = &span_main;
137 vnet_main_t *vnm = &vnet_main;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100138 u32 n_left_from, *from, *to_next;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100139 u32 n_span_packets = 0;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100140 u32 next_index;
141 u32 sw_if_index;
142 static __thread vlib_frame_t **mirror_frames = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100143
144 from = vlib_frame_vector_args (frame);
145 n_left_from = frame->n_vectors;
146 next_index = node->cached_next_index;
147
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100148 vec_validate_aligned (mirror_frames, sm->max_sw_if_index,
149 CLIB_CACHE_LINE_BYTES);
150
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100151 while (n_left_from > 0)
152 {
153 u32 n_left_to_next;
154
155 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
156
157 while (n_left_from >= 4 && n_left_to_next >= 2)
158 {
159 u32 bi0;
160 u32 bi1;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100161 vlib_buffer_t *b0;
162 vlib_buffer_t *b1;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100163 u32 sw_if_index0;
Pavel Kotuceke2e95ce2016-11-29 11:03:37 +0100164 u32 next0 = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100165 u32 sw_if_index1;
Pavel Kotuceke2e95ce2016-11-29 11:03:37 +0100166 u32 next1 = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100167
168 /* speculatively enqueue b0, b1 to the current next frame */
169 to_next[0] = bi0 = from[0];
170 to_next[1] = bi1 = from[1];
171 to_next += 2;
172 n_left_to_next -= 2;
173 from += 2;
174 n_left_from -= 2;
175
176 b0 = vlib_get_buffer (vm, bi0);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100177 b1 = vlib_get_buffer (vm, bi1);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100178 sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
179 sw_if_index1 = vnet_buffer (b1)->sw_if_index[rxtx];
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100180
Eyal Bari001fd402017-07-16 09:34:53 +0300181 span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
182 span_mirror (vm, node, sw_if_index1, b1, mirror_frames, rxtx, sf);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100183
Eyal Bari001fd402017-07-16 09:34:53 +0300184 switch (sf)
185 {
186 case SPAN_FEAT_L2:
187 if (rxtx == VLIB_RX)
188 {
189 next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
190 L2INPUT_FEAT_SPAN);
191 next1 = vnet_l2_feature_next (b1, sm->l2_input_next,
192 L2INPUT_FEAT_SPAN);
193 }
194 else
195 {
196 next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
197 L2OUTPUT_FEAT_SPAN);
198 next1 = vnet_l2_feature_next (b1, sm->l2_output_next,
199 L2OUTPUT_FEAT_SPAN);
200 }
201 break;
202 case SPAN_FEAT_DEVICE:
203 default:
Damjan Marion7d98a122018-07-19 20:42:08 +0200204 vnet_feature_next (&next0, b0);
205 vnet_feature_next (&next1, b1);
Eyal Bari001fd402017-07-16 09:34:53 +0300206 break;
207 }
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100208
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100209 /* verify speculative enqueue, maybe switch current next frame */
210 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
211 to_next, n_left_to_next,
212 bi0, bi1, next0, next1);
213 }
214 while (n_left_from > 0 && n_left_to_next > 0)
215 {
216 u32 bi0;
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100217 vlib_buffer_t *b0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100218 u32 sw_if_index0;
Pavel Kotuceke2e95ce2016-11-29 11:03:37 +0100219 u32 next0 = 0;
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100220
221 /* speculatively enqueue b0 to the current next frame */
222 to_next[0] = bi0 = from[0];
223 to_next += 1;
224 n_left_to_next -= 1;
225 from += 1;
226 n_left_from -= 1;
227
228 b0 = vlib_get_buffer (vm, bi0);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100229 sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
Pavel Kotucek077d6ae2017-01-24 08:33:38 +0100230
Eyal Bari001fd402017-07-16 09:34:53 +0300231 span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100232
Eyal Bari001fd402017-07-16 09:34:53 +0300233 switch (sf)
234 {
235 case SPAN_FEAT_L2:
236 if (rxtx == VLIB_RX)
237 next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
238 L2INPUT_FEAT_SPAN);
239 else
240 next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
241 L2OUTPUT_FEAT_SPAN);
242 break;
243 case SPAN_FEAT_DEVICE:
244 default:
Damjan Marion7d98a122018-07-19 20:42:08 +0200245 vnet_feature_next (&next0, b0);
Eyal Bari001fd402017-07-16 09:34:53 +0300246 break;
247 }
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100248
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100249 /* verify speculative enqueue, maybe switch current next frame */
250 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
251 n_left_to_next, bi0, next0);
252 }
253
254 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
255 }
256
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100257
258 for (sw_if_index = 0; sw_if_index < vec_len (mirror_frames); sw_if_index++)
259 {
Eyal Bari001fd402017-07-16 09:34:53 +0300260 vlib_frame_t *f = mirror_frames[sw_if_index];
261 if (f == 0)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100262 continue;
263
Eyal Bari001fd402017-07-16 09:34:53 +0300264 if (sf == SPAN_FEAT_L2)
265 vlib_put_frame_to_node (vnm->vlib_main, l2output_node.index, f);
266 else
267 vnet_put_frame_to_sw_interface (vnm, sw_if_index, f);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100268 mirror_frames[sw_if_index] = 0;
269 }
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100270 vlib_node_increment_counter (vm, span_node.index, SPAN_ERROR_HITS,
271 n_span_packets);
272
273 return frame->n_vectors;
274}
275
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100276static uword
Eyal Bari001fd402017-07-16 09:34:53 +0300277span_device_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
278 vlib_frame_t * frame)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100279{
Eyal Bari001fd402017-07-16 09:34:53 +0300280 return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_DEVICE);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100281}
282
283static uword
Eyal Bari001fd402017-07-16 09:34:53 +0300284span_device_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
285 vlib_frame_t * frame)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100286{
Eyal Bari001fd402017-07-16 09:34:53 +0300287 return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_DEVICE);
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100288}
289
Eyal Bari001fd402017-07-16 09:34:53 +0300290static uword
291span_l2_input_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
292 vlib_frame_t * frame)
293{
294 return span_node_inline_fn (vm, node, frame, VLIB_RX, SPAN_FEAT_L2);
295}
296
297static uword
298span_l2_output_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
299 vlib_frame_t * frame)
300{
301 return span_node_inline_fn (vm, node, frame, VLIB_TX, SPAN_FEAT_L2);
302}
303
304#define span_node_defs \
305 .vector_size = sizeof (u32), \
306 .format_trace = format_span_trace, \
307 .type = VLIB_NODE_TYPE_INTERNAL, \
308 .n_errors = ARRAY_LEN(span_error_strings), \
309 .error_strings = span_error_strings, \
310 .n_next_nodes = 0, \
311 .next_nodes = { \
312 [0] = "error-drop" \
313 }
314
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100315/* *INDENT-OFF* */
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100316VLIB_REGISTER_NODE (span_input_node) = {
Eyal Bari001fd402017-07-16 09:34:53 +0300317 span_node_defs,
318 .function = span_device_input_node_fn,
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100319 .name = "span-input",
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100320};
321
Eyal Bari001fd402017-07-16 09:34:53 +0300322VLIB_NODE_FUNCTION_MULTIARCH (span_input_node, span_device_input_node_fn)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100323
324VLIB_REGISTER_NODE (span_output_node) = {
Eyal Bari001fd402017-07-16 09:34:53 +0300325 span_node_defs,
326 .function = span_device_output_node_fn,
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100327 .name = "span-output",
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100328};
329
Eyal Bari001fd402017-07-16 09:34:53 +0300330VLIB_NODE_FUNCTION_MULTIARCH (span_output_node, span_device_output_node_fn)
Pavel Kotucek3a2a1c42016-12-06 10:10:10 +0100331
Eyal Bari001fd402017-07-16 09:34:53 +0300332VLIB_REGISTER_NODE (span_l2_input_node) = {
333 span_node_defs,
334 .function = span_l2_input_node_fn,
335 .name = "span-l2-input",
336};
337
338VLIB_NODE_FUNCTION_MULTIARCH (span_l2_input_node, span_l2_input_node_fn)
339
340VLIB_REGISTER_NODE (span_l2_output_node) = {
341 span_node_defs,
342 .function = span_l2_output_node_fn,
343 .name = "span-l2-output",
344};
345
346VLIB_NODE_FUNCTION_MULTIARCH (span_l2_output_node, span_l2_output_node_fn)
347
348clib_error_t *span_init (vlib_main_t * vm)
349{
350 span_main_t *sm = &span_main;
351
352 sm->vlib_main = vm;
353 sm->vnet_main = vnet_get_main ();
354
355 /* Initialize the feature next-node indexes */
356 feat_bitmap_init_next_nodes (vm,
357 span_l2_input_node.index,
358 L2INPUT_N_FEAT,
359 l2input_get_feat_names (),
360 sm->l2_input_next);
361
362 feat_bitmap_init_next_nodes (vm,
363 span_l2_output_node.index,
364 L2OUTPUT_N_FEAT,
365 l2output_get_feat_names (),
366 sm->l2_output_next);
367 return 0;
368}
369
370VLIB_INIT_FUNCTION (span_init);
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100371/* *INDENT-ON* */
372
Eyal Bari001fd402017-07-16 09:34:53 +0300373#undef span_node_defs
Pavel Kotucekf6e3dc42016-11-04 09:58:01 +0100374/*
375 * fd.io coding-style-patch-verification: ON
376 *
377 * Local Variables:
378 * eval: (c-set-style "gnu")
379 * End:
380 */