2 * l2_vtr.c : layer 2 vlan tag rewrite configuration
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
29 #include <vppinfra/error.h>
33 /** Just a placeholder; ensures file is not eliminated by linker. */
35 l2_vtr_init (vlib_main_t * vm)
40 VLIB_INIT_FUNCTION (l2_vtr_init);
44 * Configure vtag tag rewrite on the given interface.
45 * Return 1 if there is an error, 0 if ok
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 */
52 vnet_hw_interface_t *hi;
53 vnet_sw_interface_t *si;
56 vtr_config_t *in_config;
57 vtr_config_t *out_config;
63 hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
64 if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
66 error = VNET_API_ERROR_INVALID_INTERFACE; /* non-ethernet interface */
70 /* Init the config for this interface */
71 vec_validate (l2output_main.configs, sw_if_index);
73 &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
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;
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);
83 /* Construct the input tag-rewrite config */
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);
92 /* Determine number of vlan tags with explictly configured values */
94 if (hw_no_tags || si->sub.eth.flags.no_tags)
98 else if (si->sub.eth.flags.one_tag)
101 if (si->sub.eth.flags.outer_vlan_id_any)
106 else if (si->sub.eth.flags.two_tags)
109 if (si->sub.eth.flags.inner_vlan_id_any)
113 if (si->sub.eth.flags.outer_vlan_id_any)
121 case L2_VTR_DISABLED:
122 in_config->push_and_pop_bytes = 0;
128 /* Need one or two tags */
129 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;
132 in_config->pop_bytes = 4;
133 in_config->push_bytes = 0;
139 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
142 in_config->pop_bytes = 8;
143 in_config->push_bytes = 0;
145 out_config->push_bytes = in_config->pop_bytes;
146 out_config->pop_bytes = in_config->push_bytes;
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;
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;
165 case L2_VTR_TRANSLATE_1_1:
168 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need one or two tags */
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;
177 case L2_VTR_TRANSLATE_1_2:
180 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need one or two tags */
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;
191 case L2_VTR_TRANSLATE_2_1:
194 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
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;
203 case L2_VTR_TRANSLATE_2_2:
206 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
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;
219 * Construct the output tag-rewrite config
221 * The push/pop values are always reversed
223 out_config->push_bytes = in_config->pop_bytes;
224 out_config->pop_bytes = in_config->push_bytes;
226 /* Any pushed tags are derived from the subinterface config */
228 clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD :
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);
234 if (out_config->push_bytes == 4)
236 out_config->tags[1].priority_cfi_and_id = vtr_tag1;
237 out_config->tags[1].type = push_outer_et;
239 else if (out_config->push_bytes == 8)
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;
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 */
257 * Get vtag tag rewrite on the given interface.
258 * Return 1 if there is an error, 0 if ok
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 */
265 vnet_hw_interface_t *hi;
267 vtr_config_t *in_config;
269 if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2)
271 clib_warning ("invalid arguments");
272 error = VNET_API_ERROR_INVALID_ARGUMENT;
276 *vtr_op = L2_VTR_DISABLED;
281 hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
282 if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
284 /* non-ethernet interface */
288 if (sw_if_index >= vec_len (l2output_main.configs))
290 /* no specific config (return disabled) */
294 /* Get the config for this interface */
296 &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
299 if (in_config->push_and_pop_bytes == 0)
304 /* find out vtr_op */
305 switch (in_config->pop_bytes)
308 switch (in_config->push_bytes)
314 *vtr_op = L2_VTR_PUSH_1;
316 clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
318 (ETHERNET_TYPE_VLAN ==
319 clib_host_to_net_u16 (in_config->tags[1].type));
322 *vtr_op = L2_VTR_PUSH_2;
324 clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
326 clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
328 (ETHERNET_TYPE_VLAN ==
329 clib_host_to_net_u16 (in_config->tags[0].type));
332 clib_warning ("invalid push_bytes count: %d",
333 in_config->push_bytes);
334 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
340 switch (in_config->push_bytes)
343 *vtr_op = L2_VTR_POP_1;
346 *vtr_op = L2_VTR_TRANSLATE_1_1;
348 clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
350 (ETHERNET_TYPE_VLAN ==
351 clib_host_to_net_u16 (in_config->tags[1].type));
354 *vtr_op = L2_VTR_TRANSLATE_1_2;
356 clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
358 clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
360 (ETHERNET_TYPE_VLAN ==
361 clib_host_to_net_u16 (in_config->tags[0].type));
364 clib_warning ("invalid push_bytes count: %d",
365 in_config->push_bytes);
366 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
372 switch (in_config->push_bytes)
375 *vtr_op = L2_VTR_POP_2;
378 *vtr_op = L2_VTR_TRANSLATE_2_1;
380 clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
382 (ETHERNET_TYPE_VLAN ==
383 clib_host_to_net_u16 (in_config->tags[1].type));
386 *vtr_op = L2_VTR_TRANSLATE_2_2;
388 clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
390 clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
392 (ETHERNET_TYPE_VLAN ==
393 clib_host_to_net_u16 (in_config->tags[0].type));
396 clib_warning ("invalid push_bytes count: %d",
397 in_config->push_bytes);
398 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
404 clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
405 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
414 * Set subinterface vtr enable/disable.
416 * set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
418 * "push" can also be replaced by "translate-{1|2}-{1|2}"
420 static clib_error_t *
421 int_l2_vtr (vlib_main_t * vm,
422 unformat_input_t * input, vlib_cli_command_t * cmd)
424 vnet_main_t *vnm = vnet_get_main ();
425 clib_error_t *error = 0;
429 u32 tag1 = 0, tag2 = 0;
431 if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
433 error = clib_error_return (0, "unknown interface `%U'",
434 format_unformat_error, input);
438 vtr_op = L2_VTR_DISABLED;
440 if (unformat (input, "disable"))
442 vtr_op = L2_VTR_DISABLED;
444 else if (unformat (input, "pop 1"))
446 vtr_op = L2_VTR_POP_1;
448 else if (unformat (input, "pop 2"))
450 vtr_op = L2_VTR_POP_2;
453 else if (unformat (input, "push dot1q %d %d", &tag1, &tag2))
455 vtr_op = L2_VTR_PUSH_2;
458 else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2))
460 vtr_op = L2_VTR_PUSH_2;
463 else if (unformat (input, "push dot1q %d", &tag1))
465 vtr_op = L2_VTR_PUSH_1;
468 else if (unformat (input, "push dot1ad %d", &tag1))
470 vtr_op = L2_VTR_PUSH_1;
473 else if (unformat (input, "translate 1-1 dot1q %d", &tag1))
475 vtr_op = L2_VTR_TRANSLATE_1_1;
478 else if (unformat (input, "translate 1-1 dot1ad %d", &tag1))
480 vtr_op = L2_VTR_TRANSLATE_1_1;
483 else if (unformat (input, "translate 2-1 dot1q %d", &tag1))
485 vtr_op = L2_VTR_TRANSLATE_2_1;
488 else if (unformat (input, "translate 2-1 dot1ad %d", &tag1))
490 vtr_op = L2_VTR_TRANSLATE_2_1;
493 else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2))
495 vtr_op = L2_VTR_TRANSLATE_2_2;
498 else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2))
500 vtr_op = L2_VTR_TRANSLATE_2_2;
503 else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2))
505 vtr_op = L2_VTR_TRANSLATE_1_2;
508 else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2))
510 vtr_op = L2_VTR_TRANSLATE_1_2;
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);
523 if (l2vtr_configure (vm, vnm, sw_if_index, vtr_op, push_dot1q, tag1, tag2))
526 clib_error_return (0,
527 "vlan tag rewrite is not compatible with interface");
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,
545 * fd.io coding-style-patch-verification: ON
548 * eval: (c-set-style "gnu")