Trivial: Clean up some typos.
[vpp.git] / src / 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 u32
54 l2pbb_configure (vlib_main_t * vlib_main,
55                  vnet_main_t * vnet_main, u32 sw_if_index, u32 vtr_op,
56                  u8 * b_dmac, u8 * b_smac,
57                  u16 b_vlanid, u32 i_sid, u16 vlan_outer_tag)
58 {
59   u32 error = 0;
60   u32 enable = 0;
61
62   l2_output_config_t *config = 0;
63   vnet_hw_interface_t *hi;
64   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
65
66   if (!hi)
67     {
68       error = VNET_API_ERROR_INVALID_INTERFACE;
69       goto done;
70     }
71
72   // Config for this interface should be already initialized
73   ptr_config_t *in_config;
74   ptr_config_t *out_config;
75   config = vec_elt_at_index (l2output_main.configs, sw_if_index);
76   in_config = &(config->input_pbb_vtr);
77   out_config = &(config->output_pbb_vtr);
78
79   in_config->pop_bytes = 0;
80   in_config->push_bytes = 0;
81   out_config->pop_bytes = 0;
82   out_config->push_bytes = 0;
83   enable = (vtr_op != L2_VTR_DISABLED);
84
85   if (!enable)
86     goto done;
87
88   if (vtr_op == L2_VTR_POP_2)
89     {
90       in_config->pop_bytes = sizeof (ethernet_pbb_header_packed_t);
91     }
92   else if (vtr_op == L2_VTR_PUSH_2)
93     {
94       clib_memcpy (in_config->macs_tags.b_dst_address, b_dmac,
95                    sizeof (in_config->macs_tags.b_dst_address));
96       clib_memcpy (in_config->macs_tags.b_src_address, b_smac,
97                    sizeof (in_config->macs_tags.b_src_address));
98       in_config->macs_tags.b_type =
99         clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AD);
100       in_config->macs_tags.priority_dei_id =
101         clib_net_to_host_u16 (b_vlanid & 0xFFF);
102       in_config->macs_tags.i_type =
103         clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AH);
104       in_config->macs_tags.priority_dei_uca_res_sid =
105         clib_net_to_host_u32 (i_sid & 0xFFFFF);
106       in_config->push_bytes = sizeof (ethernet_pbb_header_packed_t);
107     }
108   else if (vtr_op == L2_VTR_TRANSLATE_2_2)
109     {
110       /* TODO after PoC */
111     }
112
113   /*
114    *  Construct the output tag-rewrite config
115    *
116    *  The push/pop values are always reversed
117    */
118   out_config->raw_data = in_config->raw_data;
119   out_config->pop_bytes = in_config->push_bytes;
120   out_config->push_bytes = in_config->pop_bytes;
121
122 done:
123   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
124   if (config)
125     config->out_vtr_flag = (u8) enable;
126
127   /* output vtr enable is checked explicitly in l2_output */
128   return error;
129 }
130
131 /**
132  * Configure vtag tag rewrite on the given interface.
133  * Return 1 if there is an error, 0 if ok
134  */
135 u32
136 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 */
137                  u32 vtr_tag1,  /* first pushed tag */
138                  u32 vtr_tag2)  /* second pushed tag */
139 {
140   vnet_hw_interface_t *hi;
141   vnet_sw_interface_t *si;
142   u32 hw_no_tags;
143   u32 error = 0;
144   l2_output_config_t *config;
145   vtr_config_t *in_config;
146   vtr_config_t *out_config;
147   u32 enable;
148   u32 push_inner_et;
149   u32 push_outer_et;
150   u32 cfg_tags;
151
152   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
153   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
154     {
155       error = VNET_API_ERROR_INVALID_INTERFACE; /* non-ethernet interface */
156       goto done;
157     }
158
159   /* Init the config for this interface */
160   vec_validate (l2output_main.configs, sw_if_index);
161   config = vec_elt_at_index (l2output_main.configs, sw_if_index);
162   in_config = &(config->input_vtr);
163   out_config = &(config->output_vtr);
164   in_config->raw_tags = 0;
165   out_config->raw_tags = 0;
166
167   /* Get the configured tags for the interface */
168   si = vnet_get_sw_interface (vnet_main, sw_if_index);
169   hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
170
171   /* Construct the input tag-rewrite config */
172
173   push_outer_et =
174     clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN :
175                           ETHERNET_TYPE_DOT1AD);
176   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
177   vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
178   vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
179
180   /* Determine number of vlan tags with explicitly configured values */
181   cfg_tags = 0;
182   if (hw_no_tags || si->sub.eth.flags.no_tags)
183     {
184       cfg_tags = 0;
185     }
186   else if (si->sub.eth.flags.one_tag)
187     {
188       cfg_tags = 1;
189       if (si->sub.eth.flags.outer_vlan_id_any)
190         {
191           cfg_tags = 0;
192         }
193     }
194   else if (si->sub.eth.flags.two_tags)
195     {
196       cfg_tags = 2;
197       if (si->sub.eth.flags.inner_vlan_id_any)
198         {
199           cfg_tags = 1;
200         }
201       if (si->sub.eth.flags.outer_vlan_id_any)
202         {
203           cfg_tags = 0;
204         }
205     }
206
207   switch (vtr_op)
208     {
209     case L2_VTR_DISABLED:
210       in_config->push_and_pop_bytes = 0;
211       break;
212
213     case L2_VTR_POP_1:
214       if (cfg_tags < 1)
215         {
216           /* Need one or two tags */
217           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;
218           goto done;
219         }
220       in_config->pop_bytes = 4;
221       in_config->push_bytes = 0;
222       break;
223
224     case L2_VTR_POP_2:
225       if (cfg_tags < 2)
226         {
227           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
228           goto done;
229         }
230       in_config->pop_bytes = 8;
231       in_config->push_bytes = 0;
232       break;
233
234     case L2_VTR_PUSH_1:
235       in_config->pop_bytes = 0;
236       in_config->push_bytes = 4;
237       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
238       in_config->tags[1].type = push_outer_et;
239       break;
240
241     case L2_VTR_PUSH_2:
242       in_config->pop_bytes = 0;
243       in_config->push_bytes = 8;
244       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
245       in_config->tags[0].type = push_outer_et;
246       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
247       in_config->tags[1].type = push_inner_et;
248       break;
249
250     case L2_VTR_TRANSLATE_1_1:
251       if (cfg_tags < 1)
252         {
253           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need one or two tags */
254           goto done;
255         }
256       in_config->pop_bytes = 4;
257       in_config->push_bytes = 4;
258       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
259       in_config->tags[1].type = push_outer_et;
260       break;
261
262     case L2_VTR_TRANSLATE_1_2:
263       if (cfg_tags < 1)
264         {
265           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need one or two tags */
266           goto done;
267         }
268       in_config->pop_bytes = 4;
269       in_config->push_bytes = 8;
270       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
271       in_config->tags[0].type = push_outer_et;
272       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
273       in_config->tags[1].type = push_inner_et;
274       break;
275
276     case L2_VTR_TRANSLATE_2_1:
277       if (cfg_tags < 2)
278         {
279           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
280           goto done;
281         }
282       in_config->pop_bytes = 8;
283       in_config->push_bytes = 4;
284       in_config->tags[1].priority_cfi_and_id = vtr_tag1;
285       in_config->tags[1].type = push_outer_et;
286       break;
287
288     case L2_VTR_TRANSLATE_2_2:
289       if (cfg_tags < 2)
290         {
291           error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;        /* Need two tags */
292           goto done;
293         }
294       in_config->pop_bytes = 8;
295       in_config->push_bytes = 8;
296       in_config->tags[0].priority_cfi_and_id = vtr_tag1;
297       in_config->tags[0].type = push_outer_et;
298       in_config->tags[1].priority_cfi_and_id = vtr_tag2;
299       in_config->tags[1].type = push_inner_et;
300       break;
301     }
302
303   /*
304    *  Construct the output tag-rewrite config
305    *
306    *  The push/pop values are always reversed
307    */
308   out_config->push_bytes = in_config->pop_bytes;
309   out_config->pop_bytes = in_config->push_bytes;
310
311   /* Any pushed tags are derived from the subinterface config */
312   push_outer_et =
313     clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD :
314                           ETHERNET_TYPE_VLAN);
315   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
316   vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
317   vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
318
319   if (out_config->push_bytes == 4)
320     {
321       out_config->tags[1].priority_cfi_and_id = vtr_tag1;
322       out_config->tags[1].type = push_outer_et;
323     }
324   else if (out_config->push_bytes == 8)
325     {
326       out_config->tags[0].priority_cfi_and_id = vtr_tag1;
327       out_config->tags[0].type = push_outer_et;
328       out_config->tags[1].priority_cfi_and_id = vtr_tag2;
329       out_config->tags[1].type = push_inner_et;
330     }
331
332   /* set the interface enable flags */
333   enable = (vtr_op != L2_VTR_DISABLED);
334   config->out_vtr_flag = (u8) enable;
335   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
336   /* output vtr enable is checked explicitly in l2_output */
337
338 done:
339   return error;
340 }
341
342 /**
343  * Get vtag tag rewrite on the given interface.
344  * Return 1 if there is an error, 0 if ok
345  */
346 u32
347 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 */
348            u32 * vtr_tag1,      /* first pushed tag */
349            u32 * vtr_tag2)      /* second pushed tag */
350 {
351   vnet_hw_interface_t *hi;
352   u32 error = 0;
353   vtr_config_t *in_config;
354
355   if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2)
356     {
357       clib_warning ("invalid arguments");
358       error = VNET_API_ERROR_INVALID_ARGUMENT;
359       goto done;
360     }
361
362   *vtr_op = L2_VTR_DISABLED;
363   *vtr_tag1 = 0;
364   *vtr_tag2 = 0;
365   *push_dot1q = 0;
366
367   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
368   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
369     {
370       /* non-ethernet interface */
371       goto done;
372     }
373
374   if (sw_if_index >= vec_len (l2output_main.configs))
375     {
376       /* no specific config (return disabled) */
377       goto done;
378     }
379
380   /* Get the config for this interface */
381   in_config =
382     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
383
384   /* DISABLED */
385   if (in_config->push_and_pop_bytes == 0)
386     {
387       goto done;
388     }
389
390   /* find out vtr_op */
391   switch (in_config->pop_bytes)
392     {
393     case 0:
394       switch (in_config->push_bytes)
395         {
396         case 0:
397           /* DISABLED */
398           goto done;
399         case 4:
400           *vtr_op = L2_VTR_PUSH_1;
401           *vtr_tag1 =
402             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
403           *push_dot1q =
404             (ETHERNET_TYPE_VLAN ==
405              clib_host_to_net_u16 (in_config->tags[1].type));
406           break;
407         case 8:
408           *vtr_op = L2_VTR_PUSH_2;
409           *vtr_tag1 =
410             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
411           *vtr_tag2 =
412             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
413           *push_dot1q =
414             (ETHERNET_TYPE_VLAN ==
415              clib_host_to_net_u16 (in_config->tags[0].type));
416           break;
417         default:
418           clib_warning ("invalid push_bytes count: %d",
419                         in_config->push_bytes);
420           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
421           goto done;
422         }
423       break;
424
425     case 4:
426       switch (in_config->push_bytes)
427         {
428         case 0:
429           *vtr_op = L2_VTR_POP_1;
430           break;
431         case 4:
432           *vtr_op = L2_VTR_TRANSLATE_1_1;
433           *vtr_tag1 =
434             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
435           *push_dot1q =
436             (ETHERNET_TYPE_VLAN ==
437              clib_host_to_net_u16 (in_config->tags[1].type));
438           break;
439         case 8:
440           *vtr_op = L2_VTR_TRANSLATE_1_2;
441           *vtr_tag1 =
442             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
443           *vtr_tag2 =
444             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
445           *push_dot1q =
446             (ETHERNET_TYPE_VLAN ==
447              clib_host_to_net_u16 (in_config->tags[0].type));
448           break;
449         default:
450           clib_warning ("invalid push_bytes count: %d",
451                         in_config->push_bytes);
452           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
453           goto done;
454         }
455       break;
456
457     case 8:
458       switch (in_config->push_bytes)
459         {
460         case 0:
461           *vtr_op = L2_VTR_POP_2;
462           break;
463         case 4:
464           *vtr_op = L2_VTR_TRANSLATE_2_1;
465           *vtr_tag1 =
466             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
467           *push_dot1q =
468             (ETHERNET_TYPE_VLAN ==
469              clib_host_to_net_u16 (in_config->tags[1].type));
470           break;
471         case 8:
472           *vtr_op = L2_VTR_TRANSLATE_2_2;
473           *vtr_tag1 =
474             clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
475           *vtr_tag2 =
476             clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
477           *push_dot1q =
478             (ETHERNET_TYPE_VLAN ==
479              clib_host_to_net_u16 (in_config->tags[0].type));
480           break;
481         default:
482           clib_warning ("invalid push_bytes count: %d",
483                         in_config->push_bytes);
484           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
485           goto done;
486         }
487       break;
488
489     default:
490       clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
491       error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
492       goto done;
493     }
494
495 done:
496   return error;
497 }
498
499 /**
500  * Set subinterface vtr enable/disable.
501  * The CLI format is:
502  *    set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
503  *
504  *  "push" can also be replaced by "translate-{1|2}-{1|2}"
505  */
506 static clib_error_t *
507 int_l2_vtr (vlib_main_t * vm,
508             unformat_input_t * input, vlib_cli_command_t * cmd)
509 {
510   vnet_main_t *vnm = vnet_get_main ();
511   clib_error_t *error = 0;
512   u32 sw_if_index;
513   u32 vtr_op;
514   u32 push_dot1q = 0;
515   u32 tag1 = 0, tag2 = 0;
516
517   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
518     {
519       error = clib_error_return (0, "unknown interface `%U'",
520                                  format_unformat_error, input);
521       goto done;
522     }
523
524   vtr_op = L2_VTR_DISABLED;
525
526   if (unformat (input, "disable"))
527     {
528       vtr_op = L2_VTR_DISABLED;
529     }
530   else if (unformat (input, "pop 1"))
531     {
532       vtr_op = L2_VTR_POP_1;
533     }
534   else if (unformat (input, "pop 2"))
535     {
536       vtr_op = L2_VTR_POP_2;
537
538     }
539   else if (unformat (input, "push dot1q %d %d", &tag1, &tag2))
540     {
541       vtr_op = L2_VTR_PUSH_2;
542       push_dot1q = 1;
543     }
544   else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2))
545     {
546       vtr_op = L2_VTR_PUSH_2;
547
548     }
549   else if (unformat (input, "push dot1q %d", &tag1))
550     {
551       vtr_op = L2_VTR_PUSH_1;
552       push_dot1q = 1;
553     }
554   else if (unformat (input, "push dot1ad %d", &tag1))
555     {
556       vtr_op = L2_VTR_PUSH_1;
557
558     }
559   else if (unformat (input, "translate 1-1 dot1q %d", &tag1))
560     {
561       vtr_op = L2_VTR_TRANSLATE_1_1;
562       push_dot1q = 1;
563     }
564   else if (unformat (input, "translate 1-1 dot1ad %d", &tag1))
565     {
566       vtr_op = L2_VTR_TRANSLATE_1_1;
567
568     }
569   else if (unformat (input, "translate 2-1 dot1q %d", &tag1))
570     {
571       vtr_op = L2_VTR_TRANSLATE_2_1;
572       push_dot1q = 1;
573     }
574   else if (unformat (input, "translate 2-1 dot1ad %d", &tag1))
575     {
576       vtr_op = L2_VTR_TRANSLATE_2_1;
577
578     }
579   else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2))
580     {
581       vtr_op = L2_VTR_TRANSLATE_2_2;
582       push_dot1q = 1;
583     }
584   else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2))
585     {
586       vtr_op = L2_VTR_TRANSLATE_2_2;
587
588     }
589   else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2))
590     {
591       vtr_op = L2_VTR_TRANSLATE_1_2;
592       push_dot1q = 1;
593     }
594   else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2))
595     {
596       vtr_op = L2_VTR_TRANSLATE_1_2;
597
598     }
599   else
600     {
601       error =
602         clib_error_return (0,
603                            "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
604                            " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
605                            format_unformat_error, input);
606       goto done;
607     }
608
609   if (l2vtr_configure (vm, vnm, sw_if_index, vtr_op, push_dot1q, tag1, tag2))
610     {
611       error =
612         clib_error_return (0,
613                            "vlan tag rewrite is not compatible with interface");
614       goto done;
615     }
616
617 done:
618   return error;
619 }
620
621 /*?
622  * VLAN tag rewrite provides the ability to change the VLAN tags on a packet.
623  * Existing tags can be popped, new tags can be pushed, and existing tags can
624  * be swapped with new tags. The rewrite feature is attached to a subinterface
625  * as input and output operations. The input operation is explicitly configured.
626  * The output operation is the symmetric opposite and is automatically derived
627  * from the input operation.
628  *
629  * <b>POP:</b> For pop operations, the subinterface encapsulation (the vlan
630  * tags specified when it was created) must have at least the number of popped
631  * tags. e.g. the \"pop 2\" operation would be rejected on a single-vlan interface.
632  * The output tag-rewrite operation for pops is to push the specified number of
633  * vlan tags onto the packet. The pushed tag values are the ones in the
634  * subinterface encapsulation.
635  *
636  * <b>PUSH:</b> For push operations, the ethertype is also specified. The
637  * output tag-rewrite operation for pushes is to pop the same number of tags
638  * off the packet. If the packet doesn't have enough tags it is dropped.
639  *
640  *
641  * @cliexpar
642  * @parblock
643  * By default a subinterface has no tag-rewrite. To return a subinterface to
644  * this state use:
645  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 disable}
646  *
647  * To pop vlan tags off packets received from a subinterface, use:
648  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 pop 1}
649  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 pop 2}
650  *
651  * To push one or two vlan tags onto packets received from an interface, use:
652  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 push dot1q 100}
653  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 push dot1ad 100 150}
654  *
655  * Tags can also be translated, which is basically a combination of a pop and push.
656  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 1-1 dot1ad 100}
657  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 2-2 dot1ad 100 150}
658  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 1-2 dot1q 100}
659  * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 2-1 dot1q 100 150}
660  *
661  * To display the VLAN Tag settings, show the associate bridge-domain:
662  * @cliexstart{show bridge-domain 200 detail}
663  *  ID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf
664  * 200     1        on         on         on         on         off          N/A
665  *
666  *          Interface           Index  SHG  BVI        VLAN-Tag-Rewrite
667  *  GigabitEthernet0/8/0.200      5     0    -       trans-1-1 dot1ad 100
668  *  GigabitEthernet0/9/0.200      4     0    -               none
669  *  GigabitEthernet0/a/0.200      6     0    -               none
670  * @cliexend
671  * @endparblock
672 ?*/
673 /* *INDENT-OFF* */
674 VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
675   .path = "set interface l2 tag-rewrite",
676   .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
677   .function = int_l2_vtr,
678 };
679 /* *INDENT-ON* */
680
681 /**
682  * Get pbb tag rewrite on the given interface.
683  * Return 1 if there is an error, 0 if ok
684  */
685 u32
686 l2pbb_get (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_index,
687            u32 * vtr_op, u16 * outer_tag, ethernet_header_t * eth_hdr,
688            u16 * b_vlanid, u32 * i_sid)
689 {
690   u32 error = 1;
691   ptr_config_t *in_config;
692
693   if (!vtr_op || !outer_tag || !b_vlanid || !i_sid)
694     {
695       clib_warning ("invalid arguments");
696       error = VNET_API_ERROR_INVALID_ARGUMENT;
697       goto done;
698     }
699
700   *vtr_op = L2_VTR_DISABLED;
701   *outer_tag = 0;
702   *b_vlanid = 0;
703   *i_sid = 0;
704
705   if (sw_if_index >= vec_len (l2output_main.configs))
706     {
707       /* no specific config (return disabled) */
708       goto done;
709     }
710
711   /* Get the config for this interface */
712   in_config =
713     &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_pbb_vtr);
714
715   if (in_config->push_and_pop_bytes == 0)
716     {
717       /* DISABLED */
718       goto done;
719     }
720   else
721     {
722       if (in_config->pop_bytes && in_config->push_bytes)
723         *vtr_op = L2_VTR_TRANSLATE_2_1;
724       else if (in_config->pop_bytes)
725         *vtr_op = L2_VTR_POP_2;
726       else if (in_config->push_bytes)
727         *vtr_op = L2_VTR_PUSH_2;
728
729       clib_memcpy (&eth_hdr->dst_address, in_config->macs_tags.b_dst_address,
730                    sizeof (eth_hdr->dst_address));
731       clib_memcpy (&eth_hdr->src_address, in_config->macs_tags.b_src_address,
732                    sizeof (eth_hdr->src_address));
733
734       *b_vlanid =
735         clib_host_to_net_u16 (in_config->macs_tags.priority_dei_id) & 0xFFF;
736       *i_sid =
737         clib_host_to_net_u32 (in_config->macs_tags.
738                               priority_dei_uca_res_sid) & 0xFFFFF;
739       error = 0;
740     }
741 done:
742   return error;
743 }
744
745 /**
746  * Set subinterface pbb vtr enable/disable.
747  * The CLI format is:
748  *    set interface l2 pbb-tag-rewrite <interface> [disable | pop | push | translate_pbb_stag <outer_tag> dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]
749  */
750 static clib_error_t *
751 int_l2_pbb_vtr (vlib_main_t * vm,
752                 unformat_input_t * input, vlib_cli_command_t * cmd)
753 {
754   vnet_main_t *vnm = vnet_get_main ();
755   clib_error_t *error = 0;
756   u32 sw_if_index, tmp;
757   u32 vtr_op = L2_VTR_DISABLED;
758   u32 outer_tag = 0;
759   u8 dmac[6];
760   u8 smac[6];
761   u8 dmac_set = 0, smac_set = 0;
762   u16 b_vlanid = 0;
763   u32 s_id = ~0;
764
765   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
766     {
767       if (unformat_user
768           (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
769         ;
770       else if (unformat (input, "disable"))
771         vtr_op = L2_VTR_DISABLED;
772       else if (vtr_op == L2_VTR_DISABLED && unformat (input, "pop"))
773         vtr_op = L2_VTR_POP_2;
774       else if (vtr_op == L2_VTR_DISABLED && unformat (input, "push"))
775         vtr_op = L2_VTR_PUSH_2;
776       else if (vtr_op == L2_VTR_DISABLED
777                && unformat (input, "translate_pbb_stag %d", &outer_tag))
778         vtr_op = L2_VTR_TRANSLATE_2_1;
779       else if (unformat (input, "dmac %U", unformat_ethernet_address, dmac))
780         dmac_set = 1;
781       else if (unformat (input, "smac %U", unformat_ethernet_address, smac))
782         smac_set = 1;
783       else if (unformat (input, "b_vlanid %d", &tmp))
784         b_vlanid = tmp;
785       else if (unformat (input, "s_id %d", &s_id))
786         ;
787       else
788         {
789           error = clib_error_return (0,
790                                      "expecting [disable | pop | push | translate_pbb_stag <outer_tag>\n"
791                                      "dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]");
792           goto done;
793         }
794     }
795
796   if ((vtr_op == L2_VTR_PUSH_2 || vtr_op == L2_VTR_TRANSLATE_2_1)
797       && (!dmac_set || !smac_set || s_id == ~0))
798     {
799       error = clib_error_return (0,
800                                  "expecting dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]");
801       goto done;
802     }
803
804   if (l2pbb_configure
805       (vm, vnm, sw_if_index, vtr_op, dmac, smac, b_vlanid, s_id, outer_tag))
806     {
807       error =
808         clib_error_return (0,
809                            "pbb tag rewrite is not compatible with interface");
810       goto done;
811     }
812
813 done:
814   return error;
815 }
816
817 /* *INDENT-OFF* */
818 VLIB_CLI_COMMAND (int_l2_pbb_vtr_cli, static) = {
819   .path = "set interface l2 pbb-tag-rewrite",
820   .short_help = "set interface l2 pbb-tag-rewrite <interface> [disable | pop | push | translate_pbb_stag <outer_tag> dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]",
821   .function = int_l2_pbb_vtr,
822 };
823 /* *INDENT-ON* */
824
825 /*
826  * fd.io coding-style-patch-verification: ON
827  *
828  * Local Variables:
829  * eval: (c-set-style "gnu")
830  * End:
831  */