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