9cb0a7e9da3b5e43d3e6beed804b847cc1bfe755
[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  * @file
34  * @brief Ethernet VLAN Tag Rewrite.
35  *
36  * VLAN tag rewrite provides the ability to change the VLAN tags on a packet.
37  * Existing tags can be popped, new tags can be pushed, and existing tags can
38  * be swapped with new tags. The rewrite feature is attached to a subinterface
39  * as input and output operations. The input operation is explicitly configured.
40  * The output operation is the symmetric opposite and is automatically derived
41  * from the input operation.
42  */
43
44 /** Just a placeholder; ensures file is not eliminated by linker. */
45 clib_error_t *
46 l2_vtr_init (vlib_main_t * vm)
47 {
48   return 0;
49 }
50
51 VLIB_INIT_FUNCTION (l2_vtr_init);
52
53
54 /**
55  * Configure vtag tag rewrite on the given interface.
56  * Return 1 if there is an error, 0 if ok
57  */
58 u32
59 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 */
60                  u32 vtr_tag1,  /* first pushed tag */
61                  u32 vtr_tag2)  /* second pushed tag */
62 {
63   vnet_hw_interface_t *hi;
64   vnet_sw_interface_t *si;
65   u32 hw_no_tags;
66   u32 error = 0;
67   vtr_config_t *in_config;
68   vtr_config_t *out_config;
69   u32 enable;
70   u32 push_inner_et;
71   u32 push_outer_et;
72   u32 cfg_tags;
73
74   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
75   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
76     {
77       error = VNET_API_ERROR_INVALID_INTERFACE; /* non-ethernet interface */
78       goto done;
79     }
80
81   /* Init the config for this interface */
82   vec_validate (l2output_main.configs, sw_if_index);
83   in_config =
84     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
85   out_config =
86     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->output_vtr);
87   in_config->raw_tags = 0;
88   out_config->raw_tags = 0;
89
90   /* Get the configured tags for the interface */
91   si = vnet_get_sw_interface (vnet_main, sw_if_index);
92   hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
93
94   /* Construct the input tag-rewrite config */
95
96   push_outer_et =
97     clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN :
98                           ETHERNET_TYPE_DOT1AD);
99   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
100   vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
101   vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
102
103   /* Determine number of vlan tags with explictly configured values */
104   cfg_tags = 0;
105   if (hw_no_tags || si->sub.eth.flags.no_tags)
106     {
107       cfg_tags = 0;
108     }
109   else if (si->sub.eth.flags.one_tag)
110     {
111       cfg_tags = 1;
112       if (si->sub.eth.flags.outer_vlan_id_any)
113         {
114           cfg_tags = 0;
115         }
116     }
117   else if (si->sub.eth.flags.two_tags)
118     {
119       cfg_tags = 2;
120       if (si->sub.eth.flags.inner_vlan_id_any)
121         {
122           cfg_tags = 1;
123         }
124       if (si->sub.eth.flags.outer_vlan_id_any)
125         {
126           cfg_tags = 0;
127         }
128     }
129
130   switch (vtr_op)
131     {
132     case L2_VTR_DISABLED:
133       in_config->push_and_pop_bytes = 0;
134       break;
135
136     case L2_VTR_POP_1:
137       if (cfg_tags < 1)
138         {
139           /* Need one or two tags */
140           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;
141           goto done;
142         }
143       in_config->pop_bytes = 4;
144       in_config->push_bytes = 0;
145       break;
146
147     case L2_VTR_POP_2:
148       if (cfg_tags < 2)
149         {
150           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
151           goto done;
152         }
153       in_config->pop_bytes = 8;
154       in_config->push_bytes = 0;
155
156       out_config->push_bytes = in_config->pop_bytes;
157       out_config->pop_bytes = in_config->push_bytes;
158       break;
159
160     case L2_VTR_PUSH_1:
161       in_config->pop_bytes = 0;
162       in_config->push_bytes = 4;
163       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
164       in_config->tags[1].type = push_outer_et;
165       break;
166
167     case L2_VTR_PUSH_2:
168       in_config->pop_bytes = 0;
169       in_config->push_bytes = 8;
170       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
171       in_config->tags[0].type = push_outer_et;
172       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
173       in_config->tags[1].type = push_inner_et;
174       break;
175
176     case L2_VTR_TRANSLATE_1_1:
177       if (cfg_tags < 1)
178         {
179           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need one or two tags */
180           goto done;
181         }
182       in_config->pop_bytes = 4;
183       in_config->push_bytes = 4;
184       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
185       in_config->tags[1].type = push_outer_et;
186       break;
187
188     case L2_VTR_TRANSLATE_1_2:
189       if (cfg_tags < 1)
190         {
191           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need one or two tags */
192           goto done;
193         }
194       in_config->pop_bytes = 4;
195       in_config->push_bytes = 8;
196       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
197       in_config->tags[0].type = push_outer_et;
198       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
199       in_config->tags[1].type = push_inner_et;
200       break;
201
202     case L2_VTR_TRANSLATE_2_1:
203       if (cfg_tags < 2)
204         {
205           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
206           goto done;
207         }
208       in_config->pop_bytes = 8;
209       in_config->push_bytes = 4;
210       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
211       in_config->tags[1].type = push_outer_et;
212       break;
213
214     case L2_VTR_TRANSLATE_2_2:
215       if (cfg_tags < 2)
216         {
217           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
218           goto done;
219         }
220       in_config->pop_bytes = 8;
221       in_config->push_bytes = 8;
222       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
223       in_config->tags[0].type = push_outer_et;
224       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
225       in_config->tags[1].type = push_inner_et;
226       break;
227     }
228
229   /*
230    *  Construct the output tag-rewrite config
231    *
232    *  The push/pop values are always reversed
233    */
234   out_config->push_bytes = in_config->pop_bytes;
235   out_config->pop_bytes = in_config->push_bytes;
236
237   /* Any pushed tags are derived from the subinterface config */
238   push_outer_et =
239     clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD :
240                           ETHERNET_TYPE_VLAN);
241   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
242   vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
243   vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
244
245   if (out_config->push_bytes == 4)
246     {
247       out_config->tags[1].priority_cfi_and_id = vtr_tag1;
248       out_config->tags[1].type = push_outer_et;
249     }
250   else if (out_config->push_bytes == 8)
251     {
252       out_config->tags[0].priority_cfi_and_id = vtr_tag1;
253       out_config->tags[0].type = push_outer_et;
254       out_config->tags[1].priority_cfi_and_id = vtr_tag2;
255       out_config->tags[1].type = push_inner_et;
256     }
257
258   /* set the interface enable flags */
259   enable = (vtr_op != L2_VTR_DISABLED);
260   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
261   /* output vtr enable is checked explicitly in l2_output */
262
263 done:
264   return error;
265 }
266
267 /**
268  * Get vtag tag rewrite on the given interface.
269  * Return 1 if there is an error, 0 if ok
270  */
271 u32
272 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 */
273            u32 * vtr_tag1,      /* first pushed tag */
274            u32 * vtr_tag2)      /* second pushed tag */
275 {
276   vnet_hw_interface_t *hi;
277   u32 error = 0;
278   vtr_config_t *in_config;
279
280   if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2)
281     {
282       clib_warning ("invalid arguments");
283       error = VNET_API_ERROR_INVALID_ARGUMENT;
284       goto done;
285     }
286
287   *vtr_op = L2_VTR_DISABLED;
288   *vtr_tag1 = 0;
289   *vtr_tag2 = 0;
290   *push_dot1q = 0;
291
292   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
293   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
294     {
295       /* non-ethernet interface */
296       goto done;
297     }
298
299   if (sw_if_index >= vec_len (l2output_main.configs))
300     {
301       /* no specific config (return disabled) */
302       goto done;
303     }
304
305   /* Get the config for this interface */
306   in_config =
307     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
308
309   /* DISABLED */
310   if (in_config->push_and_pop_bytes == 0)
311     {
312       goto done;
313     }
314
315   /* find out vtr_op */
316   switch (in_config->pop_bytes)
317     {
318     case 0:
319       switch (in_config->push_bytes)
320         {
321         case 0:
322           /* DISABLED */
323           goto done;
324         case 4:
325           *vtr_op = L2_VTR_PUSH_1;
326           *vtr_tag1 =
327             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
328           *push_dot1q =
329             (ETHERNET_TYPE_VLAN ==
330              clib_host_to_net_u16 (in_config->tags[1].type));
331           break;
332         case 8:
333           *vtr_op = L2_VTR_PUSH_2;
334           *vtr_tag1 =
335             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
336           *vtr_tag2 =
337             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
338           *push_dot1q =
339             (ETHERNET_TYPE_VLAN ==
340              clib_host_to_net_u16 (in_config->tags[0].type));
341           break;
342         default:
343           clib_warning ("invalid push_bytes count: %d",
344                         in_config->push_bytes);
345           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
346           goto done;
347         }
348       break;
349
350     case 4:
351       switch (in_config->push_bytes)
352         {
353         case 0:
354           *vtr_op = L2_VTR_POP_1;
355           break;
356         case 4:
357           *vtr_op = L2_VTR_TRANSLATE_1_1;
358           *vtr_tag1 =
359             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
360           *push_dot1q =
361             (ETHERNET_TYPE_VLAN ==
362              clib_host_to_net_u16 (in_config->tags[1].type));
363           break;
364         case 8:
365           *vtr_op = L2_VTR_TRANSLATE_1_2;
366           *vtr_tag1 =
367             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
368           *vtr_tag2 =
369             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
370           *push_dot1q =
371             (ETHERNET_TYPE_VLAN ==
372              clib_host_to_net_u16 (in_config->tags[0].type));
373           break;
374         default:
375           clib_warning ("invalid push_bytes count: %d",
376                         in_config->push_bytes);
377           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
378           goto done;
379         }
380       break;
381
382     case 8:
383       switch (in_config->push_bytes)
384         {
385         case 0:
386           *vtr_op = L2_VTR_POP_2;
387           break;
388         case 4:
389           *vtr_op = L2_VTR_TRANSLATE_2_1;
390           *vtr_tag1 =
391             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
392           *push_dot1q =
393             (ETHERNET_TYPE_VLAN ==
394              clib_host_to_net_u16 (in_config->tags[1].type));
395           break;
396         case 8:
397           *vtr_op = L2_VTR_TRANSLATE_2_2;
398           *vtr_tag1 =
399             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
400           *vtr_tag2 =
401             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
402           *push_dot1q =
403             (ETHERNET_TYPE_VLAN ==
404              clib_host_to_net_u16 (in_config->tags[0].type));
405           break;
406         default:
407           clib_warning ("invalid push_bytes count: %d",
408                         in_config->push_bytes);
409           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
410           goto done;
411         }
412       break;
413
414     default:
415       clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
416       error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
417       goto done;
418     }
419
420 done:
421   return error;
422 }
423
424 /**
425  * Set subinterface vtr enable/disable.
426  * The CLI format is:
427  *    set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
428  *
429  *  "push" can also be replaced by "translate-{1|2}-{1|2}"
430  */
431 static clib_error_t *
432 int_l2_vtr (vlib_main_t * vm,
433             unformat_input_t * input, vlib_cli_command_t * cmd)
434 {
435   vnet_main_t *vnm = vnet_get_main ();
436   clib_error_t *error = 0;
437   u32 sw_if_index;
438   u32 vtr_op;
439   u32 push_dot1q = 0;
440   u32 tag1 = 0, tag2 = 0;
441
442   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
443     {
444       error = clib_error_return (0, "unknown interface `%U'",
445                                  format_unformat_error, input);
446       goto done;
447     }
448
449   vtr_op = L2_VTR_DISABLED;
450
451   if (unformat (input, "disable"))
452     {
453       vtr_op = L2_VTR_DISABLED;
454     }
455   else if (unformat (input, "pop 1"))
456     {
457       vtr_op = L2_VTR_POP_1;
458     }
459   else if (unformat (input, "pop 2"))
460     {
461       vtr_op = L2_VTR_POP_2;
462
463     }
464   else if (unformat (input, "push dot1q %d %d", &tag1, &tag2))
465     {
466       vtr_op = L2_VTR_PUSH_2;
467       push_dot1q = 1;
468     }
469   else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2))
470     {
471       vtr_op = L2_VTR_PUSH_2;
472
473     }
474   else if (unformat (input, "push dot1q %d", &tag1))
475     {
476       vtr_op = L2_VTR_PUSH_1;
477       push_dot1q = 1;
478     }
479   else if (unformat (input, "push dot1ad %d", &tag1))
480     {
481       vtr_op = L2_VTR_PUSH_1;
482
483     }
484   else if (unformat (input, "translate 1-1 dot1q %d", &tag1))
485     {
486       vtr_op = L2_VTR_TRANSLATE_1_1;
487       push_dot1q = 1;
488     }
489   else if (unformat (input, "translate 1-1 dot1ad %d", &tag1))
490     {
491       vtr_op = L2_VTR_TRANSLATE_1_1;
492
493     }
494   else if (unformat (input, "translate 2-1 dot1q %d", &tag1))
495     {
496       vtr_op = L2_VTR_TRANSLATE_2_1;
497       push_dot1q = 1;
498     }
499   else if (unformat (input, "translate 2-1 dot1ad %d", &tag1))
500     {
501       vtr_op = L2_VTR_TRANSLATE_2_1;
502
503     }
504   else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2))
505     {
506       vtr_op = L2_VTR_TRANSLATE_2_2;
507       push_dot1q = 1;
508     }
509   else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2))
510     {
511       vtr_op = L2_VTR_TRANSLATE_2_2;
512
513     }
514   else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2))
515     {
516       vtr_op = L2_VTR_TRANSLATE_1_2;
517       push_dot1q = 1;
518     }
519   else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2))
520     {
521       vtr_op = L2_VTR_TRANSLATE_1_2;
522
523     }
524   else
525     {
526       error =
527         clib_error_return (0,
528                            "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
529                            " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
530                            format_unformat_error, input);
531       goto done;
532     }
533
534   if (l2vtr_configure (vm, vnm, sw_if_index, vtr_op, push_dot1q, tag1, tag2))
535     {
536       error =
537         clib_error_return (0,
538                            "vlan tag rewrite is not compatible with interface");
539       goto done;
540     }
541
542 done:
543   return error;
544 }
545
546 /*?
547  * VLAN tag rewrite provides the ability to change the VLAN tags on a packet.
548  * Existing tags can be popped, new tags can be pushed, and existing tags can
549  * be swapped with new tags. The rewrite feature is attached to a subinterface
550  * as input and output operations. The input operation is explicitly configured.
551  * The output operation is the symmetric opposite and is automatically derived
552  * from the input operation.
553  *
554  * <b>POP:</b> For pop operations, the subinterface encapsulation (the vlan
555  * tags specified when it was created) must have at least the number of popped
556  * tags. e.g. the \"pop 2\" operation would be rejected on a single-vlan interface.
557  * The output tag-rewrite operation for pops is to push the specified number of
558  * vlan tags onto the packet. The pushed tag values are the ones in the
559  * subinterface encapsulation.
560  *
561  * <b>PUSH:</b> For push operations, the ethertype is also specified. The
562  * output tag-rewrite operation for pushes is to pop the same number of tags
563  * off the packet. If the packet doesn't have enough tags it is dropped.
564  *
565  *
566  * @cliexpar
567  * @parblock
568  * By default a subinterface has no tag-rewrite. To return a subinterface to
569  * this state use:
570  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 disable}
571  *
572  * To pop vlan tags off packets received from a subinterface, use:
573  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 pop 1}
574  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 pop 2}
575  *
576  * To push one or two vlan tags onto packets received from an interface, use:
577  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 push dot1q 100}
578  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 push dot1ad 100 150}
579  *
580  * Tags can also be translated, which is basically a combination of a pop and push.
581  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 1-1 dot1ad 100}
582  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 2-2 dot1ad 100 150}
583  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 1-2 dot1q 100}
584  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 2-1 dot1q 100 150}
585  *
586  * To display the VLAN Tag settings, show the associate bridge-domain:
587  * @cliexstart{show bridge-domain 200 detail}
588  *  ID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf
589  * 200     1        on         on         on         on         off          N/A
590  *
591  *          Interface           Index  SHG  BVI        VLAN-Tag-Rewrite
592  *  GigabitEthernet0/8/0.200      5     0    -       trans-1-1 dot1ad 100
593  *  GigabitEthernet0/9/0.200      4     0    -               none
594  *  GigabitEthernet0/a/0.200      6     0    -               none
595  * @cliexend
596  * @endparblock
597 ?*/
598 /* *INDENT-OFF* */
599 VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
600   .path = "set interface l2 tag-rewrite",
601   .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
602   .function = int_l2_vtr,
603 };
604 /* *INDENT-ON* */
605
606
607 /*
608  * fd.io coding-style-patch-verification: ON
609  *
610  * Local Variables:
611  * eval: (c-set-style "gnu")
612  * End:
613  */