@@ -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+ }
0 commit comments