From cbc5a057a430aeb3057e8c80518565e5128a4b36 Mon Sep 17 00:00:00 2001 From: Shuotian Cheng Date: Wed, 5 Sep 2018 18:11:18 -0700 Subject: [PATCH] [mirrororch]: Enable mirror session destination to point into VLAN (#595) * [mirrororch]: Enable mirror session destination to point into VLAN - Add FdbUpdate notification when manually inserting/removing FDB entries - Refactor MirrorOrch so that it supports: 1. create mirror session pointing to VLAN 2. destination port move to VLAN/non-VALN 3. destination port move to LAG Remove unnecessary variables in MirrorEntry - Add destination IP to the NextHopUpdate message - Add subnset routes for route observers Signed-off-by: Shu0T1an ChenG * [MirrorOrch]: Update comments and fix updateSessionType function issue * [test]: Update test_mirror.py --- orchagent/fdborch.cpp | 15 + orchagent/intfsorch.cpp | 23 ++ orchagent/intfsorch.h | 1 + orchagent/mirrororch.cpp | 574 ++++++++++++++------------ orchagent/mirrororch.h | 11 +- orchagent/neighorch.cpp | 17 +- orchagent/routeorch.cpp | 33 +- orchagent/routeorch.h | 4 +- tests/test_mirror.py | 872 +++++++++++++++++++++++++++++++-------- 9 files changed, 1094 insertions(+), 456 deletions(-) diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index f5ee04ce21f6..c159ddd71452 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -454,6 +454,12 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + FdbUpdate update = {entry, port, true}; + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + return true; } @@ -485,5 +491,14 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + Port port; + m_portsOrch->getPortByBridgePortId(entry.bv_id, port); + + FdbUpdate update = {entry, port, false}; + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + return true; } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index c740deb23c78..cab07f62ae06 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -9,6 +9,7 @@ #include "logger.h" #include "swssnet.h" #include "tokenize.h" +#include "routeorch.h" #include "crmorch.h" #include "bufferorch.h" @@ -20,6 +21,7 @@ extern sai_neighbor_api_t* sai_neighbor_api; extern sai_object_id_t gSwitchId; extern PortsOrch *gPortsOrch; +extern RouteOrch *gRouteOrch; extern CrmOrch *gCrmOrch; extern BufferOrch *gBufferOrch; @@ -78,6 +80,23 @@ bool IntfsOrch::setRouterIntfsMtu(Port &port) return true; } +set IntfsOrch:: getSubnetRoutes() +{ + SWSS_LOG_ENTER(); + + set subnet_routes; + + for (auto it = m_syncdIntfses.begin(); it != m_syncdIntfses.end(); it++) + { + for (auto prefix : it->second.ip_addresses) + { + subnet_routes.emplace(prefix); + } + } + + return subnet_routes; +} + void IntfsOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -386,6 +405,8 @@ void IntfsOrch::addSubnetRoute(const Port &port, const IpPrefix &ip_prefix) { gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); } + + gRouteOrch->notifyNextHopChangeObservers(ip_prefix, IpAddresses(), true); } void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) @@ -416,6 +437,8 @@ void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) { gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); } + + gRouteOrch->notifyNextHopChangeObservers(ip_prefix, IpAddresses(), false); } void IntfsOrch::addIp2MeRoute(const IpPrefix &ip_prefix) diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 61df9dee5917..424cb62a7b55 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -33,6 +33,7 @@ class IntfsOrch : public Orch void decreaseRouterIntfsRefCount(const string&); bool setRouterIntfsMtu(Port &port); + std::set getSubnetRoutes(); private: IntfsTable m_syncdIntfses; void doTask(Consumer &consumer); diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 7266f6b87c24..21ce14441590 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -36,17 +36,12 @@ using namespace std::rel_ops; MirrorEntry::MirrorEntry(const string& platform) : status(false), - greType(0), - dscp(0), - ttl(0), + dscp(8), + ttl(255), queue(0), - addVLanTag(false), sessionId(0), refCount(0) { - nexthopInfo.resolved = false; - neighborInfo.resolved = false; - if (platform == MLNX_PLATFORM_SUBSTRING) { greType = 0x6558; @@ -58,8 +53,7 @@ MirrorEntry::MirrorEntry(const string& platform) : queue = 0; } - dscp = 8; - ttl = 255; + nexthopInfo.prefix = IpPrefix("0.0.0.0/0"); } MirrorOrch::MirrorOrch(TableConnector appDbConnector, TableConnector confDbConnector, @@ -194,6 +188,14 @@ void MirrorOrch::createEntry(const string& key, const vector& d { SWSS_LOG_ENTER(); + auto session = m_syncdMirrors.find(key); + if (session != m_syncdMirrors.end()) + { + SWSS_LOG_NOTICE("Failed to create session, session %s already exists", + key.c_str()); + return; + } + string platform = getenv("platform") ? getenv("platform") : ""; MirrorEntry entry(platform); @@ -252,19 +254,13 @@ void MirrorOrch::createEntry(const string& key, const vector& d } } - SWSS_LOG_NOTICE("Create mirror sessions %s", key.c_str()); - - auto session = m_syncdMirrors.find(key); - if (session != m_syncdMirrors.end()) - { - SWSS_LOG_ERROR("Failed to create session. Session %s already exists.\n", key.c_str()); - return; - } - m_syncdMirrors.emplace(key, entry); + SWSS_LOG_NOTICE("Created mirror session %s", key.c_str()); + setSessionState(key, entry); + // Attach the destination IP to the routeOrch m_routeOrch->attach(this, entry.dstIp); } @@ -272,8 +268,6 @@ void MirrorOrch::deleteEntry(const string& name) { SWSS_LOG_ENTER(); - SWSS_LOG_INFO("Deleting mirroring sessions %s\n", name.c_str()); - auto sessionIter = m_syncdMirrors.find(name); if (sessionIter == m_syncdMirrors.end()) { @@ -296,6 +290,8 @@ void MirrorOrch::deleteEntry(const string& name) } m_syncdMirrors.erase(sessionIter); + + SWSS_LOG_NOTICE("Removed mirror session %s", name.c_str()); } bool MirrorOrch::setSessionState(const string& name, MirrorEntry& session) @@ -320,95 +316,135 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) { SWSS_LOG_ENTER(); - NeighborEntry neighbor; - MacAddress mac; - - assert(session.nexthopInfo.resolved); - - SWSS_LOG_INFO("Getting neighbor info for %s session\n", name.c_str()); - - if (!m_neighOrch->getNeighborEntry(session.nexthopInfo.nexthop, neighbor, mac)) + // 1) If session destination IP is directly connected, and the neighbor + // information is retrieved successfully, then continue. + // 2) If session has next hop, and the next hop's neighbor information is + // retrieved successfully, then continue. + // 3) Otherwise, return false. + if (!m_neighOrch->getNeighborEntry(session.dstIp, + session.neighborInfo.neighbor, session.neighborInfo.mac) && + (session.nexthopInfo.nexthop.isZero() || + !m_neighOrch->getNeighborEntry(session.nexthopInfo.nexthop, + session.neighborInfo.neighbor, session.neighborInfo.mac))) { - session.neighborInfo.resolved = false; return false; } - return getNeighborInfo(name, session, neighbor, mac); -} + SWSS_LOG_NOTICE("Mirror session %s neighbor is %s", + name.c_str(), session.neighborInfo.neighbor.alias.c_str()); -bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session, const NeighborEntry& neighbor, const MacAddress& mac) -{ - SWSS_LOG_ENTER(); - - SWSS_LOG_INFO("Getting neighbor info for %s session\n", name.c_str()); + // Get mirror session monitor port information + m_portsOrch->getPort(session.neighborInfo.neighbor.alias, + session.neighborInfo.port); - session.neighborInfo.resolved = false; - session.neighborInfo.neighbor = neighbor; - session.neighborInfo.mac = mac; - - // Get port operation should not fail; - if (!m_portsOrch->getPort(session.neighborInfo.neighbor.alias, session.neighborInfo.port)) + switch (session.neighborInfo.port.m_type) { - throw runtime_error("Failed to get port for " + session.neighborInfo.neighbor.alias + " alias"); - } + case Port::PHY: + { + session.neighborInfo.portId = session.neighborInfo.port.m_port_id; + return true; + } + case Port::LAG: + { + if (session.neighborInfo.port.m_members.empty()) + { + return false; + } - if (session.neighborInfo.port.m_type == Port::VLAN) - { - session.neighborInfo.vlanId = session.neighborInfo.port.m_vlan_info.vlan_id; - session.neighborInfo.vlanOid = session.neighborInfo.port.m_vlan_info.vlan_oid; + // Get the firt member of the LAG + Port member; + const auto& first_member_alias = *session.neighborInfo.port.m_members.begin(); + m_portsOrch->getPort(first_member_alias, member); - Port member; - if (!m_fdbOrch->getPort(session.neighborInfo.mac, session.neighborInfo.vlanId, member)) + session.neighborInfo.portId = member.m_port_id; + return true; + } + case Port::VLAN: + { + SWSS_LOG_NOTICE("vlan id is %d", session.neighborInfo.port.m_vlan_info.vlan_id); + Port member; + if (!m_fdbOrch->getPort(session.neighborInfo.mac, session.neighborInfo.port.m_vlan_info.vlan_id, member)) + { + SWSS_LOG_NOTICE("Waiting to get FDB entry MAC %s under VLAN %s", + session.neighborInfo.mac.to_string().c_str(), + session.neighborInfo.port.m_alias.c_str()); + return false; + } + else + { + // Update monitor port + session.neighborInfo.portId = member.m_port_id; + return true; + } + } + default: { return false; } + } +} - session.neighborInfo.portId = member.m_port_id; - session.neighborInfo.resolved = true; +bool MirrorOrch::updateSession(const string& name, MirrorEntry& session) +{ + SWSS_LOG_ENTER(); - return true; - } - else if (session.neighborInfo.port.m_type == Port::LAG) - { - session.neighborInfo.vlanId = session.addVLanTag ? session.neighborInfo.port.m_port_vlan_id : 0; + bool ret = true; + MirrorEntry old_session(session); - if (session.neighborInfo.port.m_members.empty()) + // Get neighbor information + if (getNeighborInfo(name, session)) + { + // Update corresponding attributes + if (session.status) { - return false; - } + if (old_session.neighborInfo.port.m_type != + session.neighborInfo.port.m_type) + { + ret &= updateSessionType(name, session); + } - const auto& firstMember = *session.neighborInfo.port.m_members.begin(); - Port lagMember; - if (!m_portsOrch->getPort(firstMember, lagMember)) + if (old_session.neighborInfo.mac != + session.neighborInfo.mac) + { + ret &= updateSessionDstMac(name, session); + } + + if (old_session.neighborInfo.portId != + session.neighborInfo.portId) + { + ret &= updateSessionDstPort(name, session); + } + } + // Activate mirror session + else { - throw runtime_error("Failed to get port for " + firstMember + " alias"); + ret &= activateSession(name, session); } - - session.neighborInfo.portId = lagMember.m_port_id; - session.neighborInfo.resolved = true; } + // Deactivate mirror session and wait for update else { - session.neighborInfo.portId = session.neighborInfo.port.m_port_id; - session.neighborInfo.vlanId = session.addVLanTag ? session.neighborInfo.port.m_port_vlan_id : 0; - session.neighborInfo.resolved = true; + if (session.status) + { + ret &= deactivateSession(name, session); + } } - return true; + return ret; } bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) { - sai_status_t status = SAI_STATUS_SUCCESS; + SWSS_LOG_ENTER(); + + sai_status_t status; sai_attribute_t attr; vector attrs; - SWSS_LOG_INFO("Activating mirror session %s\n", name.c_str()); - assert(!session.status); - /* Some platforms don't support SAI_MIRROR_SESSION_ATTR_TC and only - * support global mirror session traffic class. */ + // Some platforms don't support SAI_MIRROR_SESSION_ATTR_TC and only + // support global mirror session traffic class. if (session.queue != 0) { attr.id = SAI_MIRROR_SESSION_ATTR_TC; @@ -416,7 +452,7 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) attrs.push_back(attr); } - attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; + attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; attr.value.oid = session.neighborInfo.portId; attrs.push_back(attr); @@ -424,19 +460,19 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) attr.value.s32 = SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE; attrs.push_back(attr); - /* Add the VLAN header when the packet is sent out from a VLAN */ - if (session.neighborInfo.vlanId) + // Add the VLAN header when the packet is sent out from a VLAN + if (session.neighborInfo.port.m_type == Port::VLAN) { attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID; attr.value.booldata = true; attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_VLAN_TPID; + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_TPID; attr.value.u16 = ETH_P_8021Q; attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_VLAN_ID; - attr.value.u16 = session.neighborInfo.vlanId; + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_ID; + attr.value.u16 = session.neighborInfo.port.m_vlan_info.vlan_id; attrs.push_back(attr); attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_PRI; @@ -452,49 +488,50 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) attr.value.s32 = SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL; attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION; + attr.id = SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION; attr.value.u8 = MIRROR_SESSION_DEFAULT_IP_HDR_VER; attrs.push_back(attr); // TOS value format is the following: // DSCP 6 bits | ECN 2 bits - attr.id =SAI_MIRROR_SESSION_ATTR_TOS; + attr.id = SAI_MIRROR_SESSION_ATTR_TOS; attr.value.u16 = (uint16_t)(session.dscp << MIRROR_SESSION_DSCP_SHIFT); attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_TTL; + attr.id = SAI_MIRROR_SESSION_ATTR_TTL; attr.value.u8 = session.ttl; attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS; + attr.id = SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS; copy(attr.value.ipaddr, session.srcIp); attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS; + attr.id = SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS; copy(attr.value.ipaddr, session.dstIp); attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS; + attr.id = SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS; memcpy(attr.value.mac, gMacAddress.getMac(), sizeof(sai_mac_t)); attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS; + attr.id = SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS; memcpy(attr.value.mac, session.neighborInfo.mac.getMac(), sizeof(sai_mac_t)); attrs.push_back(attr); - attr.id =SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE; + attr.id = SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE; attr.value.u16 = session.greType; attrs.push_back(attr); - session.status = true; - - status = sai_mirror_api->create_mirror_session(&session.sessionId, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + status = sai_mirror_api-> + create_mirror_session(&session.sessionId, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to activate mirroring session %s\n", name.c_str()); session.status = false; } + session.status = true; + if (!setSessionState(name, session)) { throw runtime_error("Failed to test session state"); @@ -503,19 +540,22 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) MirrorSessionUpdate update = { name, true }; notify(SUBJECT_TYPE_MIRROR_SESSION_CHANGE, static_cast(&update)); + SWSS_LOG_NOTICE("Activate mirror session %s", name.c_str()); + return true; } bool MirrorOrch::deactivateSession(const string& name, MirrorEntry& session) { - SWSS_LOG_INFO("Deactivating mirror session %s\n", name.c_str()); + SWSS_LOG_ENTER(); assert(session.status); MirrorSessionUpdate update = { name, false }; notify(SUBJECT_TYPE_MIRROR_SESSION_CHANGE, static_cast(&update)); - sai_status_t status = sai_mirror_api->remove_mirror_session(session.sessionId); + sai_status_t status = sai_mirror_api-> + remove_mirror_session(session.sessionId); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to deactivate mirroring session %s\n", name.c_str()); @@ -529,6 +569,8 @@ bool MirrorOrch::deactivateSession(const string& name, MirrorEntry& session) throw runtime_error("Failed to test session state"); } + SWSS_LOG_NOTICE("Deactive mirror session %s", name.c_str()); + return true; } @@ -538,21 +580,21 @@ bool MirrorOrch::updateSessionDstMac(const string& name, MirrorEntry& session) assert(session.sessionId != SAI_NULL_OBJECT_ID); - SWSS_LOG_INFO("Updating mirror session %s destination MAC address\n", name.c_str()); - - sai_status_t status = SAI_STATUS_SUCCESS; sai_attribute_t attr; - - attr.id =SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS; + attr.id = SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS; memcpy(attr.value.mac, session.neighborInfo.mac.getMac(), sizeof(sai_mac_t)); - status = sai_mirror_api->set_mirror_session_attribute(session.sessionId, &attr); + sai_status_t status = sai_mirror_api->set_mirror_session_attribute(session.sessionId, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to update mirroring session %s destination MAC address\n", name.c_str()); + SWSS_LOG_ERROR("Failed to update mirror session %s destination MAC to %s, rv:%d", + name.c_str(), session.neighborInfo.mac.to_string().c_str(), status); return false; } + SWSS_LOG_NOTICE("Update mirror session %s destination MAC to %s", + name.c_str(), session.neighborInfo.mac.to_string().c_str()); + return true; } @@ -562,175 +604,190 @@ bool MirrorOrch::updateSessionDstPort(const string& name, MirrorEntry& session) assert(session.sessionId != SAI_NULL_OBJECT_ID); - SWSS_LOG_INFO("Updating mirror session %s destination port\n", name.c_str()); + Port port; + m_portsOrch->getPort(session.neighborInfo.portId, port); - sai_status_t status = SAI_STATUS_SUCCESS; sai_attribute_t attr; - - attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; + attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; attr.value.oid = session.neighborInfo.portId; - status = sai_mirror_api->set_mirror_session_attribute(session.sessionId, &attr); + sai_status_t status = sai_mirror_api-> + set_mirror_session_attribute(session.sessionId, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to update mirroring session %s destination port\n", name.c_str()); + SWSS_LOG_ERROR("Failed to update mirror session %s monitor port to %s, rv:%d", + name.c_str(), port.m_alias.c_str(), status); return false; } + SWSS_LOG_NOTICE("Update mirror session %s monitor port to %s", + name.c_str(), port.m_alias.c_str()); + + return true; } -void MirrorOrch::updateNextHop(const NextHopUpdate& update) +bool MirrorOrch::updateSessionType(const string& name, MirrorEntry& session) { SWSS_LOG_ENTER(); - auto sessionIter = m_syncdMirrors.begin(); - for (; sessionIter != m_syncdMirrors.end(); ++sessionIter) + sai_attribute_t attr; + vector attrs; + + if (session.neighborInfo.port.m_type == Port::VLAN) + { + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID; + attr.value.booldata = true; + attrs.push_back(attr); + + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_TPID; + attr.value.u16 = ETH_P_8021Q; + attrs.push_back(attr); + + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_ID; + attr.value.u16 = session.neighborInfo.port.m_vlan_info.vlan_id; + attrs.push_back(attr); + + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_PRI; + attr.value.u8 = MIRROR_SESSION_DEFAULT_VLAN_PRI; + attrs.push_back(attr); + + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_CFI; + attr.value.u8 = MIRROR_SESSION_DEFAULT_VLAN_CFI; + attrs.push_back(attr); + } + else + { + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID; + attr.value.booldata = false; + attrs.push_back(attr); + } + + sai_status_t status; + for (auto attr : attrs) { - if (!update.prefix.isAddressInSubnet(sessionIter->second.dstIp)) + status = sai_mirror_api-> + set_mirror_session_attribute(session.sessionId, &attr); + + if (status != SAI_STATUS_SUCCESS) { - continue; + SWSS_LOG_ERROR("Failed to update mirror session %s VLAN to %s, rv:%d", + name.c_str(), session.neighborInfo.port.m_alias.c_str(), status); + return false; } + } - const auto& name = sessionIter->first; - auto& session = sessionIter->second; + SWSS_LOG_NOTICE("Update mirror session %s VLAN to %s", + name.c_str(), session.neighborInfo.port.m_alias.c_str()); - SWSS_LOG_INFO("Updating mirror session %s next hop\n", name.c_str()); + return true; +} + +// The function is called when SUBJECT_TYPE_NEXTHOP_CHANGE is received +// This function will handle the case when the session's destination IP's +// next hop changes. +void MirrorOrch::updateNextHop(const NextHopUpdate& update) +{ + SWSS_LOG_ENTER(); + + for (auto it = m_syncdMirrors.begin(); it != m_syncdMirrors.end(); it++) + { + const auto& name = it->first; + auto& session = it->second; - if (session.nexthopInfo.resolved) + // Check if mirror session's destination IP is the update's destination IP + if (session.dstIp != update.destination) { - // Check for ECMP route next hop update. If route prefix is the same - // and current next hop is still in next hop group - do nothing. - if (session.nexthopInfo.prefix == update.prefix && update.nexthopGroup.getIpAddresses().count(session.nexthopInfo.nexthop)) - { - continue; - } + continue; } - session.nexthopInfo.nexthop = *update.nexthopGroup.getIpAddresses().begin(); session.nexthopInfo.prefix = update.prefix; - session.nexthopInfo.resolved = true; - // Get neighbor - if (!getNeighborInfo(name, session)) + // This is the ECMP scenario that the new next hop group contains the previous + // next hop. There is no need to update this session's monitor port. + if (update.nexthopGroup != IpAddresses() && + update.nexthopGroup.getIpAddresses().count(session.nexthopInfo.nexthop)) + { - // Next hop changed. New neighbor is not resolved. Remove session. - if (session.status) - { - deactivateSession(name, session); - } continue; } - if (session.status) - { - if (!updateSessionDstMac(name, session)) - { - continue; - } + SWSS_LOG_NOTICE("Updating mirror session %s with route %s", + name.c_str(), update.prefix.to_string().c_str()); - if (!updateSessionDstPort(name, session)) - { - continue; - } + if (update.nexthopGroup != IpAddresses()) + { + session.nexthopInfo.nexthop = *update.nexthopGroup.getIpAddresses().begin(); } else { - if (!activateSession(name, session)) - { - continue; - } + session.nexthopInfo.nexthop = IpAddress(); } + + // Resolve the neighbor of the new next hop + updateSession(name, session); } } +// The function is called when SUBJECT_TYPE_NEIGH_CHANGE is received. +// This function will handle the case when the neighbor is created or removed. void MirrorOrch::updateNeighbor(const NeighborUpdate& update) { SWSS_LOG_ENTER(); - auto sessionIter = m_syncdMirrors.begin(); - for (; sessionIter != m_syncdMirrors.end(); ++sessionIter) + for (auto it = m_syncdMirrors.begin(); it != m_syncdMirrors.end(); it++) { - if (!sessionIter->second.nexthopInfo.resolved) - { - continue; - } + const auto& name = it->first; + auto& session = it->second; - // It is possible to have few sessions that points to one next hop - if (sessionIter->second.nexthopInfo.nexthop != update.entry.ip_address) + // Check if the session's destination IP matches the neighbor's update IP + // or if the session's next hop IP matches the neighbor's update IP + if (session.dstIp != update.entry.ip_address && + session.nexthopInfo.nexthop != update.entry.ip_address) { continue; } - SWSS_LOG_INFO("Updating neighbor info %s %s\n", update.entry.ip_address.to_string().c_str(), update.entry.alias.c_str()); + SWSS_LOG_NOTICE("Updating mirror session %s with neighbor %s", + name.c_str(), update.entry.alias.c_str()); - const auto& name = sessionIter->first; - auto& session = sessionIter->second; - - if (update.add) - { - if (!getNeighborInfo(name, session, update.entry, update.mac)) - { - if (session.status) - { - deactivateSession(name, session); - } - continue; - } - - if (session.status) - { - if (!updateSessionDstMac(name, session)) - { - continue; - } - - if (!updateSessionDstPort(name, session)) - { - continue; - } - } - else - { - activateSession(name, session); - } - } - else if (session.status) - { - deactivateSession(name, session); - session.neighborInfo.resolved = false; - } + updateSession(name, session); } } +// The function is called when SUBJECT_TYPE_FDB_CHANGE is received. +// This function will handle the case when new FDB enty is learned/added in the VLAN, +// or when the old FDB entry gets removed. Only when the neighbor is VLAN will the case +// be handled. void MirrorOrch::updateFdb(const FdbUpdate& update) { SWSS_LOG_ENTER(); - auto sessionIter = m_syncdMirrors.begin(); - for (; sessionIter != m_syncdMirrors.end(); ++sessionIter) + for (auto it = m_syncdMirrors.begin(); it != m_syncdMirrors.end(); it++) { - if (!sessionIter->second.neighborInfo.resolved || - sessionIter->second.neighborInfo.port.m_type != Port::VLAN) - { - continue; - } + const auto& name = it->first; + auto& session = it->second; - // It is possible to have few session that points to one FDB entry - if (sessionIter->second.neighborInfo.mac != update.entry.mac || - sessionIter->second.neighborInfo.vlanOid != update.entry.bv_id) + // Check the following three conditions: + // 1) mirror session is pointing to a VLAN + // 2) the VLAN matches the FDB notification VLAN ID + // 3) the destination MAC matches the FDB notifaction MAC + if (session.neighborInfo.port.m_type != Port::VLAN || + session.neighborInfo.port.m_vlan_info.vlan_oid != update.entry.bv_id || + session.neighborInfo.mac != update.entry.mac) { continue; } - const auto& name = sessionIter->first; - auto& session = sessionIter->second; + SWSS_LOG_NOTICE("Updating mirror session %s with monitor port %s", + name.c_str(), update.port.m_alias.c_str()); + // Get the new monitor port if (update.add) { if (session.status) { - // update port if changed + // Update port if changed if (session.neighborInfo.portId != update.port.m_port_id) { session.neighborInfo.portId = update.port.m_port_id; @@ -739,14 +796,12 @@ void MirrorOrch::updateFdb(const FdbUpdate& update) } else { - //activate session - session.neighborInfo.resolved = true; - session.neighborInfo.mac = update.entry.mac; + // Activate session session.neighborInfo.portId = update.port.m_port_id; - activateSession(name, session); } } + // Remvoe the monitor port else { deactivateSession(name, session); @@ -758,72 +813,56 @@ void MirrorOrch::updateFdb(const FdbUpdate& update) void MirrorOrch::updateLagMember(const LagMemberUpdate& update) { SWSS_LOG_ENTER(); - auto sessionIter = m_syncdMirrors.begin(); - for (; sessionIter != m_syncdMirrors.end(); ++sessionIter) - { - if (!sessionIter->second.neighborInfo.resolved) - { - continue; - } - if (sessionIter->second.neighborInfo.port.m_type != Port::LAG) - { - continue; - } + for (auto it = m_syncdMirrors.begin(); it != m_syncdMirrors.end(); it++) + { + const auto& name = it->first; + auto& session = it->second; - // It is possible to have few session that points to one LAG member - if (sessionIter->second.neighborInfo.port != update.lag || sessionIter->second.neighborInfo.portId != update.member.m_port_id) + // Check the following two conditions: + // 1) the neighbor is LAG + // 2) the neighbor LAG matches the update LAG + if (session.neighborInfo.port.m_type != Port::LAG || + session.neighborInfo.port != update.lag) { continue; } - const auto& name = sessionIter->first; - auto& session = sessionIter->second; - if (update.add) { - // We interesting only in first LAG member - if (update.lag.m_members.size() > 1) + // Activate mirror session if it was deactivated due to the reason + // that previously there was no member in the LAG. If the mirror + // session is already activated, no further action is needed. + if (!session.status) { - continue; - } + assert(!update.lag.m_members.empty()); + const string& member_name = *update.lag.m_members.begin(); + Port member; + m_portsOrch->getPort(member_name, member); - const string& memberName = *update.lag.m_members.begin(); - Port member; - if (!m_portsOrch->getPort(memberName, member)) - { - SWSS_LOG_ERROR("Failed to get port for %s alias\n", memberName.c_str()); - assert(false); + session.neighborInfo.portId = member.m_port_id; + activateSession(name, session); } - - session.neighborInfo.portId = member.m_port_id; - - activateSession(name, session); } else { - // If LAG is empty deactivate session + // If LAG is empty, deactivate session if (update.lag.m_members.empty()) { deactivateSession(name, session); session.neighborInfo.portId = SAI_OBJECT_TYPE_NULL; - - continue; } - - // Get another LAG member and update session - const string& memberName = *update.lag.m_members.begin(); - - Port member; - if (!m_portsOrch->getPort(memberName, member)) + // Switch to a new member of the LAG + else { - SWSS_LOG_ERROR("Failed to get port for %s alias\n", memberName.c_str()); - assert(false); - } + const string& member_name = *update.lag.m_members.begin(); + Port member; + m_portsOrch->getPort(member_name, member); - session.neighborInfo.portId = member.m_port_id; - - updateSessionDstPort(name, session); + session.neighborInfo.portId = member.m_port_id; + // The destination MAC remains the same + updateSessionDstPort(name, session); + } } } } @@ -838,32 +877,25 @@ void MirrorOrch::updateVlanMember(const VlanMemberUpdate& update) return; } - auto sessionIter = m_syncdMirrors.begin(); - for (; sessionIter != m_syncdMirrors.end(); ++sessionIter) + for (auto it = m_syncdMirrors.begin(); it != m_syncdMirrors.end(); it++) { - if (!sessionIter->second.neighborInfo.resolved) - { - continue; - } + const auto& name = it->first; + auto& session = it->second; - if (sessionIter->second.neighborInfo.port.m_type != Port::VLAN) + // Check the following three conditions: + // 1) mirror session is pointing to a VLAN + // 2) the VLAN matches the update VLAN + // 3) the monitor port matches the update VLAN member + if (session.neighborInfo.port.m_type != Port::VLAN || + session.neighborInfo.port != update.vlan || + session.neighborInfo.portId != update.member.m_port_id) { continue; } - // It is possible to have few session that points to one VLAN member - if (sessionIter->second.neighborInfo.port != update.vlan || - sessionIter->second.neighborInfo.portId != update.member.m_port_id) - { - continue; - } - - const auto& name = sessionIter->first; - auto& session = sessionIter->second; - // Deactivate session. Wait for FDB event to activate session - deactivateSession(name, session); session.neighborInfo.portId = SAI_OBJECT_TYPE_NULL; + deactivateSession(name, session); } } diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index 8bfaebd136fb..a783c2a0c999 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -30,27 +30,23 @@ struct MirrorEntry uint8_t dscp; uint8_t ttl; uint8_t queue; - bool addVLanTag; struct { - bool resolved; - IpAddress nexthop; IpPrefix prefix; + IpAddress nexthop; } nexthopInfo; struct { - bool resolved; NeighborEntry neighbor; MacAddress mac; Port port; - sai_vlan_id_t vlanId; - sai_object_id_t vlanOid; sai_object_id_t portId; } neighborInfo; sai_object_id_t sessionId; + int64_t refCount; MirrorEntry(const string& platform); @@ -93,11 +89,12 @@ class MirrorOrch : public Orch, public Observer, public Subject bool activateSession(const string&, MirrorEntry&); bool deactivateSession(const string&, MirrorEntry&); + bool updateSession(const string&, MirrorEntry&); bool updateSessionDstMac(const string&, MirrorEntry&); bool updateSessionDstPort(const string&, MirrorEntry&); + bool updateSessionType(const string&, MirrorEntry&); bool setSessionState(const string&, MirrorEntry&); bool getNeighborInfo(const string&, MirrorEntry&); - bool getNeighborInfo(const string&, MirrorEntry&, const NeighborEntry&, const MacAddress&); void updateNextHop(const NextHopUpdate&); void updateNeighbor(const NeighborUpdate&); diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 3df3f5f1ef34..e05c215ce394 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -492,9 +492,6 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) } } - SWSS_LOG_NOTICE("Removed next hop %s on %s", - ip_address.to_string().c_str(), alias.c_str()); - if (status != SAI_STATUS_ITEM_NOT_FOUND) { if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) @@ -507,6 +504,9 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) } } + SWSS_LOG_NOTICE("Removed next hop %s on %s", + ip_address.to_string().c_str(), alias.c_str()); + status = sai_neighbor_api->remove_neighbor_entry(&neighbor_entry); if (status != SAI_STATUS_SUCCESS) { @@ -524,9 +524,6 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) } } - SWSS_LOG_NOTICE("Removed neighbor %s on %s", - m_syncdNeighbors[neighborEntry].to_string().c_str(), alias.c_str()); - if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) { gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEIGHBOR); @@ -536,11 +533,15 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEIGHBOR); } - NeighborUpdate update = { neighborEntry, MacAddress(), false }; - notify(SUBJECT_TYPE_NEIGH_CHANGE, static_cast(&update)); + SWSS_LOG_NOTICE("Removed neighbor %s on %s", + m_syncdNeighbors[neighborEntry].to_string().c_str(), alias.c_str()); m_syncdNeighbors.erase(neighborEntry); m_intfsOrch->decreaseRouterIntfsRefCount(alias); + + NeighborUpdate update = { neighborEntry, MacAddress(), false }; + notify(SUBJECT_TYPE_NEIGH_CHANGE, static_cast(&update)); + removeNextHop(ip_address, alias); return true; diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 9b31924dbfd3..5ad716303c44 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -12,6 +12,7 @@ extern sai_route_api_t* sai_route_api; extern sai_switch_api_t* sai_switch_api; extern PortsOrch *gPortsOrch; +extern IntfsOrch *gIntfsOrch; extern CrmOrch *gCrmOrch; /* Default maximum number of next hop groups */ @@ -119,30 +120,49 @@ void RouteOrch::attach(Observer *observer, const IpAddress& dstAddr) { SWSS_LOG_ENTER(); - SWSS_LOG_INFO("Attaching next hop observer for %s destination IP\n", dstAddr.to_string().c_str()); - auto observerEntry = m_nextHopObservers.find(dstAddr); + /* Create a new observer entry if no current observer is observing this + * IP address */ if (observerEntry == m_nextHopObservers.end()) { m_nextHopObservers.emplace(dstAddr, NextHopObserverEntry()); observerEntry = m_nextHopObservers.find(dstAddr); + /* Find the prefixes that cover the destination IP */ for (auto route : m_syncdRoutes) { if (route.first.isAddressInSubnet(dstAddr)) { - observerEntry->second.routeTable.emplace(route.first, route.second); + SWSS_LOG_NOTICE("route%s", route.first.to_string().c_str()); + observerEntry->second.routeTable.emplace( + route.first, route.second); + } + } + + /* Find the subnets that cover the destination IP + * The next hop of the subnet routes is left empty */ + for (auto prefix : gIntfsOrch->getSubnetRoutes()) + { + if (prefix.isAddressInSubnet(dstAddr)) + { + observerEntry->second.routeTable.emplace( + prefix, IpAddresses()); } } } observerEntry->second.observers.push_back(observer); + SWSS_LOG_NOTICE("Attached next hop observer of route %s for destination IP %s", + observerEntry->second.routeTable.rbegin()->first.to_string().c_str(), + dstAddr.to_string().c_str()); + + // Trigger next hop change for the first time the observer is attached auto route = observerEntry->second.routeTable.rbegin(); if (route != observerEntry->second.routeTable.rend()) { - NextHopUpdate update = { route->first, route->second }; + NextHopUpdate update = { dstAddr, route->first, route->second }; observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, static_cast(&update)); } } @@ -387,7 +407,7 @@ void RouteOrch::notifyNextHopChangeObservers(IpPrefix prefix, IpAddresses nextho if (add) { bool update_required = false; - NextHopUpdate update = { prefix, nexthops }; + NextHopUpdate update = { entry.first, prefix, nexthops }; /* Table should not be empty. Default route should always exists. */ assert(!entry.second.routeTable.empty()); @@ -438,7 +458,7 @@ void RouteOrch::notifyNextHopChangeObservers(IpPrefix prefix, IpAddresses nextho assert(!entry.second.routeTable.empty()); auto route = entry.second.routeTable.rbegin(); - NextHopUpdate update = { route->first, route->second }; + NextHopUpdate update = { entry.first, route->first, route->second }; for (auto observer : entry.second.observers) { @@ -955,5 +975,6 @@ bool RouteOrch::removeRoute(IpPrefix ipPrefix) /* Notify about the route next hop removal */ notifyNextHopChangeObservers(ipPrefix, IpAddresses(), false); } + return true; } diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index af17b627a45e..b4d87b695863 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -26,6 +26,7 @@ struct NextHopGroupEntry struct NextHopUpdate { + IpAddress destination; IpPrefix prefix; IpAddresses nexthopGroup; }; @@ -66,6 +67,7 @@ class RouteOrch : public Orch, public Subject bool validnexthopinNextHopGroup(const IpAddress &); bool invalidnexthopinNextHopGroup(const IpAddress &); + void notifyNextHopChangeObservers(IpPrefix, IpAddresses, bool); private: NeighOrch *m_neighOrch; @@ -83,8 +85,6 @@ class RouteOrch : public Orch, public Subject bool removeRoute(IpPrefix); void doTask(Consumer& consumer); - - void notifyNextHopChangeObservers(IpPrefix, IpAddresses, bool); }; #endif /* SWSS_ROUTEORCH_H */ diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 2ea299b0e833..25ffe6e6d085 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -6,228 +6,776 @@ class TestMirror(object): - def get_acl_table_id(self, dvs): - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - keys = tbl.getKeys() - for k in dvs.asicdb.default_acl_tables: - assert k in keys - - acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_tables] - assert len(acl_tables) == 1 - - return acl_tables[0] + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + self.sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + + def set_interface_status(self, interface, admin_status): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL" + elif interface.startswith("Vlan"): + tbl_name = "VLAN" + else: + tbl_name = "PORT" + tbl = swsscommon.Table(self.cdb, tbl_name) + fvs = swsscommon.FieldValuePairs([("admin_status", "up")]) + tbl.set(interface, fvs) + time.sleep(1) - def get_mirror_session_id(self, dvs): - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") - mirror_sessions = tbl.getKeys() - assert len(mirror_sessions) == 1 + def add_ip_address(self, interface, ip): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(1) - return mirror_sessions[0] + def remove_ip_address(self, interface, ip): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl._del(interface + "|" + ip) + time.sleep(1) - def test_AclMirrorTableCreation(self, dvs): - pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + def add_neighbor(self, interface, ip, mac): + tbl = swsscommon.ProducerStateTable(self.pdb, "NEIGH_TABLE") + fvs = swsscommon.FieldValuePairs([("neigh", mac), + ("family", "IPv4")]) + tbl.set(interface + ":" + ip, fvs) + time.sleep(1) - bind_ports = ["Ethernet0", "Ethernet4"] - tbl = swsscommon.Table(cdb, "ACL_TABLE") + def remove_neighbor(self, interface, ip): + tbl = swsscommon.ProducerStateTable(self.pdb, "NEIGH_TABLE") + tbl._del(interface + ":" + ip) + time.sleep(1) - fvs = swsscommon.FieldValuePairs([("POLICY_DESC", "MIRROR_TEST"), - ("TYPE", "MIRROR"), - ("PORTS", ",".join(bind_ports))]) - # create the ACL table - tbl.set("EVERFLOW_TABLE", fvs) + def add_route(self, dvs, prefix, nexthop): + dvs.runcmd("ip route add " + prefix + " via " + nexthop) time.sleep(1) - # assert the table is created - tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") - acl_table_ids = tbl.getKeys() - assert len(acl_table_ids) == 2 + def remove_route(self, dvs, prefix): + dvs.runcmd("ip route del " + prefix) + time.sleep(1) - tbl = swsscommon.Table(cdb, "MIRROR_SESSION") - fvs = swsscommon.FieldValuePairs([("src_ip", "10.1.0.32"), - ("dst_ip", "10.20.30.40")]) - # create the mirror session - tbl.set("EVERFLOW_SESSION", fvs) + def create_mirror_session(self, name, src, dst, gre, dscp, ttl, queue): + tbl = swsscommon.Table(self.cdb, "MIRROR_SESSION") + fvs = swsscommon.FieldValuePairs([("src_ip", src), + ("dst_ip", dst), + ("gre_type", gre), + ("dscp", dscp), + ("ttl", ttl), + ("queue", queue)]) + tbl.set(name, fvs) time.sleep(1) - # assert the mirror session is created - tbl = swsscommon.Table(pdb, "MIRROR_SESSION") - mirror_sessions = tbl.getKeys() - assert len(mirror_sessions) == 1 + def remove_mirror_session(self, name): + tbl = swsscommon.Table(self.cdb, "MIRROR_SESSION") + tbl._del(name) + time.sleep(1) - # assert the mirror session is inactive - (status, fvs) = tbl.get(mirror_sessions[0]) + def get_mirror_session_status(self, name): + status = "" + # TODO: the status of mirror session will be moved to state database + tbl = swsscommon.Table(self.pdb, "MIRROR_SESSION") + (status, fvs) = tbl.get(name) assert status == True assert len(fvs) == 1 for fv in fvs: if fv[0] == "status": - assert fv[1] == "inactive" + status = fv[1] + else: + assert False + return status + + def test_MirrorAddRemove(self, dvs): + """ + This test covers the basic mirror session creation and removal operations + Operation flow: + 1. Create mirror session + The session remains inactive because no nexthop/neighbor exists + 2. Bring up port; assign IP; create neighbor; create route + The session remains inactive until the route is created + 3. Remove route; remove neighbor; remove IP; bring down port + The session becomes inactive again till the end + 4. Remove miror session + """ + self.setup_db(dvs) + + session = "TEST_SESSION" + + # create mirror session + self.create_mirror_session(session, "1.1.1.1", "2.2.2.2", "0x6558", "8", "100", "0") + assert self.get_mirror_session_status(session) == "inactive" + + # bring up Ethernet16 + self.set_interface_status("Ethernet16", "up") + assert self.get_mirror_session_status(session) == "inactive" + + # add IP address to Ethernet16 + self.add_ip_address("Ethernet16", "10.0.0.0/31") + assert self.get_mirror_session_status(session) == "inactive" + + # add neighbor to Ethernet16 + self.add_neighbor("Ethernet16", "10.0.0.1", "02:04:06:08:10:12") + assert self.get_mirror_session_status(session) == "inactive" + + # add route to mirror destination via 10.0.0.1 + self.add_route(dvs, "2.2.2.2", "10.0.0.1") + assert self.get_mirror_session_status(session) == "active" + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + mirror_entries = tbl.getKeys() + assert len(mirror_entries) == 1 + + (status, fvs) = tbl.get(mirror_entries[0]) + assert status == True + assert len(fvs) == 11 + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet16" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_TYPE": + assert fv[1] == "SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_ERSPAN_ENCAPSULATION_TYPE": + assert fv[1] == "SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION": + assert fv[1] == "4" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_TOS": + assert fv[1] == "32" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_TTL": + assert fv[1] == "100" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS": + assert fv[1] == "1.1.1.1" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS": + assert fv[1] == "2.2.2.2" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS": + assert fv[1] == dvs.runcmd("bash -c \"ip link show eth0 | grep ether | awk '{print $2}'\"")[1].strip().upper() + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "02:04:06:08:10:12" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE": + assert fv[1] == "25944" # 0x6558 else: assert False - def test_MirrorSessionActivation(self, dvs): - # assign the IP address to Ethernet0 - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/24 up") + # remove route + self.remove_route(dvs, "2.2.2.2") + assert self.get_mirror_session_status(session) == "inactive" + + # remove neighbor + self.remove_neighbor("Ethernet16", "10.0.0.1") + assert self.get_mirror_session_status(session) == "inactive" + + # remove IP address + self.remove_ip_address("Ethernet16", "10.0.0.0/31") + assert self.get_mirror_session_status(session) == "inactive" + + # bring down Ethernet16 + self.set_interface_status("Ethernet16", "down") + assert self.get_mirror_session_status(session) == "inactive" + + # remove mirror session + self.remove_mirror_session(session) + + def create_vlan(self, dvs, vlan): + #dvs.runcmd("ip link del Bridge") + #dvs.runcmd("ip link add Bridge up type bridge") + tbl = swsscommon.Table(self.cdb, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", vlan)]) + tbl.set("Vlan" + vlan, fvs) time.sleep(1) - pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) - tbl = swsscommon.ProducerStateTable(pdb, "NEIGH_TABLE") - fvs = swsscommon.FieldValuePairs([("NEIGH", "02:04:06:08:10:12"), - ("FAMILY", "IPv4")]) - # create the neighbor entry associated with Ethernet0 - tbl.set("Ethernet0:10.0.0.1", fvs) + def remove_vlan(self, vlan): + tbl = swsscommon.Table(self.cdb, "VLAN") + tbl._del("Vlan" + vlan) time.sleep(1) - # add the route of mirror session destination via the neighbor - dvs.runcmd("ip route add 10.20.30.40/32 via 10.0.0.1") + def create_vlan_member(self, vlan, interface): + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + tbl.set("Vlan" + vlan + "|" + interface, fvs) time.sleep(1) - # assert the mirror session is active - tbl = swsscommon.Table(pdb, "MIRROR_SESSION") - (status, fvs) = tbl.get("EVERFLOW_SESSION") - assert status == True - assert len(fvs) == 1 - for fv in fvs: - if fv[0] == "status": - fv[1] == "active" + def remove_vlan_member(self, vlan, interface): + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + tbl._del("Vlan" + vlan + "|" + interface) + time.sleep(1) - # assert the mirror session is created - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") - mirror_session_ids = tbl.getKeys() - assert len(mirror_session_ids) == 1 + def create_fdb(self, vlan, mac, interface): + tbl = swsscommon.ProducerStateTable(self.pdb, "FDB_TABLE") + fvs = swsscommon.FieldValuePairs([("port", interface), + ("type", "dynamic")]) + tbl.set("Vlan" + vlan + ":" + mac, fvs) + time.sleep(1) + + def remove_fdb(self, vlan, mac): + tbl = swsscommon.ProducerStateTable(self.pdb, "FDB_TABLE") + tbl._del("Vlan" + vlan + ":" + mac) + time.sleep(1) - def test_AclRuleDscpWithoutMask(self, dvs): + def test_MirrorToVlanAddRemove(self, dvs): """ - hmset ACL_RULE|EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_1 priority 1000 PACKET_ACTION FORWARD DSCP 48 + This test covers basic mirror session creation and removal operation + with destination port sits in a VLAN + Opeartion flow: + 1. Create mirror session + 2. Create VLAN; assign IP; create neighbor; create FDB + The session should be up only at this time. + 3. Remove FDB; remove neighbor; remove IP; remove VLAN + 4. Remove mirror session """ + self.setup_db(dvs) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + session = "TEST_SESSION" - tbl = swsscommon.Table(cdb, "ACL_RULE") - fvs = swsscommon.FieldValuePairs([("PRIORITY", "1000"), - ("MIRROR_ACTION", "EVERFLOW_SESSION"), - ("DSCP", "48")]) - # create the ACL rule contains DSCP match field with a mask - tbl.set("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_1", fvs) - time.sleep(1) + # create mirror session + self.create_mirror_session(session, "5.5.5.5", "6.6.6.6", "0x6558", "8", "100", "0") + assert self.get_mirror_session_status(session) == "inactive" - test_acl_table_id = self.get_acl_table_id(dvs) - test_mirror_session_id = self.get_mirror_session_id(dvs) + # create vlan; create vlan member + self.create_vlan(dvs, "6") + self.create_vlan_member("6", "Ethernet4") - # assert the ACL rule is created - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") - keys = atbl.getKeys() - acl_entries = [k for k in keys if k not in dvs.asicdb.default_acl_entries] - assert len(acl_entries) == 1 + # bring up vlan and member + self.set_interface_status("Vlan6", "up") + self.set_interface_status("Ethernet4", "up") - # assert the ACL rule content is correct - (status, fvs) = atbl.get(acl_entries[0]) + # add ip address to vlan 6 + self.add_ip_address("Vlan6", "6.6.6.0/24") + assert self.get_mirror_session_status(session) == "inactive" + + # create neighbor to vlan 6 + self.add_neighbor("Vlan6", "6.6.6.6", "66:66:66:66:66:66") + assert self.get_mirror_session_status(session) == "inactive" + + # create fdb entry to ethernet4 + self.create_fdb("6", "66-66-66-66-66-66", "Ethernet4") + assert self.get_mirror_session_status(session) == "active" + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + mirror_entries = tbl.getKeys() + assert len(mirror_entries) == 1 + + (status, fvs) = tbl.get(mirror_entries[0]) assert status == True - assert len(fvs) == 6 + assert len(fvs) == 16 for fv in fvs: - if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": - assert fv[1] == test_acl_table_id - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet4" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_TYPE": + assert fv[1] == "SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_ERSPAN_ENCAPSULATION_TYPE": + assert fv[1] == "SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION": + assert fv[1] == "4" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_TOS": + assert fv[1] == "32" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_TTL": + assert fv[1] == "100" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS": + assert fv[1] == "5.5.5.5" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS": + assert fv[1] == "6.6.6.6" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS": + assert fv[1] == dvs.runcmd("bash -c \"ip link show eth0 | grep ether | awk '{print $2}'\"")[1].strip().upper() + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "66:66:66:66:66:66" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE": + assert fv[1] == "25944" # 0x6558 + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID": assert fv[1] == "true" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": - assert fv[1] == "1000" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": - assert True - elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DSCP": - assert fv[1] == "48&mask:0x3f" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": - assert fv[1] == "1:" + test_mirror_session_id + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_TPID": + assert fv[1] == "33024" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_ID": + assert fv[1] == "6" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_PRI": + assert fv[1] == "0" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_CFI": + assert fv[1] == "0" else: assert False - # remove the ACL rule - tbl._del("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_1") + # remove fdb entry + self.remove_fdb("6", "66-66-66-66-66-66") + assert self.get_mirror_session_status(session) == "inactive" + + # remove neighbor + self.remove_neighbor("Vlan6", "6.6.6.6") + assert self.get_mirror_session_status(session) == "inactive" + + # remove ip address + self.remove_ip_address("Vlan6", "6.6.6.0/24") + assert self.get_mirror_session_status(session) == "inactive" + + # bring down vlan and member + self.set_interface_status("Ethernet4", "down") + self.set_interface_status("Vlan6", "down") + + # remove vlan member; remove vlan + self.remove_vlan_member("6", "Ethernet4") + self.remove_vlan("6") + + # remove mirror session + self.remove_mirror_session(session) + + def create_port_channel(self, dvs, channel): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "9100")]) + tbl.set("PortChannel" + channel, fvs) + dvs.runcmd("ip link add PortChannel" + channel + " type bond") + tbl = swsscommon.Table(self.sdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("state", "ok")]) + tbl.set("PortChannel" + channel, fvs) + time.sleep(1) + + def remove_port_channel(self, dvs, channel): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") + tbl._del("PortChannel" + channel) + dvs.runcmd("ip link del PortChannel" + channel) + tbl = swsscommon.Table(self.sdb, "LAG_TABLE") + tbl._del("PortChannel" + channel) time.sleep(1) - # assert the ACL rule is removed - (status, fvs) = atbl.get(acl_entries[0]) - assert status == False + def create_port_channel_member(self, channel, interface): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + tbl.set("PortChannel" + channel + ":" + interface, fvs) + time.sleep(1) + + def remove_port_channel_member(self, channel, interface): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_MEMBER_TABLE") + tbl._del("PortChannel" + channel + ":" + interface) + time.sleep(1) - def test_AclRuleDscpWithMask(self, dvs): + def test_MirrorToLagAddRemove(self, dvs): """ - hmset ACL_RULE|EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_2 priority 1000 PACKET_ACTION FORWARD DSCP 16/16 + This test covers basic mirror session creation and removal operations + with destination port sits in a LAG + Operation flow: + 1. Create mirror sesion + 2. Create LAG; assign IP; create directly connected neighbor + The session shoudl be up only at this time. + 3. Remove neighbor; remove IP; remove LAG + 4. Remove mirror session + """ + self.setup_db(dvs) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + session = "TEST_SESSION" - tbl = swsscommon.Table(cdb, "ACL_RULE") - fvs = swsscommon.FieldValuePairs([("PRIORITY", "1000"), - ("MIRROR_ACTION", "EVERFLOW_SESSION"), - ("DSCP", "16/16")]) - # create the ACL rule contains DSCP match field with a mask - tbl.set("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_2", fvs) - time.sleep(1) + # create mirror session + self.create_mirror_session(session, "10.10.10.10", "11.11.11.11", "0x6558", "8", "100", "0") + assert self.get_mirror_session_status(session) == "inactive" + + # create port channel; create port channel member + self.create_port_channel(dvs, "008") + self.create_port_channel_member("008", "Ethernet88") + + # bring up port channel and port channel member + self.set_interface_status("PortChannel008", "up") + self.set_interface_status("Ethernet88", "up") + + # add ip address to port channel 008 + self.add_ip_address("PortChannel008", "11.11.11.0/24") + assert self.get_mirror_session_status(session) == "inactive" - test_acl_table_id = self.get_acl_table_id(dvs) - test_mirror_session_id = self.get_mirror_session_id(dvs) + # create neighbor to port channel 008 + self.add_neighbor("PortChannel008", "11.11.11.11", "88:88:88:88:88:88") + assert self.get_mirror_session_status(session) == "active" - # assert the ACL rule is created - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") - keys = atbl.getKeys() - acl_entries = [k for k in keys if k not in dvs.asicdb.default_acl_entries] - assert len(acl_entries) == 1 + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 - # assert the ACL rule content is correct - (status, fvs) = atbl.get(acl_entries[0]) + (status, fvs) = tbl.get(tbl.getKeys()[0]) assert status == True - assert len(fvs) == 6 for fv in fvs: - if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": - assert fv[1] == test_acl_table_id - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet88" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "88:88:88:88:88:88" + + # remove neighbor + self.remove_neighbor("PortChannel008", "11.11.11.11") + assert self.get_mirror_session_status(session) == "inactive" + + # remove ip address + self.remove_ip_address("PortChannel008", "11.11.11.0/24") + assert self.get_mirror_session_status(session) == "inactive" + + # bring down port channel and port channel member + self.set_interface_status("PortChannel008", "down") + self.set_interface_status("Ethernet88", "down") + + # remove port channel member; remove port channel + self.remove_port_channel_member("008", "Ethernet88") + self.remove_port_channel(dvs, "008") + + # remove mirror session + self.remove_mirror_session(session) + + + def test_MirrorDestMoveVlan(self, dvs): + """ + This test tests mirror session destination move from non-VLAN to VLAN + and back to non-VLAN port + 1. Create mirror session + 2. Enable non-VLAN monitor port + 3. Create VLAN; move to VLAN without FDB entry + 4. Create FDB entry + 5. Remove FDB entry + 6. Remove VLAN; move to non-VLAN + 7. Disable non-VLAN monitor port + 8. Remove mirror session + """ + self.setup_db(dvs) + + session = "TEST_SESSION" + + # create mirror session + self.create_mirror_session(session, "7.7.7.7", "8.8.8.8", "0x6558", "8", "100", "0") + assert self.get_mirror_session_status(session) == "inactive" + + # bring up port; add ip; add neighbor; add route + self.set_interface_status("Ethernet32", "up") + self.add_ip_address("Ethernet32", "80.0.0.0/31") + self.add_neighbor("Ethernet32", "80.0.0.1", "02:04:06:08:10:12") + self.add_route(dvs, "8.8.0.0/16", "80.0.0.1") + assert self.get_mirror_session_status(session) == "active" + + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet32" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID": + assert fv[1] == "false" + + # mirror session move round 1 + # create vlan; create vlan member; bring up vlan and member + self.create_vlan(dvs, "9") + self.create_vlan_member("9", "Ethernet48") + self.set_interface_status("Vlan9", "up") + self.set_interface_status("Ethernet48", "up") + assert self.get_mirror_session_status(session) == "active" + + # add ip address to vlan 9 + self.add_ip_address("Vlan9", "8.8.8.0/24") + assert self.get_mirror_session_status(session) == "inactive" + + # create neighbor to vlan 9 + self.add_neighbor("Vlan9", "8.8.8.8", "88:88:88:88:88:88") + assert self.get_mirror_session_status(session) == "inactive" + + # create fdb entry to ethernet48 + self.create_fdb("9", "88-88-88-88-88-88", "Ethernet48") + assert self.get_mirror_session_status(session) == "active" + + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet48" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID": assert fv[1] == "true" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": - assert fv[1] == "1000" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": - assert True - elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DSCP": - assert fv[1] == "16&mask:0x10" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": - assert fv[1] == "1:" + test_mirror_session_id - else: - assert False + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_TPID": + assert fv[1] == "33024" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_ID": + assert fv[1] == "9" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_PRI": + assert fv[1] == "0" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_CFI": + assert fv[1] == "0" + + # mirror session move round 2 + # remove fdb entry + self.remove_fdb("9", "88-88-88-88-88-88") + assert self.get_mirror_session_status(session) == "inactive" + + # remove neighbor + self.remove_neighbor("Vlan9", "8.8.8.8") + assert self.get_mirror_session_status(session) == "inactive" + + # remove ip address + self.remove_ip_address("Vlan9", "8.8.8.0/24") + assert self.get_mirror_session_status(session) == "active" + + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet32" + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID": + assert fv[1] == "false" - # remove the ACL rule - tbl._del("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_2") - time.sleep(1) + # bring down vlan and member; remove vlan member; remove vlan + self.set_interface_status("Ethernet48", "down") + self.set_interface_status("Vlan9", "down") + self.remove_vlan_member("9", "Ethernet48") + self.remove_vlan("9") + + # remove route; remove neighbor; remove ip; bring down port + self.remove_route(dvs, "8.8.8.0/24") + self.remove_neighbor("Ethernet32", "80.0.0.1") + self.remove_ip_address("Ethernet32", "80.0.0.0/31") + self.set_interface_status("Ethernet32", "down") + + # remove mirror session + self.remove_mirror_session(session) + + + def test_MirrorDestMoveLag(self, dvs): + """ + This test tests mirror session destination move from non-LAG to LAG + and back to non-LAG port + 1. Create mirror session + 2. Enable non-LAG monitor port + 3. Create LAG; move to LAG with one member + 4. Remove LAG member + 5. Create LAG member + 6. Remove LAG; move to non-LAG + 7. Disable non-LAG monitor port + 8. Remove mirror session + """ + self.setup_db(dvs) + + session = "TEST_SESSION" - # assert the ACL rule is removed - (status, fvs) = atbl.get(acl_entries[0]) - assert status == False + # create mirror session + self.create_mirror_session(session, "12.12.12.12", "13.13.13.13", "0x6558", "8", "100", "0") + assert self.get_mirror_session_status(session) == "inactive" - def test_AclMirrorTableDeletion(self, dvs): - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + # bring up port; add ip; add neighbor; add route + self.set_interface_status("Ethernet64", "up") + self.add_ip_address("Ethernet64", "100.0.0.0/31") + self.add_neighbor("Ethernet64", "100.0.0.1", "02:04:06:08:10:12") + self.add_route(dvs, "13.13.0.0/16", "100.0.0.1") + assert self.get_mirror_session_status(session) == "active" - tbl = swsscommon.Table(cdb, "ACL_TABLE") - # remove the ACL table - tbl._del("EVERFLOW_TABLE") + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet64" + if fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "02:04:06:08:10:12" + + # mirror session move round 1 + # create port channel; create port channel member; bring up + self.create_port_channel(dvs, "080") + self.create_port_channel_member("080", "Ethernet32") + self.set_interface_status("PortChannel080", "up") + self.set_interface_status("Ethernet32", "up") + + # add ip address to port channel 080; create neighbor to port channel 080 + self.add_ip_address("PortChannel080", "200.0.0.0/31") + self.add_neighbor("PortChannel080", "200.0.0.1", "12:10:08:06:04:02") + assert self.get_mirror_session_status(session) == "active" + + # add route + self.add_route(dvs, "13.13.13.0/24", "200.0.0.1") + assert self.get_mirror_session_status(session) == "active" + + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet32" + if fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "12:10:08:06:04:02" + + # mirror session move round 2 + # remove port channel member + self.remove_port_channel_member("080", "Ethernet32") + assert self.get_mirror_session_status(session) == "inactive" + + # mirror session move round 3 + # create port channel member + self.create_port_channel_member("080", "Ethernet32") + assert self.get_mirror_session_status(session) == "active" + + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet32" + if fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "12:10:08:06:04:02" + + # mirror session move round 4 + # remove route + self.remove_route(dvs, "13.13.13.0/24") + assert self.get_mirror_session_status(session) == "active" + + port_oid = "" + # check monitor port + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet64" + if fv[0] == "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": + assert fv[1] == "02:04:06:08:10:12" + + # remove neighbor; remove ip address to port channel 080 + self.remove_neighbor("PortChannel080", "200.0.0.1") + self.remove_ip_address("PortChannel080", "200.0.0.0/31") + + # bring down; remove port channel member; remove port channel + self.set_interface_status("Ethernet32", "down") + self.set_interface_status("PortChannel080", "down") + self.remove_port_channel_member("080", "Ethernet32") + self.remove_port_channel(dvs, "080") + assert self.get_mirror_session_status(session) == "active" + + # remove route; remove neighbor; remove ip; bring down port + self.remove_route(dvs, "13.13.0.0/16") + self.remove_neighbor("Ethernet64", "100.0.0.1") + self.remove_ip_address("Ethernet64", "100.0.0.0/31") + self.set_interface_status("Ethernet64", "down") + assert self.get_mirror_session_status(session) == "inactive" + + # remove mirror session + self.remove_mirror_session(session) + + + def create_acl_table(self, table, interfaces): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "mirror_test"), + ("type", "mirror"), + ("ports", ",".join(interfaces))]) + tbl.set(table, fvs) + time.sleep(1) + + def remove_acl_table(self, table): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + tbl._del(table) time.sleep(1) - # assert the ACL table is removed - tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - acl_table_ids = tbl.getKeys() - assert len(acl_table_ids) == 3 + def create_mirror_acl_dscp_rule(self, table, rule, dscp, session): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1000"), + ("mirror_action", session), + ("DSCP", dscp)]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) - tbl = swsscommon.Table(cdb, "MIRROR_SESSION") - # remove the mirror session - tbl._del("EVERFLOW_SESSION") + def remove_mirror_acl_dscp_rule(self, table, rule): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table + "|" + rule) time.sleep(1) - # assert the mirror session is created - tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") - mirror_session_ids = tbl.getKeys() - assert len(mirror_session_ids) == 0 + def test_AclBindMirror(self, dvs): + """ + This test tests ACL associated with mirror session with DSCP value + The DSCP value is tested on both with mask and without mask + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_rule = "MIRROR_RULE" + + # bring up port; assign ip; create neighbor; create route + self.set_interface_status("Ethernet32", "up") + self.add_ip_address("Ethernet32", "20.0.0.0/31") + self.add_neighbor("Ethernet32", "20.0.0.1", "02:04:06:08:10:12") + self.add_route(dvs, "4.4.4.4", "20.0.0.1") + + # create mirror session + self.create_mirror_session(session, "3.3.3.3", "4.4.4.4", "0x6558", "8", "100", "0") + assert self.get_mirror_session_status(session) == "active" + + # assert mirror session in asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + mirror_session_oid = tbl.getKeys()[0] + + # create acl table + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"]) + + # create acl rule with dscp value 48 + self.create_mirror_acl_dscp_rule(acl_table, acl_rule, "48", session) + + # assert acl rule is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(rule_entries) == 1 + + (status, fvs) = tbl.get(rule_entries[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DSCP": + assert fv[1] == "48&mask:0x3f" + if fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": + assert fv[1] == "1:" + mirror_session_oid + + # remove acl rule + self.remove_mirror_acl_dscp_rule(acl_table, acl_rule) + + # create acl rule with dscp value 16/16 + self.create_mirror_acl_dscp_rule(acl_table, acl_rule, "16/16", session) + + # assert acl rule is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(rule_entries) == 1 + + (status, fvs) = tbl.get(rule_entries[0]) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DSCP": + assert fv[1] == "16&mask:0x10" + if fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": + assert fv[1] == "1:" + mirror_session_oid + + # remove acl rule + self.remove_mirror_acl_dscp_rule(acl_table, acl_rule) + + # remove acl table + self.remove_acl_table(acl_table) + + # remove mirror session + self.remove_mirror_session(session) + + # assert no mirror session in asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 0 + + # remove route; remove neighbor; remove ip; bring down port + self.remove_route(dvs, "4.4.4.4") + self.remove_neighbor("Ethernet32", "20.0.0.1") + self.remove_ip_address("Ethernet32", "20.0.0.0/31") + self.set_interface_status("Ethernet32", "down")