diff --git a/vslib/vpp/SwitchVpp.h b/vslib/vpp/SwitchVpp.h index 8a3b2ed8e0..0bab2691c8 100644 --- a/vslib/vpp/SwitchVpp.h +++ b/vslib/vpp/SwitchVpp.h @@ -323,6 +323,10 @@ namespace saivs _In_ const char *dev, _In_ const sai_mac_t& mac); + static int vs_set_dev_admin_up( + _In_ const char *dev, + _In_ bool up); + static int promisc( _In_ const char *dev); diff --git a/vslib/vpp/SwitchVppFdb.cpp b/vslib/vpp/SwitchVppFdb.cpp index e1000ec58a..8fa0ee3e99 100644 --- a/vslib/vpp/SwitchVppFdb.cpp +++ b/vslib/vpp/SwitchVppFdb.cpp @@ -1364,6 +1364,13 @@ sai_status_t SwitchVpp::vpp_create_lag_member( return SAI_STATUS_FAILURE; } + // Enslaving a port into the bond clears its promiscuous flag in VPP, so + // re-apply it here. Traffic forwarded over the PortChannel arrives with the + // common SONiC router MAC (different from the member's hardware MAC), and + // PortChannel sub-interface traffic is VLAN tagged against the bond rather + // than the member, neither of which the member accepts unless promiscuous. + interface_set_promiscuous(hwifname, true); + CHECK_STATUS(vpp_ensure_lag_lcp(lag_oid)); return SAI_STATUS_SUCCESS; @@ -1396,14 +1403,17 @@ sai_status_t SwitchVpp::vpp_ensure_lag_lcp( configure_lcp_interface(hw_ifname, tap.c_str(), true); - std::string portchannel = std::string("PortChannel") + std::to_string(bond_id); - std::string be = std::string("be") + std::to_string(bond_id); - CHECK_STATUS(add_tc_filter_redirect(be, portchannel)); - + /* + * Control-plane punt for the port channel is handled by the sonic_ext + * plugin's punt-via-member path (sonic-ext-capture + aggr-tap-redirect), + * which steers the punted copy to the originating member tap. The legacy + * be -> PortChannel tc mirred redirect is no longer needed and is + * intentionally not installed (SONiC PR #2440 §5.3). + */ bond_info.lcp_created = true; m_lag_bond_map[lag_oid] = bond_info; - SWSS_LOG_NOTICE("Created LCP and tc redirect for LAG %s", sai_serialize_object_id(lag_oid).c_str()); + SWSS_LOG_NOTICE("Created LCP for LAG %s", sai_serialize_object_id(lag_oid).c_str()); return SAI_STATUS_SUCCESS; } @@ -1596,6 +1606,9 @@ sai_status_t SwitchVpp::vpp_set_lag_member_egress_disable( } else { + // Re-attaching enslaves the port again, which clears its promiscuous + // flag in VPP, so re-apply it just like vpp_create_lag_member() does. + interface_set_promiscuous(hwif_name, true); m_egress_disabled_lag_member_ports.erase(port_oid); } diff --git a/vslib/vpp/SwitchVppHostif.cpp b/vslib/vpp/SwitchVppHostif.cpp index 5f490e58ed..4c73e6f83f 100644 --- a/vslib/vpp/SwitchVppHostif.cpp +++ b/vslib/vpp/SwitchVppHostif.cpp @@ -98,6 +98,60 @@ int SwitchVpp::vs_set_dev_mac_address( return err; } +int SwitchVpp::vs_set_dev_admin_up( + _In_ const char *dev, + _In_ bool up) +{ + SWSS_LOG_ENTER(); + + int s = socket(AF_INET, SOCK_DGRAM, 0); + + if (s < 0) + { + SWSS_LOG_ERROR("failed to create socket, errno: %d", errno); + + return -1; + } + + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + + strncpy(ifr.ifr_name, dev, MAX_INTERFACE_NAME_LEN); + + int err = ioctl(s, SIOCGIFFLAGS, &ifr); + + if (err < 0) + { + SWSS_LOG_ERROR("ioctl SIOCGIFFLAGS on %s failed, err %d", dev, err); + + close(s); + + return err; + } + + if (up) + { + ifr.ifr_flags |= IFF_UP; + } + else + { + ifr.ifr_flags &= ~IFF_UP; + } + + err = ioctl(s, SIOCSIFFLAGS, &ifr); + + if (err < 0) + { + SWSS_LOG_ERROR("ioctl SIOCSIFFLAGS %s on %s failed, err %d", + (up ? "UP" : "DOWN"), dev, err); + } + + close(s); + + return err; +} + int SwitchVpp::promisc( _In_ const char *dev) { diff --git a/vslib/vpp/SwitchVppRif.cpp b/vslib/vpp/SwitchVppRif.cpp index 6283370878..ccf856b404 100644 --- a/vslib/vpp/SwitchVppRif.cpp +++ b/vslib/vpp/SwitchVppRif.cpp @@ -1166,8 +1166,19 @@ sai_status_t SwitchVpp::vpp_add_del_intf_ip_addr_norif ( snprintf(hw_bviifname, sizeof(hw_bviifname), "%s%d","bvi",vlan_id); hw_ifname = hw_bviifname; } else if (full_if_name.compare(0, strlen(PORTCHANNEL_PREFIX), PORTCHANNEL_PREFIX) == 0) { - uint32_t bond_id = std::stoi(full_if_name.substr(strlen(PORTCHANNEL_PREFIX))); - snprintf(hw_bondifname, sizeof(hw_bondifname), "%s%d", BONDETHERNET_PREFIX, bond_id); + /* full_if_name is PortChannel[.]; get_intf_vlanid() above + * already split it into if_name / vlan_id. Parse the id from the + * name *without* the VLAN suffix: std::stoi("1.20") stops at the + * '.' and silently yields 1, which used to drop the sub-interface + * and program the address onto the bond main interface. */ + uint32_t bond_id = std::stoi(if_name.substr(strlen(PORTCHANNEL_PREFIX))); + if (vlan_id) { + snprintf(hw_bondifname, sizeof(hw_bondifname), "%s%u.%u", + BONDETHERNET_PREFIX, bond_id, vlan_id); + } else { + snprintf(hw_bondifname, sizeof(hw_bondifname), "%s%u", + BONDETHERNET_PREFIX, bond_id); + } hw_ifname = hw_bondifname; } else { hwifname = tap_to_hwif_name(if_name.c_str()); @@ -1748,11 +1759,22 @@ sai_status_t SwitchVpp::vpp_create_router_interface( const char *parent_hwif; char hw_subif_parent[32]; + char lcp_host_subif[64]; if (ot == SAI_OBJECT_TYPE_LAG) { snprintf(hw_subif_parent, sizeof(hw_subif_parent), "%s%u", BONDETHERNET_PREFIX, bond_info.id); parent_hwif = hw_subif_parent; + /* + * For a port-channel sub-port the LCP host tap must be be. + * (a VLAN netdev on the be bond tap), NOT PortChannel.: + * that name collides with the kernel 8021q netdev owned by the Linux + * bond/team stack. linux-cp-punt-xc lands on be. and the + * sonic_ext aggr-tap-redirect steers the punted copy to the + * originating member tap (SONiC PR #2440 §5.3). + */ + snprintf(lcp_host_subif, sizeof(lcp_host_subif), "be%u.%u", bond_info.id, vlan_id); } else { parent_hwif = tap_to_hwif_name(dev); + snprintf(lcp_host_subif, sizeof(lcp_host_subif), "%s", host_subifname); } create_sub_interface(parent_hwif, vlan_id, vlan_id); @@ -1760,13 +1782,28 @@ sai_status_t SwitchVpp::vpp_create_router_interface( * lcp-auto-subint is disabled in VPP startup config (vlan-bvi HLD §3.6), * so the VPP sub-interface does NOT get an automatic linux-cp pair. * Explicitly create the LCP pair binding . (VPP side) - * to the kernel sub-vlan netdev (.). Without this the - * sub-interface will not show up in `vppctl show lcp` and host punt - * will not work for the SUB_PORT RIF. + * to its host tap (Ethernet. for a port, be. for a + * port-channel). Without this the sub-interface will not show up in + * `vppctl show lcp` and host punt will not work for the SUB_PORT RIF. */ char vpp_subif_name[64]; snprintf(vpp_subif_name, sizeof(vpp_subif_name), "%s.%u", parent_hwif, vlan_id); - configure_lcp_interface(vpp_subif_name, host_subifname, true); + configure_lcp_interface(vpp_subif_name, lcp_host_subif, true); + + /* + * lcp-auto-subint is disabled and linux-cp lcp-sync is off, so nothing + * brings the freshly created kernel sub-interface host netdev UP: it is + * created admin-down and stays down. A down host netdev drops the + * for-us punt (the kernel never processes it / generates no reply), so + * SUB_PORT datapath silently breaks. Explicitly bring the host tap UP + * here (SAI-driven, consistent with the no-lcp-sync design). + */ + if (vs_set_dev_admin_up(lcp_host_subif, true) < 0) + { + SWSS_LOG_ERROR("Failed to bring host sub-interface %s admin up; " + "for-us traffic punted to this SUB_PORT RIF will be dropped", + lcp_host_subif); + } /* Get new list of physical interfaces from VS */ refresh_interfaces_list(); @@ -2102,7 +2139,15 @@ sai_status_t SwitchVpp::vpp_remove_router_interface(sai_object_id_t rif_id) char vpp_subif_name[64]; char host_subifname[64]; snprintf(vpp_subif_name, sizeof(vpp_subif_name), "%s.%u", parent_hwif, vlan_id); - snprintf(host_subifname, sizeof(host_subifname), "%s.%u", dev, vlan_id); + if (ot == SAI_OBJECT_TYPE_LAG) { + /* Symmetric with create (SONiC PR #2440 §5.3): the host tap is + * be.. The host name is ignored by the LCP plugin on + * delete (the pair is keyed by the VPP sub-if), but keep it + * symmetric for log clarity. */ + snprintf(host_subifname, sizeof(host_subifname), "be%u.%u", bond_info.id, vlan_id); + } else { + snprintf(host_subifname, sizeof(host_subifname), "%s.%u", dev, vlan_id); + } configure_lcp_interface(vpp_subif_name, host_subifname, false); delete_sub_interface(parent_hwif, vlan_id);