misc: Purge unused pg includes
[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 /* *INDENT-OFF* */
210 VLIB_REGISTER_NODE (l2_patch_node) = {
211   .name = "l2-patch",
212   .vector_size = sizeof (u32),
213   .format_trace = format_l2_patch_trace,
214   .type = VLIB_NODE_TYPE_INTERNAL,
215
216   .n_errors = ARRAY_LEN(l2_patch_error_strings),
217   .error_strings = l2_patch_error_strings,
218
219   .n_next_nodes = L2_PATCH_N_NEXT,
220
221   /* edit / add dispositions here */
222   .next_nodes = {
223         [L2_PATCH_NEXT_DROP] = "error-drop",
224   },
225 };
226 /* *INDENT-ON* */
227
228 extern int
229 vnet_l2_patch_add_del (u32 rx_sw_if_index, u32 tx_sw_if_index, int is_add);
230 #ifndef CLIB_MARCH_VARIANT
231 int
232 vnet_l2_patch_add_del (u32 rx_sw_if_index, u32 tx_sw_if_index, int is_add)
233 {
234   l2_patch_main_t *l2pm = &l2_patch_main;
235   vnet_hw_interface_t *rxhi, *txhi;
236   u32 tx_next_index;
237
238   /*
239    * We assume that the API msg handler has used 2x VALIDATE_SW_IF_INDEX
240    * macros...
241    */
242
243   rxhi = vnet_get_sup_hw_interface (l2pm->vnet_main, rx_sw_if_index);
244
245   /* Make sure caller didn't pass a vlan subif, etc. */
246   if (rxhi->sw_if_index != rx_sw_if_index)
247     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
248
249   txhi = vnet_get_sup_hw_interface (l2pm->vnet_main, tx_sw_if_index);
250   if (txhi->sw_if_index != tx_sw_if_index)
251     return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
252
253   if (is_add)
254     {
255       tx_next_index = vlib_node_add_next (l2pm->vlib_main,
256                                           l2_patch_node.index,
257                                           txhi->output_node_index);
258
259       vec_validate_init_empty (l2pm->tx_next_by_rx_sw_if_index,
260                                rx_sw_if_index, ~0);
261
262       l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = tx_next_index;
263       vec_validate_init_empty (l2pm->tx_sw_if_index_by_rx_sw_if_index,
264                                rx_sw_if_index, ~0);
265       l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index]
266         = txhi->sw_if_index;
267
268       ethernet_set_flags (l2pm->vnet_main, rxhi->hw_if_index,
269                           ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
270
271       vnet_feature_enable_disable ("device-input", "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       if (vec_len (l2pm->tx_next_by_rx_sw_if_index) > rx_sw_if_index)
282         {
283           l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = ~0;
284           l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index] = ~0;
285         }
286     }
287
288   return 0;
289 }
290 #endif
291
292 static clib_error_t *
293 test_patch_command_fn (vlib_main_t * vm,
294                        unformat_input_t * input, vlib_cli_command_t * cmd)
295 {
296   l2_patch_main_t *l2pm = &l2_patch_main;
297   unformat_input_t _line_input, *line_input = &_line_input;
298   u32 rx_sw_if_index, tx_sw_if_index;
299   int rv;
300   int rx_set = 0;
301   int tx_set = 0;
302   int is_add = 1;
303   clib_error_t *error = NULL;
304
305   /* Get a line of input. */
306   if (!unformat_user (input, unformat_line_input, line_input))
307     return 0;
308
309   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
310     {
311       if (unformat (line_input, "rx %U", unformat_vnet_sw_interface,
312                     l2pm->vnet_main, &rx_sw_if_index))
313         rx_set = 1;
314       else if (unformat (line_input, "tx %U", unformat_vnet_sw_interface,
315                          l2pm->vnet_main, &tx_sw_if_index))
316         tx_set = 1;
317       else if (unformat (line_input, "del"))
318         is_add = 0;
319       else
320         break;
321     }
322
323   if (rx_set == 0)
324     {
325       error = clib_error_return (0, "rx interface not set");
326       goto done;
327     }
328
329   if (tx_set == 0)
330     {
331       error = clib_error_return (0, "tx interface not set");
332       goto done;
333     }
334
335   rv = vnet_l2_patch_add_del (rx_sw_if_index, tx_sw_if_index, is_add);
336
337   switch (rv)
338     {
339     case 0:
340       break;
341
342     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
343       error = clib_error_return (0, "rx interface not a physical port");
344       goto done;
345
346     case VNET_API_ERROR_INVALID_SW_IF_INDEX_2:
347       error = clib_error_return (0, "tx interface not a physical port");
348       goto done;
349
350     default:
351       error = clib_error_return
352         (0, "WARNING: vnet_l2_patch_add_del returned %d", rv);
353       goto done;
354     }
355
356
357 done:
358   unformat_free (line_input);
359
360   return error;
361 }
362
363 /*?
364  * Create or delete a Layer 2 patch.
365  *
366  * @cliexpar
367  * @cliexstart{test l2patch rx <intfc> tx <intfc> [del]}
368  * @cliexend
369  * @todo This is incomplete. This needs a detailed description and a
370  * practical example.
371 ?*/
372 /* *INDENT-OFF* */
373 VLIB_CLI_COMMAND (test_patch_command, static) = {
374     .path = "test l2patch",
375     .short_help = "test l2patch rx <intfc> tx <intfc> [del]",
376     .function = test_patch_command_fn,
377 };
378 /* *INDENT-ON* */
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 /* *INDENT-OFF* */
425 VLIB_CLI_COMMAND (show_l2patch_cli, static) = {
426   .path = "show l2patch",
427   .short_help = "Show l2 interface cross-connect entries",
428   .function = show_l2patch,
429 };
430 /* *INDENT-ON* */
431
432 static clib_error_t *
433 l2_patch_init (vlib_main_t * vm)
434 {
435   l2_patch_main_t *mp = &l2_patch_main;
436
437   mp->vlib_main = vm;
438   mp->vnet_main = vnet_get_main ();
439
440   return 0;
441 }
442
443 VLIB_INIT_FUNCTION (l2_patch_init);
444
445 /*
446  * fd.io coding-style-patch-verification: ON
447  *
448  * Local Variables:
449  * eval: (c-set-style "gnu")
450  * End:
451  */