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