Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 350 additions & 9 deletions orchagent/mirrororch.cpp

Large diffs are not rendered by default.

25 changes: 24 additions & 1 deletion orchagent/mirrororch.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
#define MIRROR_SESSION_SPAN "SPAN"
#define MIRROR_SESSION_ERSPAN "ERSPAN"

enum class MirrorBindDirection
{
Ingress,
Egress
};

/*
* Contains session data specified by user in config file
* and data required for MAC address and port resolution
Expand Down Expand Up @@ -57,6 +63,11 @@ struct MirrorEntry
sai_object_id_t portId;
} neighborInfo;

// Sampled mirroring fields
uint32_t sample_rate; // 0 = full mirror (default)
uint32_t truncate_size; // 0 = no truncation (default)
sai_object_id_t samplepacketId; // SAI_NULL_OBJECT_ID if not sampled

sai_object_id_t sessionId;

int64_t refCount;
Expand Down Expand Up @@ -136,9 +147,21 @@ class MirrorOrch : public Orch, public Observer, public Subject
bool validateSrcPortList(const string& srcPort);
bool validateDstPort(const string& dstPort);
bool setUnsetPortMirror(Port port, bool ingress, bool set,
sai_object_id_t sessionId);
sai_object_id_t sessionId,
sai_object_id_t samplepacketId = SAI_NULL_OBJECT_ID,
uint32_t sample_rate = 0);
bool configurePortMirrorSession(const string&, MirrorEntry&, bool enable);

// Sampled mirroring helpers
bool createSamplePacket(const string& name, MirrorEntry& session);
bool removeSamplePacket(const string& name, MirrorEntry& session);
bool setUnsetSampledMirrorOnPhyPort(sai_object_id_t phy_port_id,
const std::string& phy_port_alias,
bool set,
MirrorBindDirection direction,
sai_object_id_t sessionId,
sai_object_id_t samplepacketId);

void doTask(Consumer& consumer);
};

Expand Down
61 changes: 59 additions & 2 deletions orchagent/sfloworch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,36 @@ bool SflowOrch::sflowUpdateRate(sai_object_id_t port_id, uint32_t rate)
return true;
}

bool SflowOrch::isSflowSamplePacket(sai_object_id_t oid)
{
for (auto& it : m_sflowRateSampleMap)
{
if (it.second.m_sample_id == oid)
return true;
}
return false;
}

// Check-before-set: refuse to bind sFlow if this port's samplepacket attr is
// already owned by another (non-sFlow) feature
bool SflowOrch::isSamplepacketFreeForSflow(sai_object_id_t port_id, sai_port_attr_t attr_id,
sai_object_id_t sample_id, const char* dir_name)
{
sai_attribute_t check_attr;
check_attr.id = attr_id;
if (sai_port_api->get_port_attribute(port_id, 1, &check_attr) == SAI_STATUS_SUCCESS
&& check_attr.value.oid != SAI_NULL_OBJECT_ID
&& check_attr.value.oid != sample_id
&& !isSflowSamplePacket(check_attr.value.oid))
{
SWSS_LOG_ERROR("Port %" PRIx64 " %s_SAMPLEPACKET_ENABLE already bound to "
"OID 0x%" PRIx64 ", cannot bind sFlow",
port_id, dir_name, check_attr.value.oid);
return false;
}
return true;
}

bool SflowOrch::sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id, string direction)
{
sai_attribute_t attr;
Expand All @@ -115,7 +145,22 @@ bool SflowOrch::sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id,
SWSS_LOG_DEBUG("sflowAddPort %" PRIx64 " portOid %" PRIx64 " dir %s",
sample_id, port_id, direction.c_str());

if (direction == "both" || direction == "rx")
bool need_ingress = (direction == "both" || direction == "rx");
bool need_egress = (direction == "both" || direction == "tx");

// If the port's samplepacket is already owned by another (non-sFlow) feature, refuse to bind.
if (need_ingress &&
!isSamplepacketFreeForSflow(port_id, SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE, sample_id, "INGRESS"))
{
return false;
}
if (need_egress &&
!isSamplepacketFreeForSflow(port_id, SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE, sample_id, "EGRESS"))
{
return false;
}

if (need_ingress)
{
attr.id = SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE;
attr.value.oid = sample_id;
Expand All @@ -132,7 +177,7 @@ bool SflowOrch::sflowAddPort(sai_object_id_t sample_id, sai_object_id_t port_id,
}
}

if (direction == "both" || direction == "tx")
if (need_egress)
{
attr.id = SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE;
attr.value.oid = sample_id;
Expand Down Expand Up @@ -224,6 +269,18 @@ bool SflowOrch::sflowUpdateSampleDirection(sai_object_id_t port_id, string old_d
egr_sample_oid = port_info->second.m_sample_id;
}

// Check-before-set: on a direction change
if (ing_sample_oid != SAI_NULL_OBJECT_ID &&
!isSamplepacketFreeForSflow(port_id, SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE, ing_sample_oid, "INGRESS"))
{
return false;
}
if (egr_sample_oid != SAI_NULL_OBJECT_ID &&
!isSamplepacketFreeForSflow(port_id, SAI_PORT_ATTR_EGRESS_SAMPLEPACKET_ENABLE, egr_sample_oid, "EGRESS"))
{
return false;
}

attr.id = SAI_PORT_ATTR_INGRESS_SAMPLEPACKET_ENABLE;
attr.value.oid = ing_sample_oid;
sai_rc = sai_port_api->set_port_attribute(port_id, &attr);
Expand Down
3 changes: 3 additions & 0 deletions orchagent/sfloworch.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ class SflowOrch : public Orch
uint32_t sflowSessionGetRate(sai_object_id_t sample_id);
bool handleSflowSessionDel(sai_object_id_t port_id);
void sflowExtractInfo(std::vector<FieldValueTuple> &fvs, bool &admin, uint32_t &rate, string &dir);
bool isSflowSamplePacket(sai_object_id_t oid);
bool isSamplepacketFreeForSflow(sai_object_id_t port_id, sai_port_attr_t attr_id,
sai_object_id_t sample_id, const char* dir_name);
};
82 changes: 82 additions & 0 deletions orchagent/switchorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ SwitchOrch::SwitchOrch(DBConnector *db, vector<TableConnector>& connectors, Tabl
querySwitchTpidCapability();
querySwitchPortEgressSampleCapability();
querySwitchPortMirrorCapability();
querySwitchSamplePacketCapability();
querySwitchHashDefaults();
setSwitchIcmpOffloadCapability();
setFastLinkupCapability();
Expand Down Expand Up @@ -1957,6 +1958,87 @@ void SwitchOrch::querySwitchPortMirrorCapability()
set_switch_capability(fvVector);
}

void SwitchOrch::querySwitchSamplePacketCapability()
{
vector<FieldValueTuple> fvVector;
sai_status_t status = SAI_STATUS_SUCCESS;
sai_attr_capability_t capability;

// Check if SAI is capable of handling Port ingress sample mirror session
status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_PORT,
SAI_PORT_ATTR_INGRESS_SAMPLE_MIRROR_SESSION, &capability);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_WARN("Could not query port ingress sample mirror capability %d", status);
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_INGRESS_SAMPLE_MIRROR_CAPABLE, "false");
m_portIngressSampleMirrorSupported = false;
}
else
{
if (capability.set_implemented)
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_INGRESS_SAMPLE_MIRROR_CAPABLE, "true");
m_portIngressSampleMirrorSupported = true;
}
else
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_INGRESS_SAMPLE_MIRROR_CAPABLE, "false");
m_portIngressSampleMirrorSupported = false;
}
SWSS_LOG_NOTICE("port ingress sample mirror capability %d", capability.set_implemented);
}

// Check if SAI is capable of handling Port egress sample mirror session
status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_PORT,
SAI_PORT_ATTR_EGRESS_SAMPLE_MIRROR_SESSION, &capability);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_WARN("Could not query port egress sample mirror capability %d", status);
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_MIRROR_CAPABLE, "false");
m_portEgressSampleMirrorSupported = false;
}
else
{
if (capability.set_implemented)
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_MIRROR_CAPABLE, "true");
m_portEgressSampleMirrorSupported = true;
}
else
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_MIRROR_CAPABLE, "false");
m_portEgressSampleMirrorSupported = false;
}
SWSS_LOG_NOTICE("port egress sample mirror capability %d", capability.set_implemented);
}

// Check if SAI is capable of handling samplepacket truncation
status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SAMPLEPACKET,
SAI_SAMPLEPACKET_ATTR_TRUNCATE_ENABLE, &capability);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_WARN("Could not query samplepacket truncation capability %d", status);
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_SAMPLEPACKET_TRUNCATION_CAPABLE, "false");
m_samplepacketTruncationSupported = false;
}
else
{
if (capability.set_implemented)
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_SAMPLEPACKET_TRUNCATION_CAPABLE, "true");
m_samplepacketTruncationSupported = true;
}
else
{
fvVector.emplace_back(SWITCH_CAPABILITY_TABLE_SAMPLEPACKET_TRUNCATION_CAPABLE, "false");
m_samplepacketTruncationSupported = false;
}
SWSS_LOG_NOTICE("samplepacket truncation capability %d", capability.set_implemented);
}

set_switch_capability(fvVector);
}

void SwitchOrch::querySwitchTpidCapability()
{
SWSS_LOG_ENTER();
Expand Down
10 changes: 10 additions & 0 deletions orchagent/switchorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
#define SWITCH_CAPABILITY_TABLE_REG_NOTICE_ASIC_SDK_HEALTH_CATEGORY "REG_NOTICE_ASIC_SDK_HEALTH_CATEGORY"
#define SWITCH_CAPABILITY_TABLE_PORT_INGRESS_MIRROR_CAPABLE "PORT_INGRESS_MIRROR_CAPABLE"
#define SWITCH_CAPABILITY_TABLE_PORT_EGRESS_MIRROR_CAPABLE "PORT_EGRESS_MIRROR_CAPABLE"
#define SWITCH_CAPABILITY_TABLE_PORT_INGRESS_SAMPLE_MIRROR_CAPABLE "PORT_INGRESS_SAMPLE_MIRROR_CAPABLE"
#define SWITCH_CAPABILITY_TABLE_PORT_EGRESS_SAMPLE_MIRROR_CAPABLE "PORT_EGRESS_SAMPLE_MIRROR_CAPABLE"
#define SWITCH_CAPABILITY_TABLE_SAMPLEPACKET_TRUNCATION_CAPABLE "SAMPLEPACKET_TRUNCATION_CAPABLE"

#define SWITCH_STAT_COUNTER_FLEX_COUNTER_GROUP "SWITCH_STAT_COUNTER"

Expand Down Expand Up @@ -88,6 +91,9 @@ class SwitchOrch : public Orch
// Mirror capability interface for MirrorOrch
bool isPortIngressMirrorSupported() const { return m_portIngressMirrorSupported; }
bool isPortEgressMirrorSupported() const { return m_portEgressMirrorSupported; }
bool isPortIngressSampleMirrorSupported() const { return m_portIngressSampleMirrorSupported; }
bool isPortEgressSampleMirrorSupported() const { return m_portEgressSampleMirrorSupported; }
bool isSamplepacketTruncationSupported() const { return m_samplepacketTruncationSupported; }

private:
void doTask(Consumer &consumer);
Expand All @@ -102,6 +108,7 @@ class SwitchOrch : public Orch
void querySwitchTpidCapability();
void querySwitchPortEgressSampleCapability();
void querySwitchPortMirrorCapability();
void querySwitchSamplePacketCapability();

// Statistics
void generateSwitchCounterNameMap() const;
Expand Down Expand Up @@ -184,6 +191,9 @@ class SwitchOrch : public Orch
// Port mirror capabilities
bool m_portIngressMirrorSupported = false;
bool m_portEgressMirrorSupported = false;
bool m_portIngressSampleMirrorSupported = false;
bool m_portEgressSampleMirrorSupported = false;
bool m_samplepacketTruncationSupported = false;

// ASIC SDK health event
std::shared_ptr<swss::DBConnector> m_stateDbForNotification = nullptr;
Expand Down
27 changes: 27 additions & 0 deletions tests/dvslib/dvs_mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def create_span_session(self, name, dst_port, src_ports=None, direction="BOTH",

def create_erspan_session(self, name, src, dst, gre, dscp, ttl, queue, policer=None, src_ports=None, direction="BOTH"):
mirror_entry = {
"type": "ERSPAN",
"src_ip": src,
"dst_ip": dst,
"gre_type": gre,
Expand All @@ -41,6 +42,32 @@ def create_erspan_session(self, name, src, dst, gre, dscp, ttl, queue, policer=N

self.config_db.create_entry("MIRROR_SESSION", name, mirror_entry)


def create_erspan_session_sampled(self, name, src, dst, gre, dscp, ttl, queue,
policer=None, src_ports=None, direction="RX",
sample_rate=None, truncate_size=None):
mirror_entry = {
"type": "ERSPAN",
"src_ip": src,
"dst_ip": dst,
"gre_type": gre,
"dscp": dscp,
"ttl": ttl,
"queue": queue,
"direction": direction
}

if policer:
mirror_entry["policer"] = policer
if src_ports:
mirror_entry["src_port"] = src_ports
if sample_rate:
mirror_entry["sample_rate"] = sample_rate
if truncate_size:
mirror_entry["truncate_size"] = truncate_size

self.config_db.create_entry("MIRROR_SESSION", name, mirror_entry)

def remove_mirror_session(self, name):
self.config_db.delete_entry("MIRROR_SESSION", name)

Expand Down
1 change: 1 addition & 0 deletions tests/mock_tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ tests_SOURCES = aclorch_ut.cpp \
saihelper_ut.cpp \
mock_saihelper.cpp \
mirrororch_ut.cpp \
mirrororch_sample_port_sai_wrap.cpp \
hftelorch_ut.cpp \
hftelorch_notify_ut.cpp \
hftelorch_is_supported_sai_wrap.cpp \
Expand Down
Loading
Loading