summaryrefslogtreecommitdiffstats
path: root/vnet/vnet/l2/l2_vtr.c
diff options
context:
space:
mode:
Diffstat (limited to 'vnet/vnet/l2/l2_vtr.c')
-rw-r--r--vnet/vnet/l2/l2_vtr.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/vnet/vnet/l2/l2_vtr.c b/vnet/vnet/l2/l2_vtr.c
new file mode 100644
index 00000000000..a7499041009
--- /dev/null
+++ b/vnet/vnet/l2/l2_vtr.c
@@ -0,0 +1,448 @@
+/*
+ * l2_vtr.c : layer 2 vlan tag rewrite configuration
+ *
+ * Copyright (c) 2013 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ethernet/packet.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
+#include <vnet/l2/feat_bitmap.h>
+#include <vnet/l2/l2_vtr.h>
+#include <vnet/l2/l2_input_vtr.h>
+#include <vnet/l2/l2_output.h>
+
+#include <vppinfra/error.h>
+#include <vlib/cli.h>
+
+
+// Just a placeholder. Also insures file is not eliminated by linker.
+clib_error_t *l2_vtr_init (vlib_main_t *vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION(l2_vtr_init);
+
+
+// Configure vtag tag rewrite on the given interface.
+// Return 1 if there is an error, 0 if ok
+u32 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
+ u32 vtr_tag1, // first pushed tag
+ u32 vtr_tag2) // second pushed tag
+{
+ vnet_hw_interface_t * hi;
+ vnet_sw_interface_t * si;
+ u32 hw_no_tags;
+ u32 error = 0;
+ vtr_config_t * in_config;
+ vtr_config_t * out_config;
+ u32 enable;
+ u32 push_inner_et;
+ u32 push_outer_et;
+ u32 cfg_tags;
+
+ hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
+ if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index)) {
+ error = VNET_API_ERROR_INVALID_INTERFACE; // non-ethernet interface
+ goto done;
+ }
+
+ // Init the config for this interface
+ vec_validate (l2output_main.configs, sw_if_index);
+ in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
+ out_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->output_vtr);
+ in_config->raw_tags = 0;
+ out_config->raw_tags = 0;
+
+ // Get the configured tags for the interface
+ si = vnet_get_sw_interface (vnet_main, sw_if_index);
+ hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
+
+ // Construct the input tag-rewrite config
+
+ push_outer_et = clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN : ETHERNET_TYPE_DOT1AD);
+ push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
+ vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
+ vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
+
+ // Determine number of vlan tags with explictly configured values
+ cfg_tags = 0;
+ if (hw_no_tags || si->sub.eth.flags.no_tags) {
+ cfg_tags = 0;
+ } else if (si->sub.eth.flags.one_tag) {
+ cfg_tags = 1;
+ if (si->sub.eth.flags.outer_vlan_id_any) {
+ cfg_tags = 0;
+ }
+ } else if (si->sub.eth.flags.two_tags) {
+ cfg_tags = 2;
+ if (si->sub.eth.flags.inner_vlan_id_any) {
+ cfg_tags = 1;
+ }
+ if (si->sub.eth.flags.outer_vlan_id_any) {
+ cfg_tags = 0;
+ }
+ }
+
+ switch (vtr_op) {
+ case L2_VTR_DISABLED:
+ in_config->push_and_pop_bytes = 0;
+ break;
+
+ case L2_VTR_POP_1:
+ if (cfg_tags < 1) {
+ error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
+ goto done;
+ }
+ in_config->pop_bytes = 4;
+ in_config->push_bytes = 0;
+ break;
+
+ case L2_VTR_POP_2:
+ if (cfg_tags < 2) {
+ error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
+ goto done;
+ }
+ in_config->pop_bytes = 8;
+ in_config->push_bytes = 0;
+
+ out_config->push_bytes = in_config->pop_bytes;
+ out_config->pop_bytes = in_config->push_bytes;
+ break;
+
+ case L2_VTR_PUSH_1:
+ in_config->pop_bytes = 0;
+ in_config->push_bytes = 4;
+ in_config->tags[1].priority_cfi_and_id = vtr_tag1;
+ in_config->tags[1].type = push_outer_et;
+ break;
+
+ case L2_VTR_PUSH_2:
+ in_config->pop_bytes = 0;
+ in_config->push_bytes = 8;
+ in_config->tags[0].priority_cfi_and_id = vtr_tag1;
+ in_config->tags[0].type = push_outer_et;
+ in_config->tags[1].priority_cfi_and_id = vtr_tag2;
+ in_config->tags[1].type = push_inner_et;
+ break;
+
+ case L2_VTR_TRANSLATE_1_1:
+ if (cfg_tags < 1) {
+ error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
+ goto done;
+ }
+ in_config->pop_bytes = 4;
+ in_config->push_bytes = 4;
+ in_config->tags[1].priority_cfi_and_id = vtr_tag1;
+ in_config->tags[1].type = push_outer_et;
+ break;
+
+ case L2_VTR_TRANSLATE_1_2:
+ if (cfg_tags < 1) {
+ error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
+ goto done;
+ }
+ in_config->pop_bytes = 4;
+ in_config->push_bytes = 8;
+ in_config->tags[0].priority_cfi_and_id = vtr_tag1;
+ in_config->tags[0].type = push_outer_et;
+ in_config->tags[1].priority_cfi_and_id = vtr_tag2;
+ in_config->tags[1].type = push_inner_et;
+ break;
+
+ case L2_VTR_TRANSLATE_2_1:
+ if (cfg_tags < 2) {
+ error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
+ goto done;
+ }
+ in_config->pop_bytes = 8;
+ in_config->push_bytes = 4;
+ in_config->tags[1].priority_cfi_and_id = vtr_tag1;
+ in_config->tags[1].type = push_outer_et;
+ break;
+
+ case L2_VTR_TRANSLATE_2_2:
+ if (cfg_tags < 2) {
+ error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
+ goto done;
+ }
+ in_config->pop_bytes = 8;
+ in_config->push_bytes = 8;
+ in_config->tags[0].priority_cfi_and_id = vtr_tag1;
+ in_config->tags[0].type = push_outer_et;
+ in_config->tags[1].priority_cfi_and_id = vtr_tag2;
+ in_config->tags[1].type = push_inner_et;
+ break;
+ }
+
+ // Construct the output tag-rewrite config
+
+ // The push/pop values are always reversed
+ out_config->push_bytes = in_config->pop_bytes;
+ out_config->pop_bytes = in_config->push_bytes;
+
+ // Any pushed tags are derived from the subinterface config
+ push_outer_et = clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD : ETHERNET_TYPE_VLAN);
+ push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
+ vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
+ vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
+
+ if (out_config->push_bytes == 4) {
+ out_config->tags[1].priority_cfi_and_id = vtr_tag1;
+ out_config->tags[1].type = push_outer_et;
+ } else if (out_config->push_bytes == 8) {
+ out_config->tags[0].priority_cfi_and_id = vtr_tag1;
+ out_config->tags[0].type = push_outer_et;
+ out_config->tags[1].priority_cfi_and_id = vtr_tag2;
+ out_config->tags[1].type = push_inner_et;
+ }
+
+ // set the interface enable flags
+ enable = (vtr_op != L2_VTR_DISABLED);
+ l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
+ // output vtr enable is checked explicitly in l2_output
+
+ done:
+ return error;
+}
+
+// Get vtag tag rewrite on the given interface.
+// Return 1 if there is an error, 0 if ok
+u32 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
+ u32 *vtr_tag1, // first pushed tag
+ u32 *vtr_tag2) // second pushed tag
+{
+ vnet_hw_interface_t * hi;
+ u32 error = 0;
+ vtr_config_t * in_config;
+
+ if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2) {
+ clib_warning ("invalid arguments");
+ error = VNET_API_ERROR_INVALID_ARGUMENT;
+ goto done;
+ }
+
+ *vtr_op = L2_VTR_DISABLED;
+ *vtr_tag1 = 0;
+ *vtr_tag2 = 0;
+ *push_dot1q = 0;
+
+ hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
+ if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index)) {
+ // non-ethernet interface
+ goto done;
+ }
+
+ if (sw_if_index >= vec_len(l2output_main.configs)) {
+ // no specific config (return disabled)
+ goto done;
+ }
+
+ // Get the config for this interface
+ in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
+
+ // DISABLED
+ if (in_config->push_and_pop_bytes == 0) {
+ goto done;
+ }
+
+ // find out vtr_op
+ switch (in_config->pop_bytes) {
+ case 0:
+ switch (in_config->push_bytes) {
+ case 0:
+ // DISABLED
+ goto done;
+ case 4:
+ *vtr_op = L2_VTR_PUSH_1;
+ *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
+ *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
+ break;
+ case 8:
+ *vtr_op = L2_VTR_PUSH_2;
+ *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
+ *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
+ *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
+ break;
+ default:
+ clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
+ error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
+ goto done;
+ }
+ break;
+
+ case 4:
+ switch (in_config->push_bytes) {
+ case 0:
+ *vtr_op = L2_VTR_POP_1;
+ break;
+ case 4:
+ *vtr_op = L2_VTR_TRANSLATE_1_1;
+ *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
+ *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
+ break;
+ case 8:
+ *vtr_op = L2_VTR_TRANSLATE_1_2;
+ *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
+ *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
+ *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
+ break;
+ default:
+ clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
+ error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
+ goto done;
+ }
+ break;
+
+ case 8:
+ switch (in_config->push_bytes) {
+ case 0:
+ *vtr_op = L2_VTR_POP_2;
+ break;
+ case 4:
+ *vtr_op = L2_VTR_TRANSLATE_2_1;
+ *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
+ *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
+ break;
+ case 8:
+ *vtr_op = L2_VTR_TRANSLATE_2_2;
+ *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
+ *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
+ *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
+ break;
+ default:
+ clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
+ error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
+ goto done;
+ }
+ break;
+
+ default:
+ clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
+ error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
+ goto done;
+ }
+
+ done:
+ return error;
+}
+
+// set subinterface vtr enable/disable
+// The CLI format is:
+// set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
+// "push" can also be replaced by "translate-{1|2}-{1|2}"
+static clib_error_t *
+int_l2_vtr (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ vnet_main_t * vnm = vnet_get_main();
+ clib_error_t * error = 0;
+ u32 sw_if_index;
+ u32 vtr_op;
+ u32 push_dot1q = 0;
+ u32 tag1 = 0, tag2 = 0;
+
+ if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
+ {
+ error = clib_error_return (0, "unknown interface `%U'",
+ format_unformat_error, input);
+ goto done;
+ }
+
+ vtr_op = L2_VTR_DISABLED;
+
+ if (unformat (input, "disable")) {
+ vtr_op = L2_VTR_DISABLED;
+ } else if (unformat (input, "pop 1")) {
+ vtr_op = L2_VTR_POP_1;
+ } else if (unformat (input, "pop 2")) {
+ vtr_op = L2_VTR_POP_2;
+
+ } else if (unformat (input, "push dot1q %d %d", &tag1, &tag2)) {
+ vtr_op = L2_VTR_PUSH_2;
+ push_dot1q = 1;
+ } else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2)) {
+ vtr_op = L2_VTR_PUSH_2;
+
+ } else if (unformat (input, "push dot1q %d", &tag1)) {
+ vtr_op = L2_VTR_PUSH_1;
+ push_dot1q = 1;
+ } else if (unformat (input, "push dot1ad %d", &tag1)) {
+ vtr_op = L2_VTR_PUSH_1;
+
+ } else if (unformat (input, "translate 1-1 dot1q %d", &tag1)) {
+ vtr_op = L2_VTR_TRANSLATE_1_1;
+ push_dot1q = 1;
+ } else if (unformat (input, "translate 1-1 dot1ad %d", &tag1)) {
+ vtr_op = L2_VTR_TRANSLATE_1_1;
+
+ } else if (unformat (input, "translate 2-1 dot1q %d", &tag1)) {
+ vtr_op = L2_VTR_TRANSLATE_2_1;
+ push_dot1q = 1;
+ } else if (unformat (input, "translate 2-1 dot1ad %d", &tag1)) {
+ vtr_op = L2_VTR_TRANSLATE_2_1;
+
+ } else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2)) {
+ vtr_op = L2_VTR_TRANSLATE_2_2;
+ push_dot1q = 1;
+ } else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2)) {
+ vtr_op = L2_VTR_TRANSLATE_2_2;
+
+ } else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2)) {
+ vtr_op = L2_VTR_TRANSLATE_1_2;
+ push_dot1q = 1;
+ } else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2)) {
+ vtr_op = L2_VTR_TRANSLATE_1_2;
+
+ } else {
+ error = clib_error_return (0, "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
+ " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
+ format_unformat_error, input);
+ goto done;
+ }
+
+ if (l2vtr_configure (vm,
+ vnm,
+ sw_if_index,
+ vtr_op,
+ push_dot1q,
+ tag1,
+ tag2)) {
+ error = clib_error_return (0, "vlan tag rewrite is not compatible with interface");
+ goto done;
+ }
+
+ done:
+ return error;
+}
+
+VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
+ .path = "set interface l2 tag-rewrite",
+ .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
+ .function = int_l2_vtr,
+};
+