Skip to content

Commit fa82bb7

Browse files
committed
vpp: support L2 VXLAN EVPN tunnels
Cherry-picked from sonic-net/sonic-sairedis PR sonic-net#1761 (sambenko). Fixes syncd segfault when EVPN creates P2P tunnel bridge ports: - Handle SAI_BRIDGE_PORT_TYPE_TUNNEL in vpp_create_vlan_member - Create/remove VPP VxLAN tunnels for L2 EVPN P2P tunnels - Add tunnel interface to VLAN bridge domain in VPP Signed-off-by: Tamer Ahmed <[email protected]>
1 parent 566c04a commit fa82bb7

4 files changed

Lines changed: 300 additions & 6 deletions

File tree

vslib/vpp/SwitchVpp.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,20 @@ sai_status_t SwitchVpp::create(
956956
return createLagMember(object_id, switch_id, attr_count, attr_list);
957957
}
958958

959+
if (object_type == SAI_OBJECT_TYPE_TUNNEL)
960+
{
961+
sai_object_id_t object_id;
962+
sai_deserialize_object_id(serializedObjectId, object_id);
963+
964+
CHECK_STATUS(create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list));
965+
966+
uint32_t sw_if_index;
967+
sai_status_t status = m_tunnel_mgr.create_l2_vxlan_tunnel(object_id, sw_if_index);
968+
SWSS_LOG_INFO("L2 VXLAN tunnel create for %s: status=%d sw_if_index=%u",
969+
serializedObjectId.c_str(), status, sw_if_index);
970+
return status;
971+
}
972+
959973
return create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list);
960974
}
961975

@@ -1190,6 +1204,20 @@ sai_status_t SwitchVpp::remove(
11901204
return bfd_session_del(serializedObjectId);
11911205
}
11921206

1207+
if (object_type == SAI_OBJECT_TYPE_TUNNEL)
1208+
{
1209+
sai_object_id_t object_id;
1210+
sai_deserialize_object_id(serializedObjectId, object_id);
1211+
1212+
sai_status_t status = m_tunnel_mgr.remove_l2_vxlan_tunnel(object_id);
1213+
if (status != SAI_STATUS_SUCCESS) {
1214+
SWSS_LOG_ERROR("Failed to remove L2 VXLAN tunnel resources");
1215+
}
1216+
1217+
// still need to clean up internal SAI state
1218+
return remove_internal(object_type, serializedObjectId);
1219+
}
1220+
11931221
return remove_internal(object_type, serializedObjectId);
11941222
}
11951223

vslib/vpp/SwitchVppFdb.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,21 @@ sai_status_t SwitchVpp::vpp_create_vlan_member(
6868
return SAI_STATUS_FAILURE;
6969
}
7070

71+
auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_id));
72+
auto meta_type = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_TYPE);
73+
auto it_type = br_port_attrs.find(meta_type->attridname);
74+
if (it_type != br_port_attrs.end()) {
75+
sai_bridge_port_type_t bp_type = (sai_bridge_port_type_t)it_type->second->getAttr()->value.s32;
76+
if (bp_type == SAI_BRIDGE_PORT_TYPE_TUNNEL) {
77+
SWSS_LOG_NOTICE("Skipping VLAN member VPP ops for tunnel bridge port %s",
78+
sai_serialize_object_id(br_port_id).c_str());
79+
return SAI_STATUS_SUCCESS;
80+
}
81+
}
82+
7183
const char *hwifname = nullptr;
7284
uint32_t lag_swif_idx;
7385

74-
auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_id));
7586
auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_PORT_ID);
7687
auto bp_attr = br_port_attrs[meta->attridname];
7788
auto port_id = bp_attr->getAttr()->value.oid;

