c11 safe string handling support
[vpp.git] / src / plugins / l2e / l2e.c
1 /*
2  * l2_emulation.h : Extract L3 packets from the L2 input and feed
3  *                   them into the L3 path.
4  *
5  * Copyright (c) 2013 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <plugins/l2e/l2e.h>
20 #include <vnet/l2/l2_input.h>
21 #include <vnet/l2/feat_bitmap.h>
22
23 /**
24  * Grouping of global data for the L2 emulation feature
25  */
26 typedef struct l2_emulation_main_t_
27 {
28   /**
29    * Next nodes for L2 output features
30    */
31   u32 l2_input_feat_next[32];
32 } l2_emulation_main_t;
33
34 static l2_emulation_main_t l2_emulation_main;
35
36 /**
37  * Per-interface L2 configuration
38  */
39 typedef struct l2_emulation_t_
40 {
41   /**
42    * Enabled or Disabled.
43    *  this is required since one L3 protocl can be enabled, but others not
44    */
45   u8 enabled;
46 } l2_emulation_t;
47
48 /**
49  * A zero'd out struct we can use in the vec_validate
50  */
51 static const l2_emulation_t ezero = { };
52
53 /**
54  * Per-interface vector of emulation configs
55  */
56 l2_emulation_t *l2_emulations;
57
58 void
59 l2_emulation_enable (u32 sw_if_index)
60 {
61   vec_validate_init_empty (l2_emulations, sw_if_index, ezero);
62
63   l2_emulation_t *l23e = &l2_emulations[sw_if_index];
64
65   l23e->enabled = 1;
66
67   /*
68    * L3 enable the interface - using IP unnumbered from the control
69    * plane may not be possible since there may be no BVI interface
70    * to which to unnumber
71    */
72   ip4_sw_interface_enable_disable (sw_if_index, 1);
73   ip6_sw_interface_enable_disable (sw_if_index, 1);
74
75   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_EMULATION, 1);
76 }
77
78
79 void
80 l2_emulation_disable (u32 sw_if_index)
81 {
82   if (vec_len (l2_emulations) >= sw_if_index)
83     {
84       l2_emulation_t *l23e = &l2_emulations[sw_if_index];
85       clib_memset (l23e, 0, sizeof (*l23e));
86
87       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_EMULATION, 0);
88       ip4_sw_interface_enable_disable (sw_if_index, 0);
89       ip6_sw_interface_enable_disable (sw_if_index, 0);
90     }
91 }
92
93 static clib_error_t *
94 l2_emulation_interface_add_del (vnet_main_t * vnm,
95                                 u32 sw_if_index, u32 is_add)
96 {
97   if (is_add)
98     {
99       vec_validate_init_empty (l2_emulations, sw_if_index, ezero);
100     }
101
102   return (NULL);
103 }
104
105 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (l2_emulation_interface_add_del);
106
107 static clib_error_t *
108 l2_emulation_cli (vlib_main_t * vm,
109                   unformat_input_t * input, vlib_cli_command_t * cmd)
110 {
111   vnet_main_t *vnm = vnet_get_main ();
112   u32 sw_if_index = ~0;
113   u8 enable = 1;
114
115   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
116     {
117       if (unformat (input, "%U", unformat_vnet_sw_interface,
118                     vnm, &sw_if_index))
119         ;
120       else if (unformat (input, "enable"))
121         enable = 1;
122       else if (unformat (input, "disable"))
123         enable = 0;
124       else
125         break;
126     }
127
128   if (~0 == sw_if_index)
129     return clib_error_return (0, "interface must be specified");
130
131   if (enable)
132     l2_emulation_enable (sw_if_index);
133   else
134     l2_emulation_disable (sw_if_index);
135
136   return (NULL);
137 }
138
139 /*?
140  * Configure l2 emulation.
141  *  When the interface is in L2 mode, configure the extraction of L3
142  *  packets out of the L2 path and into the L3 path.
143  *
144  * @cliexpar
145  * @cliexstart{set interface l2 input l2-emulation <interface-name> [disable]}
146  * @cliexend
147  ?*/
148 /* *INDENT-OFF* */
149 VLIB_CLI_COMMAND (l2_emulation_cli_node, static) = {
150   .path = "set interface l2 l2-emulation",
151   .short_help =
152   "set interface l2 l2-emulation <interface-name> [disable|enable]\n",
153   .function = l2_emulation_cli,
154 };
155 /* *INDENT-ON* */
156
157 static clib_error_t *
158 l2_emulation_show (vlib_main_t * vm,
159                    unformat_input_t * input, vlib_cli_command_t * cmd)
160 {
161   vnet_main_t *vnm = vnet_get_main ();
162   l2_emulation_t *l23e;
163   u32 sw_if_index;
164
165   vec_foreach_index (sw_if_index, l2_emulations)
166   {
167     l23e = &l2_emulations[sw_if_index];
168     if (l23e->enabled)
169       {
170         vlib_cli_output (vm, "%U\n",
171                          format_vnet_sw_if_index_name, vnm, sw_if_index);
172       }
173   }
174   return (NULL);
175 }
176
177 /*?
178  * Show l2 emulation.
179  *  When the interface is in L2 mode, configure the extraction of L3
180  *  packets out of the L2 path and into the L3 path.
181  *
182  * @cliexpar
183  * @cliexstart{show interface l2 l2-emulation}
184  * @cliexend
185  ?*/
186 /* *INDENT-OFF* */
187 VLIB_CLI_COMMAND (l2_emulation_show_node, static) = {
188   .path = "show interface l2 l2-emulation",
189   .short_help = "show interface l2 l2-emulation\n",
190   .function = l2_emulation_show,
191 };
192 /* *INDENT-ON* */
193
194 #define foreach_l2_emulation                    \
195   _(IP4, "Extract IPv4")                        \
196   _(IP6, "Extract IPv6")
197
198 typedef enum
199 {
200 #define _(sym,str) L2_EMULATION_ERROR_##sym,
201   foreach_l2_emulation
202 #undef _
203     L2_EMULATION_N_ERROR,
204 } l2_emulation_error_t;
205
206 static char *l2_emulation_error_strings[] = {
207 #define _(sym,string) string,
208   foreach_l2_emulation
209 #undef _
210 };
211
212 typedef enum
213 {
214 #define _(sym,str) L2_EMULATION_NEXT_##sym,
215   foreach_l2_emulation
216 #undef _
217     L2_EMULATION_N_NEXT,
218 } l2_emulation_next_t;
219
220 /**
221  * per-packet trace data
222  */
223 typedef struct l2_emulation_trace_t_
224 {
225   /* per-pkt trace data */
226   u8 extracted;
227 } l2_emulation_trace_t;
228
229 static uword
230 l2_emulation_node_fn (vlib_main_t * vm,
231                       vlib_node_runtime_t * node, vlib_frame_t * frame)
232 {
233   l2_emulation_main_t *em = &l2_emulation_main;
234   u32 n_left_from, *from, *to_next;
235   l2_emulation_next_t next_index;
236   u32 ip4_hits = 0;
237   u32 ip6_hits = 0;
238
239   next_index = 0;
240   n_left_from = frame->n_vectors;
241   from = vlib_frame_vector_args (frame);
242
243   while (n_left_from > 0)
244     {
245       u32 n_left_to_next;
246
247       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
248       while (n_left_from >= 4 && n_left_to_next >= 2)
249         {
250           vlib_buffer_t *b0, *b1;
251           u32 sw_if_index0, sw_if_index1;
252           u16 ether_type0, ether_type1;
253           u32 next0 = ~0, next1 = ~0;
254           u8 l2_len0, l2_len1;
255           u32 bi0, bi1;
256           u8 *h0, *h1;
257
258           bi0 = to_next[0] = from[0];
259           bi1 = to_next[1] = from[1];
260
261           from += 2;
262           n_left_from -= 2;
263           to_next += 2;
264           n_left_to_next -= 2;
265
266           b0 = vlib_get_buffer (vm, bi0);
267           b1 = vlib_get_buffer (vm, bi1);
268           l2_len0 = vnet_buffer (b0)->l2.l2_len;
269           l2_len1 = vnet_buffer (b1)->l2.l2_len;
270
271           h0 = vlib_buffer_get_current (b0);
272           h1 = vlib_buffer_get_current (b1);
273
274           ether_type0 = clib_net_to_host_u16 (*(u16 *) (h0 + l2_len0 - 2));
275           ether_type1 = clib_net_to_host_u16 (*(u16 *) (h1 + l2_len1 - 2));
276           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
277           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
278
279           /*
280            * only extract unicast
281            */
282           if (PREDICT_TRUE (!(h0[0] & 0x1)))
283             {
284               switch (ether_type0)
285                 {
286                 case ETHERNET_TYPE_IP4:
287                   ASSERT (l2_emulations[sw_if_index0].enabled);
288                   ++ip4_hits;
289                   next0 = L2_EMULATION_NEXT_IP4;
290                   vlib_buffer_advance (b0, l2_len0);
291                   break;
292                 case ETHERNET_TYPE_IP6:
293                   ASSERT (l2_emulations[sw_if_index0].enabled);
294                   ++ip6_hits;
295                   next0 = L2_EMULATION_NEXT_IP6;
296                   vlib_buffer_advance (b0, l2_len0);
297                 default:
298                   break;
299                 }
300             }
301           if (PREDICT_TRUE (!(h1[0] & 0x1)))
302             {
303               switch (ether_type1)
304                 {
305                 case ETHERNET_TYPE_IP4:
306                   ASSERT (l2_emulations[sw_if_index1].enabled);
307                   ++ip4_hits;
308                   next1 = L2_EMULATION_NEXT_IP4;
309                   vlib_buffer_advance (b1, l2_len1);
310                   break;
311                 case ETHERNET_TYPE_IP6:
312                   ASSERT (l2_emulations[sw_if_index1].enabled);
313                   ++ip6_hits;
314                   next1 = L2_EMULATION_NEXT_IP6;
315                   vlib_buffer_advance (b1, l2_len1);
316                 default:
317                   break;
318                 }
319             }
320           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
321                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
322             {
323               l2_emulation_trace_t *t =
324                 vlib_add_trace (vm, node, b0, sizeof (*t));
325               t->extracted = (next0 != ~0);
326             }
327           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
328                              && (b1->flags & VLIB_BUFFER_IS_TRACED)))
329             {
330               l2_emulation_trace_t *t =
331                 vlib_add_trace (vm, node, b1, sizeof (*t));
332               t->extracted = (next1 != ~0);
333             }
334
335           /* Determine the next node and remove ourself from bitmap */
336           if (PREDICT_TRUE (next0 == ~0))
337             next0 = vnet_l2_feature_next (b0, em->l2_input_feat_next,
338                                           L2INPUT_FEAT_L2_EMULATION);
339
340           /* Determine the next node and remove ourself from bitmap */
341           if (PREDICT_TRUE (next1 == ~0))
342             next1 = vnet_l2_feature_next (b1, em->l2_input_feat_next,
343                                           L2INPUT_FEAT_L2_EMULATION);
344
345           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
346                                            to_next, n_left_to_next,
347                                            bi0, bi1, next0, next1);
348         }
349       while (n_left_from > 0 && n_left_to_next > 0)
350         {
351           vlib_buffer_t *b0;
352           u32 sw_if_index0;
353           u16 ether_type0;
354           u32 next0 = ~0;
355           u8 l2_len0;
356           u32 bi0;
357           u8 *h0;
358
359           bi0 = from[0];
360           to_next[0] = bi0;
361           from += 1;
362           to_next += 1;
363           n_left_from -= 1;
364           n_left_to_next -= 1;
365
366           b0 = vlib_get_buffer (vm, bi0);
367           l2_len0 = vnet_buffer (b0)->l2.l2_len;
368
369           h0 = vlib_buffer_get_current (b0);
370           ether_type0 = clib_net_to_host_u16 (*(u16 *) (h0 + l2_len0 - 2));
371           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
372
373           /*
374            * only extract unicast
375            */
376           if (PREDICT_TRUE (!(h0[0] & 0x1)))
377             {
378               switch (ether_type0)
379                 {
380                 case ETHERNET_TYPE_IP4:
381                   ASSERT (l2_emulations[sw_if_index0].enabled);
382                   ++ip4_hits;
383                   next0 = L2_EMULATION_NEXT_IP4;
384                   vlib_buffer_advance (b0, l2_len0);
385                   break;
386                 case ETHERNET_TYPE_IP6:
387                   ASSERT (l2_emulations[sw_if_index0].enabled);
388                   ++ip6_hits;
389                   next0 = L2_EMULATION_NEXT_IP6;
390                   vlib_buffer_advance (b0, l2_len0);
391                 default:
392                   break;
393                 }
394             }
395
396           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
397                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
398             {
399               l2_emulation_trace_t *t =
400                 vlib_add_trace (vm, node, b0, sizeof (*t));
401               t->extracted = (next0 != ~0);
402             }
403
404           /* Determine the next node and remove ourself from bitmap */
405           if (PREDICT_TRUE (next0 == ~0))
406             next0 = vnet_l2_feature_next (b0, em->l2_input_feat_next,
407                                           L2INPUT_FEAT_L2_EMULATION);
408
409           /* verify speculative enqueue, maybe switch current next frame */
410           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
411                                            to_next, n_left_to_next,
412                                            bi0, next0);
413         }
414
415       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
416     }
417
418   vlib_node_increment_counter (vm, node->node_index,
419                                L2_EMULATION_ERROR_IP4, ip4_hits);
420   vlib_node_increment_counter (vm, node->node_index,
421                                L2_EMULATION_ERROR_IP6, ip6_hits);
422
423   return frame->n_vectors;
424 }
425
426 /* packet trace format function */
427 static u8 *
428 format_l2_emulation_trace (u8 * s, va_list * args)
429 {
430   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
431   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
432   l2_emulation_trace_t *t = va_arg (*args, l2_emulation_trace_t *);
433
434   s = format (s, "l2-emulation: %s", (t->extracted ? "yes" : "no"));
435
436   return s;
437 }
438
439 /* *INDENT-OFF* */
440 VLIB_REGISTER_NODE (l2_emulation_node) = {
441   .function = l2_emulation_node_fn,
442   .name = "l2-emulation",
443   .vector_size = sizeof (u32),
444   .format_trace = format_l2_emulation_trace,
445   .type = VLIB_NODE_TYPE_INTERNAL,
446
447   .n_errors = ARRAY_LEN(l2_emulation_error_strings),
448   .error_strings = l2_emulation_error_strings,
449
450   .n_next_nodes = L2_EMULATION_N_NEXT,
451
452   /* edit / add dispositions here */
453   .next_nodes = {
454     [L2_EMULATION_NEXT_IP4] = "ip4-input",
455     [L2_EMULATION_NEXT_IP6] = "ip6-input",
456   },
457 };
458 /* *INDENT-ON* */
459
460 VLIB_NODE_FUNCTION_MULTIARCH (l2_emulation_node, l2_emulation_node_fn);
461
462
463 static clib_error_t *
464 l2_emulation_init (vlib_main_t * vm)
465 {
466   l2_emulation_main_t *em = &l2_emulation_main;
467
468   /* Initialize the feature next-node indexes */
469   feat_bitmap_init_next_nodes (vm,
470                                l2_emulation_node.index,
471                                L2INPUT_N_FEAT,
472                                l2input_get_feat_names (),
473                                em->l2_input_feat_next);
474
475   return 0;
476 }
477
478 VLIB_INIT_FUNCTION (l2_emulation_init);
479
480 /*
481  * fd.io coding-style-patch-verification: ON
482  *
483  * Local Variables:
484  * eval: (c-set-style "gnu")
485  * End:
486  */