From 99314503e7e947a441caae652c18b0551b997d9c Mon Sep 17 00:00:00 2001 From: Shuotian Cheng Date: Mon, 29 Apr 2019 09:46:09 -0700 Subject: [PATCH] [aclorch]: Add MIRRORv6 and MIRRORv4v6 support for ACL table (#825) Enable both IPv4 and IPv6 source and destination IP match and mirror functionality in SONiC. Signed-off-by: Shu0T1an ChenG --- orchagent/aclorch.cpp | 298 ++++++++++++++++-- orchagent/aclorch.h | 52 +++- orchagent/orchdaemon.cpp | 5 +- tests/test_mirror_ipv6_combined.py | 484 +++++++++++++++++++++++++++++ tests/test_mirror_ipv6_separate.py | 478 ++++++++++++++++++++++++++++ 5 files changed, 1270 insertions(+), 47 deletions(-) create mode 100644 tests/test_mirror_ipv6_combined.py create mode 100644 tests/test_mirror_ipv6_separate.py diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index d79f8213c6d1..675897592c6e 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -85,6 +85,7 @@ static acl_table_type_lookup_t aclTableTypeLookUp = { TABLE_TYPE_L3, ACL_TABLE_L3 }, { TABLE_TYPE_L3V6, ACL_TABLE_L3V6 }, { TABLE_TYPE_MIRROR, ACL_TABLE_MIRROR }, + { TABLE_TYPE_MIRRORV6, ACL_TABLE_MIRRORV6 }, { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE }, { TABLE_TYPE_DTEL_FLOW_WATCHLIST, ACL_TABLE_DTEL_FLOW_WATCHLIST }, { TABLE_TYPE_DTEL_DROP_WATCHLIST, ACL_TABLE_DTEL_DROP_WATCHLIST } @@ -589,12 +590,17 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir throw runtime_error("ACL rule action is not found in rule " + rule); } - if (type != ACL_TABLE_L3 && type != ACL_TABLE_L3V6 && type != ACL_TABLE_MIRROR && type != ACL_TABLE_DTEL_FLOW_WATCHLIST && type != ACL_TABLE_DTEL_DROP_WATCHLIST) + if (type != ACL_TABLE_L3 && + type != ACL_TABLE_L3V6 && + type != ACL_TABLE_MIRROR && + type != ACL_TABLE_MIRRORV6 && + type != ACL_TABLE_DTEL_FLOW_WATCHLIST && + type != ACL_TABLE_DTEL_DROP_WATCHLIST) { - throw runtime_error("Unknown table type."); + throw runtime_error("Unknown table type"); } - /* Mirror rules can exist in both tables*/ + /* Mirror rules can exist in both tables */ if (action == ACTION_MIRROR_ACTION) { return make_shared(acl, mirror, rule, table, type); @@ -948,6 +954,37 @@ bool AclRuleMirror::validateAddMatch(string attr_name, string attr_value) return false; } + /* + * Type of Tables and Supported Match Types (Configuration) + * |--------------------------------------------------| + * | Match Type | TABLE_MIRROR | TABLE_MIRRORV6 | + * |--------------------------------------------------| + * | MATCH_SRC_IP | √ | | + * | MATCH_DST_IP | √ | | + * |--------------------------------------------------| + * | MATCH_SRC_IPV6 | | √ | + * | MATCH_DST_IPV6 | | √ | + * |--------------------------------------------------| + * | MARTCH_ETHERTYPE | √ | | + * |--------------------------------------------------| + */ + + if (m_tableType == ACL_TABLE_MIRROR && + (attr_name == MATCH_SRC_IPV6 || attr_name == MATCH_DST_IPV6)) + { + SWSS_LOG_ERROR("%s match is not supported for the table of type MIRROR", + attr_name.c_str()); + return false; + } + + if (m_tableType == ACL_TABLE_MIRRORV6 && + (attr_name == MATCH_SRC_IP || attr_name == MATCH_DST_IP || attr_name == MATCH_ETHER_TYPE)) + { + SWSS_LOG_ERROR("%s match is not supported for the table of type MIRRORV6", + attr_name.c_str()); + return false; + } + return AclRule::validateAddMatch(attr_name, attr_value); } @@ -1118,9 +1155,12 @@ bool AclTable::create() return status == SAI_STATUS_SUCCESS; } - attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; - attr.value.booldata = true; - table_attrs.push_back(attr); + if (type != ACL_TABLE_MIRRORV6) + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + } attr.id = SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE; attr.value.booldata = true; @@ -1130,7 +1170,47 @@ bool AclTable::create() attr.value.booldata = true; table_attrs.push_back(attr); - if (type == ACL_TABLE_L3V6) + /* + * Type of Tables and Supported Match Types (ASIC database) + * |-----------------------------------------------------------------| + * | | TABLE_MIRROR | TABLE_MIRROR | TABLE_MIRRORV6 | + * | Match Type |----------------------------------------------| + * | | combined | separated | + * |-----------------------------------------------------------------| + * | MATCH_SRC_IP | √ | √ | | + * | MATCH_DST_IP | √ | √ | | + * |-----------------------------------------------------------------| + * | MATCH_SRC_IPV6 | √ | | √ | + * | MATCH_DST_IPV6 | √ | | √ | + * |-----------------------------------------------------------------| + * | MARTCH_ETHERTYPE | √ | √ | | + * |-----------------------------------------------------------------| + */ + + if (type == ACL_TABLE_MIRROR) + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + // If the switch supports v6 and requires one single table + if (m_pAclOrch->m_mirrorTableCapabilities[ACL_TABLE_MIRRORV6] && + m_pAclOrch->m_isCombinedMirrorV6Table) + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IPV6; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6; + attr.value.booldata = true; + table_attrs.push_back(attr); + } + } + else if (type == ACL_TABLE_L3V6 || type == ACL_TABLE_MIRRORV6) // v6 only { attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IPV6; attr.value.booldata = true; @@ -1140,7 +1220,7 @@ bool AclTable::create() attr.value.booldata = true; table_attrs.push_back(attr); } - else + else // v4 only { attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IP; attr.value.booldata = true; @@ -1780,6 +1860,65 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr { SWSS_LOG_ENTER(); + // TODO: Query SAI to get mirror table capabilities + // Right now, verified platforms that support mirroring IPv6 packets are + // Broadcom and Mellanox. Virtual switch is also supported for testing + // purposes. + string platform = getenv("platform") ? getenv("platform") : ""; + if (platform == BRCM_PLATFORM_SUBSTRING || + platform == MLNX_PLATFORM_SUBSTRING) + { + m_mirrorTableCapabilities = + { + { ACL_TABLE_MIRROR, true }, + { ACL_TABLE_MIRRORV6, true }, + }; + } + else + { + m_mirrorTableCapabilities = + { + { ACL_TABLE_MIRROR, true }, + { ACL_TABLE_MIRRORV6, false }, + }; + } + + SWSS_LOG_NOTICE("%s switch capability:", platform.c_str()); + SWSS_LOG_NOTICE(" ACL_TABLE_MIRROR: %s", + m_mirrorTableCapabilities[ACL_TABLE_MIRROR] ? "yes" : "no"); + SWSS_LOG_NOTICE(" ACL_TABLE_MIRRORV6: %s", + m_mirrorTableCapabilities[ACL_TABLE_MIRRORV6] ? "yes" : "no"); + + // In Broadcom platform, V4 and V6 rules are stored in the same table + if (platform == BRCM_PLATFORM_SUBSTRING) { + m_isCombinedMirrorV6Table = true; + } + + // In Mellanox platform, V4 and V6 rules are stored in different tables + if (platform == MLNX_PLATFORM_SUBSTRING) { + m_isCombinedMirrorV6Table = false; + } + + // Store the capabilities in state database + // TODO: Move this part of the code into syncd + vector fvVector; + for (auto const& it : m_mirrorTableCapabilities) + { + string value = it.second ? "true" : "false"; + switch (it.first) + { + case ACL_TABLE_MIRROR: + fvVector.emplace_back(TABLE_TYPE_MIRROR, value); + break; + case ACL_TABLE_MIRRORV6: + fvVector.emplace_back(TABLE_TYPE_MIRRORV6, value); + break; + default: + break; + } + } + m_switchTable.set("switch", fvVector); + sai_attribute_t attrs[2]; attrs[0].id = SAI_SWITCH_ATTR_ACL_ENTRY_MINIMUM_PRIORITY; attrs[1].id = SAI_SWITCH_ATTR_ACL_ENTRY_MAXIMUM_PRIORITY; @@ -1809,20 +1948,10 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr timer->start(); } -AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : - Orch(connectors), - m_mirrorOrch(mirrorOrch), - m_neighOrch(neighOrch), - m_routeOrch(routeOrch), - m_dTelOrch(NULL) -{ - SWSS_LOG_ENTER(); - - init(connectors, portOrch, mirrorOrch, neighOrch, routeOrch); -} - -AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *dtelOrch) : +AclOrch::AclOrch(vector& connectors, TableConnector switchTable, + PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *dtelOrch) : Orch(connectors), + m_switchTable(switchTable.first, switchTable.second), m_mirrorOrch(mirrorOrch), m_neighOrch(neighOrch), m_routeOrch(routeOrch), @@ -1835,9 +1964,8 @@ AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, Mirror if (m_dTelOrch) { m_dTelOrch->attach(this); + createDTelWatchListTables(); } - - createDTelWatchListTables(); } AclOrch::~AclOrch() @@ -1937,11 +2065,51 @@ bool AclOrch::addAclTable(AclTable &newTable, string table_id) } } + // Check if a separate mirror table is needed or not based on the platform + if (newTable.type == ACL_TABLE_MIRROR || newTable.type == ACL_TABLE_MIRRORV6) + { + + if (m_isCombinedMirrorV6Table && + (!m_mirrorTableId.empty() || !m_mirrorV6TableId.empty())) { + + string orig_table_name; + + // If v4 table is created, mark v6 table is created + if (!m_mirrorTableId.empty()) + { + orig_table_name = m_mirrorTableId; + m_mirrorV6TableId = newTable.id; + } + // If v6 table is created, mark v4 table is created + else + { + orig_table_name = m_mirrorV6TableId; + m_mirrorTableId = newTable.id; + } + + SWSS_LOG_NOTICE("Created ACL table %s as a sibling of %s", + newTable.id.c_str(), orig_table_name.c_str()); + + return true; + } + } + if (createBindAclTable(newTable, table_oid)) { m_AclTables[table_oid] = newTable; SWSS_LOG_NOTICE("Created ACL table %s oid:%lx", newTable.id.c_str(), table_oid); + + // Mark the existence of the mirror table + if (newTable.type == ACL_TABLE_MIRROR) + { + m_mirrorTableId = table_id; + } + else if (newTable.type == ACL_TABLE_MIRRORV6) + { + m_mirrorV6TableId = table_id; + } + return true; } else @@ -1974,6 +2142,26 @@ bool AclOrch::removeAclTable(string table_id) SWSS_LOG_NOTICE("Successfully deleted ACL table %s", table_id.c_str()); m_AclTables.erase(table_oid); + // Clear mirror table information + // If the v4 and v6 ACL mirror tables are combined together, + // remove both of them. + if (table_id == m_mirrorTableId) + { + m_mirrorTableId.clear(); + if (m_isCombinedMirrorV6Table) + { + m_mirrorV6TableId.clear(); + } + } + else if (table_id == m_mirrorV6TableId) + { + m_mirrorV6TableId.clear(); + if (m_isCombinedMirrorV6Table) + { + m_mirrorTableId.clear(); + } + } + return true; } else @@ -2007,6 +2195,11 @@ bool AclOrch::removeAclRule(string table_id, string rule_id) return m_AclTables[table_oid].remove(rule_id); } +bool AclOrch::isCombinedMirrorV6Table() +{ + return m_isCombinedMirrorV6Table; +} + void AclOrch::doAclTableTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -2024,9 +2217,10 @@ void AclOrch::doAclTableTask(Consumer &consumer) if (op == SET_COMMAND) { - AclTable newTable; + AclTable newTable(this); bool bAllAttributesOk = true; + // Scan all attributes for (auto itp : kfvFieldsValues(t)) { newTable.id = table_id; @@ -2133,12 +2327,15 @@ void AclOrch::doAclRuleTask(Consumer &consumer) { bool bAllAttributesOk = true; shared_ptr newRule; + + // Get the ACL table OID sai_object_id_t table_oid = getTableById(table_id); /* ACL table is not yet created or ACL table is a control plane table */ /* TODO: Remove ACL_TABLE_UNKNOWN as a table with this type cannot be successfully created */ if (table_oid == SAI_NULL_OBJECT_ID || m_AclTables[table_oid].type == ACL_TABLE_UNKNOWN) { + /* Skip the control plane rules */ if (m_ctrlAclTables.find(table_id) != m_ctrlAclTables.end()) { @@ -2152,7 +2349,14 @@ void AclOrch::doAclRuleTask(Consumer &consumer) continue; } - newRule = AclRule::makeShared(m_AclTables[table_oid].type, this, m_mirrorOrch, m_dTelOrch, rule_id, table_id, t); + auto type = m_AclTables[table_oid].type; + if (type == ACL_TABLE_MIRROR || type == ACL_TABLE_MIRRORV6) + { + type = table_id == m_mirrorTableId ? ACL_TABLE_MIRROR : ACL_TABLE_MIRRORV6; + } + + + newRule = AclRule::makeShared(type, this, m_mirrorOrch, m_dTelOrch, rule_id, table_id, t); for (const auto& itr : kfvFieldsValues(t)) { @@ -2254,14 +2458,34 @@ bool AclOrch::processAclTableType(string type, acl_table_type_t &table_type) { SWSS_LOG_ENTER(); - auto tt = aclTableTypeLookUp.find(to_upper(type)); + auto iter = aclTableTypeLookUp.find(to_upper(type)); - if (tt == aclTableTypeLookUp.end()) + if (iter == aclTableTypeLookUp.end()) { return false; } - table_type = tt->second; + table_type = iter->second; + + // Mirror table check procedure + if (table_type == ACL_TABLE_MIRROR || table_type == ACL_TABLE_MIRRORV6) + { + // Check the switch capability + if (!m_mirrorTableCapabilities[table_type]) + { + SWSS_LOG_ERROR("Mirror table type %s is not supported", type.c_str()); + return false; + } + + // Check the existence of current mirror tables + // Note: only one table per type could be created + if ((table_type == ACL_TABLE_MIRROR && !m_mirrorTableId.empty()) || + (table_type == ACL_TABLE_MIRRORV6 && !m_mirrorV6TableId.empty())) + { + SWSS_LOG_ERROR("Mirror table table_type %s has already been created", type.c_str()); + return false; + } + } return true; } @@ -2295,6 +2519,22 @@ sai_object_id_t AclOrch::getTableById(string table_id) } } + // Check if the table is a mirror table and a sibling mirror table is created + if (m_isCombinedMirrorV6Table && + (table_id == m_mirrorTableId || table_id == m_mirrorV6TableId)) + { + // If the table is v4, the corresponding v6 table is already created + if (table_id == m_mirrorTableId) + { + return getTableById(m_mirrorV6TableId); + } + // If the table is v6, the corresponding v4 table is already created + else + { + return getTableById(m_mirrorTableId); + } + } + return SAI_NULL_OBJECT_ID; } @@ -2538,7 +2778,7 @@ sai_status_t AclOrch::deleteDTelWatchListTables() { SWSS_LOG_ENTER(); - AclTable flowWLTable, dropWLTable; + AclTable flowWLTable(this), dropWLTable(this); sai_object_id_t table_oid; string table_id = TABLE_TYPE_DTEL_FLOW_WATCHLIST; diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 1be9704508ec..4e18924f7c0f 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -24,13 +24,14 @@ #define TABLE_PORTS "PORTS" #define TABLE_SERVICES "SERVICES" -#define TABLE_TYPE_L3 "L3" -#define TABLE_TYPE_L3V6 "L3V6" -#define TABLE_TYPE_MIRROR "MIRROR" -#define TABLE_TYPE_PFCWD "PFCWD" -#define TABLE_TYPE_CTRLPLANE "CTRLPLANE" -#define TABLE_TYPE_DTEL_FLOW_WATCHLIST "DTEL_FLOW_WATCHLIST" -#define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" +#define TABLE_TYPE_L3 "L3" +#define TABLE_TYPE_L3V6 "L3V6" +#define TABLE_TYPE_MIRROR "MIRROR" +#define TABLE_TYPE_MIRRORV6 "MIRRORV6" +#define TABLE_TYPE_PFCWD "PFCWD" +#define TABLE_TYPE_CTRLPLANE "CTRLPLANE" +#define TABLE_TYPE_DTEL_FLOW_WATCHLIST "DTEL_FLOW_WATCHLIST" +#define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" #define RULE_PRIORITY "PRIORITY" #define MATCH_IN_PORTS "IN_PORTS" @@ -55,8 +56,8 @@ #define MATCH_INNER_L4_SRC_PORT "INNER_L4_SRC_PORT" #define MATCH_INNER_L4_DST_PORT "INNER_L4_DST_PORT" -#define ACTION_PACKET_ACTION "PACKET_ACTION" -#define ACTION_MIRROR_ACTION "MIRROR_ACTION" +#define ACTION_PACKET_ACTION "PACKET_ACTION" +#define ACTION_MIRROR_ACTION "MIRROR_ACTION" #define ACTION_DTEL_FLOW_OP "FLOW_OP" #define ACTION_DTEL_INT_SESSION "INT_SESSION" #define ACTION_DTEL_DROP_REPORT_ENABLE "DROP_REPORT_ENABLE" @@ -95,6 +96,7 @@ typedef enum ACL_TABLE_L3, ACL_TABLE_L3V6, ACL_TABLE_MIRROR, + ACL_TABLE_MIRRORV6, ACL_TABLE_PFCWD, ACL_TABLE_CTRLPLANE, ACL_TABLE_DTEL_FLOW_WATCHLIST, @@ -301,6 +303,7 @@ class AclRuleDTelDropWatchListEntry: public AclRule class AclTable { sai_object_id_t m_oid; + AclOrch *m_pAclOrch; public: string id; string description; @@ -317,7 +320,15 @@ class AclTable { set pendingPortSet; AclTable() - : type(ACL_TABLE_UNKNOWN) + : m_pAclOrch(NULL) + , type(ACL_TABLE_UNKNOWN) + , m_oid(SAI_NULL_OBJECT_ID) + , stage(ACL_STAGE_INGRESS) + {} + + AclTable(AclOrch *aclOrch) + : m_pAclOrch(aclOrch) + , type(ACL_TABLE_UNKNOWN) , m_oid(SAI_NULL_OBJECT_ID) , stage(ACL_STAGE_INGRESS) {} @@ -363,8 +374,8 @@ inline void split(string str, Iterable& out, char delim = ' ') class AclOrch : public Orch, public Observer { public: - AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); - AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *m_dTelOrch); + AclOrch(vector& connectors, TableConnector switchTable, + PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *m_dTelOrch = NULL); ~AclOrch(); void update(SubjectType, void *); @@ -375,6 +386,8 @@ class AclOrch : public Orch, public Observer return m_countersTable; } + Table m_switchTable; + // FIXME: Add getters for them? I'd better to add a common directory of orch objects and use it everywhere MirrorOrch *m_mirrorOrch; NeighOrch *m_neighOrch; @@ -386,6 +399,11 @@ class AclOrch : public Orch, public Observer bool addAclRule(shared_ptr aclRule, string table_id); bool removeAclRule(string table_id, string rule_id); + bool isCombinedMirrorV6Table(); + + bool m_isCombinedMirrorV6Table = true; + map m_mirrorTableCapabilities; + private: void doTask(Consumer &consumer); void doAclTableTask(Consumer &consumer); @@ -393,6 +411,8 @@ class AclOrch : public Orch, public Observer void doTask(SelectableTimer &timer); void init(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); + void queryMirrorTableCapability(); + static void collectCountersThread(AclOrch *pAclOrch); bool createBindAclTable(AclTable &aclTable, sai_object_id_t &table_oid); @@ -406,7 +426,6 @@ class AclOrch : public Orch, public Observer sai_status_t createDTelWatchListTables(); sai_status_t deleteDTelWatchListTables(); - //vector m_AclTables; map m_AclTables; // TODO: Move all ACL tables into one map: name -> instance map m_ctrlAclTables; @@ -414,8 +433,11 @@ class AclOrch : public Orch, public Observer static mutex m_countersMutex; static condition_variable m_sleepGuard; static bool m_bCollectCounters; - static swss::DBConnector m_db; - static swss::Table m_countersTable; + static DBConnector m_db; + static Table m_countersTable; + + string m_mirrorTableId; + string m_mirrorV6TableId; }; #endif /* SWSS_ACLORCH_H */ diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 88b2d3ac06d3..19a548254b8b 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -190,10 +190,9 @@ bool OrchDaemon::init() { dtel_orch = new DTelOrch(m_configDb, dtel_tables, gPortsOrch); m_orchList.push_back(dtel_orch); - gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch, dtel_orch); - } else { - gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); } + TableConnector stateDbSwitchTable(m_stateDb, "SWITCH_CAPABILITY"); + gAclOrch = new AclOrch(acl_table_connectors, stateDbSwitchTable, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch, dtel_orch); m_orchList.push_back(gFdbOrch); m_orchList.push_back(mirror_orch); diff --git a/tests/test_mirror_ipv6_combined.py b/tests/test_mirror_ipv6_combined.py new file mode 100644 index 000000000000..f97315836a12 --- /dev/null +++ b/tests/test_mirror_ipv6_combined.py @@ -0,0 +1,484 @@ +# This test suite covers the functionality of mirror feature in SwSS + +import platform +import pytest +import time +from distutils.version import StrictVersion + +from swsscommon import swsscommon + +DVS_FAKE_PLATFORM = "broadcom" + + +class TestMirror(object): + 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 setup_mirrorv6_mode(self, mode): + tbl = swsscommon.Table(self.sdb, "SWITCH_CAPABILITY") + fvs = swsscommon.FieldValuePairs([("mirror_v6_table_mode", mode)]) + tbl.set("switch", fvs) + time.sleep(1) + + 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 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) + + 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 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) + + def remove_neighbor(self, interface, ip): + tbl = swsscommon.ProducerStateTable(self.pdb, "NEIGH_TABLE") + tbl._del(interface + ":" + ip) + time.sleep(1) + + def add_route(self, dvs, prefix, nexthop): + dvs.runcmd("ip route add " + prefix + " via " + nexthop) + time.sleep(1) + + def remove_route(self, dvs, prefix): + dvs.runcmd("ip route del " + prefix) + time.sleep(1) + + 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) + + def remove_mirror_session(self, name): + tbl = swsscommon.Table(self.cdb, "MIRROR_SESSION") + tbl._del(name) + time.sleep(1) + + def get_mirror_session_status(self, name): + return self.get_mirror_session_state(name)["status"] + + def get_mirror_session_state(self, name): + tbl = swsscommon.Table(self.sdb, "MIRROR_SESSION") + (status, fvs) = tbl.get(name) + assert status == True + assert len(fvs) > 0 + return { fv[0]: fv[1] for fv in fvs } + + def create_acl_table(self, table, interfaces, type): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "mirror_test"), + ("type", type), + ("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) + + def create_mirror_acl_ipv4_rule(self, table, rule, session): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1000"), + ("mirror_action", session), + ("SRC_IP", "10.0.0.0/32"), + ("DST_IP", "20.0.0.0/23")]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) + + def create_mirror_acl_ipv6_rule(self, table, rule, session): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1000"), + ("mirror_action", session), + ("SRC_IPV6", "2777::0/64"), + ("DST_IPV6", "3666::0/128")]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) + + def remove_mirror_acl_rule(self, table, rule): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table + "|" + rule) + time.sleep(1) + + # Test case - create a MIRROR table and a MIRRORV6 table in combined mode + # 0. predefine the VS capability: combined + # 1. create a mirror session + # 2. create two ACL tables that support both IPv4 and IPv6 + # 3. create two ACL rules with both IPv4 and IPv6 source and destination IP + # verify the ACL rules are created successfully + # 4. remove all the configurations + def test_AclBindMirrorV6Combined(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + self.setup_mirrorv6_mode("combined") + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # assert acl table is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + table_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_tables] + assert len(table_entries) == 1 + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # assert acl table is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + table_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_tables] + assert len(table_entries) == 1 + + table_id = table_entries[0] + + # create acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table, acl_rule_1, session) + + # assert acl rule ipv4 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 + + rule_id_v4 = rule_entries[0] + + # assert acl rule is assocaited with table ipv4 + (status, fvs) = tbl.get(rule_id_v4) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == table_id + + # create acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table_v6, acl_rule_2, session) + + # assert acl rule ipv6 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) == 2 + + rule_id_v6 = rule_entries[1] if rule_entries[0] == rule_id_v4 else rule_entries[0] + + # assert acl rule is associated with table ipv6 + (status, fvs) = tbl.get(rule_id_v6) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == table_id + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + + # Test case - intervene rule creation in table creation + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create the ipv4 ACL table + # 3. create the ipv4 ACL rule + # 4. create the ipv6 ACL table + # 5. create the ipv6 ACL rule + # 6. verify two rules are inserted successfully + def test_AclBindMirrorV6Reorder1(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # create acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table, acl_rule_1, session) + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # create acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table_v6, acl_rule_2, session) + + # assert acl rules are 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) == 2 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + + # Test case - intervene rule creation in table creation + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create the ipv4 ACL table + # 3. create the ipv6 ACL rule + # 4. create the ipv6 ACL table + # 5. create the ipv4 ACL rule + # 6. verify two rules are inserted successfully + def test_AclBindMirrorV6Reorder2(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # create acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table_v6, acl_rule_2, session) + + # assert acl rule is not 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) == 0 + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # create acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table, acl_rule_1, session) + + # assert acl rules are 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) == 2 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + + # Test case - create ACL rules associated with wrong table + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create the ipv4 ACL table + # 3. create the ipv6 ACL rule associated with ipv4 table + # 4. create the ipv6 ACL table + # 5. create the ipv4 ACL rule associated with ipv6 table + # 6. verify two rules are inserted successfully + def test_AclBindMirrorV6WrongConfig(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # create WRONG acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table, acl_rule_2, session) + + # assert acl rule is not 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) == 0 + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # create WRONG acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table_v6, acl_rule_1, session) + + # assert acl rules are 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) == 0 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + diff --git a/tests/test_mirror_ipv6_separate.py b/tests/test_mirror_ipv6_separate.py new file mode 100644 index 000000000000..839e79c018a4 --- /dev/null +++ b/tests/test_mirror_ipv6_separate.py @@ -0,0 +1,478 @@ +# This test suite covers the functionality of mirror feature in SwSS + +import platform +import pytest +import time +from distutils.version import StrictVersion + +from swsscommon import swsscommon + +DVS_FAKE_PLATFORM = "mellanox" + + +class TestMirror(object): + 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 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) + + 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 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) + + def remove_neighbor(self, interface, ip): + tbl = swsscommon.ProducerStateTable(self.pdb, "NEIGH_TABLE") + tbl._del(interface + ":" + ip) + time.sleep(1) + + def add_route(self, dvs, prefix, nexthop): + dvs.runcmd("ip route add " + prefix + " via " + nexthop) + time.sleep(1) + + def remove_route(self, dvs, prefix): + dvs.runcmd("ip route del " + prefix) + time.sleep(1) + + 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) + + def remove_mirror_session(self, name): + tbl = swsscommon.Table(self.cdb, "MIRROR_SESSION") + tbl._del(name) + time.sleep(1) + + def get_mirror_session_status(self, name): + return self.get_mirror_session_state(name)["status"] + + def get_mirror_session_state(self, name): + tbl = swsscommon.Table(self.sdb, "MIRROR_SESSION") + (status, fvs) = tbl.get(name) + assert status == True + assert len(fvs) > 0 + return { fv[0]: fv[1] for fv in fvs } + + def create_acl_table(self, table, interfaces, type): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "mirror_test"), + ("type", type), + ("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) + + def create_mirror_acl_ipv4_rule(self, table, rule, session): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1000"), + ("mirror_action", session), + ("SRC_IP", "10.0.0.0/32"), + ("DST_IP", "20.0.0.0/23")]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) + + def create_mirror_acl_ipv6_rule(self, table, rule, session): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1000"), + ("mirror_action", session), + ("SRC_IPV6", "2777::0/64"), + ("DST_IPV6", "3666::0/128")]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) + + def remove_mirror_acl_rule(self, table, rule): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table + "|" + rule) + time.sleep(1) + + + # Test case - create a MIRROR table and a MIRRORV6 table in separated mode + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create two ACL tables that support IPv4 and IPv6 separatedly + # 3. create two ACL rules with both IPv4 and IPv6 source and destination IP + # verify the ACL rules are created successfully + # 4. remove all the configurations + def test_AclBindMirrorSeparated(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # assert acl table ipv4 is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + table_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_tables] + assert len(table_entries) == 1 + + table_id_v4 = table_entries[0] + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # assert acl table ipv6 is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + table_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_tables] + assert len(table_entries) == 2 + + table_id_v6 = table_entries[1] if table_entries[0] == table_id_v4 else table_entries[0] + + # create acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table, acl_rule_1, session) + + # assert acl rule ipv4 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 + + rule_id_v4 = rule_entries[0] + + # assert acl rule is assocaited with table ipv4 + (status, fvs) = tbl.get(rule_id_v4) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == table_id_v4 + + # create acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table_v6, acl_rule_2, session) + + # assert acl rule ipv6 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) == 2 + + rule_id_v6 = rule_entries[1] if rule_entries[0] == rule_id_v4 else rule_entries[0] + + # assert acl rule is associated with table ipv6 + (status, fvs) = tbl.get(rule_id_v6) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == table_id_v6 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + # Test case - intervene rule creation in table creation + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create the ipv4 ACL table + # 3. create the ipv4 ACL rule + # 4. create the ipv6 ACL table + # 5. create the ipv6 ACL rule + # 6. verify two rules are inserted successfully + def test_AclBindMirrorV6Reorder1(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # create acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table, acl_rule_1, session) + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # create acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table_v6, acl_rule_2, session) + + # assert acl rules are 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) == 2 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + + # Test case - intervene rule creation in table creation + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create the ipv4 ACL table + # 3. create the ipv6 ACL rule + # 4. create the ipv6 ACL table + # 5. create the ipv4 ACL rule + # 6. verify two rules are inserted successfully + def test_AclBindMirrorV6Reorder2(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # create acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table_v6, acl_rule_2, session) + + # assert acl rule is not 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) == 0 + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # create acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table, acl_rule_1, session) + + # assert acl rules are 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) == 2 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + + + # Test case - create ACL rules associated with wrong table + # 0. predefine the VS platform: mellanox platform + # 1. create a mirror session + # 2. create the ipv4 ACL table + # 3. create the ipv6 ACL rule associated with ipv4 table + # 4. create the ipv6 ACL table + # 5. create the ipv4 ACL rule associated with ipv6 table + # 6. verify two rules are inserted successfully + def test_AclBindMirrorV6WrongConfig(self, dvs, testlog): + """ + This test verifies IPv6 rules cannot be inserted into MIRROR table + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_table_v6 = "MIRROR_TABLE_V6" + acl_rule_1 = "MIRROR_RULE_1" + acl_rule_2 = "MIRROR_RULE_2" + + # 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_state(session)["status"] == "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 ipv4 + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"], "MIRROR") + + # create WRONG acl rule with IPv6 addresses + self.create_mirror_acl_ipv6_rule(acl_table, acl_rule_2, session) + + # assert acl rule is not 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) == 0 + + # create acl table ipv6 + self.create_acl_table(acl_table_v6, ["Ethernet0", "Ethernet4"], "MIRRORV6") + + # create WRONG acl rule with IPv4 addresses + self.create_mirror_acl_ipv4_rule(acl_table_v6, acl_rule_1, session) + + # assert acl rules are 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) == 0 + + # remove acl rule + self.remove_mirror_acl_rule(acl_table, acl_rule_1) + self.remove_mirror_acl_rule(acl_table_v6, acl_rule_2) + + # remove acl table + self.remove_acl_table(acl_table) + self.remove_acl_table(acl_table_v6) + + # 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") + +