From 192b13f96d6b4d1b4cbbb64a3d447329bf2ba900 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Fri, 15 Mar 2019 02:16:20 -0700 Subject: BVI Interface a new dedicated BVI interface as opposed to [re]using a loopback. benefits: - removes ambiguity over the purpose of a loopback interface - TX node dedicated to BVI only functions. Change-Id: I749d6b38440d450ac5b909a28053c75ec9df946a Signed-off-by: Neale Ranns --- src/vnet/CMakeLists.txt | 2 + src/vnet/ethernet/ethernet.h | 2 +- src/vnet/ethernet/interface.c | 2 +- src/vnet/l2/l2.api | 38 ++++++ src/vnet/l2/l2_api.c | 36 ++++- src/vnet/l2/l2_bvi.c | 311 ++++++++++++++++++++++++++++++++++++++++++ src/vnet/l2/l2_bvi.h | 5 + src/vnet/l2/l2_bvi_node.c | 116 ++++++++++++++++ 8 files changed, 509 insertions(+), 3 deletions(-) create mode 100644 src/vnet/l2/l2_bvi_node.c (limited to 'src') diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index b6028e90742..822ad6891fc 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -161,6 +161,7 @@ list(APPEND VNET_SOURCES l2/l2_api.c l2/l2_bd.c l2/l2_bvi.c + l2/l2_bvi_node.c l2/l2_input_classify.c l2/l2_output_classify.c l2/l2_efp_filter.c @@ -181,6 +182,7 @@ list(APPEND VNET_SOURCES ) list(APPEND VNET_MULTIARCH_SOURCES + l2/l2_bvi_node.c l2/l2_fwd.c l2/l2_learn.c l2/l2_output.c diff --git a/src/vnet/ethernet/ethernet.h b/src/vnet/ethernet/ethernet.h index 9b19143b03f..94322a715f9 100644 --- a/src/vnet/ethernet/ethernet.h +++ b/src/vnet/ethernet/ethernet.h @@ -314,7 +314,7 @@ ethernet_interface_t *ethernet_get_interface (ethernet_main_t * em, clib_error_t *ethernet_register_interface (vnet_main_t * vnm, u32 dev_class_index, u32 dev_instance, - u8 * address, + const u8 * address, u32 * hw_if_index_return, ethernet_flag_change_function_t flag_change); diff --git a/src/vnet/ethernet/interface.c b/src/vnet/ethernet/interface.c index 1c13c405a61..0f54aa1e393 100644 --- a/src/vnet/ethernet/interface.c +++ b/src/vnet/ethernet/interface.c @@ -278,7 +278,7 @@ clib_error_t * ethernet_register_interface (vnet_main_t * vnm, u32 dev_class_index, u32 dev_instance, - u8 * address, + const u8 * address, u32 * hw_if_index_return, ethernet_flag_change_function_t flag_change) { diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index fc851e4203d..dc743763f70 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -553,6 +553,44 @@ autoreply define sw_interface_set_vpath u8 enable; }; +/** \brief Create BVI interface instance request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param mac_address - mac addr to assign to the interface if none-zero + @param user_instance - requested instance, ~0 => dynamically allocate +*/ +define bvi_create +{ + u32 client_index; + u32 context; + vl_api_mac_address_t mac; + u32 user_instance; +}; + +/** \brief Create BVI interface instance response + @param context - sender context, to match reply w/ request + @param sw_if_index - sw index of the interface that was created + @param retval - return code for the request +*/ +define bvi_create_reply +{ + u32 context; + i32 retval; + u32 sw_if_index; +}; + +/** \brief Delete BVI interface request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - sw index of the interface that was created +*/ +autoreply define bvi_delete +{ + u32 client_index; + u32 context; + u32 sw_if_index; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index 059f668d664..f60cd41bb39 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,9 @@ _(BRIDGE_FLAGS, bridge_flags) \ _(L2_INTERFACE_VLAN_TAG_REWRITE, l2_interface_vlan_tag_rewrite) \ _(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \ _(BRIDGE_DOMAIN_SET_MAC_AGE, bridge_domain_set_mac_age) \ -_(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) +_(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) \ +_(BVI_CREATE, bvi_create) \ +_(BVI_DELETE, bvi_delete) static void send_l2_xconnect_details (vl_api_registration_t * reg, u32 context, @@ -993,6 +996,37 @@ vl_api_sw_interface_set_vpath_t_handler (vl_api_sw_interface_set_vpath_t * mp) REPLY_MACRO (VL_API_SW_INTERFACE_SET_VPATH_REPLY); } +static void +vl_api_bvi_create_t_handler (vl_api_bvi_create_t * mp) +{ + vl_api_bvi_create_reply_t *rmp; + mac_address_t mac; + u32 sw_if_index; + int rv; + + mac_address_decode (mp->mac, &mac); + + rv = l2_bvi_create (ntohl (mp->user_instance), &mac, &sw_if_index); + + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_BVI_CREATE_REPLY, + ({ + rmp->sw_if_index = ntohl (sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_bvi_delete_t_handler (vl_api_bvi_delete_t * mp) +{ + vl_api_bvi_delete_reply_t *rmp; + int rv; + + rv = l2_bvi_delete (ntohl (mp->sw_if_index)); + + REPLY_MACRO (VL_API_BVI_DELETE_REPLY); +} + /* * l2_api_hookup * Add vpe's API message handlers to the table. diff --git a/src/vnet/l2/l2_bvi.c b/src/vnet/l2/l2_bvi.c index f239743a9c4..87738fc7820 100644 --- a/src/vnet/l2/l2_bvi.c +++ b/src/vnet/l2/l2_bvi.c @@ -21,6 +21,8 @@ #include #include +/* Allocated BVI instances */ +static uword *l2_bvi_instances; /* Call the L2 nodes that need the ethertype mapping */ void @@ -31,6 +33,315 @@ l2bvi_register_input_type (vlib_main_t * vm, l2flood_register_input_type (vm, type, node_index); } +static u8 * +format_bvi_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "bvi%d", dev_instance); +} + +static clib_error_t * +bvi_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) +{ + u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? + VNET_HW_INTERFACE_FLAG_LINK_UP : 0; + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); + return 0; +} + +static clib_error_t * +bvi_mac_change (vnet_hw_interface_t * hi, + const u8 * old_address, const u8 * mac_address) +{ + l2input_interface_mac_change (hi->sw_if_index, old_address, mac_address); + + return (NULL); +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (bvi_device_class) = { + .name = "BVI", + .format_device_name = format_bvi_name, + .admin_up_down_function = bvi_admin_up_down, + .mac_addr_change_function = bvi_mac_change, +}; +/* *INDENT-ON* */ + +/* + * Maintain a bitmap of allocated bvi instance numbers. + */ +#define BVI_MAX_INSTANCE (16 * 1024) + +static u32 +bvi_instance_alloc (u32 want) +{ + /* + * Check for dynamically allocaetd instance number. + */ + if (~0 == want) + { + u32 bit; + + bit = clib_bitmap_first_clear (l2_bvi_instances); + if (bit >= BVI_MAX_INSTANCE) + { + return ~0; + } + l2_bvi_instances = clib_bitmap_set (l2_bvi_instances, bit, 1); + return bit; + } + + /* + * In range? + */ + if (want >= BVI_MAX_INSTANCE) + { + return ~0; + } + + /* + * Already in use? + */ + if (clib_bitmap_get (l2_bvi_instances, want)) + { + return ~0; + } + + /* + * Grant allocation request. + */ + l2_bvi_instances = clib_bitmap_set (l2_bvi_instances, want, 1); + + return want; +} + +static int +bvi_instance_free (u32 instance) +{ + if (instance >= BVI_MAX_INSTANCE) + { + return -1; + } + + if (clib_bitmap_get (l2_bvi_instances, instance) == 0) + { + return -1; + } + + l2_bvi_instances = clib_bitmap_set (l2_bvi_instances, instance, 0); + return 0; +} + +int +l2_bvi_create (u32 user_instance, + const mac_address_t * mac_in, u32 * sw_if_indexp) +{ + vnet_main_t *vnm = vnet_get_main (); + vlib_main_t *vm = vlib_get_main (); + u32 instance, hw_if_index, slot; + vnet_hw_interface_t *hw_if; + clib_error_t *error; + mac_address_t mac; + + int rv = 0; + + ASSERT (sw_if_indexp); + + *sw_if_indexp = (u32) ~ 0; + + /* + * Allocate a bvi instance. Either select on dynamically + * or try to use the desired user_instance number. + */ + instance = bvi_instance_alloc (user_instance); + if (instance == ~0) + { + return VNET_API_ERROR_INVALID_REGISTRATION; + } + + /* + * Default MAC address (b0b0:0000:0000 + instance) is allocated + * if zero mac_address is configured. Otherwise, user-configurable MAC + * address is programmed on the bvi interface. + */ + if (mac_address_is_zero (mac_in)) + { + u8 bytes[] = { + [0] = 0xb0, + [1] = 0xb0, + [5] = instance, + }; + mac_address_from_bytes (&mac, bytes); + } + else + { + mac_address_copy (&mac, mac_in); + } + + error = ethernet_register_interface (vnm, + bvi_device_class.index, + instance, mac.bytes, &hw_if_index, + /* flag change */ 0); + + if (error) + { + rv = VNET_API_ERROR_INVALID_REGISTRATION; + clib_error_report (error); + return rv; + } + + hw_if = vnet_get_hw_interface (vnm, hw_if_index); + + slot = vlib_node_add_named_next_with_slot (vm, hw_if->tx_node_index, + "l2-input", 0); + ASSERT (slot == 0); + + { + vnet_sw_interface_t *si = vnet_get_hw_sw_interface (vnm, hw_if_index); + *sw_if_indexp = si->sw_if_index; + + si->flood_class = VNET_FLOOD_CLASS_BVI; + } + + return 0; +} + +int +l2_bvi_delete (u32 sw_if_index) +{ + vnet_main_t *vnm = vnet_get_main (); + + if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index)) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); + if (hw == 0 || hw->dev_class_index != bvi_device_class.index) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + if (bvi_instance_free (hw->dev_instance) < 0) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + ethernet_delete_interface (vnm, hw->hw_if_index); + + return 0; +} + +static clib_error_t * +l2_bvi_create_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 instance, sw_if_index; + clib_error_t *error; + mac_address_t mac; + int rv; + + error = NULL; + instance = sw_if_index = ~0; + mac_address_set_zero (&mac); + + if (unformat_user (input, unformat_line_input, line_input)) + { + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "mac %U", unformat_mac_address_t, &mac)) + ; + else if (unformat (line_input, "instance %d", &instance)) + ; + else + { + error = clib_error_return (0, "unknown input: %U", + format_unformat_error, line_input); + break; + } + } + + unformat_free (line_input); + + if (error) + return error; + } + + rv = l2_bvi_create (instance, &mac, &sw_if_index); + + if (rv) + return clib_error_return (0, "BVI create failed"); + + vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (), + sw_if_index); + return 0; +} + +/*? + * Create a BVI interface. Optionally, a MAC Address can be + * provided. If not provided, 0b:0b::00:00:00: will be used. + * + * @cliexpar + * The following two command syntaxes are equivalent: + * @cliexcmd{bvi create [mac ] [instance ]} + * Example of how to create a bvi interface: + * @cliexcmd{bvi create} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2_bvi_create_command, static) = { + .path = "bvi create", + .short_help = "bvi create [mac ] [instance ]", + .function = l2_bvi_create_cli, +}; +/* *INDENT-ON* */ + +static clib_error_t * +l2_bvi_delete_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm; + u32 sw_if_index; + int rv; + + vnm = vnet_get_main (); + sw_if_index = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat + (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else + break; + } + + if (~0 != sw_if_index) + { + rv = l2_bvi_delete (sw_if_index); + + if (rv) + return clib_error_return (0, "BVI delete failed"); + } + else + return clib_error_return (0, "no such interface: %U", + format_unformat_error, input); + + return 0; +} + +/*? + * Delete a BVI interface. + * + * @cliexpar + * The following two command syntaxes are equivalent: + * @cliexcmd{bvi delete + * Example of how to create a bvi interface: + * @cliexcmd{bvi delete bvi0} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2_bvi_delete_command, static) = { + .path = "bvi delete", + .short_help = "bvi delete ", + .function = l2_bvi_delete_cli, +}; +/* *INDENT-ON* */ + + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/vnet/l2/l2_bvi.h b/src/vnet/l2/l2_bvi.h index 51c8dac23e4..d7d410b06ab 100644 --- a/src/vnet/l2/l2_bvi.h +++ b/src/vnet/l2/l2_bvi.h @@ -97,6 +97,11 @@ l2_to_bvi (vlib_main_t * vlib_main, void l2bvi_register_input_type (vlib_main_t * vm, ethernet_type_t type, u32 node_index); + +extern int l2_bvi_create (u32 instance, const mac_address_t * mac, + u32 * sw_if_index); +extern int l2_bvi_delete (u32 sw_if_index); + #endif /* diff --git a/src/vnet/l2/l2_bvi_node.c b/src/vnet/l2/l2_bvi_node.c new file mode 100644 index 00000000000..dd7e1df46ce --- /dev/null +++ b/src/vnet/l2/l2_bvi_node.c @@ -0,0 +1,116 @@ +/* + * l2_bvi.c : layer 2 Bridged Virtual Interface + * + * 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 + +/** + * send packets to l2-input. + */ +VNET_DEVICE_CLASS_TX_FN (bvi_device_class) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u16 nexts[VLIB_FRAME_SIZE]; + u32 n_left, *from; + + n_left = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + vlib_get_buffers (vm, from, bufs, n_left); + + b = bufs; + sw_if_index = sw_if_indices; + + /* It's all going to l2-input */ + clib_memset_u16 (nexts, 0, VLIB_FRAME_SIZE); + + /* + * For each packet: + * - fixup the L2 length of the packet + * - set the RX interface (which the bridge will use) to the + * TX interface (which routing has chosen) + * - Set the TX interface to the special ID so the DP knows this is a BVI + * Don't counts packets and bytes, that's done in the bviX-output node + */ + while (n_left >= 4) + { + /* Prefetch next iteration. */ + if (PREDICT_TRUE (n_left >= 8)) + { + /* LOAD pre-fetch since meta and packet data is read */ + vlib_prefetch_buffer_header (b[4], LOAD); + vlib_prefetch_buffer_header (b[5], LOAD); + vlib_prefetch_buffer_header (b[6], LOAD); + vlib_prefetch_buffer_header (b[7], LOAD); + + vlib_prefetch_buffer_data (b[4], LOAD); + vlib_prefetch_buffer_data (b[5], LOAD); + vlib_prefetch_buffer_data (b[6], LOAD); + vlib_prefetch_buffer_data (b[7], LOAD); + } + + vnet_update_l2_len (b[0]); + vnet_update_l2_len (b[1]); + vnet_update_l2_len (b[2]); + vnet_update_l2_len (b[3]); + + sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX]; + sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_TX]; + sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_TX]; + sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_TX]; + + vnet_buffer (b[0])->sw_if_index[VLIB_TX] = L2INPUT_BVI; + vnet_buffer (b[1])->sw_if_index[VLIB_TX] = L2INPUT_BVI; + vnet_buffer (b[2])->sw_if_index[VLIB_TX] = L2INPUT_BVI; + vnet_buffer (b[3])->sw_if_index[VLIB_TX] = L2INPUT_BVI; + + vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index[0]; + vnet_buffer (b[1])->sw_if_index[VLIB_RX] = sw_if_index[1]; + vnet_buffer (b[2])->sw_if_index[VLIB_RX] = sw_if_index[2]; + vnet_buffer (b[3])->sw_if_index[VLIB_RX] = sw_if_index[3]; + + b += 4; + n_left -= 4; + sw_if_index += 4; + } + while (n_left) + { + sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX]; + vnet_buffer (b[0])->sw_if_index[VLIB_TX] = L2INPUT_BVI; + vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index[0]; + + vnet_update_l2_len (b[0]); + + b += 1; + n_left -= 1; + sw_if_index += 1; + } + + vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); + + return frame->n_vectors; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- cgit 1.2.3-korg