From b474380f82b75d9640f9bf6ee78c891a6794dbfb Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Wed, 5 Sep 2018 09:13:57 -0700 Subject: [PATCH] L2 BD: introduce a BD interface on which to send UU packets Change-Id: I21ad6b04c19c8735d057174b1f260a59f2812241 Signed-off-by: Neale Ranns --- extras/vom/vom/l2_binding_cmds.cpp | 6 +- src/plugins/gtpu/gtpu.c | 4 +- src/vat/api_format.c | 24 ++-- src/vnet/CMakeLists.txt | 1 + src/vnet/ethernet/p2p_ethernet.c | 3 +- src/vnet/geneve/geneve.c | 4 +- src/vnet/gre/interface.c | 3 +- src/vnet/interface.c | 7 +- src/vnet/ipsec-gre/interface.c | 3 +- src/vnet/l2/l2.api | 36 +++++- src/vnet/l2/l2_api.c | 66 ++++++++-- src/vnet/l2/l2_bd.c | 41 +++++-- src/vnet/l2/l2_bd.h | 38 ++++-- src/vnet/l2/l2_flood.c | 6 +- src/vnet/l2/l2_fwd.c | 4 +- src/vnet/l2/l2_input.c | 68 ++++++++--- src/vnet/l2/l2_input.h | 4 +- src/vnet/l2/l2_uu_fwd.c | 242 +++++++++++++++++++++++++++++++++++++ src/vnet/l2/l2_xcrw.c | 7 +- src/vnet/lisp-gpe/interface.c | 4 +- src/vnet/vxlan-gpe/vxlan_gpe.c | 4 +- src/vpp/api/custom_dump.c | 20 +-- test/test_acl_plugin_l2l3.py | 4 +- test/test_acl_plugin_macip.py | 4 +- test/test_dvr.py | 8 +- test/test_gbp.py | 9 +- test/test_ip4_irb.py | 4 +- test/test_l2_flood.py | 111 ++++++++++++++++- test/vpp_papi_provider.py | 24 +++- 29 files changed, 649 insertions(+), 110 deletions(-) create mode 100644 src/vnet/l2/l2_uu_fwd.c diff --git a/extras/vom/vom/l2_binding_cmds.cpp b/extras/vom/vom/l2_binding_cmds.cpp index c072f67fd56..6976e345a04 100644 --- a/extras/vom/vom/l2_binding_cmds.cpp +++ b/extras/vom/vom/l2_binding_cmds.cpp @@ -44,7 +44,8 @@ bind_cmd::issue(connection& con) payload.rx_sw_if_index = m_itf.value(); payload.bd_id = m_bd; payload.shg = 0; - payload.bvi = m_is_bvi; + payload.port_type = + (m_is_bvi ? L2_API_PORT_TYPE_BVI : L2_API_PORT_TYPE_NORMAL); payload.enable = 1; VAPI_CALL(req.execute()); @@ -89,7 +90,8 @@ unbind_cmd::issue(connection& con) payload.rx_sw_if_index = m_itf.value(); payload.bd_id = m_bd; payload.shg = 0; - payload.bvi = m_is_bvi; + payload.port_type = + (m_is_bvi ? L2_API_PORT_TYPE_BVI : L2_API_PORT_TYPE_NORMAL); payload.enable = 0; VAPI_CALL(req.execute()); diff --git a/src/plugins/gtpu/gtpu.c b/src/plugins/gtpu/gtpu.c index 4f58f1176cf..027af9b4aec 100644 --- a/src/plugins/gtpu/gtpu.c +++ b/src/plugins/gtpu/gtpu.c @@ -578,8 +578,8 @@ int vnet_gtpu_add_del_tunnel si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; /* make sure tunnel is removed from l2 bd or xconnect */ - set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, 0, 0, - 0); + set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); vec_add1 (gtm->free_gtpu_tunnel_hw_if_indices, t->hw_if_index); gtm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 0a0198940cf..88d4c0167e4 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -7115,15 +7115,17 @@ api_sw_interface_set_l2_bridge (vat_main_t * vam) { unformat_input_t *i = vam->input; vl_api_sw_interface_set_l2_bridge_t *mp; + vl_api_l2_port_type_t port_type; u32 rx_sw_if_index; u8 rx_sw_if_index_set = 0; u32 bd_id; u8 bd_id_set = 0; - u8 bvi = 0; u32 shg = 0; u8 enable = 1; int ret; + port_type = L2_API_PORT_TYPE_NORMAL; + /* Parse args required to build the message */ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) { @@ -7138,7 +7140,9 @@ api_sw_interface_set_l2_bridge (vat_main_t * vam) else if (unformat (i, "shg %d", &shg)) ; else if (unformat (i, "bvi")) - bvi = 1; + port_type = L2_API_PORT_TYPE_BVI; + else if (unformat (i, "uu-fwd")) + port_type = L2_API_PORT_TYPE_UU_FWD; else if (unformat (i, "enable")) enable = 1; else if (unformat (i, "disable")) @@ -7164,7 +7168,7 @@ api_sw_interface_set_l2_bridge (vat_main_t * vam) mp->rx_sw_if_index = ntohl (rx_sw_if_index); mp->bd_id = ntohl (bd_id); mp->shg = (u8) shg; - mp->bvi = bvi; + mp->port_type = ntohl (port_type); mp->enable = enable; S (mp); @@ -7610,7 +7614,7 @@ api_bridge_flags (vat_main_t * vam) u32 bd_id; u8 bd_id_set = 0; u8 is_set = 1; - u32 flags = 0; + bd_flags_t flags = 0; int ret; /* Parse args required to build the message */ @@ -7619,15 +7623,15 @@ api_bridge_flags (vat_main_t * vam) if (unformat (i, "bd_id %d", &bd_id)) bd_id_set = 1; else if (unformat (i, "learn")) - flags |= L2_LEARN; + flags |= BRIDGE_API_FLAG_LEARN; else if (unformat (i, "forward")) - flags |= L2_FWD; + flags |= BRIDGE_API_FLAG_FWD; else if (unformat (i, "flood")) - flags |= L2_FLOOD; + flags |= BRIDGE_API_FLAG_FLOOD; else if (unformat (i, "uu-flood")) - flags |= L2_UU_FLOOD; + flags |= BRIDGE_API_FLAG_UU_FLOOD; else if (unformat (i, "arp-term")) - flags |= L2_ARP_TERM; + flags |= BRIDGE_API_FLAG_ARP_TERM; else if (unformat (i, "off")) is_set = 0; else if (unformat (i, "disable")) @@ -7645,7 +7649,7 @@ api_bridge_flags (vat_main_t * vam) M (BRIDGE_FLAGS, mp); mp->bd_id = ntohl (bd_id); - mp->feature_bitmap = ntohl (flags); + mp->flags = ntohl (flags); mp->is_set = is_set; S (mp); diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 5f89b694afd..8820d284cc7 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -154,6 +154,7 @@ list(APPEND VNET_SOURCES l2/l2_in_out_acl.c l2/l2_patch.c l2/l2_rw.c + l2/l2_uu_fwd.c l2/l2_vtr.c l2/l2_xcrw.c ) diff --git a/src/vnet/ethernet/p2p_ethernet.c b/src/vnet/ethernet/p2p_ethernet.c index cf3c56b5d24..ddf23901419 100644 --- a/src/vnet/ethernet/p2p_ethernet.c +++ b/src/vnet/ethernet/p2p_ethernet.c @@ -153,7 +153,8 @@ p2p_ethernet_add_del (vlib_main_t * vm, u32 parent_if_index, } p2pm->p2p_ethernet_by_sw_if_index[parent_if_index]++; /* set the interface mode */ - set_int_l2_mode (vm, vnm, MODE_L3, p2pe_subif_id, 0, 0, 0, 0); + set_int_l2_mode (vm, vnm, MODE_L3, p2pe_subif_id, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); return 0; } return VNET_API_ERROR_SUBIF_ALREADY_EXISTS; diff --git a/src/vnet/geneve/geneve.c b/src/vnet/geneve/geneve.c index 2cc43eda71d..ac097f915cb 100644 --- a/src/vnet/geneve/geneve.c +++ b/src/vnet/geneve/geneve.c @@ -581,8 +581,8 @@ int vnet_geneve_add_del_tunnel si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; /* make sure tunnel is removed from l2 bd or xconnect */ - set_int_l2_mode (vxm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, 0, 0, - 0); + set_int_l2_mode (vxm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); vec_add1 (vxm->free_geneve_tunnel_hw_if_indices, t->hw_if_index); vxm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; diff --git a/src/vnet/gre/interface.c b/src/vnet/gre/interface.c index 0215f9b0860..181a908022b 100644 --- a/src/vnet/gre/interface.c +++ b/src/vnet/gre/interface.c @@ -423,7 +423,8 @@ vnet_gre_tunnel_delete (vnet_gre_add_del_tunnel_args_t * a, vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ ); /* make sure tunnel is removed from l2 bd or xconnect */ - set_int_l2_mode (gm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); + set_int_l2_mode (gm->vlib_main, vnm, MODE_L3, sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; if (t->type == GRE_TUNNEL_TYPE_L3) diff --git a/src/vnet/interface.c b/src/vnet/interface.c index 60f11bc3957..5cbbbf96055 100644 --- a/src/vnet/interface.c +++ b/src/vnet/interface.c @@ -650,10 +650,11 @@ vnet_delete_sw_interface (vnet_main_t * vnm, u32 sw_if_index) { config = vec_elt_at_index (l2input_main.configs, sw_if_index); if (config->xconnect) - set_int_l2_mode (vm, vnm, MODE_L3, config->output_sw_if_index, 0, 0, - 0, 0); + set_int_l2_mode (vm, vnm, MODE_L3, config->output_sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); if (config->xconnect || config->bridge) - set_int_l2_mode (vm, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); + set_int_l2_mode (vm, vnm, MODE_L3, sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); } vnet_clear_sw_interface_tag (vnm, sw_if_index); diff --git a/src/vnet/ipsec-gre/interface.c b/src/vnet/ipsec-gre/interface.c index a51ca7f69dd..4faf66ddde8 100644 --- a/src/vnet/ipsec-gre/interface.c +++ b/src/vnet/ipsec-gre/interface.c @@ -204,7 +204,8 @@ vnet_ipsec_gre_add_del_tunnel (vnet_ipsec_gre_add_del_tunnel_args_t * a, ip4_sw_interface_enable_disable (sw_if_index, 0); vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ ); /* make sure tunnel is removed from l2 bd or xconnect */ - set_int_l2_mode (igm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); + set_int_l2_mode (igm->vlib_main, vnm, MODE_L3, sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); vec_add1 (igm->free_ipsec_gre_tunnel_hw_if_indices, t->hw_if_index); igm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index fdb7db7551d..2164ca74406 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -309,17 +309,27 @@ manual_print manual_endian define bridge_domain_details u8 mac_age; u8 bd_tag[64]; u32 bvi_sw_if_index; + u32 uu_fwd_sw_if_index; u32 n_sw_ifs; vl_api_bridge_domain_sw_if_t sw_if_details[n_sw_ifs]; }; -/** \brief Set bridge flags (such as L2_LEARN, L2_FWD, L2_FLOOD, - L2_UU_FLOOD, or L2_ARP_TERM bits) request +/** \brief Flags that can be changed on a bridge domain */ +enum bd_flags +{ + BRIDGE_API_FLAG_LEARN = 0x1, + BRIDGE_API_FLAG_FWD = 0x2, + BRIDGE_API_FLAG_FLOOD = 0x4, + BRIDGE_API_FLAG_UU_FLOOD = 0x8, + BRIDGE_API_FLAG_ARP_TERM = 0x10, +}; + +/** \brief Set bridge flags request @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param bd_id - the bridge domain to set the flags for @param is_set - if non-zero, set the flags, else clear them - @param feature_bitmap - bits (as above) that are non-zero to set or clear + @param flags - flags that are non-zero to set or clear */ define bridge_flags { @@ -327,7 +337,7 @@ define bridge_flags u32 context; u32 bd_id; u8 is_set; - u32 feature_bitmap; + vl_api_bd_flags_t flags; }; /** \brief Set bridge flags response @@ -419,6 +429,21 @@ autoreply define sw_interface_set_l2_xconnect u8 enable; }; +/** + * @brief An enumeration of the type of ports that can be added + * to a bridge domain + */ +enum l2_port_type +{ + /* a 'normal' interface, i.e. not BVI or UU-Flood */ + L2_API_PORT_TYPE_NORMAL = 0, + /* a BVI interface in the BD */ + L2_API_PORT_TYPE_BVI = 1, + /* The interface on which to forward unknown unicast packets + * If this is not set for a BD then UU is flooded */ + L2_API_PORT_TYPE_UU_FWD = 2, +}; + /** \brief Interface bridge mode request @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -428,14 +453,15 @@ autoreply define sw_interface_set_l2_xconnect @param shg - Split horizon group, for bridge mode only @param enable - Enable beige mode if not 0, else set to L3 mode */ + autoreply define sw_interface_set_l2_bridge { u32 client_index; u32 context; u32 rx_sw_if_index; u32 bd_id; + vl_api_l2_port_type_t port_type; u8 shg; - u8 bvi; u8 enable; }; diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index ab3a7c6fd64..3f27feb638a 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -464,6 +465,7 @@ send_bridge_domain_details (l2input_main_t * l2im, mp->learn = bd_feature_learn (bd_config); mp->arp_term = bd_feature_arp_term (bd_config); mp->bvi_sw_if_index = ntohl (bd_config->bvi_sw_if_index); + mp->uu_fwd_sw_if_index = ntohl (bd_config->uu_fwd_sw_if_index); mp->mac_age = bd_config->mac_age; if (bd_config->bd_tag) { @@ -527,6 +529,27 @@ vl_api_bridge_domain_dump_t_handler (vl_api_bridge_domain_dump_t * mp) } } +static bd_flags_t +bd_flags_decode (vl_api_bd_flags_t v) +{ + bd_flags_t f = L2_NONE; + + v = ntohl (v); + + if (v & BRIDGE_API_FLAG_LEARN) + f |= L2_LEARN; + if (v & BRIDGE_API_FLAG_FWD) + f |= L2_FWD; + if (v & BRIDGE_API_FLAG_FLOOD) + f |= L2_FLOOD; + if (v & BRIDGE_API_FLAG_UU_FLOOD) + f |= L2_UU_FLOOD; + if (v & BRIDGE_API_FLAG_ARP_TERM) + f |= L2_ARP_TERM; + + return (f); +} + static void vl_api_bridge_flags_t_handler (vl_api_bridge_flags_t * mp) { @@ -535,7 +558,7 @@ vl_api_bridge_flags_t_handler (vl_api_bridge_flags_t * mp) vl_api_bridge_flags_reply_t *rmp; int rv = 0; - u32 flags = ntohl (mp->feature_bitmap); + bd_flags_t flags = bd_flags_decode (mp->flags); u32 bd_id = ntohl (mp->bd_id); if (bd_id == 0) { @@ -656,11 +679,13 @@ static void { VALIDATE_TX_SW_IF_INDEX (mp); rv = set_int_l2_mode (vm, vnm, MODE_L2_XC, - rx_sw_if_index, 0, 0, 0, tx_sw_if_index); + rx_sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, tx_sw_if_index); } else { - rv = set_int_l2_mode (vm, vnm, MODE_L3, rx_sw_if_index, 0, 0, 0, 0); + rv = set_int_l2_mode (vm, vnm, MODE_L3, rx_sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); } BAD_RX_SW_IF_INDEX_LABEL; @@ -669,6 +694,27 @@ static void REPLY_MACRO (VL_API_SW_INTERFACE_SET_L2_XCONNECT_REPLY); } +static int +l2_bd_port_type_decode (vl_api_l2_port_type_t v, l2_bd_port_type_t * l) +{ + v = clib_net_to_host_u32 (v); + + switch (v) + { + case L2_API_PORT_TYPE_NORMAL: + *l = L2_BD_PORT_TYPE_NORMAL; + return 0; + case L2_API_PORT_TYPE_BVI: + *l = L2_BD_PORT_TYPE_BVI; + return 0; + case L2_API_PORT_TYPE_UU_FWD: + *l = L2_BD_PORT_TYPE_UU_FWD; + return 0; + } + + return (VNET_API_ERROR_INVALID_VALUE); +} + static void vl_api_sw_interface_set_l2_bridge_t_handler (vl_api_sw_interface_set_l2_bridge_t * mp) @@ -678,29 +724,31 @@ static void int rv = 0; vlib_main_t *vm = vlib_get_main (); vnet_main_t *vnm = vnet_get_main (); + l2_bd_port_type_t pt; VALIDATE_RX_SW_IF_INDEX (mp); u32 rx_sw_if_index = ntohl (mp->rx_sw_if_index); + rv = l2_bd_port_type_decode (mp->port_type, &pt); - + if (0 != rv) + goto out; if (mp->enable) { VALIDATE_BD_ID (mp); u32 bd_id = ntohl (mp->bd_id); u32 bd_index = bd_find_or_add_bd_index (bdm, bd_id); - u32 bvi = mp->bvi; - u8 shg = mp->shg; + rv = set_int_l2_mode (vm, vnm, MODE_L2_BRIDGE, - rx_sw_if_index, bd_index, bvi, shg, 0); + rx_sw_if_index, bd_index, pt, mp->shg, 0); } else { - rv = set_int_l2_mode (vm, vnm, MODE_L3, rx_sw_if_index, 0, 0, 0, 0); + rv = set_int_l2_mode (vm, vnm, MODE_L3, rx_sw_if_index, 0, pt, 0, 0); } BAD_RX_SW_IF_INDEX_LABEL; BAD_BD_ID_LABEL; - +out: REPLY_MACRO (VL_API_SW_INTERFACE_SET_L2_BRIDGE_REPLY); } diff --git a/src/vnet/l2/l2_bd.c b/src/vnet/l2/l2_bd.c index 973beb71cd0..47bdce6af68 100644 --- a/src/vnet/l2/l2_bd.c +++ b/src/vnet/l2/l2_bd.c @@ -52,8 +52,9 @@ bd_validate (l2_bridge_domain_t * bd_config) { if (bd_is_valid (bd_config)) return; - bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM; + bd_config->feature_bitmap = ~(L2INPUT_FEAT_ARP_TERM | L2INPUT_FEAT_UU_FWD); bd_config->bvi_sw_if_index = ~0; + bd_config->uu_fwd_sw_if_index = ~0; bd_config->members = 0; bd_config->flood_count = 0; bd_config->tun_master_count = 0; @@ -240,7 +241,7 @@ VLIB_INIT_FUNCTION (l2bd_init); Return 0 if ok, non-zero if for an error. */ u32 -bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable) +bd_set_flags (vlib_main_t * vm, u32 bd_index, bd_flags_t flags, u32 enable) { l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index); @@ -894,7 +895,7 @@ VLIB_CLI_COMMAND (bd_arp_entry_cli, static) = { }; /* *INDENT-ON* */ -u8 * +static u8 * format_vtr (u8 * s, va_list * args) { u32 vtr_op = va_arg (*args, u32); @@ -929,6 +930,20 @@ format_vtr (u8 * s, va_list * args) } } +static u8 * +format_uu_cfg (u8 * s, va_list * args) +{ + l2_bridge_domain_t *bd_config = va_arg (*args, l2_bridge_domain_t *); + + if (bd_config->feature_bitmap & L2INPUT_FEAT_UU_FWD) + return (format (s, "%U", format_vnet_sw_if_index_name_with_NA, + vnet_get_main (), bd_config->uu_fwd_sw_if_index)); + else if (bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD) + return (format (s, "flood")); + else + return (format (s, "drop")); +} + /** Show bridge-domain state. The CLI format is: @@ -1002,10 +1017,10 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { printed = 1; vlib_cli_output (vm, - "%=8s %=7s %=4s %=9s %=9s %=9s %=9s %=9s %=9s %=9s", + "%=8s %=7s %=4s %=9s %=9s %=9s %=11s %=9s %=9s %=11s", "BD-ID", "Index", "BSN", "Age(min)", - "Learning", "U-Forwrd", "UU-Flood", "Flooding", - "ARP-Term", "BVI-Intf"); + "Learning", "U-Forwrd", "UU-Flood", + "Flooding", "ARP-Term", "BVI-Intf"); } if (bd_config->mac_age) @@ -1013,14 +1028,13 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) else as = format (as, "off"); vlib_cli_output (vm, - "%=8d %=7d %=4d %=9v %=9s %=9s %=9s %=9s %=9s %=9U", + "%=8d %=7d %=4d %=9v %=9s %=9s %=11U %=9s %=9s %=11U", bd_config->bd_id, bd_index, bd_config->seq_num, as, bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" : "off", - bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD ? - "on" : "off", + format_uu_cfg, bd_config, bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off", bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? @@ -1055,6 +1069,13 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) "-", i < bd_config->flood_count ? "*" : "-", format_vtr, vtr_opr, dot1q, tag1, tag2); } + if (~0 != bd_config->uu_fwd_sw_if_index) + vlib_cli_output (vm, "%=30U%=7d%=5d%=5d%=5s%=9s%=30s", + format_vnet_sw_if_index_name, vnm, + bd_config->uu_fwd_sw_if_index, + bd_config->uu_fwd_sw_if_index, + 0, 0, "uu", "-", "None"); + } if ((detail || arp) && @@ -1150,7 +1171,7 @@ bd_add_del (l2_bridge_domain_add_del_args_t * a) return VNET_API_ERROR_BD_ID_EXCEED_MAX; bd_index = bd_add_bd_index (bdm, a->bd_id); - u32 enable_flags = 0, disable_flags = 0; + bd_flags_t enable_flags = 0, disable_flags = 0; if (a->flood) enable_flags |= L2_FLOOD; else diff --git a/src/vnet/l2/l2_bd.h b/src/vnet/l2/l2_bd.h index ffc75339e97..226e30ecfd4 100644 --- a/src/vnet/l2/l2_bd.h +++ b/src/vnet/l2/l2_bd.h @@ -21,6 +21,13 @@ #include #include +typedef enum l2_bd_port_type_t_ +{ + L2_BD_PORT_TYPE_NORMAL = 0, + L2_BD_PORT_TYPE_BVI = 1, + L2_BD_PORT_TYPE_UU_FWD = 2, +} l2_bd_port_type_t; + typedef struct { /* hash bd_id -> bd_index */ @@ -53,16 +60,23 @@ typedef struct typedef struct { - u32 feature_bitmap; /* * Contains bit enables for flooding, learning, and forwarding. * All other feature bits should always be set. - * + */ + u32 feature_bitmap; + /* * identity of the bridge-domain's BVI interface * set to ~0 if there is no BVI */ u32 bvi_sw_if_index; + /* + * identity of the bridge-domain's UU flood interface + * set to ~0 if there is no such configuration + */ + u32 uu_fwd_sw_if_index; + /* bridge domain id, not to be confused with bd_index */ u32 bd_id; @@ -128,14 +142,18 @@ bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member); u32 bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index); - -#define L2_LEARN (1<<0) -#define L2_FWD (1<<1) -#define L2_FLOOD (1<<2) -#define L2_UU_FLOOD (1<<3) -#define L2_ARP_TERM (1<<4) - -u32 bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable); +typedef enum bd_flags_t_ +{ + L2_NONE = 0, + L2_LEARN = (1 << 0), + L2_FWD = (1 << 1), + L2_FLOOD = (1 << 2), + L2_UU_FLOOD = (1 << 3), + L2_ARP_TERM = (1 << 4), +} bd_flags_t; + +u32 bd_set_flags (vlib_main_t * vm, u32 bd_index, bd_flags_t flags, + u32 enable); void bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age); int bd_add_del (l2_bridge_domain_add_del_args_t * args); diff --git a/src/vnet/l2/l2_flood.c b/src/vnet/l2/l2_flood.c index ee3d6d4621f..97a4ff59da7 100644 --- a/src/vnet/l2/l2_flood.c +++ b/src/vnet/l2/l2_flood.c @@ -372,10 +372,12 @@ VLIB_REGISTER_NODE (l2flood_node,static) = { [L2FLOOD_NEXT_DROP] = "error-drop", }, }; -/* *INDENT-ON* */ VLIB_NODE_FUNCTION_MULTIARCH (l2flood_node, l2flood_node_fn) - clib_error_t *l2flood_init (vlib_main_t * vm) +/* *INDENT-ON* */ + +clib_error_t * +l2flood_init (vlib_main_t * vm) { l2flood_main_t *mp = &l2flood_main; diff --git a/src/vnet/l2/l2_fwd.c b/src/vnet/l2/l2_fwd.c index 0fad124b89f..c647e3dbd06 100644 --- a/src/vnet/l2/l2_fwd.c +++ b/src/vnet/l2/l2_fwd.c @@ -211,7 +211,8 @@ l2fwd_process (vlib_main_t * vm, * lookup miss, so flood which is typically the next feature * unless some other feature is inserted before uu_flood */ - if (vnet_buffer (b0)->l2.feature_bitmap & L2INPUT_FEAT_UU_FLOOD) + if (vnet_buffer (b0)->l2.feature_bitmap & + (L2INPUT_FEAT_UU_FLOOD | L2INPUT_FEAT_UU_FWD)) { *next0 = vnet_l2_feature_next (b0, msm->feat_next_node_index, L2INPUT_FEAT_FWD); @@ -223,7 +224,6 @@ l2fwd_process (vlib_main_t * vm, *next0 = L2FWD_NEXT_DROP; } } - } diff --git a/src/vnet/l2/l2_input.c b/src/vnet/l2/l2_input.c index 1c9ddaac04a..f94ef668c41 100644 --- a/src/vnet/l2/l2_input.c +++ b/src/vnet/l2/l2_input.c @@ -573,7 +573,7 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ u32 mode, /* One of L2 modes or back to L3 mode */ u32 sw_if_index, /* sw interface index */ u32 bd_index, /* for bridged interface */ - u32 bvi, /* the bridged interface is the BVI */ + l2_bd_port_type_t port_type, /* port_type */ u32 shg, /* the bridged interface split horizon group */ u32 xc_sw_if_index) /* peer interface for xconnect */ { @@ -613,6 +613,11 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ si = vnet_get_sw_interface (vnm, sw_if_index); si->flood_class = VNET_FLOOD_CLASS_NO_FLOOD; } + if (bd_config->uu_fwd_sw_if_index == sw_if_index) + { + bd_config->uu_fwd_sw_if_index = ~0; + bd_config->feature_bitmap &= ~L2INPUT_FEAT_UU_FWD; + } /* Clear MACs learned on the interface */ if ((config->feature_bitmap & L2INPUT_FEAT_LEARN) || @@ -657,6 +662,8 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ if (mode == MODE_L2_BRIDGE) { + u8 member_flags; + /* * Remove a check that the interface must be an Ethernet. * Specifically so we can bridge to L3 tunnel interfaces. @@ -676,8 +683,12 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ * Enable forwarding, flooding, learning and ARP termination by default * (note that ARP term is disabled on BD feature bitmap by default) */ - config->feature_bitmap |= L2INPUT_FEAT_FWD | L2INPUT_FEAT_UU_FLOOD | - L2INPUT_FEAT_FLOOD | L2INPUT_FEAT_LEARN | L2INPUT_FEAT_ARP_TERM; + config->feature_bitmap |= (L2INPUT_FEAT_FWD | + L2INPUT_FEAT_UU_FLOOD | + L2INPUT_FEAT_UU_FWD | + L2INPUT_FEAT_FLOOD | + L2INPUT_FEAT_LEARN | + L2INPUT_FEAT_ARP_TERM); /* Make sure last-chance drop is configured */ config->feature_bitmap |= L2INPUT_FEAT_DROP; @@ -692,7 +703,7 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ /* TODO: think: add l2fib entry even for non-bvi interface? */ /* Do BVI interface initializations */ - if (bvi) + if (L2_BD_PORT_TYPE_BVI == port_type) { vnet_sw_interface_t *si; @@ -715,16 +726,29 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, /* */ /* since this is a BVI interface we want to flood to it */ si = vnet_get_sw_interface (vnm, sw_if_index); si->flood_class = VNET_FLOOD_CLASS_BVI; + member_flags = L2_FLOOD_MEMBER_BVI; + } + else if (L2_BD_PORT_TYPE_UU_FWD == port_type) + { + bd_config->uu_fwd_sw_if_index = sw_if_index; + bd_config->feature_bitmap |= L2INPUT_FEAT_UU_FWD; + } + else + { + member_flags = L2_FLOOD_MEMBER_NORMAL; } - /* Add interface to bridge-domain flood vector */ - l2_flood_member_t member = { - .sw_if_index = sw_if_index, - .flags = bvi ? L2_FLOOD_MEMBER_BVI : L2_FLOOD_MEMBER_NORMAL, - .shg = shg, - }; - bd_add_member (bd_config, &member); - + if (L2_BD_PORT_TYPE_NORMAL == port_type || + L2_BD_PORT_TYPE_BVI == port_type) + { + /* Add interface to bridge-domain flood vector */ + l2_flood_member_t member = { + .sw_if_index = sw_if_index, + .flags = member_flags, + .shg = shg, + }; + bd_add_member (bd_config, &member); + } } else if (mode == MODE_L2_XC) { @@ -827,10 +851,10 @@ int_l2_bridge (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { vnet_main_t *vnm = vnet_get_main (); + l2_bd_port_type_t port_type; clib_error_t *error = 0; u32 bd_index, bd_id; u32 sw_if_index; - u32 bvi; u32 rc; u32 shg; @@ -857,7 +881,11 @@ int_l2_bridge (vlib_main_t * vm, bd_index = bd_find_or_add_bd_index (&bd_main, bd_id); /* optional bvi */ - bvi = unformat (input, "bvi"); + port_type = L2_BD_PORT_TYPE_NORMAL; + if (unformat (input, "bvi")) + port_type = L2_BD_PORT_TYPE_BVI; + if (unformat (input, "uu-fwd")) + port_type = L2_BD_PORT_TYPE_UU_FWD; /* optional split horizon group */ shg = 0; @@ -865,8 +893,8 @@ int_l2_bridge (vlib_main_t * vm, /* set the interface mode */ if ((rc = - set_int_l2_mode (vm, vnm, MODE_L2_BRIDGE, sw_if_index, bd_index, bvi, - shg, 0))) + set_int_l2_mode (vm, vnm, MODE_L2_BRIDGE, sw_if_index, bd_index, + port_type, shg, 0))) { if (rc == MODE_ERROR_ETH) { @@ -920,7 +948,7 @@ done: /* *INDENT-OFF* */ VLIB_CLI_COMMAND (int_l2_bridge_cli, static) = { .path = "set interface l2 bridge", - .short_help = "set interface l2 bridge [bvi] [shg]", + .short_help = "set interface l2 bridge [bvi|uu-fwd] [shg]", .function = int_l2_bridge, }; /* *INDENT-ON* */ @@ -956,7 +984,8 @@ int_l2_xc (vlib_main_t * vm, /* set the interface mode */ if (set_int_l2_mode - (vm, vnm, MODE_L2_XC, sw_if_index, 0, 0, 0, xc_sw_if_index)) + (vm, vnm, MODE_L2_XC, sw_if_index, 0, L2_BD_PORT_TYPE_NORMAL, + 0, xc_sw_if_index)) { error = clib_error_return (0, "invalid configuration for interface", format_unformat_error, input); @@ -1010,7 +1039,8 @@ int_l3 (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) } /* set the interface mode */ - if (set_int_l2_mode (vm, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0)) + if (set_int_l2_mode (vm, vnm, MODE_L3, sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0)) { error = clib_error_return (0, "invalid configuration for interface", format_unformat_error, input); diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h index fb601334f37..4d6f1a32177 100644 --- a/src/vnet/l2/l2_input.h +++ b/src/vnet/l2/l2_input.h @@ -104,6 +104,7 @@ l2input_bd_config (u32 bd_index) _(FLOOD, "l2-flood") \ _(ARP_TERM, "arp-term-l2bd") \ _(UU_FLOOD, "l2-flood") \ + _(UU_FWD, "l2-uu-fwd") \ _(GBP_FWD, "gbp-fwd") \ _(FWD, "l2-fwd") \ _(RW, "l2-rw") \ @@ -213,7 +214,8 @@ u32 set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, u32 mode, u32 sw_if_index, - u32 bd_index, u32 bvi, u32 shg, u32 xc_sw_if_index); + u32 bd_index, l2_bd_port_type_t port_type, + u32 shg, u32 xc_sw_if_index); static inline void vnet_update_l2_len (vlib_buffer_t * b) diff --git a/src/vnet/l2/l2_uu_fwd.c b/src/vnet/l2/l2_uu_fwd.c new file mode 100644 index 00000000000..fd79387bbe0 --- /dev/null +++ b/src/vnet/l2/l2_uu_fwd.c @@ -0,0 +1,242 @@ +/* + * l2_uu_fwd.c : Foward unknown unicast packets to BD's configured interface + * + * Copyright (c) 2018 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 +#include + +#define foreach_l2_uu_fwd_error \ +_(L2_UU_FWD, "L2 UU fwd") + +typedef enum +{ +#define _(sym,str) L2_UU_FWD_ERROR_##sym, + foreach_l2_uu_fwd_error +#undef _ + L2_UU_FWD_N_ERROR, +} l2_uu_fwd_error_t; + +static char *l2_uu_fwd_error_strings[] = { +#define _(sym,string) string, + foreach_l2_uu_fwd_error +#undef _ +}; + +typedef enum +{ + L2_UU_FWD_NEXT_DROP, + L2_UU_FWD_NEXT_L2_OUTPUT, + L2_UU_FWD_N_NEXT, +} l2_uu_fwd_next_t; + +typedef struct +{ + u32 sw_if_index; +} l2_uu_fwd_trace_t; + +/* packet trace format function */ +static u8 * +format_l2_uu_fwd_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + l2_uu_fwd_trace_t *t = va_arg (*args, l2_uu_fwd_trace_t *); + + s = format (s, "l2-uu-fwd: sw_if_index %d", t->sw_if_index); + return s; +} + +static uword +l2_uu_fwd_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + l2_uu_fwd_next_t next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 8 && n_left_to_next >= 4) + { + const l2_bridge_domain_t *bdc0, *bdc1, *bdc2, *bdc3; + l2_uu_fwd_next_t next0, next1, next2, next3; + vlib_buffer_t *b0, *b1, *b2, *b3; + u32 bi0, bi1, bi2, bi3; + + { + vlib_buffer_t *b4, *b5, *b6, *b7; + + b4 = vlib_get_buffer (vm, from[4]); + b5 = vlib_get_buffer (vm, from[5]); + b6 = vlib_get_buffer (vm, from[6]); + b7 = vlib_get_buffer (vm, from[7]); + + vlib_prefetch_buffer_header (b4, STORE); + vlib_prefetch_buffer_header (b5, STORE); + vlib_prefetch_buffer_header (b6, STORE); + vlib_prefetch_buffer_header (b7, STORE); + } + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + bi2 = to_next[2] = from[2]; + bi3 = to_next[3] = from[3]; + + from += 4; + to_next += 4; + n_left_from -= 4; + n_left_to_next -= 4; + + next3 = next2 = next1 = next0 = L2_UU_FWD_NEXT_L2_OUTPUT; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + b2 = vlib_get_buffer (vm, bi2); + b3 = vlib_get_buffer (vm, bi3); + + bdc0 = vec_elt_at_index (l2input_main.bd_configs, + vnet_buffer (b0)->l2.bd_index); + bdc1 = vec_elt_at_index (l2input_main.bd_configs, + vnet_buffer (b1)->l2.bd_index); + bdc2 = vec_elt_at_index (l2input_main.bd_configs, + vnet_buffer (b2)->l2.bd_index); + bdc3 = vec_elt_at_index (l2input_main.bd_configs, + vnet_buffer (b3)->l2.bd_index); + + ASSERT (~0 != bdc0->uu_fwd_sw_if_index); + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = bdc0->uu_fwd_sw_if_index; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = bdc1->uu_fwd_sw_if_index; + vnet_buffer (b2)->sw_if_index[VLIB_TX] = bdc2->uu_fwd_sw_if_index; + vnet_buffer (b3)->sw_if_index[VLIB_TX] = bdc3->uu_fwd_sw_if_index; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_uu_fwd_trace_t *t; + + t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = bdc0->uu_fwd_sw_if_index; + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_uu_fwd_trace_t *t; + + t = vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = bdc1->uu_fwd_sw_if_index; + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_uu_fwd_trace_t *t; + + t = vlib_add_trace (vm, node, b2, sizeof (*t)); + t->sw_if_index = bdc2->uu_fwd_sw_if_index; + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_uu_fwd_trace_t *t; + + t = vlib_add_trace (vm, node, b3, sizeof (*t)); + t->sw_if_index = bdc3->uu_fwd_sw_if_index; + } + vlib_validate_buffer_enqueue_x4 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, bi2, bi3, + next0, next1, next2, next3); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + const l2_bridge_domain_t *bdc0; + l2_uu_fwd_next_t next0; + vlib_buffer_t *b0; + u32 bi0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + next0 = L2_UU_FWD_NEXT_L2_OUTPUT; + b0 = vlib_get_buffer (vm, bi0); + + bdc0 = vec_elt_at_index (l2input_main.bd_configs, + vnet_buffer (b0)->l2.bd_index); + ASSERT (~0 != bdc0->uu_fwd_sw_if_index); + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = bdc0->uu_fwd_sw_if_index; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_uu_fwd_trace_t *t; + + t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = bdc0->uu_fwd_sw_if_index; + } + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + L2_UU_FWD_ERROR_L2_UU_FWD, frame->n_vectors); + + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (l2_uu_fwd_node,static) = { + .function = l2_uu_fwd_node_fn, + .name = "l2-uu-fwd", + .vector_size = sizeof (u32), + .format_trace = format_l2_uu_fwd_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(l2_uu_fwd_error_strings), + .error_strings = l2_uu_fwd_error_strings, + + .n_next_nodes = L2_UU_FWD_N_NEXT, + + .next_nodes = { + [L2_UU_FWD_NEXT_DROP] = "error-drop", + [L2_UU_FWD_NEXT_L2_OUTPUT] = "l2-output", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (l2_uu_fwd_node, l2_uu_fwd_node_fn) +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2/l2_xcrw.c b/src/vnet/l2/l2_xcrw.c index d08a5d8f8e8..334e8641455 100644 --- a/src/vnet/l2/l2_xcrw.c +++ b/src/vnet/l2/l2_xcrw.c @@ -366,8 +366,8 @@ vnet_configure_l2_xcrw (vlib_main_t * vm, vnet_main_t * vnm, if (vec_len (rewrite)) vnet_rewrite_set_data (a[0], rewrite, vec_len (rewrite)); - set_int_l2_mode (vm, vnm, MODE_L2_XC, t->l2_sw_if_index, 0, 0, 0, - t->tunnel_sw_if_index); + set_int_l2_mode (vm, vnm, MODE_L2_XC, t->l2_sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, t->tunnel_sw_if_index); hash_set (xcm->tunnel_index_by_l2_sw_if_index, t->l2_sw_if_index, t - xcm->tunnels); return 0; @@ -384,7 +384,8 @@ vnet_configure_l2_xcrw (vlib_main_t * vm, vnet_main_t * vnm, /* Reset adj to drop traffic */ memset (a, 0, sizeof (*a)); - set_int_l2_mode (vm, vnm, MODE_L3, t->l2_sw_if_index, 0, 0, 0, 0); + set_int_l2_mode (vm, vnm, MODE_L3, t->l2_sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); vnet_sw_interface_set_flags (vnm, t->tunnel_sw_if_index, 0 /* down */ ); diff --git a/src/vnet/lisp-gpe/interface.c b/src/vnet/lisp-gpe/interface.c index 127508331e0..1ccb53dda72 100644 --- a/src/vnet/lisp-gpe/interface.c +++ b/src/vnet/lisp-gpe/interface.c @@ -700,7 +700,7 @@ lisp_gpe_add_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id) /* we're ready. add iface to l2 bridge domain */ set_int_l2_mode (lgm->vlib_main, vnm, MODE_L2_BRIDGE, hi->sw_if_index, - bd_index, 0, 0, 0); + bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0); return (hi->sw_if_index); } @@ -736,7 +736,7 @@ lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id) /* Remove interface from bridge .. by enabling L3 mode */ hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]); set_int_l2_mode (lgm->vlib_main, lgm->vnet_main, MODE_L3, hi->sw_if_index, - 0, 0, 0, 0); + 0, L2_BD_PORT_TYPE_NORMAL, 0, 0); lisp_gpe_remove_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces); } diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.c b/src/vnet/vxlan-gpe/vxlan_gpe.c index b511a7dbdd4..ac3e1784009 100644 --- a/src/vnet/vxlan-gpe/vxlan_gpe.c +++ b/src/vnet/vxlan-gpe/vxlan_gpe.c @@ -713,8 +713,8 @@ int vnet_vxlan_gpe_add_del_tunnel vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ ); vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index); si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; - set_int_l2_mode (ngm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, 0, 0, - 0); + set_int_l2_mode (ngm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, + L2_BD_PORT_TYPE_NORMAL, 0, 0); vec_add1 (ngm->free_vxlan_gpe_tunnel_hw_if_indices, t->hw_if_index); ngm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 70fd7142aeb..9e063be5c34 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -302,8 +302,12 @@ static void *vl_api_sw_interface_set_l2_bridge_t_print if (mp->enable) { - s = format (s, "bd_id %d shg %d %senable ", ntohl (mp->bd_id), - mp->shg, ((mp->bvi) ? "bvi " : " ")); + s = format (s, "bd_id %d shg %d ", ntohl (mp->bd_id), mp->shg); + if (L2_API_PORT_TYPE_BVI == ntohl (mp->port_type)) + s = format (s, "bvi "); + if (L2_API_PORT_TYPE_UU_FWD == ntohl (mp->port_type)) + s = format (s, "uu-fwd "); + s = format (s, "enable"); } else s = format (s, "disable "); @@ -458,21 +462,21 @@ static void *vl_api_bridge_flags_t_print (vl_api_bridge_flags_t * mp, void *handle) { u8 *s; - u32 flags = ntohl (mp->feature_bitmap); + u32 flags = ntohl (mp->flags); s = format (0, "SCRIPT: bridge_flags "); s = format (s, "bd_id %d ", ntohl (mp->bd_id)); - if (flags & L2_LEARN) + if (flags & BRIDGE_API_FLAG_LEARN) s = format (s, "learn "); - if (flags & L2_FWD) + if (flags & BRIDGE_API_FLAG_FWD) s = format (s, "forward "); - if (flags & L2_FLOOD) + if (flags & BRIDGE_API_FLAG_FLOOD) s = format (s, "flood "); - if (flags & L2_UU_FLOOD) + if (flags & BRIDGE_API_FLAG_UU_FLOOD) s = format (s, "uu-flood "); - if (flags & L2_ARP_TERM) + if (flags & BRIDGE_API_FLAG_ARP_TERM) s = format (s, "arp-term "); if (mp->is_set == 0) diff --git a/test/test_acl_plugin_l2l3.py b/test/test_acl_plugin_l2l3.py index 26b562e8ad0..66d053ff584 100644 --- a/test/test_acl_plugin_l2l3.py +++ b/test/test_acl_plugin_l2l3.py @@ -36,6 +36,7 @@ from scapy.layers.inet6 import ICMPv6EchoReply, IPv6ExtHdrRouting from scapy.layers.inet6 import IPv6ExtHdrFragment from framework import VppTestCase, VppTestRunner +from vpp_papi_provider import L2_PORT_TYPE import time @@ -70,7 +71,8 @@ class TestIpIrb(VppTestCase): # Create BD with MAC learning enabled and put interfaces to this BD cls.vapi.sw_interface_set_l2_bridge( - cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1) + cls.loop0.sw_if_index, bd_id=cls.bd_id, + port_type=L2_PORT_TYPE.BVI) cls.vapi.sw_interface_set_l2_bridge( cls.pg0.sw_if_index, bd_id=cls.bd_id) cls.vapi.sw_interface_set_l2_bridge( diff --git a/test/test_acl_plugin_macip.py b/test/test_acl_plugin_macip.py index f35db55fb7e..611bc73e312 100644 --- a/test/test_acl_plugin_macip.py +++ b/test/test_acl_plugin_macip.py @@ -16,6 +16,7 @@ from framework import VppTestCase, VppTestRunner, running_extended_tests from vpp_lo_interface import VppLoInterface from vpp_papi_provider import L2_VTR_OP from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint +from vpp_papi_provider import L2_PORT_TYPE class MethodHolder(VppTestCase): @@ -90,7 +91,8 @@ class MethodHolder(VppTestCase): # Create BD with MAC learning enabled and put interfaces to this BD cls.vapi.sw_interface_set_l2_bridge( - cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1) + cls.loop0.sw_if_index, bd_id=cls.bd_id, + port_type=L2_PORT_TYPE.BVI) cls.vapi.sw_interface_set_l2_bridge( cls.pg0.sw_if_index, bd_id=cls.bd_id) cls.vapi.sw_interface_set_l2_bridge( diff --git a/test/test_dvr.py b/test/test_dvr.py index 9d8675832ea..7a744baf76c 100644 --- a/test/test_dvr.py +++ b/test/test_dvr.py @@ -4,7 +4,7 @@ import unittest from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppDot1QSubint from vpp_ip_route import VppIpRoute, VppRoutePath -from vpp_papi_provider import L2_VTR_OP +from vpp_papi_provider import L2_VTR_OP, L2_PORT_TYPE from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q @@ -88,7 +88,8 @@ class TestDVR(VppTestCase): self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1) - self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, bvi=1) + self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, + port_type=L2_PORT_TYPE.BVI) self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index, L2_VTR_OP.L2_POP_1, @@ -208,7 +209,8 @@ class TestDVR(VppTestCase): self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, - 1, bvi=1, enable=0) + 1, port_type=L2_PORT_TYPE.BVI, + enable=0) # # Do a FIB dump to make sure the paths are correctly reported as DVR diff --git a/test/test_gbp.py b/test/test_gbp.py index 0d5dd154be7..132bd247dd1 100644 --- a/test/test_gbp.py +++ b/test/test_gbp.py @@ -9,6 +9,7 @@ from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable from vpp_ip import * from vpp_mac import * +from vpp_papi_provider import L2_PORT_TYPE from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP @@ -585,9 +586,11 @@ class TestGBP(VppTestCase): # epg[1] shares the same BVI to epg[0] if epg != epgs[1] and epg != epgs[4]: # BVI in BD - self.vapi.sw_interface_set_l2_bridge(epg.bvi.sw_if_index, - epg.bd, - bvi=1) + self.vapi.sw_interface_set_l2_bridge( + epg.bvi.sw_if_index, + epg.bd, + port_type=L2_PORT_TYPE.BVI) + # BVI L2 FIB entry self.vapi.l2fib_add_del(self.router_mac, epg.bd, diff --git a/test/test_ip4_irb.py b/test/test_ip4_irb.py index 6aad60a7609..cf7d89e4f6e 100644 --- a/test/test_ip4_irb.py +++ b/test/test_ip4_irb.py @@ -32,6 +32,7 @@ from scapy.layers.inet import IP, UDP from framework import VppTestCase, VppTestRunner from util import mactobinary +from vpp_papi_provider import L2_PORT_TYPE class TestIpIrb(VppTestCase): @@ -65,7 +66,8 @@ class TestIpIrb(VppTestCase): # Create BD with MAC learning enabled and put interfaces to this BD cls.vapi.sw_interface_set_l2_bridge( - cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1) + cls.loop0.sw_if_index, bd_id=cls.bd_id, + port_type=L2_PORT_TYPE.BVI) cls.vapi.sw_interface_set_l2_bridge( cls.pg0.sw_if_index, bd_id=cls.bd_id) cls.vapi.sw_interface_set_l2_bridge( diff --git a/test/test_l2_flood.py b/test/test_l2_flood.py index a8b6b10f11a..50a692e57e8 100644 --- a/test/test_l2_flood.py +++ b/test/test_l2_flood.py @@ -5,6 +5,7 @@ import socket from framework import VppTestCase, VppTestRunner from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_papi_provider import L2_PORT_TYPE, BRIDGE_FLAGS from scapy.packet import Raw from scapy.layers.l2 import Ether @@ -58,7 +59,8 @@ class TestL2Flood(VppTestCase): for i in self.pg_interfaces[8:12]: self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2) for i in self.lo_interfaces: - self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2, bvi=1) + self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2, + port_type=L2_PORT_TYPE.BVI) p = (Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:de:ad:be:ef") / @@ -137,7 +139,112 @@ class TestL2Flood(VppTestCase): self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, enable=0) for i in self.lo_interfaces: self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 2, - bvi=1, enable=0) + port_type=L2_PORT_TYPE.BVI, + enable=0) + + self.vapi.bridge_domain_add_del(1, is_add=0) + + def test_uu_fwd(self): + """ UU Flood """ + + # + # Create a single bridge Domain + # + self.vapi.bridge_domain_add_del(1, uu_flood=1) + + # + # add each interface to the BD. 3 interfaces per split horizon group + # + for i in self.pg_interfaces[0:4]: + self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, 0) + + # + # an unknown unicast packet + # + p_uu = (Ether(dst="00:00:00:c1:5c:00", + src="00:00:de:ad:be:ef") / + IP(src="10.10.10.10", dst="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + # + # input on pg0, expected copies on pg1->4 + # + self.pg0.add_stream(p_uu*65) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + for i in self.pg_interfaces[1:4]: + rx0 = i.get_capture(65, timeout=1) + + # + # use pg8 as the uu-fwd interface + # + self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0, + port_type=L2_PORT_TYPE.UU_FWD) + + # + # expect the UU packet on the uu-fwd interface and not be flooded + # + self.pg0.add_stream(p_uu*65) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx0 = self.pg8.get_capture(65, timeout=1) + + for i in self.pg_interfaces[0:4]: + i.assert_nothing_captured(remark="UU not flooded") + + # + # remove the uu-fwd interface and expect UU to be flooded again + # + self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0, + port_type=L2_PORT_TYPE.UU_FWD, + enable=0) + + self.pg0.add_stream(p_uu*65) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + for i in self.pg_interfaces[1:4]: + rx0 = i.get_capture(65, timeout=1) + + # + # change the BD config to not support UU-flood + # + self.vapi.bridge_flags(1, 0, BRIDGE_FLAGS.UU_FLOOD) + + self.send_and_assert_no_replies(self.pg0, p_uu) + + # + # re-add the uu-fwd interface + # + self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0, + port_type=L2_PORT_TYPE.UU_FWD) + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + + self.pg0.add_stream(p_uu*65) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx0 = self.pg8.get_capture(65, timeout=1) + + for i in self.pg_interfaces[0:4]: + i.assert_nothing_captured(remark="UU not flooded") + + # + # remove the uu-fwd interface + # + self.vapi.sw_interface_set_l2_bridge(self.pg8.sw_if_index, 1, 0, + port_type=L2_PORT_TYPE.UU_FWD, + enable=0) + self.send_and_assert_no_replies(self.pg0, p_uu) + + # + # cleanup + # + for i in self.pg_interfaces[:4]: + self.vapi.sw_interface_set_l2_bridge(i.sw_if_index, 1, enable=0) self.vapi.bridge_domain_add_del(1, is_add=0) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 0c98f7ae1a0..ee45a5fb4cd 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -43,6 +43,21 @@ class QOS_SOURCE: IP = 3 +class L2_PORT_TYPE: + NORMAL = 0 + BVI = 1 + UU_FWD = 2 + + +class BRIDGE_FLAGS: + NONE = 0 + LEARN = 1 + FWD = 2 + FLOOD = 4 + UU_FLOOD = 8 + ARP_TERM = 16 + + class UnexpectedApiReturnValueError(Exception): """ exception raised when the API return value is unexpected """ pass @@ -627,7 +642,8 @@ class VppPapiProvider(object): return self.api(self.papi.l2fib_flush_all, {}) def sw_interface_set_l2_bridge(self, sw_if_index, bd_id, - shg=0, bvi=0, enable=1): + shg=0, port_type=L2_PORT_TYPE.NORMAL, + enable=1): """Add/remove interface to/from bridge domain. :param int sw_if_index: Software interface index of the interface. @@ -641,7 +657,7 @@ class VppPapiProvider(object): {'rx_sw_if_index': sw_if_index, 'bd_id': bd_id, 'shg': shg, - 'bvi': bvi, + 'port_type': port_type, 'enable': enable}) def bridge_flags(self, bd_id, is_set, feature_bitmap): @@ -650,7 +666,7 @@ class VppPapiProvider(object): :param int bd_id: Bridge domain ID. :param int is_set: Set to 1 to enable, set to 0 to disable the feature. - :param int feature_bitmap: Bitmap value of the feature to be set: + :param int flags: Bitmap value of the feature to be set: - learn (1 << 0), - forward (1 << 1), - flood (1 << 2), @@ -660,7 +676,7 @@ class VppPapiProvider(object): return self.api(self.papi.bridge_flags, {'bd_id': bd_id, 'is_set': is_set, - 'feature_bitmap': feature_bitmap}) + 'flags': feature_bitmap}) def bridge_domain_dump(self, bd_id=0): """ -- 2.16.6