VPP-346 A swathe of doc fixes
[vpp.git] / vnet / vnet / l2 / l2_vtr.c
1 /*
2  * l2_vtr.c : layer 2 vlan tag rewrite configuration
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ethernet/packet.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vnet/l2/l2_output.h>
24 #include <vnet/l2/feat_bitmap.h>
25 #include <vnet/l2/l2_vtr.h>
26 #include <vnet/l2/l2_input_vtr.h>
27 #include <vnet/l2/l2_output.h>
28
29 #include <vppinfra/error.h>
30 #include <vlib/cli.h>
31
32
33 /** Just a placeholder; ensures file is not eliminated by linker. */
34 clib_error_t *
35 l2_vtr_init (vlib_main_t * vm)
36 {
37   return 0;
38 }
39
40 VLIB_INIT_FUNCTION (l2_vtr_init);
41
42
43 /**
44  * Configure vtag tag rewrite on the given interface.
45  * Return 1 if there is an error, 0 if ok
46  */
47 u32
48 l2vtr_configure (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_index, u32 vtr_op, u32 push_dot1q, /* ethertype of first pushed tag is dot1q/dot1ad */
49                  u32 vtr_tag1,  /* first pushed tag */
50                  u32 vtr_tag2)  /* second pushed tag */
51 {
52   vnet_hw_interface_t *hi;
53   vnet_sw_interface_t *si;
54   u32 hw_no_tags;
55   u32 error = 0;
56   vtr_config_t *in_config;
57   vtr_config_t *out_config;
58   u32 enable;
59   u32 push_inner_et;
60   u32 push_outer_et;
61   u32 cfg_tags;
62
63   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
64   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
65     {
66       error = VNET_API_ERROR_INVALID_INTERFACE; /* non-ethernet interface */
67       goto done;
68     }
69
70   /* Init the config for this interface */
71   vec_validate (l2output_main.configs, sw_if_index);
72   in_config =
73     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
74   out_config =
75     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->output_vtr);
76   in_config->raw_tags = 0;
77   out_config->raw_tags = 0;
78
79   /* Get the configured tags for the interface */
80   si = vnet_get_sw_interface (vnet_main, sw_if_index);
81   hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
82
83   /* Construct the input tag-rewrite config */
84
85   push_outer_et =
86     clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN :
87                           ETHERNET_TYPE_DOT1AD);
88   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
89   vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
90   vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
91
92   /* Determine number of vlan tags with explictly configured values */
93   cfg_tags = 0;
94   if (hw_no_tags || si->sub.eth.flags.no_tags)
95     {
96       cfg_tags = 0;
97     }
98   else if (si->sub.eth.flags.one_tag)
99     {
100       cfg_tags = 1;
101       if (si->sub.eth.flags.outer_vlan_id_any)
102         {
103           cfg_tags = 0;
104         }
105     }
106   else if (si->sub.eth.flags.two_tags)
107     {
108       cfg_tags = 2;
109       if (si->sub.eth.flags.inner_vlan_id_any)
110         {
111           cfg_tags = 1;
112         }
113       if (si->sub.eth.flags.outer_vlan_id_any)
114         {
115           cfg_tags = 0;
116         }
117     }
118
119   switch (vtr_op)
120     {
121     case L2_VTR_DISABLED:
122       in_config->push_and_pop_bytes = 0;
123       break;
124
125     case L2_VTR_POP_1:
126       if (cfg_tags < 1)
127         {
128           /* Need one or two tags */
129           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;
130           goto done;
131         }
132       in_config->pop_bytes = 4;
133       in_config->push_bytes = 0;
134       break;
135
136     case L2_VTR_POP_2:
137       if (cfg_tags < 2)
138         {
139           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
140           goto done;
141         }
142       in_config->pop_bytes = 8;
143       in_config->push_bytes = 0;
144
145       out_config->push_bytes = in_config->pop_bytes;
146       out_config->pop_bytes = in_config->push_bytes;
147       break;
148
149     case L2_VTR_PUSH_1:
150       in_config->pop_bytes = 0;
151       in_config->push_bytes = 4;
152       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
153       in_config->tags[1].type = push_outer_et;
154       break;
155
156     case L2_VTR_PUSH_2:
157       in_config->pop_bytes = 0;
158       in_config->push_bytes = 8;
159       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
160       in_config->tags[0].type = push_outer_et;
161       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
162       in_config->tags[1].type = push_inner_et;
163       break;
164
165     case L2_VTR_TRANSLATE_1_1:
166       if (cfg_tags < 1)
167         {
168           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need one or two tags */
169           goto done;
170         }
171       in_config->pop_bytes = 4;
172       in_config->push_bytes = 4;
173       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
174       in_config->tags[1].type = push_outer_et;
175       break;
176
177     case L2_VTR_TRANSLATE_1_2:
178       if (cfg_tags < 1)
179         {
180           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need one or two tags */
181           goto done;
182         }
183       in_config->pop_bytes = 4;
184       in_config->push_bytes = 8;
185       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
186       in_config->tags[0].type = push_outer_et;
187       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
188       in_config->tags[1].type = push_inner_et;
189       break;
190
191     case L2_VTR_TRANSLATE_2_1:
192       if (cfg_tags < 2)
193         {
194           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
195           goto done;
196         }
197       in_config->pop_bytes = 8;
198       in_config->push_bytes = 4;
199       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
200       in_config->tags[1].type = push_outer_et;
201       break;
202
203     case L2_VTR_TRANSLATE_2_2:
204       if (cfg_tags < 2)
205         {
206           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
207           goto done;
208         }
209       in_config->pop_bytes = 8;
210       in_config->push_bytes = 8;
211       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
212       in_config->tags[0].type = push_outer_et;
213       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
214       in_config->tags[1].type = push_inner_et;
215       break;
216     }
217
218   /*
219    *  Construct the output tag-rewrite config
220    *
221    *  The push/pop values are always reversed
222    */
223   out_config->push_bytes = in_config->pop_bytes;
224   out_config->pop_bytes = in_config->push_bytes;
225
226   /* Any pushed tags are derived from the subinterface config */
227   push_outer_et =
228     clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD :
229                           ETHERNET_TYPE_VLAN);
230   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
231   vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
232   vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
233
234   if (out_config->push_bytes == 4)
235     {
236       out_config->tags[1].priority_cfi_and_id = vtr_tag1;
237       out_config->tags[1].type = push_outer_et;
238     }
239   else if (out_config->push_bytes == 8)
240     {
241       out_config->tags[0].priority_cfi_and_id = vtr_tag1;
242       out_config->tags[0].type = push_outer_et;
243       out_config->tags[1].priority_cfi_and_id = vtr_tag2;
244       out_config->tags[1].type = push_inner_et;
245     }
246
247   /* set the interface enable flags */
248   enable = (vtr_op != L2_VTR_DISABLED);
249   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
250   /* output vtr enable is checked explicitly in l2_output */
251
252 done:
253   return error;
254 }
255
256 /**
257  * Get vtag tag rewrite on the given interface.
258  * Return 1 if there is an error, 0 if ok
259  */
260 u32
261 l2vtr_get (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_index, u32 * vtr_op, u32 * push_dot1q,   /* ethertype of first pushed tag is dot1q/dot1ad */
262            u32 * vtr_tag1,      /* first pushed tag */
263            u32 * vtr_tag2)      /* second pushed tag */
264 {
265   vnet_hw_interface_t *hi;
266   u32 error = 0;
267   vtr_config_t *in_config;
268
269   if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2)
270     {
271       clib_warning ("invalid arguments");
272       error = VNET_API_ERROR_INVALID_ARGUMENT;
273       goto done;
274     }
275
276   *vtr_op = L2_VTR_DISABLED;
277   *vtr_tag1 = 0;
278   *vtr_tag2 = 0;
279   *push_dot1q = 0;
280
281   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
282   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
283     {
284       /* non-ethernet interface */
285       goto done;
286     }
287
288   if (sw_if_index >= vec_len (l2output_main.configs))
289     {
290       /* no specific config (return disabled) */
291       goto done;
292     }
293
294   /* Get the config for this interface */
295   in_config =
296     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
297
298   /* DISABLED */
299   if (in_config->push_and_pop_bytes == 0)
300     {
301       goto done;
302     }
303
304   /* find out vtr_op */
305   switch (in_config->pop_bytes)
306     {
307     case 0:
308       switch (in_config->push_bytes)
309         {
310         case 0:
311           /* DISABLED */
312           goto done;
313         case 4:
314           *vtr_op = L2_VTR_PUSH_1;
315           *vtr_tag1 =
316             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
317           *push_dot1q =
318             (ETHERNET_TYPE_VLAN ==
319              clib_host_to_net_u16 (in_config->tags[1].type));
320           break;
321         case 8:
322           *vtr_op = L2_VTR_PUSH_2;
323           *vtr_tag1 =
324             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
325           *vtr_tag2 =
326             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
327           *push_dot1q =
328             (ETHERNET_TYPE_VLAN ==
329              clib_host_to_net_u16 (in_config->tags[0].type));
330           break;
331         default:
332           clib_warning ("invalid push_bytes count: %d",
333                         in_config->push_bytes);
334           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
335           goto done;
336         }
337       break;
338
339     case 4:
340       switch (in_config->push_bytes)
341         {
342         case 0:
343           *vtr_op = L2_VTR_POP_1;
344           break;
345         case 4:
346           *vtr_op = L2_VTR_TRANSLATE_1_1;
347           *vtr_tag1 =
348             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
349           *push_dot1q =
350             (ETHERNET_TYPE_VLAN ==
351              clib_host_to_net_u16 (in_config->tags[1].type));
352           break;
353         case 8:
354           *vtr_op = L2_VTR_TRANSLATE_1_2;
355           *vtr_tag1 =
356             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
357           *vtr_tag2 =
358             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
359           *push_dot1q =
360             (ETHERNET_TYPE_VLAN ==
361              clib_host_to_net_u16 (in_config->tags[0].type));
362           break;
363         default:
364           clib_warning ("invalid push_bytes count: %d",
365                         in_config->push_bytes);
366           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
367           goto done;
368         }
369       break;
370
371     case 8:
372       switch (in_config->push_bytes)
373         {
374         case 0:
375           *vtr_op = L2_VTR_POP_2;
376           break;
377         case 4:
378           *vtr_op = L2_VTR_TRANSLATE_2_1;
379           *vtr_tag1 =
380             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
381           *push_dot1q =
382             (ETHERNET_TYPE_VLAN ==
383              clib_host_to_net_u16 (in_config->tags[1].type));
384           break;
385         case 8:
386           *vtr_op = L2_VTR_TRANSLATE_2_2;
387           *vtr_tag1 =
388             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
389           *vtr_tag2 =
390             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
391           *push_dot1q =
392             (ETHERNET_TYPE_VLAN ==
393              clib_host_to_net_u16 (in_config->tags[0].type));
394           break;
395         default:
396           clib_warning ("invalid push_bytes count: %d",
397                         in_config->push_bytes);
398           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
399           goto done;
400         }
401       break;
402
403     default:
404       clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
405       error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
406       goto done;
407     }
408
409 done:
410   return error;
411 }
412
413 /**
414  * Set subinterface vtr enable/disable.
415  * The CLI format is:
416  *    set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
417  *
418  *  "push" can also be replaced by "translate-{1|2}-{1|2}"
419  */
420 static clib_error_t *
421 int_l2_vtr (vlib_main_t * vm,
422             unformat_input_t * input, vlib_cli_command_t * cmd)
423 {
424   vnet_main_t *vnm = vnet_get_main ();
425   clib_error_t *error = 0;
426   u32 sw_if_index;
427   u32 vtr_op;
428   u32 push_dot1q = 0;
429   u32 tag1 = 0, tag2 = 0;
430
431   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
432     {
433       error = clib_error_return (0, "unknown interface `%U'",
434                                  format_unformat_error, input);
435       goto done;
436     }
437
438   vtr_op = L2_VTR_DISABLED;
439
440   if (unformat (input, "disable"))
441     {
442       vtr_op = L2_VTR_DISABLED;
443     }
444   else if (unformat (input, "pop 1"))
445     {
446       vtr_op = L2_VTR_POP_1;
447     }
448   else if (unformat (input, "pop 2"))
449     {
450       vtr_op = L2_VTR_POP_2;
451
452     }
453   else if (unformat (input, "push dot1q %d %d", &tag1, &tag2))
454     {
455       vtr_op = L2_VTR_PUSH_2;
456       push_dot1q = 1;
457     }
458   else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2))
459     {
460       vtr_op = L2_VTR_PUSH_2;
461
462     }
463   else if (unformat (input, "push dot1q %d", &tag1))
464     {
465       vtr_op = L2_VTR_PUSH_1;
466       push_dot1q = 1;
467     }
468   else if (unformat (input, "push dot1ad %d", &tag1))
469     {
470       vtr_op = L2_VTR_PUSH_1;
471
472     }
473   else if (unformat (input, "translate 1-1 dot1q %d", &tag1))
474     {
475       vtr_op = L2_VTR_TRANSLATE_1_1;
476       push_dot1q = 1;
477     }
478   else if (unformat (input, "translate 1-1 dot1ad %d", &tag1))
479     {
480       vtr_op = L2_VTR_TRANSLATE_1_1;
481
482     }
483   else if (unformat (input, "translate 2-1 dot1q %d", &tag1))
484     {
485       vtr_op = L2_VTR_TRANSLATE_2_1;
486       push_dot1q = 1;
487     }
488   else if (unformat (input, "translate 2-1 dot1ad %d", &tag1))
489     {
490       vtr_op = L2_VTR_TRANSLATE_2_1;
491
492     }
493   else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2))
494     {
495       vtr_op = L2_VTR_TRANSLATE_2_2;
496       push_dot1q = 1;
497     }
498   else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2))
499     {
500       vtr_op = L2_VTR_TRANSLATE_2_2;
501
502     }
503   else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2))
504     {
505       vtr_op = L2_VTR_TRANSLATE_1_2;
506       push_dot1q = 1;
507     }
508   else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2))
509     {
510       vtr_op = L2_VTR_TRANSLATE_1_2;
511
512     }
513   else
514     {
515       error =
516         clib_error_return (0,
517                            "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
518                            " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
519                            format_unformat_error, input);
520       goto done;
521     }
522
523   if (l2vtr_configure (vm, vnm, sw_if_index, vtr_op, push_dot1q, tag1, tag2))
524     {
525       error =
526         clib_error_return (0,
527                            "vlan tag rewrite is not compatible with interface");
528       goto done;
529     }
530
531 done:
532   return error;
533 }
534
535 /* *INDENT-OFF* */
536 VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
537   .path = "set interface l2 tag-rewrite",
538   .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
539   .function = int_l2_vtr,
540 };
541 /* *INDENT-ON* */
542
543
544 /*
545  * fd.io coding-style-patch-verification: ON
546  *
547  * Local Variables:
548  * eval: (c-set-style "gnu")
549  * End:
550  */