From 095f481e63997b64392bc46c2f176a04b8889667 Mon Sep 17 00:00:00 2001 From: Rajesh Sankaran <48232228+srj102@users.noreply.github.com> Date: Wed, 2 Dec 2020 12:36:18 +0530 Subject: [PATCH] [Orchagent] Vxlanorch and Portsorch changes for EVPN VXLAN (#1264) * https://github.com/Azure/SONiC/pull/437/commits 1. Existing code supports a single mapper type to be created. Either BRIDGE or VRF type mappers are supported. Change to support multiple mapper type creations is being added. 2. Changes to support P2P tunnel creation and deletion. A refcnt is maintained per resource type (IMR or IP prefix) and based on this a P2P tunnel is created or deleted. 3. Changes to support handling of VXLAN_REMOTE_VNI table. Interfaces with portsorch to create a Port object of type TUNNEL on tunnel discovery. 4. SAI interface function changes to support P2P tunnels. 5. EVPN NVO table handling. Signed-off-by: Rajesh Sankaran Co-authored-by: Tapash Das --- orchagent/orchdaemon.cpp | 11 +- orchagent/port.h | 10 +- orchagent/portsorch.cpp | 110 ++- orchagent/portsorch.h | 14 +- orchagent/vxlanorch.cpp | 1367 ++++++++++++++++++++++++++++++++++-- orchagent/vxlanorch.h | 264 ++++++- tests/test_vxlan_tunnel.py | 32 +- 7 files changed, 1692 insertions(+), 116 deletions(-) diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index b6124b3dca..480891a189 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -135,13 +135,20 @@ bool OrchDaemon::init() CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); - VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_applDb, APP_VXLAN_TUNNEL_TABLE_NAME); + VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_stateDb, m_applDb, APP_VXLAN_TUNNEL_TABLE_NAME); gDirectory.set(vxlan_tunnel_orch); VxlanTunnelMapOrch *vxlan_tunnel_map_orch = new VxlanTunnelMapOrch(m_applDb, APP_VXLAN_TUNNEL_MAP_TABLE_NAME); gDirectory.set(vxlan_tunnel_map_orch); VxlanVrfMapOrch *vxlan_vrf_orch = new VxlanVrfMapOrch(m_applDb, APP_VXLAN_VRF_TABLE_NAME); gDirectory.set(vxlan_vrf_orch); + EvpnRemoteVniOrch* evpn_remote_vni_orch = new EvpnRemoteVniOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); + gDirectory.set(evpn_remote_vni_orch); + + EvpnNvoOrch* evpn_nvo_orch = new EvpnNvoOrch(m_applDb, APP_VXLAN_EVPN_NVO_TABLE_NAME); + gDirectory.set(evpn_nvo_orch); + + vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, CFG_SCHEDULER_TABLE_NAME, @@ -274,7 +281,9 @@ bool OrchDaemon::init() m_orchList.push_back(chassis_frontend_orch); m_orchList.push_back(vrf_orch); m_orchList.push_back(vxlan_tunnel_orch); + m_orchList.push_back(evpn_nvo_orch); m_orchList.push_back(vxlan_tunnel_map_orch); + m_orchList.push_back(evpn_remote_vni_orch); m_orchList.push_back(vxlan_vrf_orch); m_orchList.push_back(cfg_vnet_rt_orch); m_orchList.push_back(vnet_orch); diff --git a/orchagent/port.h b/orchagent/port.h index d3ba42c4a0..6f00bf28f4 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -20,6 +20,8 @@ extern "C" { */ #define DEFAULT_MTU 1492 +#define VNID_NONE 0xFFFFFFFF + namespace swss { struct VlanMemberEntry @@ -46,6 +48,7 @@ class Port LOOPBACK, VLAN, LAG, + TUNNEL, SUBPORT, UNKNOWN } ; @@ -89,6 +92,7 @@ class Port sai_object_id_t m_hif_id = 0; sai_object_id_t m_lag_id = 0; sai_object_id_t m_lag_member_id = 0; + sai_object_id_t m_tunnel_id = 0; sai_object_id_t m_ingress_acl_table_group_id = 0; sai_object_id_t m_egress_acl_table_group_id = 0; vlan_members_t m_vlan_members; @@ -100,8 +104,10 @@ class Port std::vector m_queue_ids; std::vector m_priority_group_ids; sai_port_priority_flow_control_mode_t m_pfc_asym = SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED; - uint8_t m_pfc_bitmask = 0; - uint32_t m_nat_zone_id = 0; + uint8_t m_pfc_bitmask = 0; + uint32_t m_nat_zone_id = 0; + uint32_t m_vnid = VNID_NONE; + uint32_t m_fdb_count = 0; /* * Following two bit vectors are used to lock diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 0a6c3f4b4e..15cad5bc1f 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -3,6 +3,8 @@ #include "bufferorch.h" #include "neighorch.h" #include "gearboxutils.h" +#include "vxlanorch.h" +#include "directory.h" #include #include @@ -42,6 +44,7 @@ extern NeighOrch *gNeighOrch; extern CrmOrch *gCrmOrch; extern BufferOrch *gBufferOrch; extern FdbOrch *gFdbOrch; +extern Directory gDirectory; #define VLAN_PREFIX "Vlan" #define DEFAULT_VLAN_ID 1 @@ -1284,6 +1287,12 @@ bool PortsOrch::setPortPvid(Port &port, sai_uint32_t pvid) { SWSS_LOG_ENTER(); + if(port.m_type == Port::TUNNEL) + { + SWSS_LOG_ERROR("pvid setting for tunnel %s is not allowed", port.m_alias.c_str()); + return true; + } + if (port.m_rif_id) { SWSS_LOG_ERROR("pvid setting for router interface %s is not allowed", port.m_alias.c_str()); @@ -1348,6 +1357,11 @@ bool PortsOrch::setHostIntfsStripTag(Port &port, sai_hostif_vlan_tag_t strip) SWSS_LOG_ENTER(); vector portv; + if(port.m_type == Port::TUNNEL) + { + return true; + } + /* * Before SAI_HOSTIF_VLAN_TAG_ORIGINAL is supported by libsai from all asic vendors, * the VLAN tag on hostif is explicitly controlled with SAI_HOSTIF_VLAN_TAG_STRIP & @@ -1717,6 +1731,13 @@ void PortsOrch::updateDbPortOperStatus(const Port& port, sai_port_oper_status_t { SWSS_LOG_ENTER(); + if(port.m_type == Port::TUNNEL) + { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + tunnel_orch->updateDbTunnelOperStatus(port.m_alias, status); + return; + } + vector tuples; FieldValueTuple tuple("oper_status", oper_status_strings.at(status)); tuples.push_back(tuple); @@ -1809,7 +1830,7 @@ bool PortsOrch::initPort(const string &alias, const int index, const set &l /* Determine if the port has already been initialized before */ if (m_portList.find(alias) != m_portList.end() && m_portList[alias].m_port_id == id) { - SWSS_LOG_DEBUG("Port has already been initialized before alias:%s", alias.c_str()); + SWSS_LOG_INFO("Port has already been initialized before alias:%s", alias.c_str()); } else { @@ -1853,7 +1874,7 @@ bool PortsOrch::initPort(const string &alias, const int index, const set &l m_portList[alias].m_init = true; - SWSS_LOG_NOTICE("Initialized port %s", alias.c_str()); + SWSS_LOG_ERROR("Initialized port %s", alias.c_str()); } else { @@ -3333,26 +3354,46 @@ bool PortsOrch::addBridgePort(Port &port) sai_attribute_t attr; vector attrs; - attr.id = SAI_BRIDGE_PORT_ATTR_TYPE; - attr.value.s32 = SAI_BRIDGE_PORT_TYPE_PORT; - attrs.push_back(attr); - - attr.id = SAI_BRIDGE_PORT_ATTR_PORT_ID; if (port.m_type == Port::PHY) { + attr.id = SAI_BRIDGE_PORT_ATTR_TYPE; + attr.value.s32 = SAI_BRIDGE_PORT_TYPE_PORT; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_PORT_ID; attr.value.oid = port.m_port_id; + attrs.push_back(attr); } else if (port.m_type == Port::LAG) { + attr.id = SAI_BRIDGE_PORT_ATTR_TYPE; + attr.value.s32 = SAI_BRIDGE_PORT_TYPE_PORT; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_PORT_ID; attr.value.oid = port.m_lag_id; + attrs.push_back(attr); + } + else if (port.m_type == Port::TUNNEL) + { + attr.id = SAI_BRIDGE_PORT_ATTR_TYPE; + attr.value.s32 = SAI_BRIDGE_PORT_TYPE_TUNNEL; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_TUNNEL_ID; + attr.value.oid = port.m_tunnel_id; + attrs.push_back(attr); + + attr.id = SAI_BRIDGE_PORT_ATTR_BRIDGE_ID; + attr.value.oid = m_default1QBridge; + attrs.push_back(attr); } else { - SWSS_LOG_ERROR("Failed to add bridge port %s to default 1Q bridge, invalid porty type %d", + SWSS_LOG_ERROR("Failed to add bridge port %s to default 1Q bridge, invalid port type %d", port.m_alias.c_str(), port.m_type); return false; } - attrs.push_back(attr); /* Create a bridge port with admin status set to UP */ attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE; @@ -3522,6 +3563,14 @@ bool PortsOrch::removeVlan(Port vlan) return false; } + // Fail VLAN removal if there is a vnid associated + if (vlan.m_vnid != VNID_NONE) + { + SWSS_LOG_ERROR("VLAN-VNI mapping not yet removed. VLAN %s VNI %d", + vlan.m_alias.c_str(), vlan.m_vnid); + return false; + } + sai_status_t status = sai_vlan_api->remove_vlan(vlan.m_vlan_info.vlan_oid); if (status != SAI_STATUS_SUCCESS) { @@ -3660,6 +3709,14 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) return true; } +bool PortsOrch::isVlanMember(Port &vlan, Port &port) +{ + if (vlan.m_members.find(port.m_alias) == vlan.m_members.end()) + return false; + + return true; +} + bool PortsOrch::addLag(string lag_alias) { SWSS_LOG_ENTER(); @@ -3908,6 +3965,36 @@ bool PortsOrch::setDistributionOnLagMember(Port &lagMember, bool enableDistribut return true; } +bool PortsOrch::addTunnel(string tunnel_alias, sai_object_id_t tunnel_id, bool hwlearning) +{ + SWSS_LOG_ENTER(); + + Port tunnel(tunnel_alias, Port::TUNNEL); + tunnel.m_tunnel_id = tunnel_id; + if (hwlearning) + { + tunnel.m_learn_mode = "hardware"; + } + else + { + tunnel.m_learn_mode = "disable"; + } + m_portList[tunnel_alias] = tunnel; + + SWSS_LOG_INFO("addTunnel:: %" PRIx64, tunnel_id); + + return true; +} + +bool PortsOrch::removeTunnel(Port tunnel) +{ + SWSS_LOG_ENTER(); + + m_portList.erase(tunnel.m_alias); + + return true; +} + void PortsOrch::generateQueueMap() { if (m_isQueueMapGenerated) @@ -4116,6 +4203,11 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) } port.m_oper_status = status; + if(port.m_type == Port::TUNNEL) + { + return; + } + bool isUp = status == SAI_PORT_OPER_STATUS_UP; if (port.m_type == Port::PHY) { diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index cff208399e..35e3af2627 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -128,6 +128,15 @@ class PortsOrch : public Orch, public Subject void getLagMember(Port &lag, vector &portv); void updateChildPortsMtu(const Port &p, const uint32_t mtu); + bool addTunnel(string tunnel,sai_object_id_t, bool learning=true); + bool removeTunnel(Port tunnel); + bool addBridgePort(Port &port); + bool removeBridgePort(Port &port); + bool addVlanMember(Port &vlan, Port &port, string& tagging_mode); + bool removeVlanMember(Port &vlan, Port &port); + bool isVlanMember(Port &vlan, Port &port); + + private: unique_ptr m_counterTable; unique_ptr
m_counterLagTable; @@ -216,14 +225,10 @@ class PortsOrch : public Orch, public Subject bool addHostIntfs(Port &port, string alias, sai_object_id_t &host_intfs_id); bool setHostIntfsStripTag(Port &port, sai_hostif_vlan_tag_t strip); - bool addBridgePort(Port &port); - bool removeBridgePort(Port &port); bool setBridgePortLearnMode(Port &port, string learn_mode); bool addVlan(string vlan); bool removeVlan(Port vlan); - bool addVlanMember(Port &vlan, Port &port, string& tagging_mode); - bool removeVlanMember(Port &vlan, Port &port); bool addLag(string lag); bool removeLag(Port lag); @@ -278,6 +283,7 @@ class PortsOrch : public Orch, public Subject sai_acl_bind_point_type_t &sai_acl_bind_type); void initGearbox(); bool initGearboxPort(Port &port); + }; #endif /* SWSS_PORTSORCH_H */ diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index e385c64c20..c429925e5f 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -5,8 +5,6 @@ #include #include #include - - #include "sai.h" #include "macaddress.h" #include "ipaddress.h" @@ -15,6 +13,8 @@ #include "vxlanorch.h" #include "directory.h" #include "swssnet.h" +#include "warm_restart.h" +#include "tokenize.h" /* Global variables */ extern sai_object_id_t gSwitchId; @@ -75,6 +75,33 @@ static inline uint32_t tunnel_map_val (MAP_T map_t) return vxlanTunnelMapKeyVal.at(map_t).second; } +static inline MAP_T tunnel_map_type (tunnel_map_type_t type, bool isencap) +{ + if (isencap) + { + switch(type) + { + case TUNNEL_MAP_T_VLAN : return MAP_T::VLAN_ID_TO_VNI; + case TUNNEL_MAP_T_VIRTUAL_ROUTER: return MAP_T::VRID_TO_VNI; + case TUNNEL_MAP_T_BRIDGE: return MAP_T::BRIDGE_TO_VNI; + default: return MAP_T::MAP_TO_INVALID; + } + } + else + { + switch(type) + { + case TUNNEL_MAP_T_VLAN : return MAP_T::VNI_TO_VLAN_ID; + case TUNNEL_MAP_T_VIRTUAL_ROUTER: return MAP_T::VNI_TO_VRID; + case TUNNEL_MAP_T_BRIDGE: return MAP_T::VNI_TO_BRIDGE; + default: return MAP_T::MAP_TO_INVALID; + } + } +} + + +//------------------- SAI Interface functions --------------------------// + static sai_object_id_t create_tunnel_map(MAP_T map_t) { @@ -226,10 +253,11 @@ static sai_status_t create_nexthop_tunnel( // Create Tunnel static sai_object_id_t create_tunnel( - sai_object_id_t tunnel_encap_id, - sai_object_id_t tunnel_decap_id, + struct tunnel_ids_t* ids, sai_ip_address_t *src_ip, + sai_ip_address_t *dst_ip, sai_object_id_t underlay_rif, + bool p2p, sai_uint8_t encap_ttl=0) { sai_attribute_t attr; @@ -243,21 +271,42 @@ create_tunnel( attr.value.oid = underlay_rif; tunnel_attrs.push_back(attr); - sai_object_id_t decap_list[] = { tunnel_decap_id }; + sai_object_id_t map_list[TUNNEL_MAP_T_MAX_MAPPER+1]; + uint8_t num_map=0; + + for (int i=TUNNEL_MAP_T_VLAN;itunnel_decap_id[i] != SAI_NULL_OBJECT_ID) + { + map_list[num_map] = ids->tunnel_decap_id[i]; + SWSS_LOG_INFO("create_tunnel:maplist[%d]=0x%lx",num_map,map_list[num_map]); + num_map++; + } + } + attr.id = SAI_TUNNEL_ATTR_DECAP_MAPPERS; - attr.value.objlist.count = 1; - attr.value.objlist.list = decap_list; + attr.value.objlist.count = num_map; + attr.value.objlist.list = map_list; tunnel_attrs.push_back(attr); - if (tunnel_encap_id != SAI_NULL_OBJECT_ID) + sai_object_id_t emap_list[TUNNEL_MAP_T_MAX_MAPPER+1]; + uint8_t num_emap=0; + + for (int i=TUNNEL_MAP_T_VLAN;itunnel_encap_id[i] != SAI_NULL_OBJECT_ID) + { + emap_list[num_emap] = ids->tunnel_encap_id[i]; + SWSS_LOG_NOTICE("create_tunnel:encapmaplist[%d]=0x%lx",num_emap,emap_list[num_emap]); + num_emap++; + } } + attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; + attr.value.objlist.count = num_emap; + attr.value.objlist.list = emap_list; + tunnel_attrs.push_back(attr); + // source ip if (src_ip != nullptr) { @@ -266,6 +315,23 @@ create_tunnel( tunnel_attrs.push_back(attr); } + // dest ip + if ((dst_ip != nullptr) && p2p) + { + attr.id = SAI_TUNNEL_ATTR_PEER_MODE; + attr.value.s32 = SAI_TUNNEL_PEER_MODE_P2P; + tunnel_attrs.push_back(attr); + attr.id = SAI_TUNNEL_ATTR_ENCAP_DST_IP; + attr.value.ipaddr = *dst_ip; + tunnel_attrs.push_back(attr); + } + else + { + attr.id = SAI_TUNNEL_ATTR_PEER_MODE; + attr.value.s32 = SAI_TUNNEL_PEER_MODE_P2MP; + tunnel_attrs.push_back(attr); + } + if (encap_ttl != 0) { attr.id = SAI_TUNNEL_ATTR_ENCAP_TTL_MODE; @@ -320,7 +386,7 @@ create_tunnel_termination( sai_attribute_t attr; std::vector tunnel_attrs; - if(dstip == nullptr) // It's P2MP tunnel + if (dstip == nullptr) // It's P2MP tunnel { attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE; attr.value.s32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; @@ -385,27 +451,65 @@ remove_tunnel_termination(sai_object_id_t term_table_id) } } +//------------------- VxlanTunnel Implementation --------------------------// + +VxlanTunnel::VxlanTunnel(string name, IpAddress srcIp, IpAddress dstIp, tunnel_creation_src_t src) + :tunnel_name_(name), src_ip_(srcIp), dst_ip_(dstIp), src_creation_(src) +{ + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + + if (dstIp.isZero()) + { + tunnel_orch->addVTEP(this, srcIp); + vtep_ptr = NULL; + } + else if (src_creation_ == TNL_CREATION_SRC_EVPN) + { + vtep_ptr = tunnel_orch->getVTEP(srcIp); + tunnel_orch->addRemoveStateTableEntry(name,srcIp, dstIp, + src, true); + } +} + +VxlanTunnel::~VxlanTunnel() +{ + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + tunnel_orch->addRemoveStateTableEntry(tunnel_name_,src_ip_, dst_ip_, + src_creation_, false); +} + bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap, uint8_t encap_ttl) { try { sai_ip_address_t ips, ipd, *ip=nullptr; + uint8_t mapper_list = 0; swss::copy(ips, src_ip_); - ids_.tunnel_decap_id = SAI_NULL_OBJECT_ID; - ids_.tunnel_encap_id = SAI_NULL_OBJECT_ID; + // Only a single mapper type is created - if (decap != MAP_T::MAP_TO_INVALID) + if (decap == MAP_T::VNI_TO_BRIDGE) + { + TUNNELMAP_SET_BRIDGE(mapper_list); + } + else if (decap == MAP_T::VNI_TO_VLAN_ID) { - ids_.tunnel_decap_id = create_tunnel_map(decap); + TUNNELMAP_SET_VLAN(mapper_list); } + else + { + TUNNELMAP_SET_VRF(mapper_list); + } + + createMapperHw(mapper_list, (encap == MAP_T::MAP_TO_INVALID) ? + TUNNEL_MAP_USE_DECAP_ONLY: TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); + if (encap != MAP_T::MAP_TO_INVALID) { - ids_.tunnel_encap_id = create_tunnel_map(encap); ip = &ips; } - ids_.tunnel_id = create_tunnel(ids_.tunnel_encap_id, ids_.tunnel_decap_id, ip, gUnderlayIfId, encap_ttl); + ids_.tunnel_id = create_tunnel(&ids_, ip, NULL, gUnderlayIfId, false, encap_ttl); ip = nullptr; if (!dst_ip_.isZero()) @@ -429,17 +533,17 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap, uint8_t encap_ttl) return true; } -sai_object_id_t VxlanTunnel::addEncapMapperEntry(sai_object_id_t obj, uint32_t vni) +sai_object_id_t VxlanTunnel::addEncapMapperEntry(sai_object_id_t obj, uint32_t vni, tunnel_map_type_t type) { - const auto encap_id = getEncapMapId(); - const auto map_t = tunnel_map_.first; + const auto encap_id = getEncapMapId(type); + const auto map_t = tunnel_map_type(type,true); return create_tunnel_map_entry(map_t, encap_id, vni, 0, obj, true); } -sai_object_id_t VxlanTunnel::addDecapMapperEntry(sai_object_id_t obj, uint32_t vni) +sai_object_id_t VxlanTunnel::addDecapMapperEntry(sai_object_id_t obj, uint32_t vni, tunnel_map_type_t type) { - const auto decap_id = getDecapMapId(); - const auto map_t = tunnel_map_.second; + const auto decap_id = getDecapMapId(type); + const auto map_t = tunnel_map_type(type,false); return create_tunnel_map_entry(map_t, decap_id, vni, 0, obj); } @@ -458,19 +562,30 @@ std::pair VxlanTunnel::getMapperEntry(uint32_t return std::make_pair(SAI_NULL_OBJECT_ID, SAI_NULL_OBJECT_ID); } -void VxlanTunnel::updateNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni, sai_object_id_t nh_id) +void VxlanTunnel::updateNextHop(IpAddress& ipAddr, MacAddress macAddress, + uint32_t vni, sai_object_id_t nh_id) { auto key = nh_key_t(ipAddr, macAddress, vni); + SWSS_LOG_INFO("Update NH tunnel for ip %s, mac %s, vni %d", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni); + auto it = nh_tunnels_.find(key); if (it == nh_tunnels_.end()) { nh_tunnels_[key] = {nh_id, 1}; return; + } + else + { + SWSS_LOG_INFO("Dup Update NH tunnel for ip %s, mac %s, vni %d", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni); } + } -sai_object_id_t VxlanTunnel::getNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) const +sai_object_id_t VxlanTunnel::getNextHop(IpAddress& ipAddr, + MacAddress macAddress, uint32_t vni) const { auto key = nh_key_t(ipAddr, macAddress, vni); @@ -487,12 +602,20 @@ void VxlanTunnel::incNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, u { auto key = nh_key_t(ipAddr, macAddress, vni); nh_tunnels_[key].ref_count ++; + SWSS_LOG_INFO("refcnt increment NH tunnel for ip %s, mac %s, vni %d, ref_count %d", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni, + nh_tunnels_[key].ref_count); + } void VxlanTunnel::decNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) { auto key = nh_key_t(ipAddr, macAddress, vni); nh_tunnels_[key].ref_count --; + SWSS_LOG_INFO("refcnt decrement NH tunnel for ip %s, mac %s, vni %d, ref_count %d", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni, + nh_tunnels_[key].ref_count); + } bool VxlanTunnel::removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) @@ -502,11 +625,14 @@ bool VxlanTunnel::removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32 auto it = nh_tunnels_.find(key); if (it == nh_tunnels_.end()) { - SWSS_LOG_INFO("NH tunnel for '%s' doesn't exist", ipAddr.to_string().c_str()); + SWSS_LOG_INFO("remove NH tunnel for ip %s, mac %s, vni %d doesn't exist", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni); return false; } - SWSS_LOG_INFO("NH tunnel for ip '%s' ref_count '%d'", ipAddr.to_string().c_str(), nh_tunnels_[key].ref_count); + SWSS_LOG_INFO("remove NH tunnel for ip %s, mac %s, vni %d, ref_count %d", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni, + nh_tunnels_[key].ref_count); //Decrement ref count if already exists nh_tunnels_[key].ref_count --; @@ -515,6 +641,8 @@ bool VxlanTunnel::removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32 { if (sai_next_hop_api->remove_next_hop(nh_tunnels_[key].nh_id) != SAI_STATUS_SUCCESS) { + SWSS_LOG_INFO("delete NH tunnel for ip '%s', mac '%s' vni %d failed", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni); string err_msg = "NH tunnel delete failed for " + ipAddr.to_string(); throw std::runtime_error(err_msg); } @@ -522,23 +650,528 @@ bool VxlanTunnel::removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32 nh_tunnels_.erase(key); } - SWSS_LOG_INFO("NH tunnel for ip '%s', mac '%s' updated/deleted", - ipAddr.to_string().c_str(), macAddress.to_string().c_str()); + SWSS_LOG_INFO("NH tunnel for ip '%s', mac '%s' vni %d updated/deleted", + ipAddr.to_string().c_str(), macAddress.to_string().c_str(), vni); + + return true; +} + +bool VxlanTunnel::deleteMapperHw(uint8_t mapper_list, tunnel_map_use_t map_src) +{ + try + { + if (map_src == TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP) + { + if (IS_TUNNELMAP_SET_VLAN(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_decap_id[TUNNEL_MAP_T_VLAN]); + remove_tunnel_map(ids_.tunnel_encap_id[TUNNEL_MAP_T_VLAN]); + } + + if (IS_TUNNELMAP_SET_VRF(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_decap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER]); + remove_tunnel_map(ids_.tunnel_encap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER]); + } + + if (IS_TUNNELMAP_SET_BRIDGE(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_decap_id[TUNNEL_MAP_T_BRIDGE]); + remove_tunnel_map(ids_.tunnel_encap_id[TUNNEL_MAP_T_BRIDGE]); + } + } + else if (map_src == TUNNEL_MAP_USE_COMMON_DECAP_DEDICATED_ENCAP) + { + if (IS_TUNNELMAP_SET_VLAN(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_encap_id[TUNNEL_MAP_T_VLAN]); + } + + if (IS_TUNNELMAP_SET_VRF(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_encap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER]); + } + + if (IS_TUNNELMAP_SET_BRIDGE(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_encap_id[TUNNEL_MAP_T_BRIDGE]); + } + } + else if (map_src == TUNNEL_MAP_USE_DECAP_ONLY) + { + if (IS_TUNNELMAP_SET_VLAN(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_decap_id[TUNNEL_MAP_T_VLAN]); + } + + if (IS_TUNNELMAP_SET_VRF(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_decap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER]); + } + + if (IS_TUNNELMAP_SET_BRIDGE(mapper_list)) + { + remove_tunnel_map(ids_.tunnel_decap_id[TUNNEL_MAP_T_BRIDGE]); + } + } + } + + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error deleting mapper %s: %s", tunnel_name_.c_str(), error.what()); + return false; + } + + return true; +} + +bool VxlanTunnel::createMapperHw(uint8_t mapper_list, tunnel_map_use_t map_src) +{ + try + { + for (int i=TUNNEL_MAP_T_VLAN; igetDecapMapId(TUNNEL_MAP_T_VLAN); + ids_.tunnel_encap_id[TUNNEL_MAP_T_VLAN] = create_tunnel_map(MAP_T::VLAN_ID_TO_VNI); + TUNNELMAP_SET_VLAN(encap_dedicated_mappers_); + } + + if (IS_TUNNELMAP_SET_VRF(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER] = vtep_ptr->getDecapMapId(TUNNEL_MAP_T_VIRTUAL_ROUTER); + ids_.tunnel_encap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER] = create_tunnel_map(MAP_T::VRID_TO_VNI); + TUNNELMAP_SET_VRF(encap_dedicated_mappers_); + } + + if (IS_TUNNELMAP_SET_BRIDGE(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_BRIDGE] = vtep_ptr->getDecapMapId(TUNNEL_MAP_T_BRIDGE); + ids_.tunnel_encap_id[TUNNEL_MAP_T_BRIDGE] = create_tunnel_map(MAP_T::BRIDGE_TO_VNI); + TUNNELMAP_SET_BRIDGE(encap_dedicated_mappers_); + } + } + else if (TUNNEL_MAP_USE_COMMON_ENCAP_DECAP == map_src) + { + if (IS_TUNNELMAP_SET_VLAN(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_VLAN] = vtep_ptr->getDecapMapId(TUNNEL_MAP_T_VLAN); + ids_.tunnel_encap_id[TUNNEL_MAP_T_VLAN] = vtep_ptr->getEncapMapId(TUNNEL_MAP_T_VLAN); + } + + if (IS_TUNNELMAP_SET_VRF(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER] = vtep_ptr->getDecapMapId(TUNNEL_MAP_T_VIRTUAL_ROUTER); + ids_.tunnel_encap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER] = vtep_ptr->getEncapMapId(TUNNEL_MAP_T_VIRTUAL_ROUTER); + } + + if (IS_TUNNELMAP_SET_BRIDGE(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_BRIDGE] = vtep_ptr->getDecapMapId(TUNNEL_MAP_T_BRIDGE); + ids_.tunnel_encap_id[TUNNEL_MAP_T_BRIDGE] = vtep_ptr->getEncapMapId(TUNNEL_MAP_T_BRIDGE); + } + } + else if (TUNNEL_MAP_USE_DECAP_ONLY == map_src) + { + if (IS_TUNNELMAP_SET_VLAN(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_VLAN] = create_tunnel_map(MAP_T::VNI_TO_VLAN_ID); + TUNNELMAP_SET_VLAN(decap_dedicated_mappers_); + } + + if (IS_TUNNELMAP_SET_VRF(mapper_list)) + { + ids_.tunnel_decap_id[TUNNEL_MAP_T_VIRTUAL_ROUTER] = create_tunnel_map(MAP_T::VNI_TO_VRID); + TUNNELMAP_SET_VRF(decap_dedicated_mappers_); + } + + if (IS_TUNNELMAP_SET_BRIDGE(mapper_list)) + { + ids_.tunnel_encap_id[TUNNEL_MAP_T_BRIDGE] = create_tunnel_map(MAP_T::BRIDGE_TO_VNI); + TUNNELMAP_SET_BRIDGE(decap_dedicated_mappers_); + } + } + } + + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error creating tunnel %s: %s", tunnel_name_.c_str(), error.what()); + return false; + } + + return true; +} + +bool VxlanTunnel::deleteTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, + bool with_term) +{ + try + { + if (with_term) + { + remove_tunnel_termination(ids_.tunnel_term_id); + } + + remove_tunnel_termination(ids_.tunnel_id); + deleteMapperHw(mapper_list, map_src); + } + + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error deleting tunnel %s: %s", tunnel_name_.c_str(), error.what()); + return false; + } + + active_ = false; + + return true; +} + +//Creation of SAI Tunnel Object with multiple mapper types + +bool VxlanTunnel::createTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, + bool with_term) +{ + bool p2p = false; + + try + { + sai_ip_address_t ips, ipd, *ip=nullptr; + swss::copy(ips, src_ip_); + + createMapperHw(mapper_list, map_src); + + ip = nullptr; + if (!dst_ip_.isZero()) + { + swss::copy(ipd, dst_ip_); + ip = &ipd; + p2p = (src_creation_ == TNL_CREATION_SRC_EVPN)? true:false; + SWSS_LOG_WARN("creation src = %d",src_creation_); + } + + ids_.tunnel_id = create_tunnel(&ids_, &ips, ip, gUnderlayIfId, p2p); + + if (with_term) + { + ids_.tunnel_term_id = create_tunnel_termination(ids_.tunnel_id, ips, + ip, gVirtualRouterId); + } + + active_ = true; + } + + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error creating tunnel %s: %s", tunnel_name_.c_str(), error.what()); + return false; + } + + SWSS_LOG_INFO("Vxlan tunnel '%s' was created", tunnel_name_.c_str()); + return true; +} + +void VxlanTunnel::deletePendingSIPTunnel() +{ + if ((getDipTunnelCnt() == 0) && del_tnl_hw_pending) + { + uint8_t mapper_list=0; + + TUNNELMAP_SET_VLAN(mapper_list); + TUNNELMAP_SET_VRF(mapper_list); + deleteTunnelHw(mapper_list, TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); + del_tnl_hw_pending = false; + SWSS_LOG_INFO("Removing SIP Tunnel HW which is pending"); + } + + return; +} + +int VxlanTunnel::getDipTunnelCnt() +{ + int ret; + + ret = (int)tnl_users_.size(); + return ret; +} + +void VxlanTunnel::increment_spurious_imr_add(const std::string remote_vtep) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (it == tnl_users_.end()) + { + return ; + } + else + { + tnl_refcnts = it->second; + tnl_refcnts.spurious_add_imr_refcnt++; + tnl_users_[remote_vtep] = tnl_refcnts; + } +} + +void VxlanTunnel::increment_spurious_imr_del(const std::string remote_vtep) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (it == tnl_users_.end()) + { + return ; + } + else + { + tnl_refcnts = it->second; + tnl_refcnts.spurious_del_imr_refcnt++; + tnl_users_[remote_vtep] = tnl_refcnts; + } +} + +int VxlanTunnel::getDipTunnelRefCnt(const std::string remote_vtep) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (it == tnl_users_.end()) + { + return -1; + } + else + { + tnl_refcnts = it->second; + return (tnl_refcnts.imr_refcnt + tnl_refcnts.mac_refcnt + tnl_refcnts.ip_refcnt); + } +} + +int VxlanTunnel::getDipTunnelIMRRefCnt(const std::string remote_vtep) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (it == tnl_users_.end()) + { + return -1; + } + else + { + tnl_refcnts = it->second; + return (tnl_refcnts.imr_refcnt); + } +} + +int VxlanTunnel::getDipTunnelIPRefCnt(const std::string remote_vtep) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (it == tnl_users_.end()) + { + return -1; + } + else + { + tnl_refcnts = it->second; + return (tnl_refcnts.ip_refcnt); + } +} + +void VxlanTunnel::updateDipTunnelRefCnt(bool inc, tunnel_refcnt_t& tnl_refcnts, + tunnel_user_t usr) +{ + switch(usr) + { + case TUNNEL_USER_IMR: + { + if (inc) + { + tnl_refcnts.imr_refcnt++; + } + else + { + tnl_refcnts.imr_refcnt--; + } + + break; + } + case TUNNEL_USER_MAC: + { + if (inc) + { + tnl_refcnts.mac_refcnt++; + } + else + { + tnl_refcnts.mac_refcnt--; + } + + break; + } + case TUNNEL_USER_IP: + { + if (inc) + { + tnl_refcnts.ip_refcnt++; + } + else + { + tnl_refcnts.ip_refcnt--; + } + + break; + } + default : break; + } +} + +bool VxlanTunnel::createDynamicDIPTunnel(const std::string dip, tunnel_user_t usr) +{ + uint8_t mapper_list = 0; + tunnel_refcnt_t tnl_refcnts; + VxlanTunnel* dip_tunnel=NULL; + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + string tunnel_name; + + auto it = tnl_users_.find(dip); + if (it == tnl_users_.end()) + { + tunnel_orch->getTunnelNameFromDIP(dip, tunnel_name); + auto dipaddr = IpAddress(dip); + dip_tunnel = (new VxlanTunnel(tunnel_name, src_ip_, dipaddr, TNL_CREATION_SRC_EVPN)); + tunnel_orch->addTunnel(tunnel_name,dip_tunnel); + + memset(&tnl_refcnts,0,sizeof(tunnel_refcnt_t)); + updateDipTunnelRefCnt(true,tnl_refcnts,usr); + tnl_users_[dip] = tnl_refcnts; + + TUNNELMAP_SET_VLAN(mapper_list); + TUNNELMAP_SET_VRF(mapper_list); + dip_tunnel->createTunnelHw(mapper_list,TUNNEL_MAP_USE_COMMON_ENCAP_DECAP, false); + SWSS_LOG_NOTICE("Created P2P Tunnel remote IP %s ", dip.c_str()); + } + else + { + tnl_refcnts = it->second; + updateDipTunnelRefCnt(true,tnl_refcnts,usr); + tnl_users_[dip] = tnl_refcnts; + } + + return true; +} + +bool VxlanTunnel::deleteDynamicDIPTunnel(const std::string dip, tunnel_user_t usr, + bool update_refcnt) +{ + uint8_t mapper_list = 0; + tunnel_refcnt_t tnl_refcnts; + VxlanTunnel* dip_tunnel = NULL; + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port tunnelPort; + std::string tunnel_name; + + auto it = tnl_users_.find(dip); + if (it != tnl_users_.end()) + { + tnl_refcnts = it->second; + + if (update_refcnt) + { + updateDipTunnelRefCnt(false,tnl_refcnts,usr); + tnl_users_[dip] = tnl_refcnts; + } + + SWSS_LOG_INFO("diprefcnt = %d", + tnl_refcnts.imr_refcnt + tnl_refcnts.mac_refcnt + tnl_refcnts.ip_refcnt); + + if (tnl_refcnts.imr_refcnt + tnl_refcnts.mac_refcnt + tnl_refcnts.ip_refcnt) + { + return true; + } + + if (tunnel_orch->getTunnelPort(dip, tunnelPort)) + { + SWSS_LOG_NOTICE("DIP = %s Not deleting tunnel from HW as tunnelPort is not yet deleted. fdbcount = %d", + dip.c_str(),tunnelPort.m_fdb_count); + return true; + } + + tunnel_orch->getTunnelNameFromDIP(dip, tunnel_name); + dip_tunnel = tunnel_orch->getVxlanTunnel(tunnel_name); + if (!dip_tunnel) + { + SWSS_LOG_INFO("DIP Tunnel is NULL unexpected"); + return false; + } + + TUNNELMAP_SET_VLAN(mapper_list); + TUNNELMAP_SET_VRF(mapper_list); + dip_tunnel->deleteTunnelHw(mapper_list,TUNNEL_MAP_USE_COMMON_ENCAP_DECAP, false); + + tnl_users_.erase(dip); + + tunnel_orch->delTunnel(tunnel_name); + SWSS_LOG_NOTICE("P2P Tunnel deleted : %s", tunnel_name.c_str()); + } + else + { + SWSS_LOG_WARN("Unable to find dynamic tunnel for deletion"); + } return true; } +//------------------- VxlanTunnelOrch Implementation --------------------------// + sai_object_id_t -VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) +VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, + MacAddress macAddress, uint32_t vni) { SWSS_LOG_ENTER(); - if(!isTunnelExists(tunnelName)) + if (!isTunnelExists(tunnelName)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); return SAI_NULL_OBJECT_ID; } + SWSS_LOG_NOTICE("NH tunnel create for %s, ip %s, mac %s, vni %d", + tunnelName.c_str(), ipAddr.to_string().c_str(), + macAddress.to_string().c_str(), vni); + auto tunnel_obj = getVxlanTunnel(tunnelName); sai_object_id_t nh_id, tunnel_id = tunnel_obj->getTunnelId(); @@ -576,7 +1209,11 @@ VxlanTunnelOrch::removeNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAd { SWSS_LOG_ENTER(); - if(!isTunnelExists(tunnelName)) + SWSS_LOG_NOTICE("NH tunnel remove for %s, ip %s, mac %s, vni %d", + tunnelName.c_str(), ipAddr.to_string().c_str(), + macAddress.to_string().c_str(), vni); + + if (!isTunnelExists(tunnelName)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); return false; @@ -593,7 +1230,7 @@ bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t { SWSS_LOG_ENTER(); - if(!isTunnelExists(tunnelName)) + if (!isTunnelExists(tunnelName)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); return false; @@ -613,6 +1250,8 @@ bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t } } + tunnel_obj->vlan_vrf_vni_count++; + try { /* @@ -641,7 +1280,7 @@ bool VxlanTunnelOrch::removeVxlanTunnelMap(string tunnelName, uint32_t vni) { SWSS_LOG_ENTER(); - if(!isTunnelExists(tunnelName)) + if (!isTunnelExists(tunnelName)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); return false; @@ -675,6 +1314,35 @@ bool VxlanTunnelOrch::removeVxlanTunnelMap(string tunnelName, uint32_t vni) return false; } + // Update the map count and if this is the last mapping entry + // make SAI calls to delete the tunnel and tunnel termination objects. + + tunnel_obj->vlan_vrf_vni_count--; + if (tunnel_obj->vlan_vrf_vni_count == 0) + { + auto tunnel_term_id = vxlan_tunnel_table_[tunnelName].get()->getTunnelTermId(); + try + { + remove_tunnel_termination(tunnel_term_id); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error removing tunnel term entry. Tunnel: %s. Error: %s", tunnelName.c_str(), error.what()); + return false; + } + + auto tunnel_id = vxlan_tunnel_table_[tunnelName].get()->getTunnelId(); + try + { + remove_tunnel(tunnel_id); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error removing tunnel entry. Tunnel: %s. Error: %s", tunnelName.c_str(), error.what()); + return false; + } + } + SWSS_LOG_NOTICE("Vxlan map entry deleted for tunnel '%s' with vni '%d'", tunnelName.c_str(), vni); return true; } @@ -689,7 +1357,7 @@ bool VxlanTunnelOrch::addOperation(const Request& request) auto attr_names = request.getAttrFieldNames(); if (attr_names.count("dst_ip") == 0) { - if(src_ip.isV4()) { + if (src_ip.isV4()) { dst_ip = IpAddress("0.0.0.0"); } else { dst_ip = IpAddress("::"); @@ -698,22 +1366,21 @@ bool VxlanTunnelOrch::addOperation(const Request& request) else { dst_ip = request.getAttrIP("dst_ip"); - if((src_ip.isV4() && !dst_ip.isV4()) || + if ((src_ip.isV4() && !dst_ip.isV4()) || (!src_ip.isV4() && dst_ip.isV4())) { SWSS_LOG_ERROR("Format mismatch: 'src_ip' and 'dst_ip' must be of the same family"); return true; } } - const auto& tunnel_name = request.getKeyString(0); - if(isTunnelExists(tunnel_name)) + if (isTunnelExists(tunnel_name)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' is already exists", tunnel_name.c_str()); return true; } - vxlan_tunnel_table_[tunnel_name] = std::unique_ptr(new VxlanTunnel(tunnel_name, src_ip, dst_ip)); + vxlan_tunnel_table_[tunnel_name] = std::unique_ptr(new VxlanTunnel(tunnel_name, src_ip, dst_ip, TNL_CREATION_SRC_CLI)); SWSS_LOG_NOTICE("Vxlan tunnel '%s' was added", tunnel_name.c_str()); return true; @@ -725,48 +1392,291 @@ bool VxlanTunnelOrch::delOperation(const Request& request) const auto& tunnel_name = request.getKeyString(0); - if(!isTunnelExists(tunnel_name)) + if (!isTunnelExists(tunnel_name)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); return true; } - auto tunnel_term_id = vxlan_tunnel_table_[tunnel_name].get()->getTunnelTermId(); - try + auto vtep_ptr = getVxlanTunnel(tunnel_name); + if (vtep_ptr && vtep_ptr->del_tnl_hw_pending) { - remove_tunnel_termination(tunnel_term_id); + SWSS_LOG_WARN("VTEP %s not deleted as hw delete is pending", tunnel_name.c_str()); + return false; } - catch(const std::runtime_error& error) + + vxlan_tunnel_table_.erase(tunnel_name); + + SWSS_LOG_NOTICE("Vxlan tunnel '%s' was removed", tunnel_name.c_str()); + + return true; +} + +bool VxlanTunnelOrch::addTunnelUser(const std::string remote_vtep, uint32_t vni_id, + uint32_t vlan, tunnel_user_t usr, + sai_object_id_t vrf_id) +{ + EvpnNvoOrch* evpn_orch = gDirectory.get(); + VxlanTunnel* dip_tunnel=NULL; + Port tunport; + string tunnel_name; + + if (TUNNEL_USER_MAC == usr) return true; + + auto vtep_ptr = evpn_orch->getEVPNVtep(); + + if (!vtep_ptr) { - SWSS_LOG_ERROR("Error removing tunnel term entry. Tunnel: %s. Error: %s", tunnel_name.c_str(), error.what()); + SWSS_LOG_WARN("Unable to find EVPN VTEP. user=%d remote_vtep=%s", + usr,remote_vtep.c_str()); return false; } - auto tunnel_id = vxlan_tunnel_table_[tunnel_name].get()->getTunnelId(); - try + if (!vtep_ptr->isActive()) { - remove_tunnel(tunnel_id); + SWSS_LOG_WARN("VTEP not yet active.user=%d remote_vtep=%s", + usr,remote_vtep.c_str()); + return false; } - catch(const std::runtime_error& error) + + vtep_ptr->createDynamicDIPTunnel(remote_vtep, usr); + + getTunnelNameFromDIP(remote_vtep, tunnel_name); + dip_tunnel = getVxlanTunnel(tunnel_name); + + SWSS_LOG_NOTICE("diprefcnt for remote %s = %d", + remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); + + if (!getTunnelPort(remote_vtep, tunport)) { - SWSS_LOG_ERROR("Error removing tunnel entry. Tunnel: %s. Error: %s", tunnel_name.c_str(), error.what()); - return false; + Port tunnelPort; + auto port_tunnel_name = getTunnelPortName(remote_vtep); + gPortsOrch->addTunnel(port_tunnel_name,dip_tunnel->getTunnelId(), false); + gPortsOrch->getPort(port_tunnel_name,tunnelPort); + gPortsOrch->addBridgePort(tunnelPort); } - vxlan_tunnel_table_.erase(tunnel_name); + return true; +} - SWSS_LOG_NOTICE("Vxlan tunnel '%s' was removed", tunnel_name.c_str()); +bool VxlanTunnelOrch::delTunnelUser(const std::string remote_vtep, uint32_t vni_id, + uint32_t vlan, tunnel_user_t usr, + sai_object_id_t vrf_id) +{ + if (TUNNEL_USER_MAC == usr) return true; + + auto port_tunnel_name = getTunnelPortName(remote_vtep); + EvpnNvoOrch* evpn_orch = gDirectory.get(); + + auto vtep_ptr = evpn_orch->getEVPNVtep(); + + if (!vtep_ptr) + { + SWSS_LOG_WARN("Unable to find VTEP. remote=%s vlan=%d usr=%d", + remote_vtep.c_str(), vlan, usr); + return true; + } + + Port tunnelPort; + gPortsOrch->getPort(port_tunnel_name,tunnelPort); + + if ((vtep_ptr->getDipTunnelRefCnt(remote_vtep) == 1) && + tunnelPort.m_fdb_count == 0) + { + bool ret; + + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for remote = %s fdbcount = %d", + remote_vtep.c_str(), tunnelPort.m_fdb_count); + return true; + } + + gPortsOrch->removeTunnel(tunnelPort); + } + + vtep_ptr->deleteDynamicDIPTunnel(remote_vtep, usr); + SWSS_LOG_NOTICE("diprefcnt for remote %s = %d", + remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); + + vtep_ptr->deletePendingSIPTunnel(); return true; } +void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) +{ + bool ret; + EvpnNvoOrch* evpn_orch = gDirectory.get(); + std::string remote_vtep; + int refcnt; + + auto vtep_ptr = evpn_orch->getEVPNVtep(); + + if (!vtep_ptr) + { + SWSS_LOG_WARN("Unable to find VTEP. tunnelPort=%s",tunnelPort.m_alias.c_str()); + return; + } + + getTunnelDIPFromPort(tunnelPort, remote_vtep); + + //If there are IMR/IP routes to the remote VTEP then ignore this call + refcnt = vtep_ptr->getDipTunnelRefCnt(remote_vtep); + if (refcnt > 0) + { + SWSS_LOG_INFO("Tunnel bridge port not removed. remote = %s refcnt = %d", + remote_vtep.c_str(), refcnt); + return; + } + + // Remove Bridge port and Port objects for this DIP tunnel + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for remote = %s fdbcount = %d", + remote_vtep.c_str(), tunnelPort.m_fdb_count); + return; + } + gPortsOrch->removeTunnel(tunnelPort); + + // Remove DIP Tunnel HW + vtep_ptr->deleteDynamicDIPTunnel(remote_vtep, TUNNEL_USER_IMR, false); + SWSS_LOG_NOTICE("diprefcnt for remote %s = %d", + remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); + + // Remove SIP Tunnel HW which might be pending on delete + vtep_ptr->deletePendingSIPTunnel(); + + return ; +} + +std::string VxlanTunnelOrch::getTunnelPortName(const std::string& remote_vtep) +{ + std::string tunnelPortName = "Port_EVPN_" + remote_vtep; + return tunnelPortName; +} + +void VxlanTunnelOrch::getTunnelNameFromDIP(const string& dip, string& tunnel_name) +{ + tunnel_name = "EVPN_" + dip; + return; +} + +void VxlanTunnelOrch::getTunnelNameFromPort(string& tunnel_portname, string& tunnel_name) +{ + tunnel_name = tunnel_portname; + tunnel_name.erase(0, sizeof("Port_")-1); + + SWSS_LOG_DEBUG("tunnel name = %s",tunnel_name.c_str()); + + return; +} + +void VxlanTunnelOrch:: getTunnelDIPFromPort(Port& tunnelPort, string& remote_vtep) +{ + remote_vtep = tunnelPort.m_alias; + remote_vtep.erase(0,sizeof("Port_EVPN_")-1); +} + + +void VxlanTunnelOrch::updateDbTunnelOperStatus(string tunnel_portname, + sai_port_oper_status_t status) +{ + std::vector fvVector; + std::string tunnel_name; + + if (status == SAI_PORT_OPER_STATUS_UP) + { + fvVector.emplace_back("operstatus", "up"); + } + else + { + fvVector.emplace_back("operstatus", "down"); + } + + getTunnelNameFromPort(tunnel_portname, tunnel_name); + + m_stateVxlanTable.set(tunnel_name, fvVector); +} + +void VxlanTunnelOrch::addRemoveStateTableEntry(string tunnel_name, + IpAddress& sip, IpAddress& dip, + tunnel_creation_src_t src, bool add) + +{ + std::vector fvVector, tmpFvVector; + WarmStart::WarmStartState state; + + WarmStart::getWarmStartState("orchagent",state); + + if (add) + { + // Add tunnel entry only for non-warmboot case or WB with new tunnel coming up + // during WB + if ( (state != WarmStart::INITIALIZED) || + !m_stateVxlanTable.get(tunnel_name, tmpFvVector)) + { + fvVector.emplace_back("src_ip", (sip.to_string()).c_str()); + fvVector.emplace_back("dst_ip", (dip.to_string()).c_str()); + + if (src == TNL_CREATION_SRC_CLI) + { + fvVector.emplace_back("tnl_src", "CLI"); + } + else + { + fvVector.emplace_back("tnl_src", "EVPN"); + } + + fvVector.emplace_back("operstatus", "down"); + m_stateVxlanTable.set(tunnel_name, fvVector); + SWSS_LOG_INFO("adding tunnel %s during warmboot", tunnel_name.c_str()); + } + else + { + SWSS_LOG_NOTICE("Skip adding tunnel %s during warmboot", tunnel_name.c_str()); + } + } + else + { + m_stateVxlanTable.del(tunnel_name); + } +} + +bool VxlanTunnelOrch::getTunnelPort(const std::string& remote_vtep,Port& tunnelPort) +{ + auto port_tunnel_name = getTunnelPortName(remote_vtep); + + bool ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); + + SWSS_LOG_INFO("getTunnelPort and getPort return ret=%d name=%s", + ret,port_tunnel_name.c_str()); + + return ret; +} + +//------------------- VXLAN_TUNNEL_MAP Table --------------------------// + bool VxlanTunnelMapOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); - auto vlan_id = request.getAttrVlan("vlan"); + sai_vlan_id_t vlan_id = (sai_vlan_id_t)request.getAttrVlan("vlan"); Port tempPort; - if(!gPortsOrch->getVlanByVlanId(vlan_id, tempPort)) + + const auto full_tunnel_map_entry_name = request.getFullKey(); + SWSS_LOG_INFO("Full name = %s",full_tunnel_map_entry_name.c_str()); + + if (isTunnelMapExists(full_tunnel_map_entry_name)) + { + SWSS_LOG_ERROR("Vxlan tunnel map '%s' already exist", + full_tunnel_map_entry_name.c_str()); + return true; + } + + if (!gPortsOrch->getVlanByVlanId(vlan_id, tempPort)) { SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); return false; @@ -779,6 +1689,8 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) return true; } + tempPort.m_vnid = (uint32_t) vni_id; + auto tunnel_name = request.getKeyString(0); VxlanTunnelOrch* tunnel_orch = gDirectory.get(); if (!tunnel_orch->isTunnelExists(tunnel_name)) @@ -788,35 +1700,48 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) } auto tunnel_obj = tunnel_orch->getVxlanTunnel(tunnel_name); - if (!tunnel_obj->isActive()) + + // The hw delete is pending due to an earlier incomplete operation. + // process this add event when the deletion is complete. + if (tunnel_obj->del_tnl_hw_pending) { - //@Todo, currently only decap mapper is allowed - tunnel_obj->createTunnel(MAP_T::MAP_TO_INVALID, MAP_T::VNI_TO_VLAN_ID); + SWSS_LOG_WARN("Tunnel Mapper deletion is pending"); + return false; } - const auto full_tunnel_map_entry_name = request.getFullKey(); - if (isTunnelMapExists(full_tunnel_map_entry_name)) + if (!tunnel_obj->isActive()) { - SWSS_LOG_NOTICE("Vxlan tunnel map '%s' already exist", full_tunnel_map_entry_name.c_str()); - return true; + //@Todo, currently only decap mapper is allowed + //tunnel_obj->createTunnel(MAP_T::MAP_TO_INVALID, MAP_T::VNI_TO_VLAN_ID); + uint8_t mapper_list = 0; + TUNNELMAP_SET_VLAN(mapper_list); + TUNNELMAP_SET_VRF(mapper_list); + tunnel_obj->createTunnelHw(mapper_list,TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); } - const auto tunnel_map_id = tunnel_obj->getDecapMapId(); + const auto tunnel_map_id = tunnel_obj->getDecapMapId(TUNNEL_MAP_T_VLAN); const auto tunnel_map_entry_name = request.getKeyString(1); + tunnel_obj->vlan_vrf_vni_count++; + SWSS_LOG_INFO("vni count increased to %d",tunnel_obj->vlan_vrf_vni_count); + try { auto tunnel_map_entry_id = create_tunnel_map_entry(MAP_T::VNI_TO_VLAN_ID, tunnel_map_id, vni_id, vlan_id); - vxlan_tunnel_map_table_[full_tunnel_map_entry_name] = tunnel_map_entry_id; + vxlan_tunnel_map_table_[full_tunnel_map_entry_name].map_entry_id = tunnel_map_entry_id; + vxlan_tunnel_map_table_[full_tunnel_map_entry_name].vlan_id = vlan_id; + vxlan_tunnel_map_table_[full_tunnel_map_entry_name].vni_id = vni_id; } catch(const std::runtime_error& error) { - SWSS_LOG_ERROR("Error adding tunnel map entry. Tunnel: %s. Entry: %s. Error: %s", + SWSS_LOG_WARN("Error adding tunnel map entry. Tunnel: %s. Entry: %s. Error: %s", tunnel_name.c_str(), tunnel_map_entry_name.c_str(), error.what()); return false; } + tunnel_orch->addVlanMappedToVni(vni_id, vlan_id); + SWSS_LOG_NOTICE("Vxlan tunnel map entry '%s' for tunnel '%s' was created", tunnel_map_entry_name.c_str(), tunnel_name.c_str()); @@ -827,6 +1752,7 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) { SWSS_LOG_ENTER(); + Port vlanPort; const auto& tunnel_name = request.getKeyString(0); const auto& tunnel_map_entry_name = request.getKeyString(1); const auto& full_tunnel_map_entry_name = request.getFullKey(); @@ -838,7 +1764,16 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) return true; } - auto tunnel_map_entry_id = vxlan_tunnel_map_table_[full_tunnel_map_entry_name]; + auto vlan_id = (sai_vlan_id_t) vxlan_tunnel_map_table_[full_tunnel_map_entry_name].vlan_id; + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_ERROR("Delete VLAN-VNI map.vlan id doesn't exist: %d", vlan_id); + return true; + } + + vlanPort.m_vnid = (uint32_t) VNID_NONE; + + auto tunnel_map_entry_id = vxlan_tunnel_map_table_[full_tunnel_map_entry_name].map_entry_id; try { remove_tunnel_map_entry(tunnel_map_entry_id); @@ -851,12 +1786,62 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) vxlan_tunnel_map_table_.erase(full_tunnel_map_entry_name); + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); + return false; + } + + auto tunnel_obj = tunnel_orch->getVxlanTunnel(tunnel_name); + tunnel_obj->vlan_vrf_vni_count--; + + SWSS_LOG_NOTICE("vni count = %d",tunnel_obj->vlan_vrf_vni_count); + + // Update the map count and if this is the last mapping entry + // make SAI calls to delete the tunnel and tunnel termination objects. + + if (tunnel_obj->vlan_vrf_vni_count == 0) + { + // If there are Dynamic DIP Tunnels referring to this SIP Tunnel + // then mark it as pending for delete. + if (tunnel_obj->getDipTunnelCnt() == 0) + { + uint8_t mapper_list=0; + TUNNELMAP_SET_VLAN(mapper_list); + TUNNELMAP_SET_VRF(mapper_list); + tunnel_obj->deleteTunnelHw(mapper_list, TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); + } + else + { + tunnel_obj->del_tnl_hw_pending = true; + SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion DIP Tunnel count = %d", + tunnel_obj->getDipTunnelCnt()); + } + } + + vector map_entries = tokenize(tunnel_map_entry_name, '_'); + SWSS_LOG_INFO("Vxlan tunnel map '%s' size %ld", tunnel_map_entry_name.c_str(), + map_entries.size()); + if (map_entries.size() == 3) + { + SWSS_LOG_INFO("Vxlan tunnel map %s, %s, %s ", map_entries[0].c_str(), + map_entries[1].c_str(), + map_entries[2].c_str()); + uint32_t vni_id = static_cast(stoul(map_entries[1])); + if (vni_id) + { + tunnel_orch->delVlanMappedToVni(vni_id); + } + } SWSS_LOG_NOTICE("Vxlan tunnel map entry '%s' for tunnel '%s' was removed", tunnel_map_entry_name.c_str(), tunnel_name.c_str()); return true; } +//------------------- VXLAN_VRF_MAP Table --------------------------// + bool VxlanVrfMapOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); @@ -889,9 +1874,14 @@ bool VxlanVrfMapOrch::addOperation(const Request& request) string vrf_name = request.getAttrString("vrf"); VRFOrch* vrf_orch = gDirectory.get(); + SWSS_LOG_NOTICE("VRF VNI mapping '%s' update vrf %s, vni %d", + full_map_entry_name.c_str(), vrf_name.c_str(), vni_id); if (vrf_orch->isVRFexists(vrf_name)) { - tunnel_obj->createTunnel(MAP_T::VRID_TO_VNI, MAP_T::VNI_TO_VRID); + if (!tunnel_obj->isActive()) + { + tunnel_obj->createTunnel(MAP_T::VRID_TO_VNI, MAP_T::VNI_TO_VRID); + } vrf_id = vrf_orch->getVRFid(vrf_name); } else @@ -932,7 +1922,242 @@ bool VxlanVrfMapOrch::delOperation(const Request& request) { SWSS_LOG_ENTER(); - SWSS_LOG_ERROR("DEL operation is not implemented"); + VRFOrch* vrf_orch = gDirectory.get(); + const auto full_map_entry_name = request.getFullKey(); + + if (!isVrfMapExists(full_map_entry_name)) + { + SWSS_LOG_ERROR("VxlanVrfMapOrch Vxlan map '%s' do not exist", full_map_entry_name.c_str()); + return false; + } + + size_t pos = full_map_entry_name.find("Vrf"); + if (pos == string::npos) + { + SWSS_LOG_ERROR("VxlanVrfMapOrch no VRF in Vxlan map '%s'", full_map_entry_name.c_str()); + return false; + } + string vrf_name = full_map_entry_name.substr(pos); + + if (!vrf_orch->isVRFexists(vrf_name)) + { + SWSS_LOG_ERROR("VxlanVrfMapOrch VRF '%s' not present", vrf_name.c_str()); + return false; + } + SWSS_LOG_NOTICE("VxlanVrfMapOrch VRF VNI mapping '%s' remove vrf %s", full_map_entry_name.c_str(), vrf_name.c_str()); + vrf_map_entry_t entry; + try + { + /* + * Remove encap and decap mapper + */ + entry = vxlan_vrf_table_[full_map_entry_name]; + + SWSS_LOG_NOTICE("VxlanVrfMapOrch Vxlan tunnel VRF encap entry '%lx' decap entry '0x%lx'", + entry.encap_id, entry.decap_id); + + remove_tunnel_map_entry(entry.encap_id); + vrf_orch->decreaseVrfRefCount(vrf_name); + remove_tunnel_map_entry(entry.decap_id); + vrf_orch->decreaseVrfRefCount(vrf_name); + vxlan_vrf_table_.erase(full_map_entry_name); + vxlan_vrf_tunnel_.erase(vrf_name); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("VxlanVrfMapOrch Error removing tunnel map entry. Entry: %s. Error: %s", + full_map_entry_name.c_str(), error.what()); + return false; + } + + return true; +} + +//------------------- EVPN_REMOTE_VNI Table --------------------------// + +bool EvpnRemoteVniOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + // Extract DIP and tunnel + auto remote_vtep = request.getKeyString(1); + + // Extract VLAN and VNI + auto vlan_name = request.getKeyString(0); + sai_vlan_id_t vlan_id = (sai_vlan_id_t) stoi(vlan_name.substr(4)); + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= 1<<24) + { + SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); + return true; + } + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port tunnelPort, vlanPort; + + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + return false; + } + + if (tunnel_orch->getTunnelPort(remote_vtep,tunnelPort)) + { + SWSS_LOG_INFO("Vxlan tunnelPort exists: %s", remote_vtep.c_str()); + + if (gPortsOrch->isVlanMember(vlanPort, tunnelPort)) + { + EvpnNvoOrch* evpn_orch = gDirectory.get(); + auto vtep_ptr = evpn_orch->getEVPNVtep(); + if (!vtep_ptr) + { + SWSS_LOG_WARN("Remote VNI add: VTEP not found. remote=%s vid=%d", + remote_vtep.c_str(),vlan_id); + return true; + } + SWSS_LOG_WARN("tunnelPort %s already member of vid %d", + remote_vtep.c_str(),vlan_id); + vtep_ptr->increment_spurious_imr_add(remote_vtep); + return true; + } + } + + tunnel_orch->addTunnelUser(remote_vtep, vni_id, vlan_id, TUNNEL_USER_IMR); + + if (!tunnel_orch->getTunnelPort(remote_vtep,tunnelPort)) + { + SWSS_LOG_WARN("Vxlan tunnelPort doesn't exist: %s", remote_vtep.c_str()); + return false; + } + + // SAI Call to add tunnel to the VLAN flood domain + + string tagging_mode = "untagged"; + gPortsOrch->addVlanMember(vlanPort, tunnelPort, tagging_mode); + + SWSS_LOG_INFO("remote_vtep=%s vni=%d vlanid=%d ", + remote_vtep.c_str(), vni_id, vlan_id); + + return true; +} + +bool EvpnRemoteVniOrch::delOperation(const Request& request) +{ + bool ret; + + SWSS_LOG_ENTER(); + + // Extract DIP and tunnel + auto remote_vtep = request.getKeyString(1); + + // Extract VLAN and VNI + auto vlan_name = request.getKeyString(0); + sai_vlan_id_t vlan_id = (sai_vlan_id_t)stoi(vlan_name.substr(4)); + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= 1<<24) + { + SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); + return true; + } + + // SAI Call to add tunnel to the VLAN flood domain + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port vlanPort, tunnelPort; + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + return true; + } + + if (!tunnel_orch->getTunnelPort(remote_vtep,tunnelPort)) + { + SWSS_LOG_WARN("RemoteVniDel getTunnelPort Fails: %s", remote_vtep.c_str()); + return true; + } + + EvpnNvoOrch* evpn_orch = gDirectory.get(); + auto vtep_ptr = evpn_orch->getEVPNVtep(); + + if (!vtep_ptr) + { + SWSS_LOG_WARN("Remote VNI del: VTEP not found. remote=%s vid=%d", + remote_vtep.c_str(),vlan_id); + return true; + } + + if (!gPortsOrch->isVlanMember(vlanPort, tunnelPort)) + { + SWSS_LOG_WARN("marking it as spurious tunnelPort %s not a member of vid %d", + remote_vtep.c_str(), vlan_id); + vtep_ptr->increment_spurious_imr_del(remote_vtep); + return true; + } + + if (gPortsOrch->isVlanMember(vlanPort, tunnelPort)) + { + if (!gPortsOrch->removeVlanMember(vlanPort, tunnelPort)) + { + SWSS_LOG_WARN("RemoteVniDel remove vlan member fails: %s",remote_vtep.c_str()); + return true; + } + } + + SWSS_LOG_INFO("imrcount=%d fdbcount=%d ", + vtep_ptr->getDipTunnelIMRRefCnt(remote_vtep), + tunnelPort.m_fdb_count ); + + ret = tunnel_orch->delTunnelUser(remote_vtep, vni_id, vlan_id, TUNNEL_USER_IMR); + + SWSS_LOG_INFO("remote_vtep=%s vni=%d vlanid=%d ", + remote_vtep.c_str(), vni_id, vlan_id); + + + return ret; +} + +//------------------- EVPN_NVO Table --------------------------// + +bool EvpnNvoOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto nvo_name = request.getKeyString(0); + auto vtep_name = request.getAttrString("source_vtep"); + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + + source_vtep_ptr = tunnel_orch->getVxlanTunnel(vtep_name); + + SWSS_LOG_INFO("evpnnvo: %s vtep : %s \n",nvo_name.c_str(), vtep_name.c_str()); return true; } + +bool EvpnNvoOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto nvo_name = request.getKeyString(0); + + if (!source_vtep_ptr) + { + SWSS_LOG_WARN("NVO Delete failed as VTEP Ptr is NULL"); + return true; + } + + if (source_vtep_ptr->del_tnl_hw_pending) + { + SWSS_LOG_WARN("NVO not deleted as hw delete is pending"); + return false; + } + + source_vtep_ptr = NULL; + + SWSS_LOG_INFO("NVO: %s \n",nvo_name.c_str()); + + return true; +} + diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index 008175f92b..edc65d97fe 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -19,10 +19,40 @@ enum class MAP_T VNI_TO_BRIDGE }; +typedef enum +{ + TUNNEL_MAP_T_VLAN=0, + TUNNEL_MAP_T_BRIDGE, + TUNNEL_MAP_T_VIRTUAL_ROUTER, + TUNNEL_MAP_T_MAX_MAPPER, +} tunnel_map_type_t; + +#define TUNNELMAP_SET_VLAN(x) ((x)|= (1<> TunnelMapEntries; typedef std::unordered_map TunnelNHs; +typedef std::map TunnelUsers; class VxlanTunnel { public: - VxlanTunnel(string name, IpAddress srcIp, IpAddress dstIp) - :tunnel_name_(name), src_ip_(srcIp), dst_ip_(dstIp) { } + VxlanTunnel(string name, IpAddress srcIp, IpAddress dstIp, tunnel_creation_src_t src); + ~VxlanTunnel(); bool isActive() const { @@ -83,8 +138,10 @@ class VxlanTunnel } bool createTunnel(MAP_T encap, MAP_T decap, uint8_t encap_ttl=0); - sai_object_id_t addEncapMapperEntry(sai_object_id_t obj, uint32_t vni); - sai_object_id_t addDecapMapperEntry(sai_object_id_t obj, uint32_t vni); + sai_object_id_t addEncapMapperEntry(sai_object_id_t obj, uint32_t vni, + tunnel_map_type_t type=TUNNEL_MAP_T_VIRTUAL_ROUTER); + sai_object_id_t addDecapMapperEntry(sai_object_id_t obj, uint32_t vni, + tunnel_map_type_t type=TUNNEL_MAP_T_VIRTUAL_ROUTER); void insertMapperEntry(sai_object_id_t encap, sai_object_id_t decap, uint32_t vni); std::pair getMapperEntry(uint32_t vni); @@ -94,14 +151,19 @@ class VxlanTunnel return ids_.tunnel_id; } - sai_object_id_t getDecapMapId() const + sai_object_id_t getDecapMapId(tunnel_map_type_t type) const { - return ids_.tunnel_decap_id; + return ids_.tunnel_decap_id[type]; } - sai_object_id_t getEncapMapId() const + sai_object_id_t getEncapMapId(tunnel_map_type_t type) const { - return ids_.tunnel_encap_id; + return ids_.tunnel_encap_id[type]; + } + + string getTunnelName() const + { + return tunnel_name_; } sai_object_id_t getTunnelTermId() const @@ -117,11 +179,30 @@ class VxlanTunnel void incNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); void decNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); + bool deleteMapperHw(uint8_t mapper_list, tunnel_map_use_t map_src); + bool createMapperHw(uint8_t mapper_list, tunnel_map_use_t map_src); + bool createTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, bool with_term = true); + bool deleteTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, bool with_term = true); + void deletePendingSIPTunnel(); + void increment_spurious_imr_add(const std::string remote_vtep); + void increment_spurious_imr_del(const std::string remote_vtep); + void updateDipTunnelRefCnt(bool , tunnel_refcnt_t& , tunnel_user_t ); + // Total Routes using the DIP tunnel. + int getDipTunnelRefCnt(const std::string); + int getDipTunnelIMRRefCnt(const std::string); + int getDipTunnelIPRefCnt(const std::string); + // Total DIP tunnels associated with this SIP tunnel. + int getDipTunnelCnt(); + bool createDynamicDIPTunnel(const string dip, tunnel_user_t usr); + bool deleteDynamicDIPTunnel(const string dip, tunnel_user_t usr, bool update_refcnt = true); + uint32_t vlan_vrf_vni_count = 0; + bool del_tnl_hw_pending = false; + private: string tunnel_name_; bool active_ = false; - tunnel_ids_t ids_ = {0, 0, 0, 0}; + tunnel_ids_t ids_ = {{0}, {0}, 0, 0}; std::pair tunnel_map_ = { MAP_T::MAP_TO_INVALID, MAP_T::MAP_TO_INVALID }; TunnelMapEntries tunnel_map_entries_; @@ -129,6 +210,12 @@ class VxlanTunnel IpAddress src_ip_; IpAddress dst_ip_ = 0x0; + + TunnelUsers tnl_users_; + VxlanTunnel* vtep_ptr=NULL; + tunnel_creation_src_t src_creation_; + uint8_t encap_dedicated_mappers_ = 0; + uint8_t decap_dedicated_mappers_ = 0; }; const request_description_t vxlan_tunnel_request_description = { @@ -148,18 +235,17 @@ class VxlanTunnelRequest : public Request typedef std::unique_ptr VxlanTunnel_T; typedef std::map VxlanTunnelTable; - -typedef enum -{ - TUNNEL_MAP_T_VLAN, - TUNNEL_MAP_T_BRIDGE, - TUNNEL_MAP_T_VIRTUAL_ROUTER, -} tunnel_map_type_t; +typedef std::map VxlanVniVlanMapTable; +typedef std::map VTEPTable; class VxlanTunnelOrch : public Orch2 { public: - VxlanTunnelOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName) : + Orch2(db, tableName, request_), + m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME) + {} + bool isTunnelExists(const std::string& tunnelName) const { @@ -171,6 +257,34 @@ class VxlanTunnelOrch : public Orch2 return vxlan_tunnel_table_.at(tunnelName).get(); } + bool addTunnel(const std::string tunnel_name,VxlanTunnel* tnlptr) + { + vxlan_tunnel_table_[tunnel_name] = (VxlanTunnel_T)tnlptr; + return true; + } + + bool delTunnel(const std::string tunnel_name) + { + vxlan_tunnel_table_.erase(tunnel_name); + return true; + } + + bool isVTEPExists(const IpAddress& sip) const + { + return vtep_table_.find(sip) != std::end(vtep_table_); + } + + VxlanTunnel* getVTEP(const IpAddress& sip) + { + return vtep_table_.at(sip); + } + + void addVTEP(VxlanTunnel* pvtep,const IpAddress& sip) + { + vtep_table_[sip] = pvtep; + } + + bool createVxlanTunnelMap(string tunnelName, tunnel_map_type_t mapType, uint32_t vni, sai_object_id_t encap, sai_object_id_t decap, uint8_t encap_ttl=0); @@ -182,12 +296,59 @@ class VxlanTunnelOrch : public Orch2 bool removeNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni=0); + bool getTunnelPort(const std::string& remote_vtep,Port& tunnelPort); + + bool addTunnelUser(string remote_vtep, uint32_t vni_id, + uint32_t vlan, tunnel_user_t usr, + sai_object_id_t vrf_id=SAI_NULL_OBJECT_ID); + + bool delTunnelUser(string remote_vtep, uint32_t vni_id, + uint32_t vlan, tunnel_user_t usr, + sai_object_id_t vrf_id=SAI_NULL_OBJECT_ID); + + void deleteTunnelPort(Port &tunnelPort); + + void addRemoveStateTableEntry(const string, IpAddress&, IpAddress&, tunnel_creation_src_t, bool); + + std::string getTunnelPortName(const std::string& remote_vtep); + void getTunnelNameFromDIP(const string& dip, string& tunnel_name); + void getTunnelNameFromPort(string& tunnel_portname, string& tunnel_name); + void getTunnelDIPFromPort(Port& tunnelPort, string& remote_vtep); + void updateDbTunnelOperStatus(string tunnel_portname, + sai_port_oper_status_t status); + uint16_t getVlanMappedToVni(const uint32_t vni) + { + if (vxlan_vni_vlan_map_table_.find(vni) != std::end(vxlan_vni_vlan_map_table_)) + { + return vxlan_vni_vlan_map_table_.at(vni); + } + else + { + return 0; + } + } + + void addVlanMappedToVni(uint32_t vni, uint16_t vlan_id) + { + vxlan_vni_vlan_map_table_[vni] = vlan_id; + } + + void delVlanMappedToVni(uint32_t vni) + { + vxlan_vni_vlan_map_table_.erase(vni); + } + + + private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); VxlanTunnelTable vxlan_tunnel_table_; VxlanTunnelRequest request_; + VxlanVniVlanMapTable vxlan_vni_vlan_map_table_; + VTEPTable vtep_table_; + Table m_stateVxlanTable; }; const request_description_t vxlan_tunnel_map_request_description = { @@ -199,7 +360,7 @@ const request_description_t vxlan_tunnel_map_request_description = { { "vni", "vlan" } }; -typedef std::map VxlanTunnelMapTable; +typedef std::map VxlanTunnelMapTable; class VxlanTunnelMapRequest : public Request { @@ -267,3 +428,66 @@ class VxlanVrfMapOrch : public Orch2 VxlanVrfTunnel vxlan_vrf_tunnel_; VxlanVrfRequest request_; }; + +//---------------- EVPN_REMOTE_VNI table --------------------- + +const request_description_t evpn_remote_vni_request_description = { + { REQ_T_STRING, REQ_T_STRING }, + { + { "vni", REQ_T_UINT }, + }, + { "vni" } +}; + +class EvpnRemoteVniRequest : public Request +{ +public: + EvpnRemoteVniRequest() : Request(evpn_remote_vni_request_description, ':') { } +}; + +class EvpnRemoteVniOrch : public Orch2 +{ +public: + EvpnRemoteVniOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + EvpnRemoteVniRequest request_; +}; + +//------------- EVPN_NVO Table ------------------------- + +const request_description_t evpn_nvo_request_description = { + { REQ_T_STRING}, + { + { "source_vtep", REQ_T_STRING }, + }, + { "source_vtep" } +}; + +class EvpnNvoRequest : public Request +{ +public: + EvpnNvoRequest() : Request(evpn_nvo_request_description, ':') { } +}; + +class EvpnNvoOrch : public Orch2 +{ +public: + EvpnNvoOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + + VxlanTunnel* getEVPNVtep() + { + return source_vtep_ptr; + } + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + EvpnNvoRequest request_; + VxlanTunnel* source_vtep_ptr=NULL; +}; diff --git a/tests/test_vxlan_tunnel.py b/tests/test_vxlan_tunnel.py index eccffd6557..14fe28261f 100644 --- a/tests/test_vxlan_tunnel.py +++ b/tests/test_vxlan_tunnel.py @@ -40,6 +40,13 @@ def get_exist_entries(dvs, table): tbl = swsscommon.Table(db, table) return set(tbl.getKeys()) +def get_created_entry_mapid(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + new_entries.sort() + return new_entries + def get_created_entry(db, table, existed_entries): tbl = swsscommon.Table(db, table) entries = set(tbl.getKeys()) @@ -105,29 +112,35 @@ def create_vlan(dvs, vlan_name, vlan_ids): def check_vxlan_tunnel(dvs, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, lo_id): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tunnel_map_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) + tunnel_map_id = get_created_entry_mapid(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) tunnel_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL", tunnel_ids) tunnel_term_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY", tunnel_term_ids) # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") == (len(tunnel_map_ids) + 1), "The TUNNEL_MAP wasn't created" + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP") == (len(tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY") == len(tunnel_map_entry_ids), "The TUNNEL_MAP_ENTRY is created too early" assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") == (len(tunnel_ids) + 1), "The TUNNEL wasn't created" assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY") == (len(tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" default_vr_id = get_default_vr_id(asic_db) - check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_id, + check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_id[0], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', } ) + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL", tunnel_id, { 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', 'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': lo_id, - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': '1:%s' % tunnel_map_id, + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip } ) @@ -145,11 +158,11 @@ def check_vxlan_tunnel(dvs, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY", tunnel_term_id, expected_attributes) - tunnel_map_ids.add(tunnel_map_id) + tunnel_map_ids.update(tunnel_map_id) tunnel_ids.add(tunnel_id) tunnel_term_ids.add(tunnel_term_id) - return tunnel_map_id + return tunnel_map_id[0] def create_vxlan_tunnel(dvs, name, src_ip, dst_ip, tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, lo_id, skip_dst_ip=False): @@ -199,9 +212,10 @@ def create_vxlan_tunnel_entry(dvs, tunnel_name, tunnel_map_entry_name, tunnel_ma ) if (tunnel_map_map.get(tunnel_name) is None): - tunnel_map_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) + tunnel_map_id = get_created_entry_mapid(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP", tunnel_map_ids) + vni_vlan_map_id = tunnel_map_id[0] else: - tunnel_map_id = tunnel_map_map[tunnel_name] + vni_vlan_map_id = tunnel_map_map[tunnel_name] tunnel_map_entry_id = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", tunnel_map_entry_ids) @@ -212,7 +226,7 @@ def create_vxlan_tunnel_entry(dvs, tunnel_name, tunnel_map_entry_name, tunnel_ma check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY", tunnel_map_entry_id, { 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': vni_vlan_map_id, 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vlan_id, }