vslib/vpp/TunnelManager.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,3 +412,225 @@ TunnelManager::remove_vpp_vxlan_decap(
412412
tunnel_data.sw_if_index, tunnel_data.bd_id);
413413
return SAI_STATUS_SUCCESS;
414414
}
415+
416+
sai_status_t
417+
TunnelManager::create_l2_vxlan_tunnel(
418+
_In_ sai_object_id_t tunnel_oid,
419+
_Out_ uint32_t& sw_if_index)
420+
{
421+
SWSS_LOG_ENTER();
422+
423+
sw_if_index = ~0;
424+
425+
// Check if already created
426+
auto it = m_l2_tunnel_map.find(tunnel_oid);
427+
if (it != m_l2_tunnel_map.end()) {
428+
sw_if_index = it->second.sw_if_index;
429+
SWSS_LOG_NOTICE("L2 VXLAN tunnel already exists: tunnel=%s sw_if=%u",
430+
sai_serialize_object_id(tunnel_oid).c_str(), sw_if_index);
431+
return SAI_STATUS_SUCCESS;
432+
}
433+
434+
// Get tunnel object
435+
auto tunnel_obj = m_switch_db->get_sai_object(SAI_OBJECT_TYPE_TUNNEL,
436+
sai_serialize_object_id(tunnel_oid));
437+
if (!tunnel_obj) {
438+
SWSS_LOG_ERROR("Tunnel %s not found", sai_serialize_object_id(tunnel_oid).c_str());
439+
return SAI_STATUS_FAILURE;
440+
}
441+
442+
// Check tunnel type
443+
sai_attribute_t attr;
444+
attr.id = SAI_TUNNEL_ATTR_TYPE;
445+
if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) {
446+
SWSS_LOG_ERROR("Missing SAI_TUNNEL_ATTR_TYPE");
447+
return SAI_STATUS_FAILURE;
448+
}
449+
if (attr.value.s32 != SAI_TUNNEL_TYPE_VXLAN) {
450+
SWSS_LOG_NOTICE("Not a VXLAN tunnel (type=%d), skipping", attr.value.s32);
451+
return SAI_STATUS_SUCCESS;
452+
}
453+
454+
// Get src IP
455+
attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP;
456+
if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) {
457+
SWSS_LOG_ERROR("Missing ENCAP_SRC_IP");
458+
return SAI_STATUS_FAILURE;
459+
}
460+
sai_ip_address_t src_ip = attr.value.ipaddr;
461+
462+
// Get dst IP - if missing, this is local VTEP, not P2P tunnel
463+
attr.id = SAI_TUNNEL_ATTR_ENCAP_DST_IP;
464+
if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) {
465+
SWSS_LOG_NOTICE("No ENCAP_DST_IP - local VTEP tunnel, skipping VPP creation");
466+
return SAI_STATUS_SUCCESS;
467+
}
468+
sai_ip_address_t dst_ip = attr.value.ipaddr;
469+
470+
// Find VNI and VLAN from decap mappers
471+
uint32_t vni = 0;
472+
uint16_t vlan_id = 0;
473+
474+
auto decap_mappers = tunnel_obj->get_linked_objects(
475+
SAI_OBJECT_TYPE_TUNNEL_MAP, SAI_TUNNEL_ATTR_DECAP_MAPPERS);
476+
477+
SWSS_LOG_NOTICE("Found %zu decap mappers", decap_mappers.size());
478+
479+
for (auto mapper : decap_mappers) {
480+
attr.id = SAI_TUNNEL_MAP_ATTR_TYPE;
481+
if (mapper->get_attr(attr) != SAI_STATUS_SUCCESS) continue;
482+
SWSS_LOG_NOTICE("Mapper type: %d", attr.value.s32);
483+
if (attr.value.s32 != SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID) continue;
484+
485+
auto entries = mapper->get_child_objs(SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY);
486+
if (!entries) {
487+
SWSS_LOG_NOTICE("No entries in mapper");
488+
continue;
489+
}
490+
491+
SWSS_LOG_NOTICE("Mapper has %zu entries", entries->size());
492+
493+
for (auto& entry_pair : *entries) {
494+
auto entry = entry_pair.second;
495+
496+
// Get VNI
497+
attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY;
498+
if (entry->get_attr(attr) == SAI_STATUS_SUCCESS) {
499+
vni = attr.value.u32;
500+
SWSS_LOG_NOTICE("Found VNI=%u", vni);
501+
}
502+
503+
// Get VLAN ID
504+
attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE;
505+
if (entry->get_attr(attr) == SAI_STATUS_SUCCESS) {
506+
vlan_id = attr.value.u16;
507+
SWSS_LOG_NOTICE("Found VLAN=%u", vlan_id);
508+
}
509+
510+
if (vni != 0 && vlan_id != 0) break;
511+
}
512+
if (vni != 0 && vlan_id != 0) break;
513+
}
514+
515+
if (vni == 0) {
516+
SWSS_LOG_ERROR("No VNI found in tunnel mappers for tunnel %s",
517+
sai_serialize_object_id(tunnel_oid).c_str());
518+
return SAI_STATUS_FAILURE;
519+
}
520+
521+
if (vlan_id == 0) {
522+
SWSS_LOG_ERROR("No VLAN found in tunnel mappers for tunnel %s",
523+
sai_serialize_object_id(tunnel_oid).c_str());
524+
return SAI_STATUS_FAILURE;
525+
}
526+
527+
// Create VPP tunnel
528+
vpp_vxlan_tunnel_t req;
529+
memset(&req, 0, sizeof(req));
530+
req.vni = vni;
531+
req.src_port = m_vxlan_port;
532+
req.dst_port = m_vxlan_port;
533+
req.instance = ~0;
534+
req.decap_next_index = ~0;
535+
sai_ip_address_t_to_vpp_ip_addr_t(src_ip, req.src_address);
536+
sai_ip_address_t_to_vpp_ip_addr_t(dst_ip, req.dst_address);
537+
538+
TunnelVPPData tunnel_data;
539+
tunnel_data.vni = vni;
540+
tunnel_data.src_ip = src_ip;
541+
tunnel_data.dst_ip = dst_ip;
542+
tunnel_data.vlan_id = vlan_id; // Store for cleanup
543+
544+
std::string default_vrf = "default";
545+
tunnel_data.ip_vrf = std::make_shared<IpVrfInfo>(SAI_NULL_OBJECT_ID, 0, default_vrf, false);
546+
547+
if (create_vpp_vxlan_encap(req, tunnel_data) != SAI_STATUS_SUCCESS) {
548+
SWSS_LOG_ERROR("Failed to create VPP VXLAN tunnel");
549+
return SAI_STATUS_FAILURE;
550+
}
551+
552+
// Add tunnel interface to bridge domain (VLAN)
553+
if (vlan_id != 0) {
554+
int vpp_status = set_sw_interface_l2_bridge_by_index(
555+
tunnel_data.sw_if_index, vlan_id, true, VPP_API_PORT_TYPE_NORMAL);
556+
if (vpp_status != 0) {
557+
SWSS_LOG_ERROR("Failed to add tunnel sw_if %u to BD %u",
558+
tunnel_data.sw_if_index, vlan_id);
559+
// Cleanup the tunnel
560+
remove_vpp_vxlan_encap(req, tunnel_data);
561+
return SAI_STATUS_FAILURE;
562+
}
563+
SWSS_LOG_NOTICE("Added tunnel sw_if %u to BD %u", tunnel_data.sw_if_index, vlan_id);
564+
}
565+
566+
m_l2_tunnel_map[tunnel_oid] = tunnel_data;
567+
sw_if_index = tunnel_data.sw_if_index;
568+
569+
char src_str[INET6_ADDRSTRLEN], dst_str[INET6_ADDRSTRLEN];
570+
vpp_ip_addr_t_to_string(&req.src_address, src_str, sizeof(src_str));
571+
vpp_ip_addr_t_to_string(&req.dst_address, dst_str, sizeof(dst_str));
572+
573+
SWSS_LOG_NOTICE("Created L2 VXLAN: tunnel=%s src=%s dst=%s VNI=%u VLAN=%u sw_if=%u",
574+
sai_serialize_object_id(tunnel_oid).c_str(), src_str, dst_str, vni, vlan_id, sw_if_index);
575+
576+
return SAI_STATUS_SUCCESS;
577+
}
578+
579+
sai_status_t
580+
TunnelManager::remove_l2_vxlan_tunnel(
581+
_In_ sai_object_id_t tunnel_oid)
582+
{
583+
SWSS_LOG_ENTER();
584+
585+
auto it = m_l2_tunnel_map.find(tunnel_oid);
586+
if (it == m_l2_tunnel_map.end()) {
587+
// Not an L2 tunnel we manage, could be L3 or P2MP, skip silently
588+
SWSS_LOG_NOTICE("Tunnel %s not in L2 tunnel map, skipping removal",
589+
sai_serialize_object_id(tunnel_oid).c_str());
590+
return SAI_STATUS_SUCCESS;
591+
}
592+
593+
TunnelVPPData& tunnel_data = it->second;
594+
595+
// Remove tunnel interface from bridge domain
596+
int vpp_status = set_sw_interface_l2_bridge_by_index(
597+
tunnel_data.sw_if_index, tunnel_data.vlan_id,
598+
false, // false = remove from BD
599+
VPP_API_PORT_TYPE_NORMAL);
600+
if (vpp_status != 0) {
601+
SWSS_LOG_ERROR("Failed to remove tunnel sw_if %u from BD %u",
602+
tunnel_data.sw_if_index, tunnel_data.vlan_id);
603+
// Continue with tunnel deletion anyway
604+
} else {
605+
SWSS_LOG_NOTICE("Removed tunnel sw_if %u from BD %u",
606+
tunnel_data.sw_if_index, tunnel_data.vlan_id);
607+
}
608+
609+
// Delete the VPP VXLAN tunnel interface
610+
// Reconstruct the request needed by remove_vpp_vxlan_encap
611+
vpp_vxlan_tunnel_t req;
612+
memset(&req, 0, sizeof(req));
613+
req.vni = tunnel_data.vni;
614+
req.src_port = m_vxlan_port;
615+
req.dst_port = m_vxlan_port;
616+
req.instance = ~0;
617+
req.decap_next_index = ~0;
618+
sai_ip_address_t_to_vpp_ip_addr_t(tunnel_data.src_ip, req.src_address);
619+
sai_ip_address_t_to_vpp_ip_addr_t(tunnel_data.dst_ip, req.dst_address);
620+
621+
sai_status_t status = remove_vpp_vxlan_encap(req, tunnel_data);
622+
if (status != SAI_STATUS_SUCCESS) {
623+
SWSS_LOG_ERROR("Failed to remove VPP VXLAN tunnel for %s",
624+
sai_serialize_object_id(tunnel_oid).c_str());
625+
// Still remove from map to avoid stale entries
626+
}
627+
628+
SWSS_LOG_NOTICE("Removed L2 VXLAN tunnel %s (sw_if=%u, VNI=%u, VLAN=%u)",
629+
sai_serialize_object_id(tunnel_oid).c_str(),
630+
tunnel_data.sw_if_index, tunnel_data.vni, tunnel_data.vlan_id);
631+
632+
// Remove from map
633+
m_l2_tunnel_map.erase(it);
634+
635+
return SAI_STATUS_SUCCESS;
636+
}

