6109228b0b55ab7a850c14db7f249cff490aa160
[vpp.git] / src / vnet / l2 / l2_patch.c
1 /*
2  * Copyright (c) 2015 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 #include <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vnet/pg/pg.h>
18 #include <vnet/ethernet/ethernet.h>
19 #include <vnet/feature/feature.h>
20 #include <vppinfra/error.h>
21
22 typedef struct
23 {
24   /* vector of dispositions, indexed by rx_sw_if_index */
25   u32 *tx_next_by_rx_sw_if_index;
26   u32 *tx_sw_if_index_by_rx_sw_if_index;
27
28   /* convenience variables */
29   vlib_main_t *vlib_main;
30   vnet_main_t *vnet_main;
31 } l2_patch_main_t;
32
33 typedef struct
34 {
35   u32 rx_sw_if_index;
36   u32 tx_sw_if_index;
37 } l2_patch_trace_t;
38
39 /* packet trace format function */
40 static u8 *
41 format_l2_patch_trace (u8 * s, va_list * args)
42 {
43   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
44   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
45   l2_patch_trace_t *t = va_arg (*args, l2_patch_trace_t *);
46
47   s = format (s, "L2_PATCH: rx %d tx %d", t->rx_sw_if_index,
48               t->tx_sw_if_index);
49   return s;
50 }
51
52 static l2_patch_main_t l2_patch_main;
53
54 extern vlib_node_registration_t l2_patch_node;
55
56 #define foreach_l2_patch_error                  \
57 _(PATCHED, "L2 patch packets")                  \
58 _(DROPPED, "L2 patch misconfigured drops")
59
60 typedef enum
61 {
62 #define _(sym,str) L2_PATCH_ERROR_##sym,
63   foreach_l2_patch_error
64 #undef _
65     L2_PATCH_N_ERROR,
66 } l2_patch_error_t;
67
68 static char *l2_patch_error_strings[] = {
69 #define _(sym,string) string,
70   foreach_l2_patch_error
71 #undef _
72 };
73
74 typedef enum
75 {
76   L2_PATCH_NEXT_DROP,
77   L2_PATCH_N_NEXT,
78 } l2_patch_next_t;
79
80 static_always_inline void
81 l2_patch_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
82                 l2_patch_main_t * l2pm, vlib_buffer_t * b, u32 sw_if_index)
83 {
84   l2_patch_trace_t *t;
85
86   if ((b->flags & VLIB_BUFFER_IS_TRACED) == 0)
87     return;
88
89   t = vlib_add_trace (vm, node, b, sizeof (*t));
90   t->rx_sw_if_index = sw_if_index;
91   t->tx_sw_if_index = l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index];
92 }
93
94 static_always_inline void
95 l2_patch_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
96                  l2_patch_main_t * l2pm, vlib_buffer_t ** b, u16 * next,
97                  u32 n_left, int do_trace)
98 {
99   u32 sw_if_index[4];
100
101   while (n_left >= 4)
102     {
103       /* Prefetch next iteration. */
104       if (n_left >= 8)
105         {
106           vlib_buffer_t **p = b + 4;
107           vlib_prefetch_buffer_header (p[0], LOAD);
108           vlib_prefetch_buffer_header (p[1], LOAD);
109           vlib_prefetch_buffer_header (p[2], LOAD);
110           vlib_prefetch_buffer_header (p[3], LOAD);
111         }
112
113       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
114       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
115       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
116       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
117
118       ASSERT (l2pm->tx_next_by_rx_sw_if_index[sw_if_index[0]] != ~0);
119       ASSERT (l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[0]] != ~0);
120       ASSERT (l2pm->tx_next_by_rx_sw_if_index[sw_if_index[1]] != ~0);
121       ASSERT (l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[1]] != ~0);
122       ASSERT (l2pm->tx_next_by_rx_sw_if_index[sw_if_index[2]] != ~0);
123       ASSERT (l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[2]] != ~0);
124       ASSERT (l2pm->tx_next_by_rx_sw_if_index[sw_if_index[3]] != ~0);
125       ASSERT (l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[3]] != ~0);
126
127       next[0] = l2pm->tx_next_by_rx_sw_if_index[sw_if_index[0]];
128       next[1] = l2pm->tx_next_by_rx_sw_if_index[sw_if_index[1]];
129       next[2] = l2pm->tx_next_by_rx_sw_if_index[sw_if_index[2]];
130       next[3] = l2pm->tx_next_by_rx_sw_if_index[sw_if_index[3]];
131
132       vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
133         l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[0]];
134       vnet_buffer (b[1])->sw_if_index[VLIB_TX] =
135         l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[1]];
136       vnet_buffer (b[2])->sw_if_index[VLIB_TX] =
137         l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[2]];
138       vnet_buffer (b[3])->sw_if_index[VLIB_TX] =
139         l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[3]];
140
141       if (do_trace)
142         {
143           l2_patch_trace (vm, node, l2pm, b[0], sw_if_index[0]);
144           l2_patch_trace (vm, node, l2pm, b[1], sw_if_index[1]);
145           l2_patch_trace (vm, node, l2pm, b[2], sw_if_index[2]);
146           l2_patch_trace (vm, node, l2pm, b[3], sw_if_index[3]);
147         }
148
149       /* next */
150       next += 4;
151       b += 4;
152       n_left -= 4;
153     }
154
155   while (n_left)
156     {
157       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
158
159       ASSERT (l2pm->tx_next_by_rx_sw_if_index[sw_if_index[0]] != ~0);
160       ASSERT (l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[0]] != ~0);
161
162       next[0] = l2pm->tx_next_by_rx_sw_if_index[sw_if_index[0]];
163
164       vnet_buffer (b[0])->sw_if_index[VLIB_TX] =
165         l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index[0]];
166
167       if (do_trace)
168         l2_patch_trace (vm, node, l2pm, b[0], sw_if_index[0]);
169
170       /* next */
171       next += 1;
172       b += 1;
173       n_left -= 1;
174     }
175 }
176
177 VLIB_NODE_FN (l2_patch_node) (vlib_main_t * vm,
178                               vlib_node_runtime_t * node,
179                               vlib_frame_t * frame)
180 {
181   u32 *from;
182   l2_patch_main_t *l2pm = &l2_patch_main;
183   vlib_node_t *n = vlib_get_node (vm, l2_patch_node.index);
184   u32 node_counter_base_index = n->error_heap_index;
185   vlib_error_main_t *em = &vm->error_main;
186   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
187   u16 nexts[VLIB_FRAME_SIZE];
188
189   from = vlib_frame_vector_args (frame);
190
191   vlib_get_buffers (vm, from, bufs, frame->n_vectors);
192
193   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
194     l2_patch_inline (vm, node, l2pm, bufs, nexts, frame->n_vectors, 1);
195   else
196     l2_patch_inline (vm, node, l2pm, bufs, nexts, frame->n_vectors, 0);
197
198   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
199
200   em->counters[node_counter_base_index + L2_PATCH_ERROR_PATCHED] +=
201     frame->n_vectors;
202
203   return frame->n_vectors;
204 }
205
206 /* *INDENT-OFF* */
207 VLIB_REGISTER_NODE (l2_patch_node) = {
208   .name = "l2-patch",
209   .vector_size = sizeof (u32),
210   .format_trace = format_l2_patch_trace,
211   .type = VLIB_NODE_TYPE_INTERNAL,
212
213   .n_errors = ARRAY_LEN(l2_patch_error_strings),
214   .error_strings = l2_patch_error_strings,
215
216   .n_next_nodes = L2_PATCH_N_NEXT,
217
218   /* edit / add dispositions here */
219   .next_nodes = {
220         [L2_PATCH_NEXT_DROP] = "error-drop",
221   },
222 };
223 /* *INDENT-ON* */
224
225 extern int
226 vnet_l2_patch_add_del (u32 rx_sw_if_index, u32 tx_sw_if_index, int is_add);
227 #ifndef CLIB_MARCH_VARIANT
228 int
229 vnet_l2_patch_add_del (u32 rx_sw_if_index, u32 tx_sw_if_index, int is_add)
230 {
231   l2_patch_main_t *l2pm = &l2_patch_main;
232   vnet_hw_interface_t *rxhi, *txhi;
233   u32 tx_next_index;
234
235   /*
236    * We assume that the API msg handler has used 2x VALIDATE_SW_IF_INDEX
237    * macros...
238    */
239
240   rxhi = vnet_get_sup_hw_interface (l2pm->vnet_main, rx_sw_if_index);
241
242   /* Make sure caller didn't pass a vlan subif, etc. */
243   if (rxhi->sw_if_index != rx_sw_if_index)
244     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
245
246   txhi = vnet_get_sup_hw_interface (l2pm->vnet_main, tx_sw_if_index);
247   if (txhi->sw_if_index != tx_sw_if_index)
248     return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
249
250   if (is_add)
251     {
252       tx_next_index = vlib_node_add_next (l2pm->vlib_main,
253                                           l2_patch_node.index,
254                                           txhi->output_node_index);
255
256       vec_validate_init_empty (l2pm->tx_next_by_rx_sw_if_index,
257                                rx_sw_if_index, ~0);
258
259       l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = tx_next_index;
260       vec_validate_init_empty (l2pm->tx_sw_if_index_by_rx_sw_if_index,
261                                rx_sw_if_index, ~0);
262       l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index]
263         = txhi->sw_if_index;
264
265       ethernet_set_flags (l2pm->vnet_main, rxhi->hw_if_index,
266                           ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
267
268       vnet_feature_enable_disable ("device-input", "l2-patch",
269                                    rxhi->hw_if_index, 1, 0, 0);
270     }
271   else
272     {
273       ethernet_set_flags (l2pm->vnet_main, rxhi->hw_if_index,
274                           0 /* disable promiscuous mode */ );
275
276       vnet_feature_enable_disable ("device-input", "l2-patch",
277                                    rxhi->hw_if_index, 0, 0, 0);
278       if (vec_len (l2pm->tx_next_by_rx_sw_if_index) > rx_sw_if_index)
279         {
280           l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = ~0;
281           l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index] = ~0;
282         }
283     }
284
285   return 0;
286 }
287 #endif
288
289 static clib_error_t *
290 test_patch_command_fn (vlib_main_t * vm,
291                        unformat_input_t * input, vlib_cli_command_t * cmd)
292 {
293   l2_patch_main_t *l2pm = &l2_patch_main;
294   unformat_input_t _line_input, *line_input = &_line_input;
295   u32 rx_sw_if_index, tx_sw_if_index;
296   int rv;
297   int rx_set = 0;
298   int tx_set = 0;
299   int is_add = 1;
300   clib_error_t *error = NULL;
301
302   /* Get a line of input. */
303   if (!unformat_user (input, unformat_line_input, line_input))
304     return 0;
305
306   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
307     {
308       if (unformat (line_input, "rx %U", unformat_vnet_sw_interface,
309                     l2pm->vnet_main, &rx_sw_if_index))
310         rx_set = 1;
311       else if (unformat (line_input, "tx %U", unformat_vnet_sw_interface,
312                          l2pm->vnet_main, &tx_sw_if_index))
313         tx_set = 1;
314       else if (unformat (line_input, "del"))
315         is_add = 0;
316       else
317         break;
318     }
319
320   if (rx_set == 0)
321     {
322       error = clib_error_return (0, "rx interface not set");
323       goto done;
324     }
325
326   if (tx_set == 0)
327     {
328       error = clib_error_return (0, "tx interface not set");
329       goto done;
330     }
331
332   rv = vnet_l2_patch_add_del (rx_sw_if_index, tx_sw_if_index, is_add);
333
334   switch (rv)
335     {
336     case 0:
337       break;
338
339     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
340       error = clib_error_return (0, "rx interface not a physical port");
341       goto done;
342
343     case VNET_API_ERROR_INVALID_SW_IF_INDEX_2:
344       error = clib_error_return (0, "tx interface not a physical port");
345       goto done;
346
347     default:
348       error = clib_error_return
349         (0, "WARNING: vnet_l2_patch_add_del returned %d", rv);
350       goto done;
351     }
352
353
354 done:
355   unformat_free (line_input);
356
357   return error;
358 }
359
360 /*?
361  * Create or delete a Layer 2 patch.
362  *
363  * @cliexpar
364  * @cliexstart{test l2patch rx <intfc> tx <intfc> [del]}
365  * @cliexend
366  * @todo This is incomplete. This needs a detailed description and a
367  * practical example.
368 ?*/
369 /* *INDENT-OFF* */
370 VLIB_CLI_COMMAND (test_patch_command, static) = {
371     .path = "test l2patch",
372     .short_help = "test l2patch rx <intfc> tx <intfc> [del]",
373     .function = test_patch_command_fn,
374 };
375 /* *INDENT-ON* */
376
377 /** Display the contents of the l2patch table. */
378 static clib_error_t *
379 show_l2patch (vlib_main_t * vm,
380               unformat_input_t * input, vlib_cli_command_t * cmd)
381 {
382   l2_patch_main_t *l2pm = &l2_patch_main;
383   u32 rx_sw_if_index;
384   u32 no_entries = 1;
385
386   ASSERT (vec_len (l2pm->tx_next_by_rx_sw_if_index) ==
387           vec_len (l2pm->tx_sw_if_index_by_rx_sw_if_index));
388
389   for (rx_sw_if_index = 0;
390        rx_sw_if_index < vec_len (l2pm->tx_sw_if_index_by_rx_sw_if_index);
391        rx_sw_if_index++)
392     {
393       u32 tx_sw_if_index =
394         l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index];
395       if (tx_sw_if_index != ~0)
396         {
397           no_entries = 0;
398           vlib_cli_output (vm, "%26U -> %U",
399                            format_vnet_sw_if_index_name,
400                            l2pm->vnet_main, rx_sw_if_index,
401                            format_vnet_sw_if_index_name,
402                            l2pm->vnet_main, tx_sw_if_index);
403         }
404     }
405
406   if (no_entries)
407     vlib_cli_output (vm, "no l2patch entries");
408
409   return 0;
410 }
411
412 /*?
413  * Show Layer 2 patch entries.
414  *
415  * @cliexpar
416  * @cliexstart{show l2patch}
417  * @cliexend
418  * @todo This is incomplete. This needs a detailed description and a
419  * practical example.
420 ?*/
421 /* *INDENT-OFF* */
422 VLIB_CLI_COMMAND (show_l2patch_cli, static) = {
423   .path = "show l2patch",
424   .short_help = "Show l2 interface cross-connect entries",
425   .function = show_l2patch,
426 };
427 /* *INDENT-ON* */
428
429 static clib_error_t *
430 l2_patch_init (vlib_main_t * vm)
431 {
432   l2_patch_main_t *mp = &l2_patch_main;
433
434   mp->vlib_main = vm;
435   mp->vnet_main = vnet_get_main ();
436
437   return 0;
438 }
439
440 VLIB_INIT_FUNCTION (l2_patch_init);
441
442 /*
443  * fd.io coding-style-patch-verification: ON
444  *
445  * Local Variables:
446  * eval: (c-set-style "gnu")
447  * End:
448  */