Initial commit of vpp code.
[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. Also insures file is not eliminated by linker.
34 clib_error_t *l2_vtr_init (vlib_main_t *vm)
35 {
36   return 0;
37 }
38
39 VLIB_INIT_FUNCTION(l2_vtr_init);
40
41
42 // Configure vtag tag rewrite on the given interface.
43 // Return 1 if there is an error, 0 if ok
44 u32 l2vtr_configure (vlib_main_t * vlib_main,
45                      vnet_main_t * vnet_main,
46                      u32           sw_if_index,
47                      u32           vtr_op,
48                      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     error = VNET_API_ERROR_INVALID_INTERFACE;  // non-ethernet interface
66     goto done;
67   }
68
69   // Init the config for this interface
70   vec_validate (l2output_main.configs, sw_if_index);
71   in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
72   out_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->output_vtr);
73   in_config->raw_tags = 0;
74   out_config->raw_tags = 0;
75
76   // Get the configured tags for the interface
77   si = vnet_get_sw_interface (vnet_main, sw_if_index);
78   hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
79
80   // Construct the input tag-rewrite config
81
82   push_outer_et = clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN : ETHERNET_TYPE_DOT1AD);
83   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
84   vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
85   vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
86
87   // Determine number of vlan tags with explictly configured values
88   cfg_tags = 0;
89   if (hw_no_tags || si->sub.eth.flags.no_tags) {
90     cfg_tags = 0;
91   } else if (si->sub.eth.flags.one_tag) {
92     cfg_tags = 1;
93     if (si->sub.eth.flags.outer_vlan_id_any) {
94       cfg_tags = 0;
95     }
96   } else if (si->sub.eth.flags.two_tags) {
97     cfg_tags = 2;
98     if (si->sub.eth.flags.inner_vlan_id_any) {
99       cfg_tags = 1;
100     }
101     if (si->sub.eth.flags.outer_vlan_id_any) {
102       cfg_tags = 0;
103     }
104   } 
105
106   switch (vtr_op) {
107   case L2_VTR_DISABLED:
108     in_config->push_and_pop_bytes = 0;
109     break;
110
111   case L2_VTR_POP_1:
112     if (cfg_tags < 1) {
113       error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
114       goto done;
115     }
116     in_config->pop_bytes = 4;
117     in_config->push_bytes = 0;
118     break;
119
120   case L2_VTR_POP_2:
121     if (cfg_tags < 2) {
122       error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
123       goto done;
124     }
125     in_config->pop_bytes = 8;
126     in_config->push_bytes = 0;
127
128     out_config->push_bytes = in_config->pop_bytes;
129     out_config->pop_bytes = in_config->push_bytes;
130     break;
131
132   case L2_VTR_PUSH_1:
133     in_config->pop_bytes = 0;
134     in_config->push_bytes = 4;
135     in_config->tags[1].priority_cfi_and_id = vtr_tag1;
136     in_config->tags[1].type = push_outer_et;
137     break;
138
139   case L2_VTR_PUSH_2:
140     in_config->pop_bytes = 0;
141     in_config->push_bytes = 8;
142     in_config->tags[0].priority_cfi_and_id = vtr_tag1;
143     in_config->tags[0].type = push_outer_et;
144     in_config->tags[1].priority_cfi_and_id = vtr_tag2;
145     in_config->tags[1].type = push_inner_et;
146     break;
147
148   case L2_VTR_TRANSLATE_1_1:
149     if (cfg_tags < 1) {
150       error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
151       goto done;
152     }
153     in_config->pop_bytes = 4;
154     in_config->push_bytes = 4;
155     in_config->tags[1].priority_cfi_and_id = vtr_tag1;
156     in_config->tags[1].type = push_outer_et;
157     break;
158
159   case L2_VTR_TRANSLATE_1_2:
160     if (cfg_tags < 1) {
161       error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
162       goto done;
163     }
164     in_config->pop_bytes = 4;
165     in_config->push_bytes = 8;
166     in_config->tags[0].priority_cfi_and_id = vtr_tag1;
167     in_config->tags[0].type = push_outer_et;
168     in_config->tags[1].priority_cfi_and_id = vtr_tag2;
169     in_config->tags[1].type = push_inner_et;
170     break;
171
172   case L2_VTR_TRANSLATE_2_1:
173     if (cfg_tags < 2) {
174       error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
175       goto done;
176     }
177     in_config->pop_bytes = 8;
178     in_config->push_bytes = 4;
179     in_config->tags[1].priority_cfi_and_id = vtr_tag1;
180     in_config->tags[1].type = push_outer_et;
181     break;
182
183   case L2_VTR_TRANSLATE_2_2:
184     if (cfg_tags < 2) {
185       error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
186       goto done;
187     }
188     in_config->pop_bytes = 8;
189     in_config->push_bytes = 8;
190     in_config->tags[0].priority_cfi_and_id = vtr_tag1;
191     in_config->tags[0].type = push_outer_et;
192     in_config->tags[1].priority_cfi_and_id = vtr_tag2;
193     in_config->tags[1].type = push_inner_et;
194     break;
195   }
196
197   // Construct the output tag-rewrite config
198
199   // The push/pop values are always reversed
200   out_config->push_bytes = in_config->pop_bytes;
201   out_config->pop_bytes = in_config->push_bytes;
202
203   // Any pushed tags are derived from the subinterface config
204   push_outer_et = clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD : ETHERNET_TYPE_VLAN);
205   push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
206   vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
207   vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
208
209   if (out_config->push_bytes == 4) {
210     out_config->tags[1].priority_cfi_and_id = vtr_tag1;
211     out_config->tags[1].type = push_outer_et;
212   } else if (out_config->push_bytes == 8) {
213     out_config->tags[0].priority_cfi_and_id = vtr_tag1;
214     out_config->tags[0].type = push_outer_et;
215     out_config->tags[1].priority_cfi_and_id = vtr_tag2;
216     out_config->tags[1].type = push_inner_et;
217   }
218   
219   // set the interface enable flags
220   enable = (vtr_op != L2_VTR_DISABLED);
221   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
222   // output vtr enable is checked explicitly in l2_output
223
224  done:
225   return error;
226 }
227
228 // Get vtag tag rewrite on the given interface.
229 // Return 1 if there is an error, 0 if ok
230 u32 l2vtr_get (vlib_main_t * vlib_main,
231                vnet_main_t * vnet_main,
232                u32           sw_if_index,
233                u32           *vtr_op,
234                u32           *push_dot1q, // ethertype of first pushed tag is dot1q/dot1ad
235                u32           *vtr_tag1,   // first pushed tag
236                u32           *vtr_tag2)   // second pushed tag
237 {
238   vnet_hw_interface_t * hi;
239   u32 error = 0;
240   vtr_config_t * in_config;
241
242   if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2) {
243     clib_warning ("invalid arguments");
244     error = VNET_API_ERROR_INVALID_ARGUMENT;
245     goto done;
246   }
247
248   *vtr_op = L2_VTR_DISABLED;
249   *vtr_tag1 = 0;
250   *vtr_tag2 = 0;
251   *push_dot1q = 0;
252
253   hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
254   if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index)) {
255     // non-ethernet interface
256     goto done;
257   }
258
259   if (sw_if_index >= vec_len(l2output_main.configs)) {
260     // no specific config (return disabled)
261     goto done;
262   }
263
264   // Get the config for this interface
265   in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
266
267   // DISABLED
268   if (in_config->push_and_pop_bytes == 0) {
269     goto done;
270   }
271
272   // find out vtr_op
273   switch (in_config->pop_bytes) {
274     case 0:
275       switch (in_config->push_bytes) {
276         case 0:
277           // DISABLED
278           goto done;
279         case 4:
280           *vtr_op = L2_VTR_PUSH_1;
281           *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
282           *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
283           break;
284         case 8:
285           *vtr_op = L2_VTR_PUSH_2;
286           *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
287           *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
288           *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
289           break;
290         default:
291           clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
292           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
293           goto done;
294       }
295       break;
296
297     case 4:
298       switch (in_config->push_bytes) {
299         case 0:
300           *vtr_op = L2_VTR_POP_1;
301           break;
302         case 4:
303           *vtr_op = L2_VTR_TRANSLATE_1_1;
304           *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
305           *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
306           break;
307         case 8:
308           *vtr_op = L2_VTR_TRANSLATE_1_2;
309           *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
310           *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
311           *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
312           break;
313         default:
314           clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
315           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
316           goto done;
317       }
318       break;
319
320     case 8:
321       switch (in_config->push_bytes) {
322         case 0:
323           *vtr_op = L2_VTR_POP_2;
324           break;
325         case 4:
326           *vtr_op = L2_VTR_TRANSLATE_2_1;
327           *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
328           *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
329           break;
330         case 8:
331           *vtr_op = L2_VTR_TRANSLATE_2_2;
332           *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
333           *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
334           *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
335           break;
336         default:
337           clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
338           error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
339           goto done;
340       }
341       break;
342
343     default:
344       clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
345       error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
346       goto done;
347   }
348
349  done:
350   return error;
351 }
352
353 // set subinterface vtr enable/disable
354 // The CLI format is:
355 //    set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
356 // "push" can also be replaced by "translate-{1|2}-{1|2}"
357 static clib_error_t *
358 int_l2_vtr (vlib_main_t * vm,
359             unformat_input_t * input,
360             vlib_cli_command_t * cmd)
361 {
362   vnet_main_t * vnm = vnet_get_main();
363   clib_error_t * error = 0;
364   u32 sw_if_index;
365   u32 vtr_op;
366   u32 push_dot1q = 0;
367   u32 tag1 = 0, tag2 = 0;
368
369   if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
370     {
371       error = clib_error_return (0, "unknown interface `%U'",
372                                  format_unformat_error, input);
373       goto done;
374     }
375
376   vtr_op = L2_VTR_DISABLED;
377
378   if (unformat (input, "disable")) {
379     vtr_op = L2_VTR_DISABLED;
380   } else if (unformat (input, "pop 1")) {
381     vtr_op = L2_VTR_POP_1;
382   } else if (unformat (input, "pop 2")) {
383     vtr_op = L2_VTR_POP_2;
384
385   } else if (unformat (input, "push dot1q %d %d", &tag1, &tag2)) {
386     vtr_op = L2_VTR_PUSH_2;
387     push_dot1q = 1;
388   } else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2)) {
389     vtr_op = L2_VTR_PUSH_2;
390
391   } else if (unformat (input, "push dot1q %d", &tag1)) {
392     vtr_op = L2_VTR_PUSH_1;
393     push_dot1q = 1;
394   } else if (unformat (input, "push dot1ad %d", &tag1)) {
395     vtr_op = L2_VTR_PUSH_1;
396
397   } else if (unformat (input, "translate 1-1 dot1q %d", &tag1)) {
398     vtr_op = L2_VTR_TRANSLATE_1_1;
399     push_dot1q = 1;
400   } else if (unformat (input, "translate 1-1 dot1ad %d", &tag1)) {
401     vtr_op = L2_VTR_TRANSLATE_1_1;
402
403   } else if (unformat (input, "translate 2-1 dot1q %d", &tag1)) {
404     vtr_op = L2_VTR_TRANSLATE_2_1;
405     push_dot1q = 1;
406   } else if (unformat (input, "translate 2-1 dot1ad %d", &tag1)) {
407     vtr_op = L2_VTR_TRANSLATE_2_1;
408
409   } else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2)) {
410     vtr_op = L2_VTR_TRANSLATE_2_2;
411     push_dot1q = 1;
412   } else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2)) {
413     vtr_op = L2_VTR_TRANSLATE_2_2;
414
415   } else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2)) {
416     vtr_op = L2_VTR_TRANSLATE_1_2;
417     push_dot1q = 1;
418   } else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2)) {
419     vtr_op = L2_VTR_TRANSLATE_1_2;
420
421   } else {
422     error = clib_error_return (0, "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
423                                " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
424                                format_unformat_error, input);
425     goto done;
426   }
427
428   if (l2vtr_configure (vm,
429                        vnm,
430                        sw_if_index,
431                        vtr_op,
432                        push_dot1q,
433                        tag1,
434                        tag2)) {
435     error = clib_error_return (0, "vlan tag rewrite is not compatible with interface");
436     goto done;
437   }
438
439  done:
440   return error;
441 }
442
443 VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
444   .path = "set interface l2 tag-rewrite",
445   .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
446   .function = int_l2_vtr,
447 };
448