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. Also insures file is not eliminated by linker.
34 clib_error_t *l2_vtr_init (vlib_main_t *vm)
39 VLIB_INIT_FUNCTION(l2_vtr_init);
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,
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
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)) {
65 error = VNET_API_ERROR_INVALID_INTERFACE; // non-ethernet interface
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;
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);
80 // Construct the input tag-rewrite config
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);
87 // Determine number of vlan tags with explictly configured values
89 if (hw_no_tags || si->sub.eth.flags.no_tags) {
91 } else if (si->sub.eth.flags.one_tag) {
93 if (si->sub.eth.flags.outer_vlan_id_any) {
96 } else if (si->sub.eth.flags.two_tags) {
98 if (si->sub.eth.flags.inner_vlan_id_any) {
101 if (si->sub.eth.flags.outer_vlan_id_any) {
107 case L2_VTR_DISABLED:
108 in_config->push_and_pop_bytes = 0;
113 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
116 in_config->pop_bytes = 4;
117 in_config->push_bytes = 0;
122 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
125 in_config->pop_bytes = 8;
126 in_config->push_bytes = 0;
128 out_config->push_bytes = in_config->pop_bytes;
129 out_config->pop_bytes = in_config->push_bytes;
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;
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;
148 case L2_VTR_TRANSLATE_1_1:
150 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
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;
159 case L2_VTR_TRANSLATE_1_2:
161 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
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;
172 case L2_VTR_TRANSLATE_2_1:
174 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
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;
183 case L2_VTR_TRANSLATE_2_2:
185 error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
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;
197 // Construct the output tag-rewrite config
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;
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);
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;
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
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,
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
238 vnet_hw_interface_t * hi;
240 vtr_config_t * in_config;
242 if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2) {
243 clib_warning ("invalid arguments");
244 error = VNET_API_ERROR_INVALID_ARGUMENT;
248 *vtr_op = L2_VTR_DISABLED;
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
259 if (sw_if_index >= vec_len(l2output_main.configs)) {
260 // no specific config (return disabled)
264 // Get the config for this interface
265 in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
268 if (in_config->push_and_pop_bytes == 0) {
273 switch (in_config->pop_bytes) {
275 switch (in_config->push_bytes) {
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));
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));
291 clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
292 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
298 switch (in_config->push_bytes) {
300 *vtr_op = L2_VTR_POP_1;
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));
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));
314 clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
315 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
321 switch (in_config->push_bytes) {
323 *vtr_op = L2_VTR_POP_2;
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));
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));
337 clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
338 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
344 clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
345 error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
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)
362 vnet_main_t * vnm = vnet_get_main();
363 clib_error_t * error = 0;
367 u32 tag1 = 0, tag2 = 0;
369 if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
371 error = clib_error_return (0, "unknown interface `%U'",
372 format_unformat_error, input);
376 vtr_op = L2_VTR_DISABLED;
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;
385 } else if (unformat (input, "push dot1q %d %d", &tag1, &tag2)) {
386 vtr_op = L2_VTR_PUSH_2;
388 } else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2)) {
389 vtr_op = L2_VTR_PUSH_2;
391 } else if (unformat (input, "push dot1q %d", &tag1)) {
392 vtr_op = L2_VTR_PUSH_1;
394 } else if (unformat (input, "push dot1ad %d", &tag1)) {
395 vtr_op = L2_VTR_PUSH_1;
397 } else if (unformat (input, "translate 1-1 dot1q %d", &tag1)) {
398 vtr_op = L2_VTR_TRANSLATE_1_1;
400 } else if (unformat (input, "translate 1-1 dot1ad %d", &tag1)) {
401 vtr_op = L2_VTR_TRANSLATE_1_1;
403 } else if (unformat (input, "translate 2-1 dot1q %d", &tag1)) {
404 vtr_op = L2_VTR_TRANSLATE_2_1;
406 } else if (unformat (input, "translate 2-1 dot1ad %d", &tag1)) {
407 vtr_op = L2_VTR_TRANSLATE_2_1;
409 } else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2)) {
410 vtr_op = L2_VTR_TRANSLATE_2_2;
412 } else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2)) {
413 vtr_op = L2_VTR_TRANSLATE_2_2;
415 } else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2)) {
416 vtr_op = L2_VTR_TRANSLATE_1_2;
418 } else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2)) {
419 vtr_op = L2_VTR_TRANSLATE_1_2;
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);
428 if (l2vtr_configure (vm,
435 error = clib_error_return (0, "vlan tag rewrite is not compatible with interface");
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,