X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=blobdiff_plain;f=src%2Fvnet%2Fl2%2Fl2_bvi.c;h=87738fc782013d06dd0668a09e6e7065a0da0749;hp=f239743a9c4b34cd630413d1f7f66ece7cd08b27;hb=192b13f96;hpb=1ea74b5df5acfc827ee53fd5cb6bdb40140da0f0 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 *