vslib/vpp/TunnelManager.h

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,24 @@ namespace saivs
1717
*/
1818
class TunnelVPPData {
1919
public:
20-
TunnelVPPData() : sw_if_index(0), encap_vrf_id(0) {}
21-
u_int32_t sw_if_index;
22-
u_int32_t encap_vrf_id;
23-
u_int32_t bd_id;
24-
vpp_ip_addr_t bvi_addr;
20+
TunnelVPPData() {
21+
memset(&src_ip, 0, sizeof(src_ip));
22+
memset(&dst_ip, 0, sizeof(dst_ip));
23+
memset(&bvi_addr, 0, sizeof(bvi_addr));
24+
}
25+
// Common fields
26+
u_int32_t sw_if_index = 0;
2527
std::shared_ptr<IpVrfInfo> ip_vrf;
28+
29+
u_int32_t encap_vrf_id = 0;
30+
u_int32_t bd_id = 0;
31+
vpp_ip_addr_t bvi_addr;
32+
33+
// L2 VXLAN fields
34+
u_int32_t vni = 0;
35+
u_int16_t vlan_id = 0;
36+
sai_ip_address_t src_ip;
37+
sai_ip_address_t dst_ip;
2638
};
2739

2840
class TunnelManager {
@@ -100,12 +112,33 @@ namespace saivs
100112
* @brief Set VxLAN port.
101113
*/
102114
void set_vxlan_port(const sai_attribute_t* attr);
115+
116+
/**
117+
* @brief Create L2 VXLAN tunnel for EVPN.
118+
*
119+
* This function is called when a SAI P2P tunnel object is created,
120+
* typically triggered by EVPN discovering a remote VTEP via BGP IMET routes.
121+
*
122+
* @param tunnel_oid The SAI tunnel object ID.
123+
* @param sw_if_index Output parameter for the VPP interface index.
124+
* @return SAI_STATUS_SUCCESS on success or if skipped (P2MP tunnel),
125+
* error status on failure.
126+
*/
127+
sai_status_t create_l2_vxlan_tunnel(
128+
_In_ sai_object_id_t tunnel_oid,
129+
_Out_ uint32_t& sw_if_index);
130+
131+
sai_status_t remove_l2_vxlan_tunnel(
132+
_In_ sai_object_id_t tunnel_oid);
133+
103134
private:
104135
SwitchVpp* m_switch_db;
105136
std::array<uint8_t, 6> m_router_mac;
106137
u_int16_t m_vxlan_port;
107138
//nexthop SAI object ID to sw_if_index map
108139
std::unordered_map<sai_object_id_t, TunnelVPPData> m_tunnel_encap_nexthop_map;
140+
// Map from tunnel SAI OID to VPP tunnel data (L2 VXLAN / EVPN)
141+
std::unordered_map<sai_object_id_t, TunnelVPPData> m_l2_tunnel_map;
109142

110143
sai_status_t tunnel_encap_nexthop_action(
111144
_In_ const SaiObject* tunnel_nh_obj,

0 commit comments

Comments
 (0)