From be20ff44f1a47d9b4ea6dda0d6f49cd1d306fb4e Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Sun, 19 Apr 2026 12:46:37 +0000 Subject: [PATCH 01/11] [vpp] add ipinip tunnel encap/decap support Signed-off-by: Longxiang Lyu --- vslib/vpp/SwitchVpp.cpp | 30 +- vslib/vpp/SwitchVpp.h | 2 + vslib/vpp/TunnelManager.cpp | 490 ++++++++++++++++++++++++++++++- vslib/vpp/TunnelManager.h | 161 +++++++++- vslib/vpp/vppxlate/SaiVppXlate.c | 291 +++++++++++++++++- vslib/vpp/vppxlate/SaiVppXlate.h | 17 ++ 6 files changed, 975 insertions(+), 16 deletions(-) diff --git a/vslib/vpp/SwitchVpp.cpp b/vslib/vpp/SwitchVpp.cpp index 897257b11b..a5e8ed07d7 100644 --- a/vslib/vpp/SwitchVpp.cpp +++ b/vslib/vpp/SwitchVpp.cpp @@ -22,7 +22,8 @@ SwitchVpp::SwitchVpp( SwitchStateBase(switch_id, manager, config), m_object_db(this), m_tunnel_mgr(this), - m_tunnel_mgr_srv6(this) + m_tunnel_mgr_srv6(this), + m_tunnel_mgr_ipip(this) { SWSS_LOG_ENTER(); @@ -37,7 +38,8 @@ SwitchVpp::SwitchVpp( SwitchStateBase(switch_id, manager, config, warmBootState), m_object_db(this), m_tunnel_mgr(this), - m_tunnel_mgr_srv6(this) + m_tunnel_mgr_srv6(this), + m_tunnel_mgr_ipip(this) { SWSS_LOG_ENTER(); @@ -1036,6 +1038,20 @@ sai_status_t SwitchVpp::create( { CHECK_STATUS(create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list)); m_tunnel_mgr.handle_l2_vxlan_tunnel_map_entry(serializedObjectId, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY) + { + CHECK_STATUS(create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list)); + + // Check if this is an IPINIP tunnel term + for (uint32_t i = 0; i < attr_count; i++) { + if (attr_list[i].id == SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE && + attr_list[i].value.s32 == SAI_TUNNEL_TYPE_IPINIP) { + return m_tunnel_mgr_ipip.create_ipip_tunnel_term( + serializedObjectId, switch_id, attr_count, attr_list); + } + } return SAI_STATUS_SUCCESS; } @@ -1347,6 +1363,16 @@ sai_status_t SwitchVpp::remove( return remove_internal(object_type, serializedObjectId); } + if (object_type == SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY) + { + sai_status_t status = m_tunnel_mgr_ipip.remove_ipip_tunnel_term(serializedObjectId); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to remove IPinIP tunnel decap term"); + return status; + } + return remove_internal(object_type, serializedObjectId); + } + return remove_internal(object_type, serializedObjectId); } diff --git a/vslib/vpp/SwitchVpp.h b/vslib/vpp/SwitchVpp.h index abed7cf1ea..fb5fe029a2 100644 --- a/vslib/vpp/SwitchVpp.h +++ b/vslib/vpp/SwitchVpp.h @@ -1007,8 +1007,10 @@ namespace saivs std::shared_ptr m_realObjectIdManager; friend class TunnelManagerSRv6; + friend class TunnelManagerIpIp; TunnelManagerSRv6 m_tunnel_mgr_srv6; + TunnelManagerIpIp m_tunnel_mgr_ipip; protected: // switch capability related virtual sai_status_t queryNextHopGroupTypeCapability( diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index 9468baafb9..8f22037e29 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -53,6 +53,23 @@ TunnelManager::set_vxlan_port(const sai_attribute_t* attr) m_vxlan_port = attr->value.u16; } + +sai_status_t +TunnelManager::get_tunnel_if( + _In_ sai_object_id_t nexthop_oid, + _Out_ u_int32_t &sw_if_index) +{ + SWSS_LOG_ENTER(); + + auto it = m_tunnel_encap_nexthop_map.find(nexthop_oid); + if (it != m_tunnel_encap_nexthop_map.end()) { + sw_if_index = it->second.sw_if_index; + return SAI_STATUS_SUCCESS; + } + // Fall through to IPIP encap nexthop map + return m_switch_db->m_tunnel_mgr_ipip.get_tunnel_if(nexthop_oid, sw_if_index); +} + /** * VxLAN tunnel is created in response to the creation of a tunnel encap nexthop entry. This assumes VxLAN tunnel is bidirectional and symmetric. * The local VTEP sends packet through the tunnel to the remote VTEP. The remote VTEP sends packet back to the local VTEP through the same tunnel with the same VNI. @@ -112,6 +129,10 @@ TunnelManager::tunnel_encap_nexthop_action( attr.id = SAI_TUNNEL_ATTR_TYPE; CHECK_STATUS_W_MSG(tunnel_obj->get_attr(attr), "Missing SAI_TUNNEL_ATTR_TYPE in tunnel obj"); + if (attr.value.s32 == SAI_TUNNEL_TYPE_IPINIP) { + return m_switch_db->m_tunnel_mgr_ipip.ipip_encap_nexthop_action(tunnel_nh_obj, tunnel_obj.get(), action); + } + if (attr.value.s32 != SAI_TUNNEL_TYPE_VXLAN) { SWSS_LOG_ERROR("Unsupported tunnel encap type %d in %s", attr.value.s32, tunnel_obj->get_id().c_str()); @@ -740,4 +761,471 @@ TunnelManager::handle_l2_vxlan_tunnel_map_entry_removal( m_l2_tunnel_map.erase(it); return SAI_STATUS_SUCCESS; -} \ No newline at end of file +} + +TunnelManagerIpIp::TunnelManagerIpIp(SwitchVpp *switch_db) : m_switch_db(switch_db) {} + +uint8_t +TunnelManagerIpIp::map_sai_to_vpp_flags(const SaiObject* tunnel_obj) +{ + SWSS_LOG_ENTER(); + + uint8_t vpp_flags = 0; + sai_attribute_t attr; + + // Decap ECN mode + attr.id = SAI_TUNNEL_ATTR_DECAP_ECN_MODE; + if (tunnel_obj->get_attr(attr) == SAI_STATUS_SUCCESS) { + if (attr.value.s32 == SAI_TUNNEL_DECAP_ECN_MODE_COPY_FROM_OUTER) { + vpp_flags |= 0x10; // TUNNEL_API_ENCAP_DECAP_FLAG_DECAP_COPY_ECN + } + } + + // Decap TTL mode (uniform = copy from outer) + attr.id = SAI_TUNNEL_ATTR_DECAP_TTL_MODE; + if (tunnel_obj->get_attr(attr) == SAI_STATUS_SUCCESS) { + if (attr.value.s32 == SAI_TUNNEL_TTL_MODE_UNIFORM_MODEL) { + vpp_flags |= 0x40; // TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_HOP_LIMIT + } + } + + // Decap DSCP mode (uniform = copy from outer) + attr.id = SAI_TUNNEL_ATTR_DECAP_DSCP_MODE; + if (tunnel_obj->get_attr(attr) == SAI_STATUS_SUCCESS) { + if (attr.value.s32 == SAI_TUNNEL_DSCP_MODE_UNIFORM_MODEL) { + vpp_flags |= 0x04; // TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP + } + } + + // Encap TTL mode (uniform = copy from inner) + attr.id = SAI_TUNNEL_ATTR_ENCAP_TTL_MODE; + if (tunnel_obj->get_attr(attr) == SAI_STATUS_SUCCESS) { + if (attr.value.s32 == SAI_TUNNEL_TTL_MODE_UNIFORM_MODEL) { + vpp_flags |= 0x40; // TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_HOP_LIMIT + } + } + + // Encap DSCP mode (uniform = copy from inner) + attr.id = SAI_TUNNEL_ATTR_ENCAP_DSCP_MODE; + if (tunnel_obj->get_attr(attr) == SAI_STATUS_SUCCESS) { + if (attr.value.s32 == SAI_TUNNEL_DSCP_MODE_UNIFORM_MODEL) { + vpp_flags |= 0x04; // TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP + } + } + + // Encap ECN mode (copy_from_outer) + attr.id = SAI_TUNNEL_ATTR_ENCAP_ECN_MODE; + if (tunnel_obj->get_attr(attr) == SAI_STATUS_SUCCESS) { + if (attr.value.s32 == SAI_TUNNEL_ENCAP_ECN_MODE_USER_DEFINED) { + vpp_flags |= 0x08; // TUNNEL_API_ENCAP_DECAP_FLAG_ENCAP_COPY_ECN + } + } + + return vpp_flags; +} + +uint32_t TunnelManagerIpIp::resolve_vrf_id(_In_ sai_object_id_t vr_oid) +{ + if (vr_oid == SAI_NULL_OBJECT_ID) { + return 0; + } + auto vrf = m_switch_db->vpp_get_ip_vrf(vr_oid); + if (vrf) { + SWSS_LOG_NOTICE("IpIp: VR %s -> VRF %u", + sai_serialize_object_id(vr_oid).c_str(), vrf->m_vrf_id); + return vrf->m_vrf_id; + } + SWSS_LOG_WARN("IpIp: VR %s not found, using default VRF", + sai_serialize_object_id(vr_oid).c_str()); + return 0; +} + +uint32_t TunnelManagerIpIp::resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid) +{ + auto rif_obj = m_switch_db->get_sai_object(SAI_OBJECT_TYPE_ROUTER_INTERFACE, + sai_serialize_object_id(rif_oid)); + if (rif_obj) { + sai_attribute_t rif_attr; + rif_attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; + if (rif_obj->get_attr(rif_attr) == SAI_STATUS_SUCCESS) { + return resolve_vrf_id(rif_attr.value.oid); + } + } + SWSS_LOG_WARN("IpIp: could not resolve VRF from RIF %s", + sai_serialize_object_id(rif_oid).c_str()); + return 0; +} + +/* + * Shared helpers for IPIP tunnel create/remove. + * + * create_ipip_vpp_tunnel: + * 1. vpp_ipip_tunnel_add + * 2. refresh_interfaces_list + interface_set_state UP + * 3. set_interface_vrf (overlay VRF assignment) + * 4. vpp_sw_interface_find_by_ip + sw_interface_set_unnumbered + * Cleans up (deletes tunnel) on partial failure. + * + * remove_ipip_vpp_tunnel: + * 1. interface_set_state DOWN + * 2. vpp_ipip_tunnel_del + */ +sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( + _Inout_ vpp_ipip_tunnel_t &req, + _In_ uint32_t vrf_id, + _Out_ uint32_t &sw_if_index) +{ + SWSS_LOG_ENTER(); + + sw_if_index = 0; + + // Create the IPIP tunnel + int ret = vpp_ipip_tunnel_add(&req, &sw_if_index); + if (ret < 0) { + SWSS_LOG_ERROR("IpIp: vpp_ipip_tunnel_add failed: ret=%d", ret); + return SAI_STATUS_FAILURE; + } + + // Set the tunnel interface up + refresh_interfaces_list(); + const char *ifname = vpp_get_swif_name(sw_if_index); + if (!ifname) { + SWSS_LOG_ERROR("IpIp: could not get interface name for sw_if_index=%u", sw_if_index); + vpp_ipip_tunnel_del(sw_if_index); + return SAI_STATUS_FAILURE; + } + + ret = interface_set_state(ifname, true); + if (ret < 0) { + SWSS_LOG_ERROR("IpIp: failed to set interface up for %s ret=%d", ifname, ret); + vpp_ipip_tunnel_del(sw_if_index); + return SAI_STATUS_FAILURE; + } + SWSS_LOG_NOTICE("IpIp: tunnel %s (sw_if=%u) set UP", ifname, sw_if_index); + + // Assign the tunnel interface to the overlay vrf + bool is_ipv6 = (req.src_address.sa_family == AF_INET6); + ret = set_interface_vrf(ifname, 0, vrf_id, is_ipv6); + if (ret < 0) { + SWSS_LOG_ERROR("IpIp: failed to set interface vrf for %s vrf=%u is_ipv6=%d ret=%d", + ifname, vrf_id, is_ipv6, ret); + return SAI_STATUS_FAILURE; + } + + // Set unnumbered — borrow IP from the interface that owns req.src_address + uint32_t owner_sw_if_index = 0; + if (vpp_sw_interface_find_by_ip(&req.src_address, vrf_id, &owner_sw_if_index) == 0) { + ret = sw_interface_set_unnumbered(sw_if_index, owner_sw_if_index, true); + if (ret < 0) { + SWSS_LOG_ERROR("IpIp: failed to set interface unnumbered sw_if=%u use sw_if=%u ret=%d", + sw_if_index, owner_sw_if_index, ret); + return SAI_STATUS_FAILURE; + } else { + const char *owner_ifname = vpp_get_swif_name(owner_sw_if_index); + SWSS_LOG_NOTICE("IpIp: sw_if=%u set unnumbered using %s (sw_if=%u)", + sw_if_index, owner_ifname ? owner_ifname : "?", owner_sw_if_index); + } + } + else { + char ip_str[INET6_ADDRSTRLEN]; + vpp_ip_addr_t_to_string(&req.src_address, ip_str, sizeof(ip_str)); + SWSS_LOG_WARN("IpIp: No interface found for IP %s in vrf %u, unnumbered not set", + ip_str, vrf_id); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t TunnelManagerIpIp::remove_ipip_vpp_tunnel(_In_ uint32_t sw_if_index) +{ + SWSS_LOG_ENTER(); + + const char *ifname = vpp_get_swif_name(sw_if_index); + if (ifname) { + interface_set_state(ifname, false); + } + + int ret = vpp_ipip_tunnel_del(sw_if_index); + if (ret < 0) { + SWSS_LOG_ERROR("IpIp: vpp_ipip_tunnel_del failed for sw_if=%u: ret=%d", sw_if_index, ret); + return SAI_STATUS_FAILURE; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t TunnelManagerIpIp::create_ipip_tunnel_term( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t term_oid; + sai_deserialize_object_id(serializedObjectId, term_oid); + + // Parse attributes from the tunnel term table entry + sai_ip_address_t dst_ip; // our local IP (packet destination = tunnel src) + sai_ip_address_t src_ip; // remote peer IP (packet source = tunnel dst) + sai_object_id_t tunnel_oid = SAI_NULL_OBJECT_ID; + sai_object_id_t vr_oid = SAI_NULL_OBJECT_ID; + int32_t term_type = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; + bool has_src_ip = false; + + memset(&dst_ip, 0, sizeof(dst_ip)); + memset(&src_ip, 0, sizeof(src_ip)); + + for (uint32_t i = 0; i < attr_count; i++) { + switch (attr_list[i].id) { + case SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP: + dst_ip = attr_list[i].value.ipaddr; + break; + case SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP: + src_ip = attr_list[i].value.ipaddr; + has_src_ip = true; + break; + case SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID: + tunnel_oid = attr_list[i].value.oid; + break; + case SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE: + term_type = attr_list[i].value.s32; + break; + case SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID: + vr_oid = attr_list[i].value.oid; + break; + default: + break; + } + } + + if (tunnel_oid == SAI_NULL_OBJECT_ID) { + SWSS_LOG_ERROR("IpIp: missing tunnel id in tunnel term %s", serializedObjectId.c_str()); + return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; + } + + // Look up the referenced tunnel object from SaiObjectDB to read TTL/DSCP/ECN modes + auto tunnel_db_obj = m_switch_db->get_sai_object(SAI_OBJECT_TYPE_TUNNEL, + sai_serialize_object_id(tunnel_oid)); + if (!tunnel_db_obj) { + SWSS_LOG_ERROR("IpIp: tunnel object %s not found in object DB", + sai_serialize_object_id(tunnel_oid).c_str()); + return SAI_STATUS_ITEM_NOT_FOUND; + } + + // Map sai_tunnel_term_table_entry_type_t -> VPP IPIP tunnel mode + uint8_t vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_MP; + switch (term_type) { + case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P: + vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_P2P; + break; + case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP: + vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_MP; + break; + case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_MP2P: + case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_MP2MP: + default: + SWSS_LOG_ERROR("IpIp Tunnel: Unsupported tunnel term type %d", term_type); + return SAI_STATUS_NOT_SUPPORTED; + } + + // Map SAI TTL/DSCP/ECN modes to VPP flags + uint8_t vpp_flags = map_sai_to_vpp_flags(tunnel_db_obj.get()); + + // Build VPP IPIP tunnel request + // SAI term DST_IP = our local IP = VPP tunnel src + // SAI term SRC_IP = remote peer = VPP tunnel dst (0.0.0.0 for P2MP) + vpp_ipip_tunnel_t req; + memset(&req, 0, sizeof(req)); + req.instance = ~0; + req.mode = vpp_mode; + req.flags = vpp_flags; + + sai_ip_address_t_to_vpp_ip_addr_t(dst_ip, req.src_address); + if (has_src_ip && vpp_mode == IpIpTunnelVPPData::TUNNEL_API_MODE_P2P) { + sai_ip_address_t_to_vpp_ip_addr_t(src_ip, req.dst_address); + } else { + // P2MP: dst = 0.0.0.0 (already zeroed) + req.dst_address.sa_family = dst_ip.addr_family == SAI_IP_ADDR_FAMILY_IPV4 ? AF_INET : AF_INET6; + } + + // Resolve overlay VRF for the tunnel interface + uint32_t overlay_vrf_id = resolve_vrf_id(vr_oid); + + // Create tunnel, assign VRF, bring up, set unnumbered + uint32_t sw_if_index = 0; + sai_status_t status = create_ipip_vpp_tunnel(req, overlay_vrf_id, sw_if_index); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp: vpp ipip tunnel creation failed for term %s", + serializedObjectId.c_str()); + return status; + } + + // Bookkeeping + IpIpTunnelVPPData data; + data.sw_if_index = sw_if_index; + data.src_ip = dst_ip; // local endpoint + data.dst_ip = src_ip; // remote endpoint + data.mode = static_cast(vpp_mode); + data.flags = vpp_flags; + data.tunnel_oid = tunnel_oid; + data.vrf_id = overlay_vrf_id; + m_ipip_term_map[term_oid] = data; + + SWSS_LOG_NOTICE("IpIp: created tunnel term %s: sw_if=%u mode=%s", + serializedObjectId.c_str(), sw_if_index, + vpp_mode == 0 ? "P2P" : "P2MP"); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t TunnelManagerIpIp::remove_ipip_tunnel_term( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t term_oid; + sai_deserialize_object_id(serializedObjectId, term_oid); + + auto it = m_ipip_term_map.find(term_oid); + if (it == m_ipip_term_map.end()) { + SWSS_LOG_WARN("IPIP: tunnel term %s not found in map, skipping", + serializedObjectId.c_str()); + return SAI_STATUS_SUCCESS; + } + + uint32_t sw_if_index = it->second.sw_if_index; + sai_status_t status = remove_ipip_vpp_tunnel(sw_if_index); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp: remove_ipip_vpp_tunnel failed for term %s sw_if=%u", + serializedObjectId.c_str(), sw_if_index); + return status; + } + + SWSS_LOG_NOTICE("IpIp: removed IPIP tunnel term %s (sw_if=%u)", + serializedObjectId.c_str(), sw_if_index); + + m_ipip_term_map.erase(it); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t TunnelManagerIpIp::ipip_encap_nexthop_action( + _In_ const SaiObject *tunnel_nh_obj, + _In_ const SaiObject *tunnel_obj, + _In_ Action action) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t nh_oid; + sai_deserialize_object_id(tunnel_nh_obj->get_id(), nh_oid); + + sai_attribute_t attr; + + // Verify the tunnel type is IPINIP + attr.id = SAI_TUNNEL_ATTR_TYPE; + if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp Encap: missing SAI_TUNNEL_ATTR_TYPE in tunnel %s", + tunnel_obj->get_id().c_str()); + return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; + } + if (attr.value.s32 != SAI_TUNNEL_TYPE_IPINIP) { + SWSS_LOG_ERROR("IpIp Encap: tunnel %s type %d is not IPINIP", + tunnel_obj->get_id().c_str(), attr.value.s32); + return SAI_STATUS_NOT_SUPPORTED; + } + + // Get ENCAP_SRC_IP from the tunnel object (our local endpoint) + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp Encap: missing SAI_TUNNEL_ATTR_ENCAP_SRC_IP in tunnel %s", + tunnel_obj->get_id().c_str()); + return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; + } + sai_ip_address_t src_ip = attr.value.ipaddr; + + // Get the destination IP from the nexthop object (remote peer) + attr.id = SAI_NEXT_HOP_ATTR_IP; + if (tunnel_nh_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp Encap: missing SAI_NEXT_HOP_ATTR_IP in nexthop %s", + tunnel_nh_obj->get_id().c_str()); + return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; + } + sai_ip_address_t dst_ip = attr.value.ipaddr; + + // Get the tunnel overlay interface + attr.id = SAI_TUNNEL_ATTR_OVERLAY_INTERFACE; + if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp Encap: missing SAI_TUNNEL_ATTR_OVERLAY_INTERFACE in tunnel %s", + tunnel_obj->get_id().c_str()); + return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; + } + sai_object_id_t tunnel_overlay_if_oid = attr.value.oid; + + if (action == Action::CREATE) { + uint8_t vpp_flags = map_sai_to_vpp_flags(tunnel_obj); + + vpp_ipip_tunnel_t req; + memset(&req, 0, sizeof(req)); + req.instance = ~0; + req.mode = IpIpTunnelVPPData::TUNNEL_API_MODE_P2P; + req.flags = vpp_flags; + + sai_ip_address_t_to_vpp_ip_addr_t(src_ip, req.src_address); + sai_ip_address_t_to_vpp_ip_addr_t(dst_ip, req.dst_address); + + uint32_t sw_if_index = 0; + // Resolve VRF from the overlay RIF's virtual router + uint32_t vrf_id = resolve_vrf_from_rif(tunnel_overlay_if_oid); + sai_status_t status = create_ipip_vpp_tunnel(req, vrf_id, sw_if_index); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp Encap: create_ipip_vpp_tunnel failed for NH %s", + tunnel_nh_obj->get_id().c_str()); + return status; + } + + // Bookkeeping + IpIpTunnelVPPData data; + data.sw_if_index = sw_if_index; + data.src_ip = src_ip; + data.dst_ip = dst_ip; + data.mode = IpIpTunnelVPPData::TUNNEL_API_MODE_P2P; + data.flags = vpp_flags; + data.vrf_id = vrf_id; + + sai_object_id_t tunnel_oid; + sai_deserialize_object_id(tunnel_obj->get_id(), tunnel_oid); + data.tunnel_oid = tunnel_oid; + + m_ipip_encap_nh_map[nh_oid] = data; + + char src_str[INET6_ADDRSTRLEN], dst_str[INET6_ADDRSTRLEN]; + vpp_ip_addr_t_to_string(&req.src_address, src_str, sizeof(src_str)); + vpp_ip_addr_t_to_string(&req.dst_address, dst_str, sizeof(dst_str)); + SWSS_LOG_NOTICE("IpIp Encap: created P2P tunnel NH %s: src=%s dst=%s sw_if=%u", + tunnel_nh_obj->get_id().c_str(), src_str, dst_str, sw_if_index); + + } else if (action == Action::DELETE) { + auto it = m_ipip_encap_nh_map.find(nh_oid); + if (it == m_ipip_encap_nh_map.end()) { + SWSS_LOG_WARN("IpIp Encap: ipip tunnel encap nexthop %s not found, skipping", + tunnel_nh_obj->get_id().c_str()); + return SAI_STATUS_SUCCESS; + } + + uint32_t sw_if_index = it->second.sw_if_index; + sai_status_t status = remove_ipip_vpp_tunnel(sw_if_index); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("IpIp Encap: remove_ipip_vpp_tunnel failed for nexthop %s sw_if=%u", + tunnel_nh_obj->get_id().c_str(), sw_if_index); + return status; + } + + SWSS_LOG_NOTICE("IpIp Encap: removed ipip tunnel encap nexthop %s (sw_if=%u)", + tunnel_nh_obj->get_id().c_str(), sw_if_index); + + m_ipip_encap_nh_map.erase(it); + } + + return SAI_STATUS_SUCCESS; +} diff --git a/vslib/vpp/TunnelManager.h b/vslib/vpp/TunnelManager.h index d0429be588..132cd29928 100644 --- a/vslib/vpp/TunnelManager.h +++ b/vslib/vpp/TunnelManager.h @@ -38,6 +38,35 @@ namespace saivs sai_ip_address_t dst_ip; }; + /** + * @brief VPP data associated with an IPIP tunnel. + */ + class IpIpTunnelVPPData + { + public: + enum tunnel_mode : u_int8_t + { + /** point-to-point */ + TUNNEL_API_MODE_P2P = 0, + /** multi-point */ + TUNNEL_API_MODE_MP, + }; + + IpIpTunnelVPPData() + { + SWSS_LOG_ENTER(); + memset(&src_ip, 0, sizeof(src_ip)); + memset(&dst_ip, 0, sizeof(dst_ip)); + } + u_int32_t sw_if_index = 0; + sai_ip_address_t src_ip; // outer src (local endpoint) + sai_ip_address_t dst_ip; // outer dst (remote endpoint; 0.0.0.0 for P2MP) + tunnel_mode mode = TUNNEL_API_MODE_MP; // IPIP tunnel mode + u_int8_t flags = 0; // tunnel_encap_decap_flags + sai_object_id_t tunnel_oid = SAI_NULL_OBJECT_ID; // referenced SAI tunnel object + uint32_t vrf_id = 0; // vrf + }; + class TunnelManager { public: TunnelManager(SwitchVpp* switch_db); @@ -88,17 +117,7 @@ namespace saivs */ sai_status_t get_tunnel_if( _In_ sai_object_id_t nexthop_oid, - _Out_ u_int32_t &sw_if_index) - { - SWSS_LOG_ENTER(); - - auto it = m_tunnel_encap_nexthop_map.find(nexthop_oid); - if (it != m_tunnel_encap_nexthop_map.end()) { - sw_if_index = it->second.sw_if_index; - return SAI_STATUS_SUCCESS; - } - return SAI_STATUS_ITEM_NOT_FOUND; - } + _Out_ u_int32_t &sw_if_index); /** * @brief Set VxLAN router default MAC address. */ @@ -354,4 +373,124 @@ namespace saivs _In_ sai_object_id_t nexthop_oid, _Out_ vpp_ip_addr_t &bsid); }; + + class TunnelManagerIpIp + { + public: + TunnelManagerIpIp(SwitchVpp *switch_db); + ~TunnelManagerIpIp() = default; + + /** + * @brief Create an IPIP encap tunnel triggered by a TUNNEL_ENCAP nexthop. + * + * Called from TunnelManager::tunnel_encap_nexthop_action() when the tunnel type is IPINIP. + * + * @param tunnel_nh_obj The nexthop SAI object. + * @param tunnel_obj The referenced SAI tunnel object. + * @param action CREATE or DELETE. + * @return SAI_STATUS_SUCCESS on success, error status on failure. + */ + sai_status_t ipip_encap_nexthop_action( + _In_ const SaiObject *tunnel_nh_obj, + _In_ const SaiObject *tunnel_obj, + _In_ Action action); + + /** + * @brief Get the IPIP tunnel interface index for a given nexthop OID. + */ + sai_status_t get_tunnel_if( + _In_ sai_object_id_t nexthop_oid, + _Out_ u_int32_t &sw_if_index) + { + SWSS_LOG_ENTER(); + + auto it = m_ipip_encap_nh_map.find(nexthop_oid); + if (it != m_ipip_encap_nh_map.end()) + { + sw_if_index = it->second.sw_if_index; + return SAI_STATUS_SUCCESS; + } + return SAI_STATUS_ITEM_NOT_FOUND; + } + + /** + * @brief Create an IPIP decap tunnel triggered by a tunnel term table entry. + * + * Reads DST_IP, SRC_IP, TYPE from the term entry and TTL/DSCP/ECN modes + * from the referenced tunnel object. + * + * @param serializedObjectId The serialized tunnel term table entry ID. + * @param switch_id The switch ID. + * @param attr_count Number of attributes. + * @param attr_list Attribute list. + * @return SAI_STATUS_SUCCESS on success, error status on failure. + */ + sai_status_t create_ipip_tunnel_term( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + /** + * @brief Remove an IPIP decap tunnel. + * + * @param serializedObjectId The serialized tunnel term table entry ID. + * @return SAI_STATUS_SUCCESS on success, error status on failure. + */ + sai_status_t remove_ipip_tunnel_term( + _In_ const std::string &serializedObjectId); + + private: + /** + * @brief Map SAI tunnel TTL/DSCP/ECN modes to VPP tunnel_encap_decap_flags. + */ + uint8_t map_sai_to_vpp_flags(const SaiObject *tunnel_obj); + + /** + * @brief Resolve a VR OID to a VPP VRF table ID. + * @return VRF ID, or 0 if not found / NULL OID. + */ + uint32_t resolve_vrf_id(_In_ sai_object_id_t vr_oid); + + /** + * @brief Resolve a RIF OID to a VPP VRF table ID via its virtual router. + * @return VRF ID, or 0 if not found. + */ + uint32_t resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid); + + /** + * @brief Create a VPP IPIP tunnel, assign VRF, bring it up, and set unnumbered. + * + * Shared by both decap (tunnel_term) and encap (nexthop) paths. + * Sequence: tunnel_add → UP → set_interface_vrf → unnumbered. + * + * @param req VPP IPIP tunnel request (src, dst, mode, flags filled in by caller). + * req.src_address is also used for the unnumbered IP search. + * @param vrf_id VRF to assign the tunnel interface to and scope the unnumbered IP search. + * @param sw_if_index Output: VPP sw_if_index of the created tunnel. + * @return SAI_STATUS_SUCCESS on success, error on failure (tunnel cleaned up). + */ + sai_status_t create_ipip_vpp_tunnel( + _Inout_ vpp_ipip_tunnel_t &req, + _In_ uint32_t vrf_id, + _Out_ uint32_t &sw_if_index); + + /** + * @brief Tear down and delete a VPP IPIP tunnel. + * + * Sets the interface down and calls vpp_ipip_tunnel_del. + * + * @param sw_if_index VPP sw_if_index of the tunnel to remove. + * @return SAI_STATUS_SUCCESS on success, error on failure. + */ + sai_status_t remove_ipip_vpp_tunnel(_In_ uint32_t sw_if_index); + + private: + SwitchVpp *m_switch_db; + + // Encap: nexthop OID → IPIP tunnel data + std::unordered_map m_ipip_encap_nh_map; + // Decap: tunnel term OID → IPIP tunnel data + std::unordered_map m_ipip_term_map; + }; } \ No newline at end of file diff --git a/vslib/vpp/vppxlate/SaiVppXlate.c b/vslib/vpp/vppxlate/SaiVppXlate.c index b37fca0ee5..a1998f429c 100644 --- a/vslib/vpp/vppxlate/SaiVppXlate.c +++ b/vslib/vpp/vppxlate/SaiVppXlate.c @@ -71,6 +71,9 @@ #include #include +#include +#include + /* l2 API inclusion */ #define vl_typedefs @@ -280,6 +283,23 @@ #include #undef vl_api_version +/* ipip API inclusion */ +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 ipip_api_version = v; +#include +#undef vl_api_version + /* memclnt API inclusion */ #define vl_typedefs /* define message structures */ @@ -774,6 +794,21 @@ vl_api_sw_interface_set_table_reply_t_handler (vl_api_sw_interface_set_table_rep set_reply_status(retval); } +static void +vl_api_sw_interface_get_table_reply_t_handler (vl_api_sw_interface_get_table_reply_t *msg) +{ + int retval = (int)ntohl((uint32_t)msg->retval); + set_reply_status(retval); + + if (msg->context) { + uint32_t *vrf_id = (uint32_t *) get_index_ptr(msg->context); + *vrf_id = ntohl(msg->vrf_id); + } + + SAIVPP_DEBUG("sw interface get table %s(%d) vrf_id=%u", + retval ? "failed" : "successful", retval, ntohl(msg->vrf_id)); +} + static void vl_api_sw_interface_add_del_address_reply_t_handler (vl_api_sw_interface_add_del_address_reply_t *msg) { @@ -800,6 +835,15 @@ vl_api_sw_interface_set_mac_address_reply_t_handler (vl_api_sw_interface_set_mac int retval = (int)ntohl((uint32_t)msg->retval); set_reply_status(retval); } + +static void +vl_api_sw_interface_set_unnumbered_reply_t_handler (vl_api_sw_interface_set_unnumbered_reply_t *msg) +{ + int retval = (int)ntohl((uint32_t)msg->retval); + set_reply_status(retval); + + SAIVPP_DEBUG("sw interface unnumbered set %s(%d)", retval ? "failed" : "successful", retval); +} static void vl_api_hw_interface_set_mtu_reply_t_handler (vl_api_hw_interface_set_mtu_reply_t *msg) { @@ -998,6 +1042,61 @@ vl_api_vxlan_add_del_tunnel_v3_reply_t_handler ( set_reply_status(retval); } +static void +vl_api_ipip_add_tunnel_reply_t_handler(vl_api_ipip_add_tunnel_reply_t *msg) +{ + set_reply_sw_if_index(ntohl(msg->sw_if_index)); + + int retval = (int)ntohl((uint32_t)msg->retval); + set_reply_status(retval); + SAIVPP_DEBUG("ipip_add_tunnel handler: if_idx,%d,status,%d",vam->sw_if_index, vam->retval); +} + +static void +vl_api_ipip_del_tunnel_reply_t_handler(vl_api_ipip_del_tunnel_reply_t *msg) +{ + int retval = (int)ntohl((uint32_t)msg->retval); + set_reply_status(retval); + SAIVPP_DEBUG("ipip_del_tunnel handler: status,%d", vam->retval); +} + +/* + * ip_address_dump result accumulator. + * The details handler is called once per address in the W() loop. + * We pass a pointer to this struct via context. + */ +typedef struct { + uint32_t target_sw_if_index; /* OUT: sw_if_index that owns the IP, ~0 if not found */ + vpp_ip_addr_t search_ip; /* IN: the IP we are looking for */ +} ip_addr_dump_ctx_t; + +static void +vl_api_ip_address_details_t_handler(vl_api_ip_address_details_t *mp) +{ + if (!mp->context) + return; + + ip_addr_dump_ctx_t *ctx = (ip_addr_dump_ctx_t *) get_index_ptr(mp->context); + if (!ctx) + return; + + /* Already found? Skip further processing */ + if (ctx->target_sw_if_index != (uint32_t)~0) + return; + + /* Extract the address from the reply */ + vl_api_address_t *addr = &mp->prefix.address; + if (ctx->search_ip.sa_family == AF_INET && addr->af == ADDRESS_IP4) { + if (memcmp(&addr->un.ip4, &ctx->search_ip.addr.ip4.sin_addr, 4) == 0) { + ctx->target_sw_if_index = ntohl(mp->sw_if_index); + } + } else if (ctx->search_ip.sa_family == AF_INET6 && addr->af == ADDRESS_IP6) { + if (memcmp(&addr->un.ip6, &ctx->search_ip.addr.ip6.sin6_addr, 16) == 0) { + ctx->target_sw_if_index = ntohl(mp->sw_if_index); + } + } +} + static void vl_api_tunterm_acl_add_replace_reply_t_handler(vl_api_tunterm_acl_add_replace_reply_t *msg) { @@ -1102,7 +1201,7 @@ vl_api_sr_set_encap_source_reply_t_handler(vl_api_sr_set_encap_source_reply_t *m _(MEMCLNT_MSG_ID(CONTROL_PING_REPLY), control_ping_reply) static u16 interface_msg_id_base, memclnt_msg_id_base, __plugin_msg_base; -static u16 l2_msg_id_base, vxlan_msg_id_base; +static u16 l2_msg_id_base, vxlan_msg_id_base, ipip_msg_id_base; static u16 tunterm_msg_id_base; static u16 bfd_msg_id_base; static u16 sr_msg_id_base; @@ -1150,10 +1249,12 @@ static void vpp_base_vpe_init(void) _(INTERFACE_MSG_ID(CREATE_SUBIF_REPLY), create_subif_reply) \ _(INTERFACE_MSG_ID(DELETE_SUBIF_REPLY), delete_subif_reply) \ _(INTERFACE_MSG_ID(SW_INTERFACE_SET_TABLE_REPLY), sw_interface_set_table_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_GET_TABLE_REPLY), sw_interface_get_table_reply) \ _(INTERFACE_MSG_ID(SW_INTERFACE_ADD_DEL_ADDRESS_REPLY), sw_interface_add_del_address_reply) \ _(INTERFACE_MSG_ID(SW_INTERFACE_SET_FLAGS_REPLY), sw_interface_set_flags_reply) \ _(INTERFACE_MSG_ID(SW_INTERFACE_SET_MTU_REPLY), sw_interface_set_mtu_reply) \ _(INTERFACE_MSG_ID(SW_INTERFACE_SET_MAC_ADDRESS_REPLY), sw_interface_set_mac_address_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_SET_UNNUMBERED_REPLY), sw_interface_set_unnumbered_reply) \ _(INTERFACE_MSG_ID(HW_INTERFACE_SET_MTU_REPLY), hw_interface_set_mtu_reply) \ _(INTERFACE_MSG_ID(WANT_INTERFACE_EVENTS_REPLY), want_interface_events_reply) \ _(INTERFACE_MSG_ID(SW_INTERFACE_EVENT), sw_interface_event) \ @@ -1161,6 +1262,7 @@ static void vpp_base_vpe_init(void) _(IP_MSG_ID(IP_ROUTE_ADD_DEL_REPLY), ip_route_add_del_reply) \ _(IP_MSG_ID(SW_INTERFACE_IP6_ENABLE_DISABLE_REPLY), sw_interface_ip6_enable_disable_reply) \ _(IP_MSG_ID(SET_IP_FLOW_HASH_V2_REPLY), set_ip_flow_hash_v2_reply) \ + _(IP_MSG_ID(IP_ADDRESS_DETAILS), ip_address_details) \ _(IP_NBR_MSG_ID(IP_NEIGHBOR_ADD_DEL_REPLY), ip_neighbor_add_del_reply) \ _(L2_MSG_ID(BRIDGE_DOMAIN_ADD_DEL_REPLY), bridge_domain_add_del_reply) \ _(L2_MSG_ID(SW_INTERFACE_SET_L2_BRIDGE_REPLY), sw_interface_set_l2_bridge_reply) \ @@ -1259,6 +1361,9 @@ vl_api_acl_interface_add_del_reply_t_handler(vl_api_acl_interface_add_del_reply_ #define VXLAN_MSG_ID(id) \ (VL_API_##id + vxlan_msg_id_base) +#define IPIP_MSG_ID(id) \ + (VL_API_##id + ipip_msg_id_base) + #define SR_MSG_ID(id) \ (VL_API_##id + sr_msg_id_base) @@ -1277,7 +1382,9 @@ vl_api_acl_interface_add_del_reply_t_handler(vl_api_acl_interface_add_del_reply_ _(SR_MSG_ID(SR_POLICY_ADD_V2_REPLY), sr_policy_add_v2_reply) \ _(SR_MSG_ID(SR_POLICY_DEL_REPLY), sr_policy_del_reply) \ _(SR_MSG_ID(SR_STEERING_ADD_DEL_REPLY), sr_steering_add_del_reply) \ - _(SR_MSG_ID(SR_SET_ENCAP_SOURCE_REPLY), sr_set_encap_source_reply) + _(SR_MSG_ID(SR_SET_ENCAP_SOURCE_REPLY), sr_set_encap_source_reply) \ + _(IPIP_MSG_ID(IPIP_ADD_TUNNEL_REPLY), ipip_add_tunnel_reply) \ + _(IPIP_MSG_ID(IPIP_DEL_TUNNEL_REPLY), ipip_del_tunnel_reply) static void vpp_plugin_vpe_init(void) { @@ -1342,6 +1449,10 @@ static void get_base_msg_id() vxlan_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); assert(vxlan_msg_id_base != (u16) ~0); + msg_base_lookup_name = format (0, "ipip_%08x%c", ipip_api_version, 0); + ipip_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(ipip_msg_id_base != (u16) ~0); + msg_base_lookup_name = format (0, "tunterm_acl_%08x%c", tunterm_api_version, 0); tunterm_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); assert(tunterm_msg_id_base != (u16) ~0); @@ -4125,3 +4236,179 @@ int vpp_sr_set_encap_source(vpp_ip_addr_t *encap_src) return ret; } + +int vpp_ipip_tunnel_add(vpp_ipip_tunnel_t *tunnel, uint32_t *sw_if_index) +{ + int ret; + vat_main_t *vam = &vat_main; + vl_api_ipip_add_tunnel_t *mp; + + VPP_LOCK(); + + __plugin_msg_base = ipip_msg_id_base; + + M (IPIP_ADD_TUNNEL, mp); + + mp->tunnel.instance = htonl(tunnel->instance); + mp->tunnel.mode = tunnel->mode; + mp->tunnel.table_id = htonl(tunnel->table_id); + mp->tunnel.flags = tunnel->flags; + mp->tunnel.dscp = tunnel->dscp; + + if (!vpp_to_vl_api_ip_addr(&mp->tunnel.src, &tunnel->src_address)) { + SAIVPP_ERROR("Unknown protocol in src address"); + VPP_UNLOCK(); + return -EINVAL; + } + + if (!vpp_to_vl_api_ip_addr(&mp->tunnel.dst, &tunnel->dst_address)) { + SAIVPP_ERROR("Unknown protocol in dst address"); + VPP_UNLOCK(); + return -EINVAL; + } + + S (mp); + + WR (ret); + + // vam->sw_if_index is set in the reply handler for this message + *sw_if_index = vam->sw_if_index; + + SAIVPP_DEBUG("ipip_add done: if_idx,%d",vam->sw_if_index); + VPP_UNLOCK(); + return ret; +} + +int vpp_ipip_tunnel_del(uint32_t sw_if_index) +{ + int ret; + vat_main_t *vam = &vat_main; + vl_api_ipip_del_tunnel_t *mp; + + VPP_LOCK(); + + __plugin_msg_base = ipip_msg_id_base; + + M (IPIP_DEL_TUNNEL, mp); + + mp->sw_if_index = htonl(sw_if_index); + S (mp); + + WR (ret); + + SAIVPP_DEBUG("ipip_del done: if_idx,%d",vam->sw_if_index); + VPP_UNLOCK(); + return ret; +} + +int sw_interface_set_unnumbered(uint32_t unnumbered_sw_if_index, + uint32_t ip_sw_if_index, bool is_add) +{ + int ret; + vat_main_t *vam = &vat_main; + vl_api_sw_interface_set_unnumbered_t *mp; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_SET_UNNUMBERED, mp); + + mp->sw_if_index = htonl(ip_sw_if_index); + mp->unnumbered_sw_if_index = htonl(unnumbered_sw_if_index); + mp->is_add = is_add; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + return ret; +} + +static int __sw_interface_get_table(uint32_t sw_if_index, bool is_ipv6, uint32_t *out_table_id) +{ + int ret; + vat_main_t *vam = &vat_main; + vl_api_sw_interface_get_table_t *mp; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_GET_TABLE, mp); + mp->sw_if_index = htonl(sw_if_index); + mp->is_ipv6 = is_ipv6; + mp->context = store_ptr(out_table_id); + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + return ret; +} + +int vpp_sw_interface_find_by_ip(vpp_ip_addr_t *search_ip, uint32_t vrf_id, + uint32_t *out_sw_if_index) +{ + int ret; + vat_main_t *vam = &vat_main; + + if (!search_ip || !out_sw_if_index) + return -EINVAL; + + bool is_ipv6 = (search_ip->sa_family == AF_INET6); + + // Iterates all known sw intfs to collect addresses. + u32 *sw_if_idxs = NULL; + hash_pair_t *p; + hash_foreach_pair(p, interface_name_by_sw_index, ({ + vec_add1(sw_if_idxs, (u32) p->key); + })); + + for (unsigned int i = 0; i < vec_len(sw_if_idxs); i++) { + /* Pre-filter: skip interfaces not in the target VRF */ + if (vrf_id != (uint32_t)~0) { + uint32_t if_vrf_id = (uint32_t)~0; + if (__sw_interface_get_table(sw_if_idxs[i], is_ipv6, &if_vrf_id) != 0 + || if_vrf_id != vrf_id) + continue; + } + + ip_addr_dump_ctx_t ctx; + ctx.target_sw_if_index = (uint32_t)~0; + ctx.search_ip = *search_ip; + + vl_api_ip_address_dump_t *mp; + vl_api_control_ping_t *mp_ping; + + VPP_LOCK(); + + __plugin_msg_base = ip_msg_id_base; + + M (IP_ADDRESS_DUMP, mp); + mp->sw_if_index = htonl(sw_if_idxs[i]); + mp->is_ipv6 = is_ipv6; + mp->context = store_ptr(&ctx); + + S (mp); + + __plugin_msg_base = memclnt_msg_id_base; + PING (NULL, mp_ping); + S (mp_ping); + + W (ret); + + VPP_UNLOCK(); + + if (ret == 0 && ctx.target_sw_if_index != (uint32_t)~0) { + *out_sw_if_index = ctx.target_sw_if_index; + vec_free(sw_if_idxs); + return 0; + } + } + + vec_free(sw_if_idxs); + return -ENOENT; +} diff --git a/vslib/vpp/vppxlate/SaiVppXlate.h b/vslib/vpp/vppxlate/SaiVppXlate.h index c6d09eda72..fd2946a8ed 100644 --- a/vslib/vpp/vppxlate/SaiVppXlate.h +++ b/vslib/vpp/vppxlate/SaiVppXlate.h @@ -261,6 +261,16 @@ typedef enum { bool is_l3; } vpp_vxlan_tunnel_t; + typedef struct _vpp_ipip_tunnel { + vpp_ip_addr_t src_address; + vpp_ip_addr_t dst_address; + uint32_t table_id; // underlay VRF + uint8_t flags; // tunnel_encap_decap_flags + uint8_t mode; // 0=P2P, 1=MP + uint8_t dscp; // fixed DSCP value + uint32_t instance; // ~0 for auto + } vpp_ipip_tunnel_t; + extern vpp_event_info_t * vpp_ev_dequeue(); extern void vpp_ev_free(vpp_event_info_t *evp); @@ -333,6 +343,13 @@ typedef enum { extern int vpp_sidlist_del(vpp_ip_addr_t *bsid); extern int vpp_sr_steer_add_del(vpp_sr_steer_t *sr_steer, bool is_del); extern int vpp_sr_set_encap_source(vpp_ip_addr_t *encap_src); + extern int vpp_ipip_tunnel_add(vpp_ipip_tunnel_t *tunnel, uint32_t *sw_if_index); + extern int vpp_ipip_tunnel_del(uint32_t sw_if_index); + extern int sw_interface_set_unnumbered(uint32_t unnumbered_sw_if_index, + uint32_t ip_sw_if_index, bool is_add); + extern int vpp_sw_interface_find_by_ip(vpp_ip_addr_t *search_ip, + uint32_t vrf_id, + uint32_t *out_sw_if_index); #ifdef __cplusplus } #endif From 81f8dc9c669d371c2292a1d1656508a58607f1a6 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Mon, 20 Apr 2026 11:42:26 +0000 Subject: [PATCH 02/11] Fix pr checker Signed-off-by: Longxiang Lyu --- vslib/vpp/vppxlate/SaiVppXlate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vslib/vpp/vppxlate/SaiVppXlate.c b/vslib/vpp/vppxlate/SaiVppXlate.c index a1998f429c..6228b9e6a8 100644 --- a/vslib/vpp/vppxlate/SaiVppXlate.c +++ b/vslib/vpp/vppxlate/SaiVppXlate.c @@ -1049,7 +1049,7 @@ vl_api_ipip_add_tunnel_reply_t_handler(vl_api_ipip_add_tunnel_reply_t *msg) int retval = (int)ntohl((uint32_t)msg->retval); set_reply_status(retval); - SAIVPP_DEBUG("ipip_add_tunnel handler: if_idx,%d,status,%d",vam->sw_if_index, vam->retval); + SAIVPP_DEBUG("ipip_add_tunnel handler: if_idx,%d,status,%d", ntohl(msg->sw_if_index), retval); } static void @@ -1057,7 +1057,7 @@ vl_api_ipip_del_tunnel_reply_t_handler(vl_api_ipip_del_tunnel_reply_t *msg) { int retval = (int)ntohl((uint32_t)msg->retval); set_reply_status(retval); - SAIVPP_DEBUG("ipip_del_tunnel handler: status,%d", vam->retval); + SAIVPP_DEBUG("ipip_del_tunnel handler: status,%d", retval); } /* From e19fb6b4752db737ee7004c4c98e21f099018788 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Tue, 21 Apr 2026 13:00:59 +0000 Subject: [PATCH 03/11] improve code Signed-off-by: Longxiang Lyu --- vslib/vpp/TunnelManager.cpp | 21 +++++++++++++-------- vslib/vpp/TunnelManager.h | 27 +++++++++++---------------- vslib/vpp/vppxlate/SaiVppXlate.c | 9 ++++----- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index 8f22037e29..8a323ed677 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -66,6 +66,7 @@ TunnelManager::get_tunnel_if( sw_if_index = it->second.sw_if_index; return SAI_STATUS_SUCCESS; } + // Fall through to IPIP encap nexthop map return m_switch_db->m_tunnel_mgr_ipip.get_tunnel_if(nexthop_oid, sw_if_index); } @@ -831,11 +832,11 @@ uint32_t TunnelManagerIpIp::resolve_vrf_id(_In_ sai_object_id_t vr_oid) } auto vrf = m_switch_db->vpp_get_ip_vrf(vr_oid); if (vrf) { - SWSS_LOG_NOTICE("IpIp: VR %s -> VRF %u", - sai_serialize_object_id(vr_oid).c_str(), vrf->m_vrf_id); + SWSS_LOG_INFO("IpIp: VR %s -> VRF %u", + sai_serialize_object_id(vr_oid).c_str(), vrf->m_vrf_id); return vrf->m_vrf_id; } - SWSS_LOG_WARN("IpIp: VR %s not found, using default VRF", + SWSS_LOG_WARN("IpIp: vr %s not found, using default vrf", sai_serialize_object_id(vr_oid).c_str()); return 0; } @@ -851,7 +852,7 @@ uint32_t TunnelManagerIpIp::resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid) return resolve_vrf_id(rif_attr.value.oid); } } - SWSS_LOG_WARN("IpIp: could not resolve VRF from RIF %s", + SWSS_LOG_WARN("IpIp: could not resolve vrf from rif %s", sai_serialize_object_id(rif_oid).c_str()); return 0; } @@ -909,6 +910,7 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( if (ret < 0) { SWSS_LOG_ERROR("IpIp: failed to set interface vrf for %s vrf=%u is_ipv6=%d ret=%d", ifname, vrf_id, is_ipv6, ret); + vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } @@ -919,6 +921,7 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( if (ret < 0) { SWSS_LOG_ERROR("IpIp: failed to set interface unnumbered sw_if=%u use sw_if=%u ret=%d", sw_if_index, owner_sw_if_index, ret); + vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } else { const char *owner_ifname = vpp_get_swif_name(owner_sw_if_index); @@ -929,8 +932,10 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( else { char ip_str[INET6_ADDRSTRLEN]; vpp_ip_addr_t_to_string(&req.src_address, ip_str, sizeof(ip_str)); - SWSS_LOG_WARN("IpIp: No interface found for IP %s in vrf %u, unnumbered not set", - ip_str, vrf_id); + SWSS_LOG_ERROR("IpIp: No interface found for IP %s in vrf %u, unnumbered not set", + ip_str, vrf_id); + vpp_ipip_tunnel_del(sw_if_index); + return SAI_STATUS_FAILURE; } return SAI_STATUS_SUCCESS; @@ -1179,7 +1184,7 @@ sai_status_t TunnelManagerIpIp::ipip_encap_nexthop_action( uint32_t vrf_id = resolve_vrf_from_rif(tunnel_overlay_if_oid); sai_status_t status = create_ipip_vpp_tunnel(req, vrf_id, sw_if_index); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("IpIp Encap: create_ipip_vpp_tunnel failed for NH %s", + SWSS_LOG_ERROR("IpIp Encap: create_ipip_vpp_tunnel failed for nexthop %s", tunnel_nh_obj->get_id().c_str()); return status; } @@ -1202,7 +1207,7 @@ sai_status_t TunnelManagerIpIp::ipip_encap_nexthop_action( char src_str[INET6_ADDRSTRLEN], dst_str[INET6_ADDRSTRLEN]; vpp_ip_addr_t_to_string(&req.src_address, src_str, sizeof(src_str)); vpp_ip_addr_t_to_string(&req.dst_address, dst_str, sizeof(dst_str)); - SWSS_LOG_NOTICE("IpIp Encap: created P2P tunnel NH %s: src=%s dst=%s sw_if=%u", + SWSS_LOG_NOTICE("IpIp Encap: created P2P tunnel nexthop %s: src=%s dst=%s sw_if=%u", tunnel_nh_obj->get_id().c_str(), src_str, dst_str, sw_if_index); } else if (action == Action::DELETE) { diff --git a/vslib/vpp/TunnelManager.h b/vslib/vpp/TunnelManager.h index 132cd29928..806cb2abc5 100644 --- a/vslib/vpp/TunnelManager.h +++ b/vslib/vpp/TunnelManager.h @@ -41,23 +41,21 @@ namespace saivs /** * @brief VPP data associated with an IPIP tunnel. */ - class IpIpTunnelVPPData - { + class IpIpTunnelVPPData { public: - enum tunnel_mode : u_int8_t - { + enum tunnel_mode : u_int8_t { /** point-to-point */ TUNNEL_API_MODE_P2P = 0, /** multi-point */ TUNNEL_API_MODE_MP, }; - IpIpTunnelVPPData() - { + IpIpTunnelVPPData() { SWSS_LOG_ENTER(); memset(&src_ip, 0, sizeof(src_ip)); memset(&dst_ip, 0, sizeof(dst_ip)); } + u_int32_t sw_if_index = 0; sai_ip_address_t src_ip; // outer src (local endpoint) sai_ip_address_t dst_ip; // outer dst (remote endpoint; 0.0.0.0 for P2MP) @@ -374,8 +372,7 @@ namespace saivs _Out_ vpp_ip_addr_t &bsid); }; - class TunnelManagerIpIp - { + class TunnelManagerIpIp { public: TunnelManagerIpIp(SwitchVpp *switch_db); ~TunnelManagerIpIp() = default; @@ -400,13 +397,11 @@ namespace saivs */ sai_status_t get_tunnel_if( _In_ sai_object_id_t nexthop_oid, - _Out_ u_int32_t &sw_if_index) - { + _Out_ u_int32_t &sw_if_index) { SWSS_LOG_ENTER(); auto it = m_ipip_encap_nh_map.find(nexthop_oid); - if (it != m_ipip_encap_nh_map.end()) - { + if (it != m_ipip_encap_nh_map.end()) { sw_if_index = it->second.sw_if_index; return SAI_STATUS_SUCCESS; } @@ -414,12 +409,12 @@ namespace saivs } /** - * @brief Create an IPIP decap tunnel triggered by a tunnel term table entry. + * @brief Create an IPIP decap tunnel triggered by a tunnel decap term entry. * * Reads DST_IP, SRC_IP, TYPE from the term entry and TTL/DSCP/ECN modes * from the referenced tunnel object. * - * @param serializedObjectId The serialized tunnel term table entry ID. + * @param serializedObjectId The serialized tunnel decap term entry ID. * @param switch_id The switch ID. * @param attr_count Number of attributes. * @param attr_list Attribute list. @@ -434,7 +429,7 @@ namespace saivs /** * @brief Remove an IPIP decap tunnel. * - * @param serializedObjectId The serialized tunnel term table entry ID. + * @param serializedObjectId The serialized tunnel decap term entry ID. * @return SAI_STATUS_SUCCESS on success, error status on failure. */ sai_status_t remove_ipip_tunnel_term( @@ -493,4 +488,4 @@ namespace saivs // Decap: tunnel term OID → IPIP tunnel data std::unordered_map m_ipip_term_map; }; -} \ No newline at end of file +} diff --git a/vslib/vpp/vppxlate/SaiVppXlate.c b/vslib/vpp/vppxlate/SaiVppXlate.c index 6228b9e6a8..21ade642de 100644 --- a/vslib/vpp/vppxlate/SaiVppXlate.c +++ b/vslib/vpp/vppxlate/SaiVppXlate.c @@ -806,7 +806,7 @@ vl_api_sw_interface_get_table_reply_t_handler (vl_api_sw_interface_get_table_rep } SAIVPP_DEBUG("sw interface get table %s(%d) vrf_id=%u", - retval ? "failed" : "successful", retval, ntohl(msg->vrf_id)); + retval ? "failed" : "successful", retval, ntohl(msg->vrf_id)); } static void @@ -4256,13 +4256,13 @@ int vpp_ipip_tunnel_add(vpp_ipip_tunnel_t *tunnel, uint32_t *sw_if_index) mp->tunnel.dscp = tunnel->dscp; if (!vpp_to_vl_api_ip_addr(&mp->tunnel.src, &tunnel->src_address)) { - SAIVPP_ERROR("Unknown protocol in src address"); + SAIVPP_ERROR("Unknown protocol in ipip tunnel src address"); VPP_UNLOCK(); return -EINVAL; } if (!vpp_to_vl_api_ip_addr(&mp->tunnel.dst, &tunnel->dst_address)) { - SAIVPP_ERROR("Unknown protocol in dst address"); + SAIVPP_ERROR("Unknown protocol in ipip tunnel dst address"); VPP_UNLOCK(); return -EINVAL; } @@ -4371,8 +4371,7 @@ int vpp_sw_interface_find_by_ip(vpp_ip_addr_t *search_ip, uint32_t vrf_id, /* Pre-filter: skip interfaces not in the target VRF */ if (vrf_id != (uint32_t)~0) { uint32_t if_vrf_id = (uint32_t)~0; - if (__sw_interface_get_table(sw_if_idxs[i], is_ipv6, &if_vrf_id) != 0 - || if_vrf_id != vrf_id) + if (__sw_interface_get_table(sw_if_idxs[i], is_ipv6, &if_vrf_id) != 0 || if_vrf_id != vrf_id) continue; } From 4fb9a2565bec9c1e5c259e2056376c21b5a751a8 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Tue, 21 Apr 2026 13:33:37 +0000 Subject: [PATCH 04/11] fix pr checker Signed-off-by: Longxiang Lyu --- vslib/vpp/TunnelManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index 8a323ed677..dbda768ef2 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -827,6 +827,8 @@ TunnelManagerIpIp::map_sai_to_vpp_flags(const SaiObject* tunnel_obj) uint32_t TunnelManagerIpIp::resolve_vrf_id(_In_ sai_object_id_t vr_oid) { + SWSS_LOG_ENTER(); + if (vr_oid == SAI_NULL_OBJECT_ID) { return 0; } @@ -843,6 +845,8 @@ uint32_t TunnelManagerIpIp::resolve_vrf_id(_In_ sai_object_id_t vr_oid) uint32_t TunnelManagerIpIp::resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid) { + SWSS_LOG_ENTER(); + auto rif_obj = m_switch_db->get_sai_object(SAI_OBJECT_TYPE_ROUTER_INTERFACE, sai_serialize_object_id(rif_oid)); if (rif_obj) { From 811f301f79b5bb7a58b672e54db4b8faf5eaab43 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Wed, 22 Apr 2026 13:00:19 +0000 Subject: [PATCH 05/11] add ref count logic to dedup Signed-off-by: Longxiang Lyu --- vslib/vpp/TunnelManager.cpp | 85 ++++++++++++++++++++++++++++++++++--- vslib/vpp/TunnelManager.h | 26 ++++++++++++ 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index dbda768ef2..4d98c9d899 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "SwitchVppUtils.h" #include "SwitchVpp.h" #include "SaiObjectDB.h" @@ -874,7 +877,48 @@ uint32_t TunnelManagerIpIp::resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid) * remove_ipip_vpp_tunnel: * 1. interface_set_state DOWN * 2. vpp_ipip_tunnel_del + * + * Both use a refcount map keyed by {src, dst, mode} to avoid creating + * duplicate VPP tunnels when both decap (tunnel_term) and encap (nexthop) + * paths request the same tunnel (e.g. MuxTunnel0 P2P). */ + +static bool sai_ip_address_equal(const sai_ip_address_t &a, const sai_ip_address_t &b) +{ + if (a.addr_family != b.addr_family) { + return false; + } + if (a.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { + return a.addr.ip4 == b.addr.ip4; + } + return memcmp(a.addr.ip6, b.addr.ip6, sizeof(a.addr.ip6)) == 0; +} + +bool TunnelManagerIpIp::IpIpTunnelKey::operator==(const IpIpTunnelKey &o) const +{ + return (mode == o.mode && + sai_ip_address_equal(src, o.src) && + sai_ip_address_equal(dst, o.dst)); +} + +std::size_t TunnelManagerIpIp::IpIpTunnelKeyHash::operator()(const IpIpTunnelKey &k) const +{ + std::size_t h = std::hash()(k.mode); + if (k.src.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { + h ^= std::hash()(k.src.addr.ip4) << 1; + } + else { + h ^= std::hash()(*(const uint64_t *)k.src.addr.ip6) << 1; + } + if (k.dst.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { + h ^= std::hash()(k.dst.addr.ip4) << 2; + } + else { + h ^= std::hash()(*(const uint64_t *)k.dst.addr.ip6) << 2; + } + return h; +} + sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( _Inout_ vpp_ipip_tunnel_t &req, _In_ uint32_t vrf_id, @@ -884,6 +928,23 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( sw_if_index = 0; + // Check if a VPP tunnel with the same {src, dst, mode} already exists + IpIpTunnelKey key; + memset(&key, 0, sizeof(key)); + vpp_ip_addr_t_to_sai_ip_address_t(req.src_address, key.src); + vpp_ip_addr_t_to_sai_ip_address_t(req.dst_address, key.dst); + key.mode = req.mode; + + auto ref_it = m_ipip_tunnel_refcount.find(key); + if (ref_it != m_ipip_tunnel_refcount.end()) { + // Tunnel already exists + ref_it->second.refcount++; + sw_if_index = ref_it->second.sw_if_index; + SWSS_LOG_NOTICE("IpIp: reusing existing vpp ipip tunnel sw_if=%u (refcount=%u)", + sw_if_index, ref_it->second.refcount); + return SAI_STATUS_SUCCESS; + } + // Create the IPIP tunnel int ret = vpp_ipip_tunnel_add(&req, &sw_if_index); if (ret < 0) { @@ -896,14 +957,12 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( const char *ifname = vpp_get_swif_name(sw_if_index); if (!ifname) { SWSS_LOG_ERROR("IpIp: could not get interface name for sw_if_index=%u", sw_if_index); - vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } ret = interface_set_state(ifname, true); if (ret < 0) { SWSS_LOG_ERROR("IpIp: failed to set interface up for %s ret=%d", ifname, ret); - vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } SWSS_LOG_NOTICE("IpIp: tunnel %s (sw_if=%u) set UP", ifname, sw_if_index); @@ -914,7 +973,6 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( if (ret < 0) { SWSS_LOG_ERROR("IpIp: failed to set interface vrf for %s vrf=%u is_ipv6=%d ret=%d", ifname, vrf_id, is_ipv6, ret); - vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } @@ -925,7 +983,6 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( if (ret < 0) { SWSS_LOG_ERROR("IpIp: failed to set interface unnumbered sw_if=%u use sw_if=%u ret=%d", sw_if_index, owner_sw_if_index, ret); - vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } else { const char *owner_ifname = vpp_get_swif_name(owner_sw_if_index); @@ -936,12 +993,16 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( else { char ip_str[INET6_ADDRSTRLEN]; vpp_ip_addr_t_to_string(&req.src_address, ip_str, sizeof(ip_str)); - SWSS_LOG_ERROR("IpIp: No interface found for IP %s in vrf %u, unnumbered not set", + SWSS_LOG_ERROR("IpIp: no interface found for IP %s in vrf %u, unnumbered not set", ip_str, vrf_id); - vpp_ipip_tunnel_del(sw_if_index); return SAI_STATUS_FAILURE; } + IpIpTunnelRef ref_data; + ref_data.sw_if_index = sw_if_index; + ref_data.refcount = 1; + m_ipip_tunnel_refcount[key] = ref_data; + return SAI_STATUS_SUCCESS; } @@ -949,6 +1010,18 @@ sai_status_t TunnelManagerIpIp::remove_ipip_vpp_tunnel(_In_ uint32_t sw_if_index { SWSS_LOG_ENTER(); + auto it = std::find_if(m_ipip_tunnel_refcount.begin(), m_ipip_tunnel_refcount.end(), + [sw_if_index](const auto &entry) + { return entry.second.sw_if_index == sw_if_index; }); + if (it != m_ipip_tunnel_refcount.end()) { + if (--it->second.refcount > 0) { + SWSS_LOG_NOTICE("IpIp: tunnel sw_if=%u still in use (refcount=%u), skipping delete", + sw_if_index, it->second.refcount); + return SAI_STATUS_SUCCESS; + } + m_ipip_tunnel_refcount.erase(it); + } + const char *ifname = vpp_get_swif_name(sw_if_index); if (ifname) { interface_set_state(ifname, false); diff --git a/vslib/vpp/TunnelManager.h b/vslib/vpp/TunnelManager.h index 806cb2abc5..ed5b5d03cd 100644 --- a/vslib/vpp/TunnelManager.h +++ b/vslib/vpp/TunnelManager.h @@ -487,5 +487,31 @@ namespace saivs std::unordered_map m_ipip_encap_nh_map; // Decap: tunnel term OID → IPIP tunnel data std::unordered_map m_ipip_term_map; + + /** + * @brief Key for deduplicating VPP IPIP tunnels. + * + * Multiple SAI objects (decap term + encap nexthop) may map to the same + * underlying VPP tunnel (same src, dst, mode). We reference-count to + * avoid creating duplicates or deleting a tunnel still in use. + */ + struct IpIpTunnelKey { + sai_ip_address_t src; + sai_ip_address_t dst; + uint8_t mode; + + bool operator==(const IpIpTunnelKey &o) const; + }; + + struct IpIpTunnelKeyHash { + std::size_t operator()(const IpIpTunnelKey &k) const; + }; + + struct IpIpTunnelRef { + uint32_t sw_if_index; + uint32_t refcount; + }; + + std::unordered_map m_ipip_tunnel_refcount; }; } From f86cd9ced6e6819df747fe0c27328ed162063df4 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Wed, 22 Apr 2026 13:32:01 +0000 Subject: [PATCH 06/11] Fix the hash func Signed-off-by: Longxiang Lyu --- vslib/vpp/TunnelManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index 4d98c9d899..5e83ff756d 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -904,17 +904,20 @@ bool TunnelManagerIpIp::IpIpTunnelKey::operator==(const IpIpTunnelKey &o) const std::size_t TunnelManagerIpIp::IpIpTunnelKeyHash::operator()(const IpIpTunnelKey &k) const { std::size_t h = std::hash()(k.mode); + uint64_t tmp; if (k.src.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { h ^= std::hash()(k.src.addr.ip4) << 1; } else { - h ^= std::hash()(*(const uint64_t *)k.src.addr.ip6) << 1; + memcpy(&tmp, k.src.addr.ip6, sizeof(tmp)); + h ^= std::hash()(tmp) << 1; } if (k.dst.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { h ^= std::hash()(k.dst.addr.ip4) << 2; } else { - h ^= std::hash()(*(const uint64_t *)k.dst.addr.ip6) << 2; + memcpy(&tmp, k.dst.addr.ip6, sizeof(tmp)); + h ^= std::hash()(tmp) << 2; } return h; } From f092e5fb51632c0b409e9dec40d5c71a94d9a63a Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Thu, 23 Apr 2026 11:47:00 +0000 Subject: [PATCH 07/11] fix pr checker Signed-off-by: Longxiang Lyu --- vslib/vpp/TunnelManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index 5e83ff756d..f6bfb23d00 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -885,6 +885,8 @@ uint32_t TunnelManagerIpIp::resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid) static bool sai_ip_address_equal(const sai_ip_address_t &a, const sai_ip_address_t &b) { + SWSS_LOG_ENTER(); + if (a.addr_family != b.addr_family) { return false; } @@ -896,6 +898,8 @@ static bool sai_ip_address_equal(const sai_ip_address_t &a, const sai_ip_address bool TunnelManagerIpIp::IpIpTunnelKey::operator==(const IpIpTunnelKey &o) const { + SWSS_LOG_ENTER(); + return (mode == o.mode && sai_ip_address_equal(src, o.src) && sai_ip_address_equal(dst, o.dst)); @@ -903,6 +907,8 @@ bool TunnelManagerIpIp::IpIpTunnelKey::operator==(const IpIpTunnelKey &o) const std::size_t TunnelManagerIpIp::IpIpTunnelKeyHash::operator()(const IpIpTunnelKey &k) const { + SWSS_LOG_ENTER(); + std::size_t h = std::hash()(k.mode); uint64_t tmp; if (k.src.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { From af0d48d8396e46dbcc15ef3fa69bca284f3721fb Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Sun, 26 Apr 2026 08:21:50 +0000 Subject: [PATCH 08/11] Fix the vpp ipip tunnel creation failure due to rif is not ready Signed-off-by: Longxiang Lyu --- vslib/vpp/SwitchVppRif.cpp | 5 +++ vslib/vpp/TunnelManager.cpp | 69 ++++++++++++++++++++++++++++++++++--- vslib/vpp/TunnelManager.h | 37 +++++++++++++++++--- 3 files changed, 102 insertions(+), 9 deletions(-) diff --git a/vslib/vpp/SwitchVppRif.cpp b/vslib/vpp/SwitchVppRif.cpp index 42cef65789..d7588a9724 100644 --- a/vslib/vpp/SwitchVppRif.cpp +++ b/vslib/vpp/SwitchVppRif.cpp @@ -1028,6 +1028,9 @@ sai_status_t SwitchVpp::vpp_add_del_intf_ip_addr_norif ( if (ret == 0) { + if (is_add) { + m_tunnel_mgr_ipip.retry_pending_unnumbered(vpp_ip_prefix.prefix_addr); + } return SAI_STATUS_SUCCESS; } else { @@ -1187,6 +1190,8 @@ sai_status_t SwitchVpp::vpp_interface_ip_address_update ( int ret = interface_ip_address_add_del(vppIfname, &ip_route, is_add); if (ret != 0) { SWSS_LOG_ERROR("interface_ip_address_add returned error"); + } else if (is_add) { + m_tunnel_mgr_ipip.retry_pending_unnumbered(ip_route.prefix_addr); } return SAI_STATUS_SUCCESS; diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index f6bfb23d00..c4aeadc1b6 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -1002,9 +1002,13 @@ sai_status_t TunnelManagerIpIp::create_ipip_vpp_tunnel( else { char ip_str[INET6_ADDRSTRLEN]; vpp_ip_addr_t_to_string(&req.src_address, ip_str, sizeof(ip_str)); - SWSS_LOG_ERROR("IpIp: no interface found for IP %s in vrf %u, unnumbered not set", - ip_str, vrf_id); - return SAI_STATUS_FAILURE; + SWSS_LOG_WARN("IpIp: no interface found for IP %s in vrf %u yet, deferring unnumbered for sw_if=%u", + ip_str, vrf_id, sw_if_index); + PendingUnnumbered pending; + pending.sw_if_index = sw_if_index; + pending.src_address = req.src_address; + pending.vrf_id = vrf_id; + m_pending_unnumbered.emplace(std::string(ip_str), pending); } IpIpTunnelRef ref_data; @@ -1031,6 +1035,15 @@ sai_status_t TunnelManagerIpIp::remove_ipip_vpp_tunnel(_In_ uint32_t sw_if_index m_ipip_tunnel_refcount.erase(it); } + // Remove any pending unnumbered entries for this tunnel + for (auto pending_it = m_pending_unnumbered.begin(); pending_it != m_pending_unnumbered.end(); ) { + if (pending_it->second.sw_if_index == sw_if_index) { + pending_it = m_pending_unnumbered.erase(pending_it); + } else { + ++pending_it; + } + } + const char *ifname = vpp_get_swif_name(sw_if_index); if (ifname) { interface_set_state(ifname, false); @@ -1045,6 +1058,49 @@ sai_status_t TunnelManagerIpIp::remove_ipip_vpp_tunnel(_In_ uint32_t sw_if_index return SAI_STATUS_SUCCESS; } +void TunnelManagerIpIp::retry_pending_unnumbered(_In_ const vpp_ip_addr_t &rif_ip) +{ + SWSS_LOG_ENTER(); + + if (m_pending_unnumbered.empty()) { + return; + } + + vpp_ip_addr_t *rif_ip_ptr = const_cast(&rif_ip); + + char ip_str[INET6_ADDRSTRLEN]; + vpp_ip_addr_t_to_string(rif_ip_ptr, ip_str, sizeof(ip_str)); + + auto range = m_pending_unnumbered.equal_range(std::string(ip_str)); + if (range.first == range.second) { + return; + } + + SWSS_LOG_NOTICE("IpIp: retrying %zu pending unnumbered for IP %s", + (size_t)std::distance(range.first, range.second), ip_str); + + // Find the owner interface for this IP + uint32_t owner_sw_if_index = 0; + if (vpp_sw_interface_find_by_ip(rif_ip_ptr, range.first->second.vrf_id, &owner_sw_if_index) != 0) { + SWSS_LOG_WARN("IpIp: still no owner interface for IP %s", ip_str); + return; + } + + const char *owner_ifname = vpp_get_swif_name(owner_sw_if_index); + for (auto it = range.first; it != range.second; ) { + int ret = sw_interface_set_unnumbered(it->second.sw_if_index, owner_sw_if_index, true); + if (ret < 0) { + SWSS_LOG_ERROR("IpIp: deferred unnumbered failed sw_if=%u use sw_if=%u ret=%d", + it->second.sw_if_index, owner_sw_if_index, ret); + ++it; + continue; + } + SWSS_LOG_NOTICE("IpIp: deferred unnumbered succeeded sw_if=%u using %s (sw_if=%u)", + it->second.sw_if_index, owner_ifname ? owner_ifname : "?", owner_sw_if_index); + it = m_pending_unnumbered.erase(it); + } +} + sai_status_t TunnelManagerIpIp::create_ipip_tunnel_term( _In_ const std::string &serializedObjectId, _In_ sai_object_id_t switch_id, @@ -1131,6 +1187,10 @@ sai_status_t TunnelManagerIpIp::create_ipip_tunnel_term( req.instance = ~0; req.mode = vpp_mode; req.flags = vpp_flags; + // ENCAP_INNER_HASH: hash on inner 5-tuple for ECMP for P2P ipip tunnel + if (vpp_mode == IpIpTunnelVPPData::TUNNEL_API_MODE_P2P) { + req.flags |= 0x20; + } sai_ip_address_t_to_vpp_ip_addr_t(dst_ip, req.src_address); if (has_src_ip && vpp_mode == IpIpTunnelVPPData::TUNNEL_API_MODE_P2P) { @@ -1260,7 +1320,8 @@ sai_status_t TunnelManagerIpIp::ipip_encap_nexthop_action( memset(&req, 0, sizeof(req)); req.instance = ~0; req.mode = IpIpTunnelVPPData::TUNNEL_API_MODE_P2P; - req.flags = vpp_flags; + // ENCAP_INNER_HASH: hash on inner 5-tuple for ECMP for P2P ipip tunnel + req.flags = vpp_flags | 0x20; sai_ip_address_t_to_vpp_ip_addr_t(src_ip, req.src_address); sai_ip_address_t_to_vpp_ip_addr_t(dst_ip, req.dst_address); diff --git a/vslib/vpp/TunnelManager.h b/vslib/vpp/TunnelManager.h index ed5b5d03cd..bb3890dee4 100644 --- a/vslib/vpp/TunnelManager.h +++ b/vslib/vpp/TunnelManager.h @@ -435,6 +435,16 @@ namespace saivs sai_status_t remove_ipip_tunnel_term( _In_ const std::string &serializedObjectId); + /** + * @brief Retry pending unnumbered operations matching the given RIF IP. + * + * Called after a rif address is successfully programmed. + * Only processes ipip tunnels whose source IP matches the rif ip. + * + * @param rif_ip The IP address just programmed on a RIF. + */ + void retry_pending_unnumbered(_In_ const vpp_ip_addr_t &rif_ip); + private: /** * @brief Map SAI tunnel TTL/DSCP/ECN modes to VPP tunnel_encap_decap_flags. @@ -481,12 +491,17 @@ namespace saivs sai_status_t remove_ipip_vpp_tunnel(_In_ uint32_t sw_if_index); private: - SwitchVpp *m_switch_db; - // Encap: nexthop OID → IPIP tunnel data - std::unordered_map m_ipip_encap_nh_map; - // Decap: tunnel term OID → IPIP tunnel data - std::unordered_map m_ipip_term_map; + /** + * @brief Record for a deferred set-unnumbered attempt. + */ + struct PendingUnnumbered { + uint32_t sw_if_index; ///< tunnel sw_if_index + vpp_ip_addr_t src_address; ///< tunnel source IP + uint32_t vrf_id; ///< VRF for the lookup + }; + + /** * @brief Key for deduplicating VPP IPIP tunnels. @@ -512,6 +527,18 @@ namespace saivs uint32_t refcount; }; + + SwitchVpp *m_switch_db; + + // Pending unnumbered tunnels + std::unordered_multimap m_pending_unnumbered; + + // Encap: nexthop OID -> IPIP tunnel data + std::unordered_map m_ipip_encap_nh_map; + // Decap: tunnel term OID -> IPIP tunnel data + std::unordered_map m_ipip_term_map; + + // IPIP tunnel ref count map std::unordered_map m_ipip_tunnel_refcount; }; } From dfb8a3cc8e3adcd7f9c9331172a9c836fff72042 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Fri, 1 May 2026 10:11:40 +0000 Subject: [PATCH 09/11] Map SAI P2MP to VPP MP2P ipip tunnel Signed-off-by: Longxiang Lyu --- vslib/vpp/SwitchVpp.cpp | 1 + vslib/vpp/TunnelManager.cpp | 13 ++++++++----- vslib/vpp/TunnelManager.h | 4 +++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/vslib/vpp/SwitchVpp.cpp b/vslib/vpp/SwitchVpp.cpp index a5e8ed07d7..fbaa4774c9 100644 --- a/vslib/vpp/SwitchVpp.cpp +++ b/vslib/vpp/SwitchVpp.cpp @@ -1038,6 +1038,7 @@ sai_status_t SwitchVpp::create( { CHECK_STATUS(create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list)); m_tunnel_mgr.handle_l2_vxlan_tunnel_map_entry(serializedObjectId, attr_count, attr_list); + return SAI_STATUS_SUCCESS; } if (object_type == SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index c4aeadc1b6..d9fee60b56 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -1160,14 +1160,17 @@ sai_status_t TunnelManagerIpIp::create_ipip_tunnel_term( return SAI_STATUS_ITEM_NOT_FOUND; } - // Map sai_tunnel_term_table_entry_type_t -> VPP IPIP tunnel mode - uint8_t vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_MP; + // Map sai_tunnel_term_table_entry_type_t -> VPP IPIP tunnel mode. + // SAI P2MP term means "one local endpoint decapsulates traffic from many + // remote peers" -- this is decap-only and maps to VPP's MP2P mode, which + // (unlike MP / NBMA) does not require per-peer TEIB next-hops. + uint8_t vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_MP2P; switch (term_type) { case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P: vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_P2P; break; case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP: - vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_MP; + vpp_mode = IpIpTunnelVPPData::TUNNEL_API_MODE_MP2P; break; case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_MP2P: case SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_MP2MP: @@ -1196,7 +1199,7 @@ sai_status_t TunnelManagerIpIp::create_ipip_tunnel_term( if (has_src_ip && vpp_mode == IpIpTunnelVPPData::TUNNEL_API_MODE_P2P) { sai_ip_address_t_to_vpp_ip_addr_t(src_ip, req.dst_address); } else { - // P2MP: dst = 0.0.0.0 (already zeroed) + // MP2P (P2MP-style decap): dst = 0.0.0.0 (already zeroed) req.dst_address.sa_family = dst_ip.addr_family == SAI_IP_ADDR_FAMILY_IPV4 ? AF_INET : AF_INET6; } @@ -1225,7 +1228,7 @@ sai_status_t TunnelManagerIpIp::create_ipip_tunnel_term( SWSS_LOG_NOTICE("IpIp: created tunnel term %s: sw_if=%u mode=%s", serializedObjectId.c_str(), sw_if_index, - vpp_mode == 0 ? "P2P" : "P2MP"); + vpp_mode == IpIpTunnelVPPData::TUNNEL_API_MODE_P2P ? "P2P" : "MP2P"); return SAI_STATUS_SUCCESS; } diff --git a/vslib/vpp/TunnelManager.h b/vslib/vpp/TunnelManager.h index bb3890dee4..52b9dad080 100644 --- a/vslib/vpp/TunnelManager.h +++ b/vslib/vpp/TunnelManager.h @@ -48,6 +48,8 @@ namespace saivs TUNNEL_API_MODE_P2P = 0, /** multi-point */ TUNNEL_API_MODE_MP, + /** multi-point to point (decap-only) */ + TUNNEL_API_MODE_MP2P, }; IpIpTunnelVPPData() { @@ -59,7 +61,7 @@ namespace saivs u_int32_t sw_if_index = 0; sai_ip_address_t src_ip; // outer src (local endpoint) sai_ip_address_t dst_ip; // outer dst (remote endpoint; 0.0.0.0 for P2MP) - tunnel_mode mode = TUNNEL_API_MODE_MP; // IPIP tunnel mode + tunnel_mode mode = TUNNEL_API_MODE_MP2P; // IPIP tunnel mode u_int8_t flags = 0; // tunnel_encap_decap_flags sai_object_id_t tunnel_oid = SAI_NULL_OBJECT_ID; // referenced SAI tunnel object uint32_t vrf_id = 0; // vrf From 0069c3d60b1ffac0956f13366c32924132a9d878 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Tue, 5 May 2026 07:01:53 +0000 Subject: [PATCH 10/11] fix comments Signed-off-by: Longxiang Lyu --- vslib/vpp/SwitchVpp.cpp | 9 ++++----- vslib/vpp/SwitchVppUtils.cpp | 13 +++++++++++++ vslib/vpp/SwitchVppUtils.h | 2 ++ vslib/vpp/TunnelManager.cpp | 13 ------------- vslib/vpp/vppxlate/SaiVppXlate.c | 2 +- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/vslib/vpp/SwitchVpp.cpp b/vslib/vpp/SwitchVpp.cpp index fbaa4774c9..e84471f5d2 100644 --- a/vslib/vpp/SwitchVpp.cpp +++ b/vslib/vpp/SwitchVpp.cpp @@ -1043,17 +1043,16 @@ sai_status_t SwitchVpp::create( if (object_type == SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY) { - CHECK_STATUS(create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list)); - // Check if this is an IPINIP tunnel term for (uint32_t i = 0; i < attr_count; i++) { if (attr_list[i].id == SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE && attr_list[i].value.s32 == SAI_TUNNEL_TYPE_IPINIP) { - return m_tunnel_mgr_ipip.create_ipip_tunnel_term( - serializedObjectId, switch_id, attr_count, attr_list); + CHECK_STATUS(m_tunnel_mgr_ipip.create_ipip_tunnel_term( + serializedObjectId, switch_id, attr_count, attr_list)); + break; } } - return SAI_STATUS_SUCCESS; + return create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list); } return create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list); diff --git a/vslib/vpp/SwitchVppUtils.cpp b/vslib/vpp/SwitchVppUtils.cpp index 5137989491..1648931128 100644 --- a/vslib/vpp/SwitchVppUtils.cpp +++ b/vslib/vpp/SwitchVppUtils.cpp @@ -188,3 +188,16 @@ void saivs::vpp_ip_addr_t_to_sai_ip_address_t(vpp_ip_addr_t& src, sai_ip_address sizeof(sin6->sin6_addr.s6_addr)); } } + +bool saivs::sai_ip_address_equal(const sai_ip_address_t &a, const sai_ip_address_t &b) +{ + SWSS_LOG_ENTER(); + + if (a.addr_family != b.addr_family) { + return false; + } + if (a.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { + return a.addr.ip4 == b.addr.ip4; + } + return memcmp(a.addr.ip6, b.addr.ip6, sizeof(a.addr.ip6)) == 0; +} diff --git a/vslib/vpp/SwitchVppUtils.h b/vslib/vpp/SwitchVppUtils.h index fdfdc0b667..f34bb22d50 100644 --- a/vslib/vpp/SwitchVppUtils.h +++ b/vslib/vpp/SwitchVppUtils.h @@ -48,4 +48,6 @@ namespace saivs /* Utility function for IP addr translation from VS to SAI */ void vpp_ip_addr_t_to_sai_ip_address_t(vpp_ip_addr_t& src, sai_ip_address_t& dst); + + bool sai_ip_address_equal(const sai_ip_address_t &a, const sai_ip_address_t &b); } diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index d9fee60b56..295b5bce04 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -883,19 +883,6 @@ uint32_t TunnelManagerIpIp::resolve_vrf_from_rif(_In_ sai_object_id_t rif_oid) * paths request the same tunnel (e.g. MuxTunnel0 P2P). */ -static bool sai_ip_address_equal(const sai_ip_address_t &a, const sai_ip_address_t &b) -{ - SWSS_LOG_ENTER(); - - if (a.addr_family != b.addr_family) { - return false; - } - if (a.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { - return a.addr.ip4 == b.addr.ip4; - } - return memcmp(a.addr.ip6, b.addr.ip6, sizeof(a.addr.ip6)) == 0; -} - bool TunnelManagerIpIp::IpIpTunnelKey::operator==(const IpIpTunnelKey &o) const { SWSS_LOG_ENTER(); diff --git a/vslib/vpp/vppxlate/SaiVppXlate.c b/vslib/vpp/vppxlate/SaiVppXlate.c index 21ade642de..7b34009b93 100644 --- a/vslib/vpp/vppxlate/SaiVppXlate.c +++ b/vslib/vpp/vppxlate/SaiVppXlate.c @@ -4397,7 +4397,7 @@ int vpp_sw_interface_find_by_ip(vpp_ip_addr_t *search_ip, uint32_t vrf_id, PING (NULL, mp_ping); S (mp_ping); - W (ret); + WR (ret); VPP_UNLOCK(); From 485d3e98e6860c45ad8b29996da148bc351a65a1 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu Date: Tue, 26 May 2026 11:21:26 +0000 Subject: [PATCH 11/11] Fix comment Signed-off-by: Longxiang Lyu --- vslib/vpp/TunnelManager.cpp | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/vslib/vpp/TunnelManager.cpp b/vslib/vpp/TunnelManager.cpp index 295b5bce04..d0d5d29786 100644 --- a/vslib/vpp/TunnelManager.cpp +++ b/vslib/vpp/TunnelManager.cpp @@ -1265,11 +1265,7 @@ sai_status_t TunnelManagerIpIp::ipip_encap_nexthop_action( // Verify the tunnel type is IPINIP attr.id = SAI_TUNNEL_ATTR_TYPE; - if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("IpIp Encap: missing SAI_TUNNEL_ATTR_TYPE in tunnel %s", - tunnel_obj->get_id().c_str()); - return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; - } + CHECK_STATUS_QUIET(tunnel_obj->get_mandatory_attr(attr)); if (attr.value.s32 != SAI_TUNNEL_TYPE_IPINIP) { SWSS_LOG_ERROR("IpIp Encap: tunnel %s type %d is not IPINIP", tunnel_obj->get_id().c_str(), attr.value.s32); @@ -1278,29 +1274,17 @@ sai_status_t TunnelManagerIpIp::ipip_encap_nexthop_action( // Get ENCAP_SRC_IP from the tunnel object (our local endpoint) attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; - if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("IpIp Encap: missing SAI_TUNNEL_ATTR_ENCAP_SRC_IP in tunnel %s", - tunnel_obj->get_id().c_str()); - return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; - } + CHECK_STATUS_QUIET(tunnel_obj->get_mandatory_attr(attr)); sai_ip_address_t src_ip = attr.value.ipaddr; // Get the destination IP from the nexthop object (remote peer) attr.id = SAI_NEXT_HOP_ATTR_IP; - if (tunnel_nh_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("IpIp Encap: missing SAI_NEXT_HOP_ATTR_IP in nexthop %s", - tunnel_nh_obj->get_id().c_str()); - return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; - } + CHECK_STATUS_QUIET(tunnel_nh_obj->get_mandatory_attr(attr)); sai_ip_address_t dst_ip = attr.value.ipaddr; // Get the tunnel overlay interface attr.id = SAI_TUNNEL_ATTR_OVERLAY_INTERFACE; - if (tunnel_obj->get_attr(attr) != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("IpIp Encap: missing SAI_TUNNEL_ATTR_OVERLAY_INTERFACE in tunnel %s", - tunnel_obj->get_id().c_str()); - return SAI_STATUS_MANDATORY_ATTRIBUTE_MISSING; - } + CHECK_STATUS_QUIET(tunnel_obj->get_mandatory_attr(attr)); sai_object_id_t tunnel_overlay_if_oid = attr.value.oid; if (action == Action::CREATE) {