diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp old mode 100644 new mode 100755 index e6aeec9d23..a30ca40d83 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -192,6 +192,12 @@ void MirrorOrch::update(SubjectType type, void *cntx) updateVlanMember(*update); break; } + case SUBJECT_TYPE_PORT_OPER_STATE_CHANGE: + { + PortOperStateUpdate *update = static_cast(cntx); + updateLagMemberOperChange(*update); + break; + } default: // Received update in which we are not interested // Ignore it @@ -277,7 +283,7 @@ bool MirrorOrch::validateDstPort(const string& dstPort) SWSS_LOG_ERROR("Not supported port %s type %d", dstPort.c_str(), port.m_type); return false; } - if (port.m_type != Port::PHY) + if (port.m_type != Port::PHY && port.m_type != Port::LAG) { SWSS_LOG_ERROR("Not supported port %s", dstPort.c_str()); return false; @@ -590,11 +596,11 @@ void MirrorOrch::setSessionState(const string& name, const MirrorEntry& session, SWSS_LOG_ERROR("Failed to get recirc port for mirror session %s", name.c_str()); return; } - } - else - { - m_portsOrch->getPort(session.neighborInfo.portId, port); - } + } + else + { + m_portsOrch->getPort(session.neighborInfo.portId, port); + } fvVector.emplace_back(MIRROR_SESSION_MONITOR_PORT, port.m_alias); } @@ -692,12 +698,13 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) } else { - // Get the first member of the LAG - Port member; - string first_member_alias = *session.neighborInfo.port.m_members.begin(); - m_portsOrch->getPort(first_member_alias, member); - - session.neighborInfo.portId = member.m_port_id; + // Get the first linked up member of the LAG + Port upPort; + if (!m_portsOrch->getUpLagMember(session.neighborInfo.port, upPort)) + { + return false; + } + session.neighborInfo.portId = upPort.m_port_id; } return true; @@ -734,7 +741,24 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) else { // Update monitor port - session.neighborInfo.portId = member.m_port_id; + if (member.m_type == Port::LAG) + { + if (member.m_members.empty()) + { + return false; + } + + Port upPort; + if (!m_portsOrch->getUpLagMember(member, upPort)) + { + return false; + } + session.neighborInfo.portId = upPort.m_port_id; + } + else + { + session.neighborInfo.portId = member.m_port_id; + } } } @@ -929,7 +953,22 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) } attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; - attr.value.oid = dst_port.m_port_id; + // Allowed portchannel to be dst_port of mirror session + if (dst_port.m_type == Port::PHY) + { + attr.value.oid = dst_port.m_port_id; + SWSS_LOG_INFO("Destination port object id: %lu", attr.value.oid); + } + else if (dst_port.m_type == Port::LAG) + { + attr.value.oid = dst_port.m_lag_id; + SWSS_LOG_INFO("Destination port object id: %lu", attr.value.oid); + } + else + { + SWSS_LOG_ERROR("Type of Port %s is incorrect.", session.dst_port.c_str()); + return false; + } attrs.push_back(attr); attr.id = SAI_MIRROR_SESSION_ATTR_TYPE; @@ -1480,11 +1519,12 @@ void MirrorOrch::updateLagMember(const LagMemberUpdate& update) if (!session.status) { assert(!update.lag.m_members.empty()); - const string& member_name = *update.lag.m_members.begin(); - Port member; - m_portsOrch->getPort(member_name, member); - - session.neighborInfo.portId = member.m_port_id; + Port upPort; + if (!m_portsOrch->getUpLagMember(update.lag, upPort)) + { + continue; + } + session.neighborInfo.portId = upPort.m_port_id; activateSession(name, session); } } @@ -1502,11 +1542,12 @@ void MirrorOrch::updateLagMember(const LagMemberUpdate& update) // Switch to a new member of the LAG else { - const string& member_name = *update.lag.m_members.begin(); - Port member; - m_portsOrch->getPort(member_name, member); - - session.neighborInfo.portId = member.m_port_id; + Port upPort; + if (!m_portsOrch->getUpLagMember(update.lag, upPort)) + { + continue; + } + session.neighborInfo.portId = upPort.m_port_id; // The destination MAC remains the same updateSessionDstPort(name, session); } @@ -1546,6 +1587,79 @@ void MirrorOrch::updateVlanMember(const VlanMemberUpdate& update) } } +/* Handle the case when the member of the LAG is up or down. + This function is called when SUBJECT_TYPE_PORT_OPER_STATE_CHANGE is received. */ +void MirrorOrch::updateLagMemberOperChange(const PortOperStateUpdate& update) +{ + SWSS_LOG_ENTER(); + + /* Check the following two conditions: + 1) The update port is PHY port. + 2) The update port belongs to a LAG. */ + Port lag; + if (update.port.m_type != Port::PHY || + !m_portsOrch->getPort(update.port.m_lag_id, lag)) + { + return; + } + + for (auto it = m_syncdMirrors.begin(); it != m_syncdMirrors.end(); it++) + { + const auto& name = it->first; + auto& session = it->second; + + /* 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 != lag) + { + continue; + } + + Port cur_dst_port; + if (!m_portsOrch->getPort(session.neighborInfo.portId, cur_dst_port)) + { + SWSS_LOG_ERROR("Failed to locate Port/LAG %s", session.dst_port.c_str()); + continue; + } + + if (update.operStatus == SAI_PORT_OPER_STATUS_UP) + { + /* Activate mirror session if it was deactivated due to the reason + that previously there was no active member in the LAG. If the mirror + session is already activated, no further action is needed. */ + if (!session.status) + { + session.neighborInfo.portId = update.port.m_port_id; + activateSession(name, session); + } + } + else + { + // shutdown a LAG member + if (session.status) + { + // If the shoutdown port is the current dst port, try to switch to a new member of the LAG. + if (update.port == cur_dst_port) + { + Port upPort; + if (m_portsOrch->getUpLagMember(lag, upPort, update.port.m_alias)) + { + session.neighborInfo.portId = upPort.m_port_id; + updateSessionDstPort(name, session); + } + else + { + session.neighborInfo.portId = SAI_NULL_OBJECT_ID; + deactivateSession(name, session); + } + } + } + } + } +} + void MirrorOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h old mode 100644 new mode 100755 index d498a7ef6c..fa0c0f026b --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -130,6 +130,7 @@ class MirrorOrch : public Orch, public Observer, public Subject void updateFdb(const FdbUpdate&); void updateLagMember(const LagMemberUpdate&); void updateVlanMember(const VlanMemberUpdate&); + void updateLagMemberOperChange(const PortOperStateUpdate&); bool checkPortExistsInSrcPortList(const string& port, const string& srcPortList); bool validateSrcPortList(const string& srcPort); diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index a7a6e25548..41242a73c9 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -5785,6 +5785,25 @@ void PortsOrch::getLagMember(Port &lag, vector &portv) } } +/* Get the first linked up member of the LAG, + and notice that the return value from getPort() is still stale if triggered by SUBJECT_TYPE_PORT_OPER_STATE_CHANGE. + To avoid getting stale information, pass a port_alias as a filter. +*/ +bool PortsOrch::getUpLagMember(Port &lag, Port &upPort, const string &aliasExclude) +{ + vector portv; + getLagMember(lag, portv); + for (const auto p : portv) + { + if (p.m_oper_status == SAI_PORT_OPER_STATUS_UP && p.m_alias != aliasExclude) + { + upPort = p; + return true; + } + } + return false; +} + bool PortsOrch::addLagMember(Port &lag, Port &port, bool enableForwarding) { SWSS_LOG_ENTER(); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h old mode 100644 new mode 100755 index 446d6bc8de..a29f15129e --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -152,6 +152,7 @@ class PortsOrch : public Orch, public Subject bool removeSubPort(const string &alias); bool updateL3VniStatus(uint16_t vlan_id, bool status); void getLagMember(Port &lag, vector &portv); + bool getUpLagMember(Port &lag, Port &upPort, const string &aliasExclude = ""); void updateChildPortsMtu(const Port &p, const uint32_t mtu); bool addTunnel(string tunnel,sai_object_id_t, bool learning=true);