diff --git a/README.md b/README.md index e1641405e1e..a484d1a5a55 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ Broadcom[![Broadcom](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/) -Cavium[![Cavium](https://sonic-jenkins.westus2.cloudapp.azure.com/job/cavium/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/cavium/job/sonic-swss-build/) Mellanox[![Mellanox](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/) -P4[![P4](https://sonic-jenkins.westus2.cloudapp.azure.com/job/p4/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/p4/job/sonic-swss-build/) +VS[![VS](https://sonic-jenkins.westus2.cloudapp.azure.com/job/vs/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/vs/job/sonic-swss-build/) # SONiC - SWitch State Service - SWSS diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 8993c7684ea..cd509b3bbd7 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -144,7 +144,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, // Set Interface VRF except for lo if (!is_lo) { - setIntfVrf(alias, vrf_name); + if (!vrf_name.empty()) + { + setIntfVrf(alias, vrf_name); + } m_appIntfTableProducer.set(alias, data); } else diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 7b3d31f904d..ae54982a7d5 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -111,14 +111,6 @@ For example (reorder output) oper_status = "down" / "up" ; operating status mtu = 1*4DIGIT ; MTU for the IP interface of the VLAN ---------------------------------------------- -### VLAN_MEMBER_TABLE - ;Defines interfaces which are members of a vlan - ;Status: work in progress - - key = VLAN_MEMBER_TABLE:"Vlan"vlanid:ifname ; physical port "ifname" is a member of a VLAN "VlanX" - tagging_mode = "untagged" / "tagged" / "priority_tagged" ; default value as untagged - --------------------------------------------- ### LAG_TABLE ;a logical, link aggregation group interface (802.3ad) made of one or more ports @@ -570,12 +562,13 @@ Equivalent RedisDB entry: ---------------------------------------------- -### PORT\_MIRROR\_TABLE -Stores information about mirroring session and its properties. +### MIRROR\_SESSION\_TABLE +Mirror session table +Stores information about mirror sessions and their properties. - key = PORT_MIRROR_TABLE:mirror_session_name ; mirror_session_name is - ; unique session - ; identifier + key = MIRROR_SESSION_TABLE:mirror_session_name ; mirror_session_name is + ; unique session + ; identifier ; field = value status = "active"/"inactive" ; Session state. src_ip = ipv4_address ; Session souce IP address @@ -584,9 +577,11 @@ Stores information about mirroring session and its properties. dscp = h8 ; Session DSCP ttl = h8 ; Session TTL queue = h8 ; Session output queue + policer = policer_name ; Session policer name ;value annotations mirror_session_name = 1*255VCHAR + policer_name = 1*255VCHAR h8 = 1*2HEXDIG h16 = 1*4HEXDIG ipv4_address = dec-octet "." dec-octet "." dec-octet "." dec-octet “/” %d1-32 @@ -628,7 +623,45 @@ Equivalent RedisDB entry: 10) "64" 11) "queue" 12) "0" - 127.0.0.1:6379> + +--------------------------------------------- + +### POLICER_TABLE +Policer table +Stores information about policers and their properties. + +packet_action = "drop" | "forward" | "copy" | "copy_cancel" | "trap" | "log" | "deny" | "transit" + + ;Key + key = "POLICER_TABLE:name" + + ;Field-Value tuples + meter_type = "packets" | "bytes" + mode = "sr_tcm" | "tr_tcm" | "storm" + color = "aware" | "blind" + cbs = number ;packets or bytes depending on the meter_type value + cir = number ;packets or bytes depending on the meter_type value + pbs = number ;packets or bytes depending on the meter_type value + pir = number ;packets or bytes depending on the meter_type value + + green_action = packet_action + yellow_action = packet_action + red_action = packet_action + + Example: + 127.0.0.1:6379> hgetall "POLICER_TABLE:POLICER_1" + 1) "cbs" + 2) "600" + 3) "cir" + 4) "600" + 5) "meter_type" + 6) "packets" + 7) "mode" + 8) "sr_tcm" + 9) "red_action" + 10) "drop" + +---------------------------------------------- ### VXLAN\_TUNNEL\_MAP ;Stores vxlan tunnel map configuration. Defines mapping between vxlan vni and vrf diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index 414e9ed3a4e..8f05338f2d5 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -21,7 +21,8 @@ RouteSync::RouteSync(RedisPipeline *pipeline) : m_routeTable(pipeline, APP_ROUTE_TABLE_NAME, true), m_vnet_routeTable(pipeline, APP_VNET_RT_TABLE_NAME, true), m_vnet_tunnelTable(pipeline, APP_VNET_RT_TUNNEL_TABLE_NAME, true), - m_warmStartHelper(pipeline, &m_routeTable, APP_ROUTE_TABLE_NAME, "bgp", "bgp") + m_warmStartHelper(pipeline, &m_routeTable, APP_ROUTE_TABLE_NAME, "bgp", "bgp"), + m_nl_sock(NULL), m_link_cache(NULL) { m_nl_sock = nl_socket_alloc(); nl_connect(m_nl_sock, NETLINK_ROUTE); @@ -291,7 +292,8 @@ bool RouteSync::getIfName(int if_index, char *if_name, size_t name_len) /* Cannot get interface name. Possibly the interface gets re-created. */ if (!rtnl_link_i2name(m_link_cache, if_index, if_name, name_len)) { - rtnl_link_alloc_cache(m_nl_sock, AF_UNSPEC, &m_link_cache); + /* Trying to refill cache */ + nl_cache_refill(m_nl_sock, m_link_cache); if (!rtnl_link_i2name(m_link_cache, if_index, if_name, name_len)) { return false; diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 4e889e8613f..c1f83c3aac4 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -9,6 +9,7 @@ #include "ipaddress.h" #include "netmsg.h" #include "linkcache.h" +#include "macaddress.h" #include "neighsync.h" #include "warm_restart.h" @@ -78,6 +79,13 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) nl_addr2str(rtnl_neigh_get_lladdr(neigh), macStr, MAX_ADDR_SIZE); + /* Ignore neighbor entries with Broadcast Mac - Trigger for directed broadcast */ + if (!delete_key && (MacAddress(macStr) == MacAddress("ff:ff:ff:ff:ff:ff"))) + { + SWSS_LOG_INFO("Broadcast Mac recieved, ignoring for %s", ipStr); + return; + } + std::vector fvVector; FieldValueTuple f("family", family); FieldValueTuple nh("neigh", macStr); diff --git a/neighsyncd/restore_neighbors.py b/neighsyncd/restore_neighbors.py index e0a0eea9434..d0851c632ae 100755 --- a/neighsyncd/restore_neighbors.py +++ b/neighsyncd/restore_neighbors.py @@ -161,7 +161,7 @@ def set_neigh_in_kernel(ipclass, family, intf_idx, dst_ip, dmac): def build_arp_ns_pkt(family, smac, src_ip, dst_ip): if family == 'IPv4': eth = Ether(src=smac, dst='ff:ff:ff:ff:ff:ff') - pkt = eth/ARP(op=ARP.who_has, pdst=dst_ip) + pkt = eth/ARP(op='who-has', pdst=dst_ip) elif family == 'IPv6': nsma = in6_getnsma(inet_pton(AF_INET6, dst_ip)) mcast_dst_ip = inet_ntop(AF_INET6, nsma) diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index f8839fb18cd..90e56a06e31 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -49,39 +49,8 @@ orchagent_SOURCES = \ vnetorch.cpp \ dtelorch.cpp \ flexcounterorch.cpp \ - watermarkorch.cpp \ - acltable.h \ - aclorch.h \ - bufferorch.h \ - copporch.h \ - directory.h \ - fdborch.h \ - intfsorch.h \ - mirrororch.h \ - neighorch.h \ - notifications.h \ - observer.h \ - orch.h \ - orchdaemon.h \ - pfcactionhandler.h \ - pfcwdorch.h \ - port.h \ - portsorch.h \ - qosorch.h \ - routeorch.h \ - saihelper.h \ - switchorch.h \ - swssnet.h \ - tunneldecaporch.h \ - crmorch.h \ - request_parser.h \ - vrforch.h \ - dtelorch.h \ - countercheckorch.h \ - vxlanorch.h \ - vnetorch.h \ - flexcounterorch.h \ - watermarkorch.h + watermarkorch.cpp \ + policerorch.cpp orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index d79f8213c6d..44f827854bc 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -46,6 +46,10 @@ acl_rule_attr_lookup_t aclMatchLookup = { MATCH_IP_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE }, { MATCH_DSCP, SAI_ACL_ENTRY_ATTR_FIELD_DSCP }, { MATCH_TC, SAI_ACL_ENTRY_ATTR_FIELD_TC }, + { MATCH_ICMP_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_ICMP_TYPE }, + { MATCH_ICMP_CODE, SAI_ACL_ENTRY_ATTR_FIELD_ICMP_CODE }, + { MATCH_ICMPV6_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_TYPE }, + { MATCH_ICMPV6_CODE, SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_CODE }, { MATCH_L4_SRC_PORT_RANGE, (sai_acl_entry_attr_t)SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE }, { MATCH_L4_DST_PORT_RANGE, (sai_acl_entry_attr_t)SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE }, { MATCH_TUNNEL_VNI, SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_VNI }, @@ -85,6 +89,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 } @@ -333,6 +338,12 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) value.aclfield.data.u8 = to_uint(attr_value); value.aclfield.mask.u8 = 0xFF; } + else if (attr_name == MATCH_ICMP_TYPE || attr_name == MATCH_ICMP_CODE || + attr_name == MATCH_ICMPV6_TYPE || attr_name == MATCH_ICMPV6_CODE) + { + value.aclfield.data.u8 = to_uint(attr_value); + value.aclfield.mask.u8 = 0xFF; + } else if (attr_name == MATCH_TUNNEL_VNI) { value.aclfield.data.u32 = to_uint(attr_value); @@ -589,12 +600,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); @@ -844,12 +860,19 @@ bool AclRuleL3::validateAddMatch(string attr_name, string attr_value) { if (attr_name == MATCH_DSCP) { - SWSS_LOG_ERROR("DSCP match is not supported for the tables of type L3"); + SWSS_LOG_ERROR("DSCP match is not supported for table type L3"); return false; } + if (attr_name == MATCH_SRC_IPV6 || attr_name == MATCH_DST_IPV6) { - SWSS_LOG_ERROR("IPv6 address match is not supported for the tables of type L3"); + SWSS_LOG_ERROR("IPv6 address match is not supported for table type L3"); + return false; + } + + if (attr_name == MATCH_ICMPV6_TYPE || attr_name == MATCH_ICMPV6_CODE) + { + SWSS_LOG_ERROR("ICMPv6 match is not supported for table type L3"); return false; } @@ -900,12 +923,19 @@ bool AclRuleL3V6::validateAddMatch(string attr_name, string attr_value) { if (attr_name == MATCH_DSCP) { - SWSS_LOG_ERROR("DSCP match is not supported for the tables of type L3V6"); + SWSS_LOG_ERROR("DSCP match is not supported for table type L3V6"); return false; } + if (attr_name == MATCH_SRC_IP || attr_name == MATCH_DST_IP) { - SWSS_LOG_ERROR("IPv4 address match is not supported for the tables of type L3V6"); + SWSS_LOG_ERROR("IPv4 address match is not supported for table type L3V6"); + return false; + } + + if (attr_name == MATCH_ICMP_TYPE || attr_name == MATCH_ICMP_CODE) + { + SWSS_LOG_ERROR("ICMPv4 match is not supported for table type L3V6"); return false; } @@ -948,6 +978,46 @@ 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_ICMP_TYPE | √ | | + * | MATCH_ICMP_CODE | √ | | + * |---------------------------------------------------| + * | MATCH_ICMPV6_TYPE | | √ | + * | MATCH_ICMPV6_CODE | | √ | + * |---------------------------------------------------| + * | MATCH_SRC_IPV6 | | √ | + * | MATCH_DST_IPV6 | | √ | + * |---------------------------------------------------| + * | MARTCH_ETHERTYPE | √ | | + * |---------------------------------------------------| + */ + + if (m_tableType == ACL_TABLE_MIRROR && + (attr_name == MATCH_SRC_IPV6 || attr_name == MATCH_DST_IPV6 || + attr_name == MATCH_ICMPV6_TYPE || attr_name == MATCH_ICMPV6_CODE)) + { + 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_ICMP_TYPE || attr_name == MATCH_ICMP_CODE || + 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 +1188,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 +1203,69 @@ 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_ICMP_TYPE | √ | √ | | + * | MATCH_ICMP_CODE | √ | √ | | + * |------------------------------------------------------------------| + * | MATCH_SRC_IPV6 | √ | | √ | + * | MATCH_DST_IPV6 | √ | | √ | + * |------------------------------------------------------------------| + * | MATCH_ICMPV6_TYPE | √ | | √ | + * | MATCH_ICMPV6_CODE | √ | | √ | + * |------------------------------------------------------------------| + * | 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); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMP_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMP_CODE; + 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); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMPV6_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMPV6_CODE; + 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; @@ -1139,8 +1274,16 @@ bool AclTable::create() attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6; attr.value.booldata = true; table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMPV6_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMPV6_CODE; + 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; @@ -1149,6 +1292,14 @@ bool AclTable::create() attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IP; attr.value.booldata = true; table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMP_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ICMP_CODE; + attr.value.booldata = true; + table_attrs.push_back(attr); } attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT; @@ -1780,6 +1931,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 +2019,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 +2035,8 @@ AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, Mirror if (m_dTelOrch) { m_dTelOrch->attach(this); + createDTelWatchListTables(); } - - createDTelWatchListTables(); } AclOrch::~AclOrch() @@ -1937,11 +2136,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 +2213,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 +2266,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 +2288,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 +2398,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 +2420,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 +2529,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 +2590,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 +2849,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 1be9704508e..46151476e49 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" @@ -49,14 +50,18 @@ #define MATCH_L4_SRC_PORT_RANGE "L4_SRC_PORT_RANGE" #define MATCH_L4_DST_PORT_RANGE "L4_DST_PORT_RANGE" #define MATCH_TC "TC" +#define MATCH_ICMP_TYPE "ICMP_TYPE" +#define MATCH_ICMP_CODE "ICMP_CODE" +#define MATCH_ICMPV6_TYPE "ICMPV6_TYPE" +#define MATCH_ICMPV6_CODE "ICMPV6_CODE" #define MATCH_TUNNEL_VNI "TUNNEL_VNI" #define MATCH_INNER_ETHER_TYPE "INNER_ETHER_TYPE" #define MATCH_INNER_IP_PROTOCOL "INNER_IP_PROTOCOL" #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 +100,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 +307,7 @@ class AclRuleDTelDropWatchListEntry: public AclRule class AclTable { sai_object_id_t m_oid; + AclOrch *m_pAclOrch; public: string id; string description; @@ -317,7 +324,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 +378,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 +390,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 +403,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 +415,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 +430,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 +437,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/copporch.cpp b/orchagent/copporch.cpp index ea68dd0cbaf..85fa5bf9248 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -17,23 +17,23 @@ extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; -map policer_meter_map = { +static map policer_meter_map = { {"packets", SAI_METER_TYPE_PACKETS}, {"bytes", SAI_METER_TYPE_BYTES} }; -map policer_mode_map = { +static map policer_mode_map = { {"sr_tcm", SAI_POLICER_MODE_SR_TCM}, {"tr_tcm", SAI_POLICER_MODE_TR_TCM}, {"storm", SAI_POLICER_MODE_STORM_CONTROL} }; -map policer_color_aware_map = { +static map policer_color_aware_map = { {"aware", SAI_POLICER_COLOR_SOURCE_AWARE}, {"blind", SAI_POLICER_COLOR_SOURCE_BLIND} }; -map trap_id_map = { +static map trap_id_map = { {"stp", SAI_HOSTIF_TRAP_TYPE_STP}, {"lacp", SAI_HOSTIF_TRAP_TYPE_LACP}, {"eapol", SAI_HOSTIF_TRAP_TYPE_EAPOL}, @@ -71,7 +71,7 @@ map trap_id_map = { {"udld", SAI_HOSTIF_TRAP_TYPE_UDLD} }; -map packet_action_map = { +static map packet_action_map = { {"drop", SAI_PACKET_ACTION_DROP}, {"forward", SAI_PACKET_ACTION_FORWARD}, {"copy", SAI_PACKET_ACTION_COPY}, diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 982f37ec8ad..ca22b83eac2 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -215,7 +215,6 @@ void CrmOrch::doTask(Consumer &consumer) else if (op == DEL_COMMAND) { SWSS_LOG_ERROR("Unsupported operation type %s\n", op.c_str()); - it = consumer.m_toSync.erase(it); } else { diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index d890af1eece..467f3aa8ffb 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -297,7 +297,7 @@ void IntfsOrch::doTask(Consumer &consumer) sai_object_id_t vrf_id = gVirtualRouterId; if (!vrf_name.empty()) { - if (m_vrfOrch->isVRFexists(vrf_name)) + if (!m_vrfOrch->isVRFexists(vrf_name)) { it++; continue; diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 120eb7b17a2..135e3464e26 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -24,6 +24,7 @@ #define MIRROR_SESSION_MONITOR_PORT "monitor_port" #define MIRROR_SESSION_ROUTE_PREFIX "route_prefix" #define MIRROR_SESSION_VLAN_HEADER_VALID "vlan_header_valid" +#define MIRROR_SESSION_POLICER "policer" #define MIRROR_SESSION_DEFAULT_VLAN_PRI 0 #define MIRROR_SESSION_DEFAULT_VLAN_CFI 0 @@ -60,12 +61,13 @@ MirrorEntry::MirrorEntry(const string& platform) : } MirrorOrch::MirrorOrch(TableConnector stateDbConnector, TableConnector confDbConnector, - PortsOrch *portOrch, RouteOrch *routeOrch, NeighOrch *neighOrch, FdbOrch *fdbOrch) : + PortsOrch *portOrch, RouteOrch *routeOrch, NeighOrch *neighOrch, FdbOrch *fdbOrch, PolicerOrch *policerOrch) : Orch(confDbConnector.first, confDbConnector.second), m_portsOrch(portOrch), m_routeOrch(routeOrch), m_neighOrch(neighOrch), m_fdbOrch(fdbOrch), + m_policerOrch(policerOrch), m_mirrorTable(stateDbConnector.first, stateDbConnector.second) { m_portsOrch->attach(this); @@ -239,6 +241,18 @@ void MirrorOrch::createEntry(const string& key, const vector& d { entry.queue = to_uint(fvValue(i)); } + else if (fvField(i) == MIRROR_SESSION_POLICER) + { + if (!m_policerOrch->policerExists(fvValue(i))) + { + SWSS_LOG_ERROR("Failed to get policer %s", + fvValue(i).c_str()); + return; + } + + m_policerOrch->increaseRefCount(fvValue(i)); + entry.policer = fvValue(i); + } else { SWSS_LOG_ERROR("Failed to parse session %s configuration. Unknown attribute %s.\n", key.c_str(), fvField(i).c_str()); @@ -292,6 +306,11 @@ void MirrorOrch::deleteEntry(const string& name) deactivateSession(name, session); } + if (!session.policer.empty()) + { + m_policerOrch->decreaseRefCount(session.policer); + } + m_syncdMirrors.erase(sessionIter); SWSS_LOG_NOTICE("Removed mirror session %s", name.c_str()); @@ -387,7 +406,8 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) } case Port::VLAN: { - SWSS_LOG_NOTICE("vlan id is %d", session.neighborInfo.port.m_vlan_info.vlan_id); + SWSS_LOG_NOTICE("Get mirror session destination IP neighbor VLAN %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)) { @@ -550,6 +570,20 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) attr.value.u16 = session.greType; attrs.push_back(attr); + if (!session.policer.empty()) + { + sai_object_id_t oid = SAI_NULL_OBJECT_ID; + if (!m_policerOrch->getPolicerOid(session.policer, oid)) + { + SWSS_LOG_ERROR("Faield to get policer %s", session.policer.c_str()); + return false; + } + + attr.id = SAI_MIRROR_SESSION_ATTR_POLICER; + attr.value.oid = oid; + attrs.push_back(attr); + } + status = sai_mirror_api-> create_mirror_session(&session.sessionId, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) @@ -566,7 +600,7 @@ 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()); + SWSS_LOG_NOTICE("Activated mirror session %s", name.c_str()); return true; } @@ -593,7 +627,7 @@ bool MirrorOrch::deactivateSession(const string& name, MirrorEntry& session) // Store whole state into StateDB, since it is far from that frequent it's durable setSessionState(name, session); - SWSS_LOG_NOTICE("Deactive mirror session %s", name.c_str()); + SWSS_LOG_NOTICE("Deactivated mirror session %s", name.c_str()); return true; } diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index 076f6b20260..765b24cb5f5 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -7,6 +7,7 @@ #include "neighorch.h" #include "routeorch.h" #include "fdborch.h" +#include "policerorch.h" #include "ipaddress.h" #include "ipaddresses.h" @@ -30,6 +31,7 @@ struct MirrorEntry uint8_t dscp; uint8_t ttl; uint8_t queue; + string policer; struct { @@ -65,7 +67,7 @@ class MirrorOrch : public Orch, public Observer, public Subject { public: MirrorOrch(TableConnector appDbConnector, TableConnector confDbConnector, - PortsOrch *portOrch, RouteOrch *routeOrch, NeighOrch *neighOrch, FdbOrch *fdbOrch); + PortsOrch *portOrch, RouteOrch *routeOrch, NeighOrch *neighOrch, FdbOrch *fdbOrch, PolicerOrch *policerOrch); void update(SubjectType, void *); bool sessionExists(const string&); @@ -79,6 +81,7 @@ class MirrorOrch : public Orch, public Observer, public Subject RouteOrch *m_routeOrch; NeighOrch *m_neighOrch; FdbOrch *m_fdbOrch; + PolicerOrch *m_policerOrch; Table m_mirrorTable; diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 1594dfd969e..1eabb8e3aa0 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -393,9 +393,19 @@ bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) status = sai_neighbor_api->create_neighbor_entry(&neighbor_entry, 1, &neighbor_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to create neighbor %s on %s, rv:%d", + if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) + { + SWSS_LOG_ERROR("Entry exists: neighbor %s on %s, rv:%d", macAddress.to_string().c_str(), alias.c_str(), status); - return false; + /* Returning True so as to skip retry */ + return true; + } + else + { + SWSS_LOG_ERROR("Failed to create neighbor %s on %s, rv:%d", + macAddress.to_string().c_str(), alias.c_str(), status); + return false; + } } SWSS_LOG_NOTICE("Created neighbor %s on %s", macAddress.to_string().c_str(), alias.c_str()); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 88b2d3ac06d..fc3e932c0bc 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -128,9 +128,11 @@ bool OrchDaemon::init() }; gBufferOrch = new BufferOrch(m_configDb, buffer_tables); - TableConnector stateDbMirrorSession(m_stateDb, APP_MIRROR_SESSION_TABLE_NAME); + PolicerOrch *policer_orch = new PolicerOrch(m_configDb, "POLICER"); + + TableConnector stateDbMirrorSession(m_stateDb, STATE_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_configDb, CFG_MIRROR_SESSION_TABLE_NAME); - MirrorOrch *mirror_orch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch); + MirrorOrch *mirror_orch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, policer_orch); TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_NAME); TableConnector confDbAclRuleTable(m_configDb, CFG_ACL_RULE_TABLE_NAME); @@ -148,7 +150,12 @@ bool OrchDaemon::init() CFG_DTEL_EVENT_TABLE_NAME }; - WatermarkOrch *wm_orch = new WatermarkOrch(m_configDb, CFG_WATERMARK_TABLE_NAME); + vector wm_tables = { + CFG_WATERMARK_TABLE_NAME, + CFG_FLEX_COUNTER_TABLE_NAME + }; + + WatermarkOrch *wm_orch = new WatermarkOrch(m_configDb, wm_tables); /* * The order of the orch list is important for state restore of warm start and @@ -158,7 +165,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. * That is ensured implicitly by the order of map key, "LAG_TABLE" is smaller than "VLAN_TABLE" in lexicographic order. */ - m_orchList = { gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, wm_orch }; + m_orchList = { gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, wm_orch, policer_orch }; bool initialize_dtel = false; @@ -190,10 +197,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/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 09342088b1e..14d43fb2c1a 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -26,6 +26,7 @@ #include "countercheckorch.h" #include "flexcounterorch.h" #include "watermarkorch.h" +#include "policerorch.h" #include "directory.h" using namespace swss; diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index 568e104d326..43611415979 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -68,20 +68,38 @@ void PfcWdOrch::doTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); + task_process_status task_status = task_process_status::task_ignore; if (op == SET_COMMAND) { - createEntry(key, kfvFieldsValues(t)); + task_status = createEntry(key, kfvFieldsValues(t)); } else if (op == DEL_COMMAND) { - deleteEntry(key); + task_status = deleteEntry(key); } else { + task_status = task_process_status::task_invalid_entry; SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); } - - consumer.m_toSync.erase(it++); + switch (task_status) + { + case task_process_status::task_success: + consumer.m_toSync.erase(it++); + break; + case task_process_status::task_need_retry: + SWSS_LOG_INFO("Failed to process PFC watchdog %s task, retry it", op.c_str()); + ++it; + break; + case task_process_status::task_invalid_entry: + SWSS_LOG_ERROR("Failed to process PFC watchdog %s task, invalid entry", op.c_str()); + consumer.m_toSync.erase(it++); + break; + default: + SWSS_LOG_ERROR("Invalid task status %d", task_status); + consumer.m_toSync.erase(it++); + break; + } } if (consumer.m_toSync.empty()) @@ -156,7 +174,7 @@ string PfcWdOrch::serializeAction(const PfcWdAction template -void PfcWdOrch::createEntry(const string& key, +task_process_status PfcWdOrch::createEntry(const string& key, const vector& data) { SWSS_LOG_ENTER(); @@ -170,13 +188,13 @@ void PfcWdOrch::createEntry(const string& key, if (!gPortsOrch->getPort(key, port)) { SWSS_LOG_ERROR("Invalid port interface %s", key.c_str()); - return; + return task_process_status::task_invalid_entry; } if (port.m_type != Port::PHY) { SWSS_LOG_ERROR("Interface %s is not physical port", key.c_str()); - return; + return task_process_status::task_invalid_entry; } for (auto i : data) @@ -205,7 +223,7 @@ void PfcWdOrch::createEntry(const string& key, if (action == PfcWdAction::PFC_WD_ACTION_UNKNOWN) { SWSS_LOG_ERROR("Invalid PFC Watchdog action %s", value.c_str()); - return; + return task_process_status::task_invalid_entry; } } else @@ -214,7 +232,7 @@ void PfcWdOrch::createEntry(const string& key, "Failed to parse PFC Watchdog %s configuration. Unknown attribute %s.\n", key.c_str(), field.c_str()); - return; + return task_process_status::task_invalid_entry; } } catch (const exception& e) @@ -224,7 +242,7 @@ void PfcWdOrch::createEntry(const string& key, key.c_str(), field.c_str(), e.what()); - return; + return task_process_status::task_invalid_entry; } catch (...) { @@ -232,7 +250,7 @@ void PfcWdOrch::createEntry(const string& key, "Failed to parse PFC Watchdog %s attribute %s. Unknown error has been occurred", key.c_str(), field.c_str()); - return; + return task_process_status::task_invalid_entry; } } @@ -240,20 +258,21 @@ void PfcWdOrch::createEntry(const string& key, if (detectionTime == 0) { SWSS_LOG_ERROR("%s missing", PFC_WD_DETECTION_TIME); - return; + return task_process_status::task_invalid_entry; } if (!startWdOnPort(port, detectionTime, restorationTime, action)) { SWSS_LOG_ERROR("Failed to start PFC Watchdog on port %s", port.m_alias.c_str()); - return; + return task_process_status::task_need_retry; } SWSS_LOG_NOTICE("Started PFC Watchdog on port %s", port.m_alias.c_str()); + return task_process_status::task_success; } template -void PfcWdOrch::deleteEntry(const string& name) +task_process_status PfcWdOrch::deleteEntry(const string& name) { SWSS_LOG_ENTER(); @@ -263,14 +282,15 @@ void PfcWdOrch::deleteEntry(const string& name) if (!stopWdOnPort(port)) { SWSS_LOG_ERROR("Failed to stop PFC Watchdog on port %s", name.c_str()); - return; + return task_process_status::task_failed; } SWSS_LOG_NOTICE("Stopped PFC Watchdog on port %s", name.c_str()); + return task_process_status::task_success; } template -void PfcWdSwOrch::createEntry(const string& key, +task_process_status PfcWdSwOrch::createEntry(const string& key, const vector& data) { SWSS_LOG_ENTER(); @@ -297,8 +317,10 @@ void PfcWdSwOrch::createEntry(const string& key, } else { - PfcWdOrch::createEntry(key, data); + return PfcWdOrch::createEntry(key, data); } + + return task_process_status::task_success; } template @@ -457,7 +479,7 @@ void PfcWdSwOrch::enableBigRedSwitchMode() } template -void PfcWdSwOrch::registerInWdDb(const Port& port, +bool PfcWdSwOrch::registerInWdDb(const Port& port, uint32_t detectionTime, uint32_t restorationTime, PfcWdAction action) { SWSS_LOG_ENTER(); @@ -467,7 +489,7 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, if (!gPortsOrch->getPortPfc(port.m_port_id, &pfcMask)) { SWSS_LOG_ERROR("Failed to get PFC mask on port %s", port.m_alias.c_str()); - return; + return false; } set losslessTc; @@ -480,6 +502,11 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, losslessTc.insert(i); } + if (losslessTc.empty()) + { + SWSS_LOG_NOTICE("No lossless TC found on port %s", port.m_alias.c_str()); + return false; + } if (!c_portStatIds.empty()) { @@ -541,6 +568,8 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, sai_object_id_t groupId; gPortsOrch->createBindAclTableGroup(port.m_port_id, groupId, ACL_STAGE_INGRESS); gPortsOrch->createBindAclTableGroup(port.m_port_id, groupId, ACL_STAGE_EGRESS); + + return true; } template @@ -716,9 +745,7 @@ bool PfcWdSwOrch::startWdOnPort(const Port& port, { SWSS_LOG_ENTER(); - registerInWdDb(port, detectionTime, restorationTime, action); - - return true; + return registerInWdDb(port, detectionTime, restorationTime, action); } template diff --git a/orchagent/pfcwdorch.h b/orchagent/pfcwdorch.h index ac0f6f55a90..7175c85f01e 100644 --- a/orchagent/pfcwdorch.h +++ b/orchagent/pfcwdorch.h @@ -48,8 +48,8 @@ class PfcWdOrch: public Orch static PfcWdAction deserializeAction(const string& key); static string serializeAction(const PfcWdAction &action); - virtual void createEntry(const string& key, const vector& data); - void deleteEntry(const string& name); + virtual task_process_status createEntry(const string& key, const vector& data); + task_process_status deleteEntry(const string& name); protected: virtual bool startWdActionOnQueue(const string &event, sai_object_id_t queueId) = 0; @@ -79,7 +79,7 @@ class PfcWdSwOrch: public PfcWdOrch uint32_t detectionTime, uint32_t restorationTime, PfcWdAction action); virtual bool stopWdOnPort(const Port& port); - void createEntry(const string& key, const vector& data); + task_process_status createEntry(const string& key, const vector& data) override; virtual void doTask(SelectableTimer &timer); //XXX Add port/queue state change event handlers @@ -106,7 +106,7 @@ class PfcWdSwOrch: public PfcWdOrch template static string counterIdsToStr(const vector ids, string (*convert)(T)); - void registerInWdDb(const Port& port, + bool registerInWdDb(const Port& port, uint32_t detectionTime, uint32_t restorationTime, PfcWdAction action); void unregisterFromWdDb(const Port& port); void doTask(swss::NotificationConsumer &wdNotification); diff --git a/orchagent/policerorch.cpp b/orchagent/policerorch.cpp new file mode 100644 index 00000000000..aea221cb489 --- /dev/null +++ b/orchagent/policerorch.cpp @@ -0,0 +1,268 @@ +#include "sai.h" +#include "policerorch.h" + +#include "converter.h" + +using namespace std; +using namespace swss; + +extern sai_policer_api_t* sai_policer_api; + +extern sai_object_id_t gSwitchId; +extern PortsOrch* gPortsOrch; + +static const string meter_type_field = "METER_TYPE"; +static const string mode_field = "MODE"; +static const string color_source_field = "COLOR_SOURCE"; +static const string cbs_field = "CBS"; +static const string cir_field = "CIR"; +static const string pbs_field = "PBS"; +static const string pir_field = "PIR"; +static const string green_packet_action_field = "GREEN_PACKET_ACTION"; +static const string red_packet_action_field = "RED_PACKET_ACTION"; +static const string yellow_packet_action_field = "YELLOW_PACKET_ACTION"; + +static const map meter_type_map = { + {"PACKETS", SAI_METER_TYPE_PACKETS}, + {"BYTES", SAI_METER_TYPE_BYTES} +}; + +static const map policer_mode_map = { + {"SR_TCM", SAI_POLICER_MODE_SR_TCM}, + {"TR_TCM", SAI_POLICER_MODE_TR_TCM}, + {"STORM_CONTROL", SAI_POLICER_MODE_STORM_CONTROL} +}; + +static const map policer_color_source_map = { + {"AWARE", SAI_POLICER_COLOR_SOURCE_AWARE}, + {"BLIND", SAI_POLICER_COLOR_SOURCE_BLIND} +}; + +static const map packet_action_map = { + {"DROP", SAI_PACKET_ACTION_DROP}, + {"FORWARD", SAI_PACKET_ACTION_FORWARD}, + {"COPY", SAI_PACKET_ACTION_COPY}, + {"COPY_CANCEL", SAI_PACKET_ACTION_COPY_CANCEL}, + {"TRAP", SAI_PACKET_ACTION_TRAP}, + {"LOG", SAI_PACKET_ACTION_LOG}, + {"DENY", SAI_PACKET_ACTION_DENY}, + {"TRANSIT", SAI_PACKET_ACTION_TRANSIT} +}; + +bool PolicerOrch::policerExists(const string &name) +{ + SWSS_LOG_ENTER(); + + return m_syncdPolicers.find(name) != m_syncdPolicers.end(); +} + +bool PolicerOrch::getPolicerOid(const string &name, sai_object_id_t &oid) +{ + SWSS_LOG_ENTER(); + + if (policerExists(name)) + { + oid = m_syncdPolicers[name]; + SWSS_LOG_NOTICE("Get policer %s oid:%lx", name.c_str(), oid); + return true; + } + + return false; +} + +bool PolicerOrch::increaseRefCount(const string &name) +{ + SWSS_LOG_ENTER(); + + if (!policerExists(name)) + { + SWSS_LOG_WARN("Policer %s does not exist", name.c_str()); + return false; + } + + ++m_policerRefCounts[name]; + + SWSS_LOG_INFO("Policer %s reference count is increased to %d", + name.c_str(), m_policerRefCounts[name]); + return true; +} + +bool PolicerOrch::decreaseRefCount(const string &name) +{ + SWSS_LOG_ENTER(); + + if (!policerExists(name)) + { + SWSS_LOG_WARN("Policer %s does not exist", name.c_str()); + return false; + } + + --m_policerRefCounts[name]; + + SWSS_LOG_INFO("Policer %s reference count is decreased to %d", + name.c_str(), m_policerRefCounts[name]); + return true; +} + +PolicerOrch::PolicerOrch(DBConnector* db, string tableName) : + Orch(db, tableName) +{ + SWSS_LOG_ENTER(); +} + +void PolicerOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->isPortReady()) + { + return; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto tuple = it->second; + + auto key = kfvKey(tuple); + auto op = kfvOp(tuple); + + if (op == SET_COMMAND) + { + if (m_syncdPolicers.find(key) != m_syncdPolicers.end()) + { + SWSS_LOG_ERROR("Policer %s already exists", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + vector attrs; + bool meter_type = false, mode = false; + + for (auto i = kfvFieldsValues(tuple).begin(); + i != kfvFieldsValues(tuple).end(); ++i) + { + auto field = to_upper(fvField(*i)); + auto value = to_upper(fvValue(*i)); + + SWSS_LOG_DEBUG("attribute: %s value: %s", field.c_str(), value.c_str()); + + sai_attribute_t attr; + + if (field == meter_type_field) + { + attr.id = SAI_POLICER_ATTR_METER_TYPE; + attr.value.s32 = (sai_meter_type_t) meter_type_map.at(value); + meter_type = true; + } + else if (field == mode_field) + { + attr.id = SAI_POLICER_ATTR_MODE; + attr.value.s32 = (sai_policer_mode_t) policer_mode_map.at(value); + mode = true; + } + else if (field == color_source_field) + { + attr.id = SAI_POLICER_ATTR_COLOR_SOURCE; + attr.value.s32 = policer_color_source_map.at(value); + } + else if (field == cbs_field) + { + attr.id = SAI_POLICER_ATTR_CBS; + attr.value.u64 = stoul(value); + } + else if (field == cir_field) + { + attr.id = SAI_POLICER_ATTR_CIR; + attr.value.u64 = stoul(value); + } + else if (field == pbs_field) + { + attr.id = SAI_POLICER_ATTR_PBS; + attr.value.u64 = stoul(value); + } + else if (field == pir_field) + { + attr.id = SAI_POLICER_ATTR_PIR; + attr.value.u64 = stoul(value); + } + else if (field == red_packet_action_field) + { + attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION; + attr.value.s32 = packet_action_map.at(value); + } + else if (field == green_packet_action_field) + { + attr.id = SAI_POLICER_ATTR_GREEN_PACKET_ACTION; + attr.value.s32 = packet_action_map.at(value); + } + else if (field == yellow_packet_action_field) + { + attr.id = SAI_POLICER_ATTR_YELLOW_PACKET_ACTION; + attr.value.s32 = packet_action_map.at(value); + } + else + { + SWSS_LOG_ERROR("Unknown policer attribute %s specified", + field.c_str()); + continue; + } + + attrs.push_back(attr); + } + + if (!meter_type || !mode) + { + SWSS_LOG_ERROR("Failed to create policer %s,\ + missing madatory fields", key.c_str()); + } + + sai_object_id_t policer_id; + sai_status_t status = sai_policer_api->create_policer( + &policer_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create policer %s, rv:%d", + key.c_str(), status); + it++; + continue; + } + + SWSS_LOG_NOTICE("Created policer %s", key.c_str()); + m_syncdPolicers[key] = policer_id; + m_policerRefCounts[key] = 0; + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + if (m_syncdPolicers.find(key) == m_syncdPolicers.end()) + { + SWSS_LOG_ERROR("Policer %s does not exists", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (m_policerRefCounts[key] > 0) + { + SWSS_LOG_INFO("Policer %s is still referenced", key.c_str()); + it++; + continue; + } + + sai_status_t status = sai_policer_api->remove_policer( + m_syncdPolicers.at(key)); + if (status == SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d", + key.c_str(), status); + it++; + continue; + } + + SWSS_LOG_NOTICE("Removed policer %s", key.c_str()); + m_syncdPolicers.erase(key); + m_policerRefCounts.erase(key); + it = consumer.m_toSync.erase(it); + } + } +} diff --git a/orchagent/policerorch.h b/orchagent/policerorch.h new file mode 100644 index 00000000000..d735da03b79 --- /dev/null +++ b/orchagent/policerorch.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "orch.h" +#include "portsorch.h" + +using namespace std; + +typedef map PolicerTable; +typedef map PolicerRefCountTable; + +class PolicerOrch : public Orch +{ +public: + PolicerOrch(DBConnector* db, string tableName); + + bool policerExists(const string &name); + bool getPolicerOid(const string &name, sai_object_id_t &oid); + + bool increaseRefCount(const string &name); + bool decreaseRefCount(const string &name); +private: + virtual void doTask(Consumer& consumer); + + PolicerTable m_syncdPolicers; + PolicerRefCountTable m_policerRefCounts; +}; diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 514cb5ec4da..8859f8ef4bd 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -728,7 +728,7 @@ sai_object_id_t QosOrch::initSystemAclTable() } SWSS_LOG_NOTICE("Create a system ACL table for ECN coloring"); - gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, (sai_acl_stage_t) attr.value.s32, SAI_ACL_BIND_POINT_TYPE_PORT); + gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, SAI_ACL_STAGE_INGRESS, SAI_ACL_BIND_POINT_TYPE_PORT); for (auto& pair: gPortsOrch->getAllPorts()) { @@ -1361,6 +1361,27 @@ task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer) return task_process_status::task_success; } +void QosOrch::doTask() +{ + SWSS_LOG_ENTER(); + + auto *port_qos_map_cfg_exec = getExecutor(CFG_PORT_QOS_MAP_TABLE_NAME); + + for (const auto &it : m_consumerMap) + { + auto *exec = it.second.get(); + + if (exec == port_qos_map_cfg_exec) + { + continue; + } + + exec->drain(); + } + + port_qos_map_cfg_exec->drain(); +} + void QosOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index 0d9d22cd35f..9d8a81b8f0a 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -114,6 +114,7 @@ class QosOrch : public Orch static type_map& getTypeMap(); static type_map m_qos_maps; private: + void doTask() override; virtual void doTask(Consumer& consumer); typedef task_process_status (QosOrch::*qos_table_handler)(Consumer& consumer); diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index bca26e18c14..8913ab64022 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -262,9 +262,11 @@ VNetVrfObject::~VNetVrfObject() */ std::bitset VNetBitmapObject::vnetBitmap_; std::bitset VNetBitmapObject::tunnelOffsets_; +std::bitset VNetBitmapObject::tunnelIdOffsets_; map VNetBitmapObject::vnetIds_; map VNetBitmapObject::bridgeInfoMap_; map, VnetNeighInfo> VNetBitmapObject::neighInfoMap_; +map, uint16_t> VNetBitmapObject::endpointMap_; VNetBitmapObject::VNetBitmapObject(const std::string& vnet, const VNetInfo& vnetInfo, vector& attrs) : VNetObject(vnetInfo) @@ -349,6 +351,29 @@ void VNetBitmapObject::recycleTunnelRouteTableOffset(uint32_t offset) tunnelOffsets_[offset] = false; } +uint16_t VNetBitmapObject::getFreeTunnelId() +{ + SWSS_LOG_ENTER(); + + for (uint16_t i = 1; i < tunnelIdOffsets_.size(); i++) + { + if (tunnelIdOffsets_[i] == false) + { + tunnelIdOffsets_[i] = true; + return i; + } + } + + return 0; +} + +void VNetBitmapObject::recycleTunnelId(uint16_t offset) +{ + SWSS_LOG_ENTER(); + + tunnelIdOffsets_[offset] = false; +} + VnetBridgeInfo VNetBitmapObject::getBridgeInfoByVni(uint32_t vni, string tunnelName) { SWSS_LOG_ENTER(); @@ -828,6 +853,8 @@ bool VNetBitmapObject::addTunnelRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp) uint32_t peerBitmap = vnet_id_; MacAddress mac = endp.mac ? endp.mac : gVxlanMacAddress; TunnelRouteInfo tunnelRouteInfo; + sai_ip_address_t underlayAddr; + copy(underlayAddr, endp.ip); VNetOrch* vnet_orch = gDirectory.get(); for (auto peer : peer_list) @@ -847,8 +874,6 @@ bool VNetBitmapObject::addTunnelRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp) /* FDB entry to the tunnel */ vector fdb_attrs; - sai_ip_address_t underlayAddr; - copy(underlayAddr, endp.ip); neighInfo.fdb_entry.switch_id = gSwitchId; mac.getMac(neighInfo.fdb_entry.mac_address); neighInfo.fdb_entry.bv_id = bInfo.bridge_id; @@ -858,11 +883,11 @@ bool VNetBitmapObject::addTunnelRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp) fdb_attrs.push_back(attr); attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; - attr.value.oid = bInfo.bridge_port_tunnel_id; + attr.value.oid = bInfo.bridge_port_rif_id; fdb_attrs.push_back(attr); - attr.id = SAI_FDB_ENTRY_ATTR_ENDPOINT_IP; - attr.value.ipaddr = underlayAddr; + attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_FORWARD; fdb_attrs.push_back(attr); status = sai_fdb_api->create_fdb_entry( @@ -930,6 +955,52 @@ bool VNetBitmapObject::addTunnelRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp) throw std::runtime_error("VNet route creation failed"); } + /* Tunnel endpoint */ + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); + auto *tunnel = vxlan_orch->getVxlanTunnel(getTunnelName()); + auto endpoint = make_tuple(endp.ip, tunnel->getTunnelId()); + uint16_t tunnelIndex = 0; + if (endpointMap_.find(endpoint) == endpointMap_.end()) + { + tunnelIndex = getFreeTunnelId(); + vector vxlan_attrs; + + sai_object_id_t tunnelL3VxlanEntryId; + attr.id = SAI_TABLE_META_TUNNEL_ENTRY_ATTR_ACTION; + attr.value.s32 = SAI_TABLE_META_TUNNEL_ENTRY_ACTION_TUNNEL_ENCAP; + vxlan_attrs.push_back(attr); + + attr.id = SAI_TABLE_META_TUNNEL_ENTRY_ATTR_METADATA_KEY; + attr.value.u16 = tunnelIndex; + vxlan_attrs.push_back(attr); + + attr.id = SAI_TABLE_META_TUNNEL_ENTRY_ATTR_UNDERLAY_DIP; + attr.value.ipaddr = underlayAddr; + vxlan_attrs.push_back(attr); + + attr.id = SAI_TABLE_META_TUNNEL_ENTRY_ATTR_TUNNEL_ID; + attr.value.oid = tunnel->getTunnelId(); + vxlan_attrs.push_back(attr); + + status = sai_bmtor_api->create_table_meta_tunnel_entry( + &tunnelL3VxlanEntryId, + gSwitchId, + (uint32_t)vxlan_attrs.size(), + vxlan_attrs.data()); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create L3 VXLAN entry, SAI rc: %d", status); + throw std::runtime_error("VNet route creation failed"); + } + + endpointMap_.emplace(endpoint, tunnelIndex); + } + else + { + tunnelIndex = endpointMap_.at(endpoint); + } + /* Tunnel route */ vector tr_attrs; sai_ip_prefix_t pfx; @@ -960,6 +1031,10 @@ bool VNetBitmapObject::addTunnelRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp) attr.value.oid = tunnelRouteInfo.nexthopId; tr_attrs.push_back(attr); + attr.id = SAI_TABLE_BITMAP_ROUTER_ENTRY_ATTR_TUNNEL_INDEX; + attr.value.u16 = tunnelIndex; + tr_attrs.push_back(attr); + status = sai_bmtor_api->create_table_bitmap_router_entry( &tunnelRouteInfo.tunnelRouteTableEntryId, gSwitchId, @@ -1105,6 +1180,11 @@ bool VNetBitmapObject::addRoute(IpPrefix& ipPrefix, nextHop& nh) attr.id = SAI_TABLE_BITMAP_ROUTER_ENTRY_ATTR_NEXT_HOP; attr.value.oid = nh_id; attrs.push_back(attr); + + attr.id = SAI_TABLE_BITMAP_ROUTER_ENTRY_ATTR_TUNNEL_INDEX; + attr.value.u16 = 0; + attrs.push_back(attr); + } else { diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index 6327913f07d..364d499e4ea 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -239,6 +239,10 @@ class VNetBitmapObject: public VNetObject static uint32_t getFreeTunnelRouteTableOffset(); static void recycleTunnelRouteTableOffset(uint32_t offset); + static uint16_t getFreeTunnelId(); + + static void recycleTunnelId(uint16_t offset); + static VnetBridgeInfo getBridgeInfoByVni(uint32_t vni, string tunnelName); static bool clearBridgeInfoByVni(uint32_t vni, string tunnelName); @@ -249,8 +253,10 @@ class VNetBitmapObject: public VNetObject static std::bitset vnetBitmap_; static map vnetIds_; static std::bitset tunnelOffsets_; + static std::bitset tunnelIdOffsets_; static map bridgeInfoMap_; static map, VnetNeighInfo> neighInfoMap_; + static map, uint16_t> endpointMap_; map routeMap_; map tunnelRouteMap_; diff --git a/orchagent/watermarkorch.cpp b/orchagent/watermarkorch.cpp index 5069aaec723..77f8aaa6367 100644 --- a/orchagent/watermarkorch.cpp +++ b/orchagent/watermarkorch.cpp @@ -14,8 +14,8 @@ extern PortsOrch *gPortsOrch; -WatermarkOrch::WatermarkOrch(DBConnector *db, const string tableName): - Orch(db, tableName) +WatermarkOrch::WatermarkOrch(DBConnector *db, const vector &tables): + Orch(db, tables) { SWSS_LOG_ENTER(); @@ -36,9 +36,6 @@ WatermarkOrch::WatermarkOrch(DBConnector *db, const string tableName): m_telemetryTimer = new SelectableTimer(intervT); auto executorT = new ExecutableTimer(m_telemetryTimer, this, "WM_TELEMETRY_TIMER"); Orch::addExecutor(executorT); - m_telemetryTimer->start(); - - m_telemetryInterval = DEFAULT_TELEMETRY_INTERVAL; } WatermarkOrch::~WatermarkOrch() @@ -66,19 +63,13 @@ void WatermarkOrch::doTask(Consumer &consumer) if (op == SET_COMMAND) { - if (key == "TELEMETRY_INTERVAL") + if (consumer.getTableName() == CFG_WATERMARK_TABLE_NAME) { - for (std::pair, std::basic_string > i: fvt) - { - if (i.first == "interval") - { - m_telemetryInterval = to_uint(i.second.c_str()); - } - else - { - SWSS_LOG_WARN("Unsupported key: %s", i.first.c_str()); - } - } + handleWmConfigUpdate(key, fvt); + } + else if (consumer.getTableName() == CFG_FLEX_COUNTER_TABLE_NAME) + { + handleFcConfigUpdate(key, fvt); } } else if (op == DEL_COMMAND) @@ -94,13 +85,74 @@ void WatermarkOrch::doTask(Consumer &consumer) } } +void WatermarkOrch::handleWmConfigUpdate(const std::string &key, const std::vector &fvt) +{ + SWSS_LOG_ENTER(); + if (key == "TELEMETRY_INTERVAL") + { + for (std::pair, std::basic_string > i: fvt) + { + if (i.first == "interval") + { + auto intervT = timespec { .tv_sec = to_uint(i.second.c_str()) , .tv_nsec = 0 }; + m_telemetryTimer->setInterval(intervT); + // reset the timer interval when current timer expires + m_timerChanged = true; + } + else + { + SWSS_LOG_WARN("Unsupported key: %s", i.first.c_str()); + } + } + } +} + +void WatermarkOrch::handleFcConfigUpdate(const std::string &key, const std::vector &fvt) +{ + SWSS_LOG_ENTER(); + uint8_t prevStatus = m_wmStatus; + if (key == "QUEUE_WATERMARK" || key == "PG_WATERMARK") + { + for (std::pair, std::basic_string > i: fvt) + { + if (i.first == "FLEX_COUNTER_STATUS") + { + if (i.second == "enable") + { + m_wmStatus = (uint8_t) (m_wmStatus | groupToMask.at(key)); + } + else if (i.second == "disable") + { + m_wmStatus = (uint8_t) (m_wmStatus & ~(groupToMask.at(key))); + } + } + } + if (!prevStatus && m_wmStatus) + { + m_telemetryTimer->start(); + } + SWSS_LOG_DEBUG("Status of WMs: %u", m_wmStatus); + } +} + void WatermarkOrch::doTask(NotificationConsumer &consumer) { + SWSS_LOG_ENTER(); if (!gPortsOrch->isPortReady()) { return; } + if (m_pg_ids.empty()) + { + init_pg_ids(); + } + + if (m_multicast_queue_ids.empty() and m_unicast_queue_ids.empty()) + { + init_queue_ids(); + } + std::string op; std::string data; std::vector values; @@ -170,16 +222,21 @@ void WatermarkOrch::doTask(SelectableTimer &timer) if (&timer == m_telemetryTimer) { - /* If the interval was changed */ - auto intervT = timespec { .tv_sec = m_telemetryInterval , .tv_nsec = 0 }; - m_telemetryTimer->setInterval(intervT); - m_telemetryTimer->reset(); + if (m_timerChanged) + { + m_telemetryTimer->reset(); + m_timerChanged = false; + } + if (!m_wmStatus) + { + m_telemetryTimer->stop(); + } clearSingleWm(m_periodicWatermarkTable.get(), "SAI_INGRESS_PRIORITY_GROUP_STAT_XOFF_ROOM_WATERMARK_BYTES", m_pg_ids); clearSingleWm(m_periodicWatermarkTable.get(), "SAI_INGRESS_PRIORITY_GROUP_STAT_SHARED_WATERMARK_BYTES", m_pg_ids); clearSingleWm(m_periodicWatermarkTable.get(), "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES", m_unicast_queue_ids); clearSingleWm(m_periodicWatermarkTable.get(), "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES", m_multicast_queue_ids); - SWSS_LOG_INFO("Periodic watermark cleared by timer!"); + SWSS_LOG_DEBUG("Periodic watermark cleared by timer!"); } } diff --git a/orchagent/watermarkorch.h b/orchagent/watermarkorch.h index b2a9847debd..be3bfb43964 100644 --- a/orchagent/watermarkorch.h +++ b/orchagent/watermarkorch.h @@ -1,17 +1,27 @@ #ifndef WATERMARKORCH_H #define WATERMARKORCH_H +#include + #include "orch.h" #include "port.h" #include "notificationconsumer.h" #include "timer.h" +const uint8_t queue_wm_status_mask = 1 << 0; +const uint8_t pg_wm_status_mask = 1 << 1; + +static const map groupToMask = +{ + { "QUEUE_WATERMARK", queue_wm_status_mask }, + { "PG_WATERMARK", pg_wm_status_mask } +}; class WatermarkOrch : public Orch { public: - WatermarkOrch(DBConnector *db, const std::string tableName); + WatermarkOrch(DBConnector *db, const vector &tables); virtual ~WatermarkOrch(void); void doTask(Consumer &consumer); @@ -21,6 +31,9 @@ class WatermarkOrch : public Orch void init_pg_ids(); void init_queue_ids(); + void handleWmConfigUpdate(const std::string &key, const std::vector &fvt); + void handleFcConfigUpdate(const std::string &key, const std::vector &fvt); + void clearSingleWm(Table *table, string wm_name, vector &obj_ids); shared_ptr getCountersTable(void) @@ -34,6 +47,14 @@ class WatermarkOrch : public Orch } private: + /* + [7-2] - unused + [1] - pg wm status + [0] - queue wm status (least significant bit) + */ + uint8_t m_wmStatus = 0; + bool m_timerChanged = false; + shared_ptr m_countersDb = nullptr; shared_ptr m_appDb = nullptr; shared_ptr
m_countersTable = nullptr; @@ -47,8 +68,6 @@ class WatermarkOrch : public Orch vector m_unicast_queue_ids; vector m_multicast_queue_ids; vector m_pg_ids; - - int m_telemetryInterval; }; #endif // WATERMARKORCH_H diff --git a/swssconfig/sample/00-copp.config.json b/swssconfig/sample/00-copp.config.json index c7b0473ffc4..1fade2d172a 100644 --- a/swssconfig/sample/00-copp.config.json +++ b/swssconfig/sample/00-copp.config.json @@ -22,7 +22,7 @@ { "COPP_TABLE:trap.group.arp": { "trap_ids": "arp_req,arp_resp,neigh_discovery", - "trap_action":"trap", + "trap_action":"copy", "trap_priority":"4", "queue": "4", "meter_type":"packets", diff --git a/tests/test_acl.py b/tests/test_acl.py index 7d57645bd87..197026d548f 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -4,13 +4,33 @@ import json class TestAcl(object): - def get_acl_table_id(self, dvs, adb): - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - keys = atbl.getKeys() + 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 create_acl_table(self, table, type, ports): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), + ("type", type), + ("ports", ",".join(ports))]) + 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 get_acl_table_id(self, dvs): + tbl = swsscommon.Table(self.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] + 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] @@ -105,6 +125,7 @@ def verify_acl_port_binding(self, dvs, adb, bind_ports): assert set(port_groups) == set(acl_table_groups) def test_AclTableCreation(self, dvs, testlog): + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -116,7 +137,7 @@ def test_AclTableCreation(self, dvs, testlog): time.sleep(1) # check acl table in asic db - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) assert test_acl_table_id # check acl table group in asic db @@ -138,6 +159,7 @@ def test_AclRuleL4SrcPort(self, dvs, testlog): hmset ACL_RULE|test|acl_test_rule priority 55 PACKET_ACTION FORWARD L4_SRC_PORT 65000 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -148,7 +170,7 @@ def test_AclRuleL4SrcPort(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -189,6 +211,7 @@ def test_AclRuleInOutPorts(self, dvs, testlog): hmset ACL_RULE|test|acl_test_rule priority 55 PACKET_ACTION FORWARD IN_PORTS Ethernet0,Ethernet4 OUT_PORTS Ethernet8,Ethernet12 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -202,7 +225,7 @@ def test_AclRuleInOutPorts(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -246,6 +269,7 @@ def test_AclRuleInOutPorts(self, dvs, testlog): def test_AclTableDeletion(self, dvs, testlog): + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -262,6 +286,7 @@ def test_AclTableDeletion(self, dvs, testlog): def test_V6AclTableCreation(self, dvs, testlog): + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -274,7 +299,7 @@ def test_V6AclTableCreation(self, dvs, testlog): time.sleep(1) # check acl table in asic db - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table group in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") @@ -338,6 +363,7 @@ def test_V6AclRuleIPv6Any(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule1 priority 1000 PACKET_ACTION FORWARD IPv6Any """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -348,7 +374,7 @@ def test_V6AclRuleIPv6Any(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -389,6 +415,7 @@ def test_V6AclRuleIPv6AnyDrop(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule2 priority 1002 PACKET_ACTION DROP IPv6Any """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -399,7 +426,7 @@ def test_V6AclRuleIPv6AnyDrop(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -440,6 +467,7 @@ def test_V6AclRuleIpProtocol(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule3 priority 1003 PACKET_ACTION DROP IP_PROTOCOL 6 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -450,7 +478,7 @@ def test_V6AclRuleIpProtocol(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -491,6 +519,7 @@ def test_V6AclRuleSrcIPv6(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule4 priority 1004 PACKET_ACTION DROP SRC_IPV6 2777::0/64 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -501,7 +530,7 @@ def test_V6AclRuleSrcIPv6(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -542,6 +571,7 @@ def test_V6AclRuleDstIPv6(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule5 priority 1005 PACKET_ACTION DROP DST_IPV6 2002::2/128 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -552,7 +582,7 @@ def test_V6AclRuleDstIPv6(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -593,6 +623,7 @@ def test_V6AclRuleL4SrcPort(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule6 priority 1006 PACKET_ACTION DROP L4_SRC_PORT 65000 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -603,7 +634,7 @@ def test_V6AclRuleL4SrcPort(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -644,6 +675,7 @@ def test_V6AclRuleL4DstPort(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule7 priority 1007 PACKET_ACTION DROP L4_DST_PORT 65001 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -654,7 +686,7 @@ def test_V6AclRuleL4DstPort(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -695,6 +727,7 @@ def test_V6AclRuleTCPFlags(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule8 priority 1008 PACKET_ACTION DROP TCP_FLAGS 0x7/0x3f """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -705,7 +738,7 @@ def test_V6AclRuleTCPFlags(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -746,6 +779,7 @@ def test_V6AclRuleL4SrcPortRange(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule9 priority 1009 PACKET_ACTION DROP L4_SRC_PORT_RANGE 1-100 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -756,7 +790,7 @@ def test_V6AclRuleL4SrcPortRange(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -811,6 +845,7 @@ def test_V6AclRuleL4DstPortRange(self, dvs, testlog): hmset ACL_RULE|test-aclv6|test_rule10 priority 1010 PACKET_ACTION DROP L4_DST_PORT_RANGE 101-200 """ + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -821,7 +856,7 @@ def test_V6AclRuleL4DstPortRange(self, dvs, testlog): time.sleep(1) - test_acl_table_id = self.get_acl_table_id(dvs, adb) + test_acl_table_id = self.get_acl_table_id(dvs) # check acl table in asic db atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") @@ -873,6 +908,7 @@ def test_V6AclRuleL4DstPortRange(self, dvs, testlog): def test_V6AclTableDeletion(self, dvs, testlog): + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -903,6 +939,7 @@ def check_rule_existence(self, entry, rules, verifs): return False def test_InsertAclRuleBetweenPriorities(self, dvs, testlog): + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -1001,6 +1038,7 @@ def test_InsertAclRuleBetweenPriorities(self, dvs, testlog): assert len(keys) >= 1 def test_RulesWithDiffMaskLengths(self, dvs, testlog): + self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -1084,3 +1122,87 @@ def test_RulesWithDiffMaskLengths(self, dvs, testlog): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() assert len(keys) >= 1 + + def create_acl_rule(self, table, rule, field, value): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "666"), + ("PACKET_ACTION", "FORWARD"), + (field, value)]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) + + def remove_acl_rule(self, table, rule): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table + "|" + rule) + time.sleep(1) + + def verify_acl_rule(self, dvs, field, value): + acl_table_id = self.get_acl_table_id(dvs) + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + acl_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entries) == 1 + + (status, fvs) = tbl.get(acl_entries[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "666" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_FORWARD" + elif fv[0] == field: + assert fv[1] == value + else: + assert False + + + def test_AclRuleIcmp(self, dvs, testlog): + self.setup_db(dvs) + + acl_table = "TEST_TABLE" + acl_rule = "TEST_RULE" + + self.create_acl_table(acl_table, "L3", ["Ethernet0", "Ethernet4"]) + + self.create_acl_rule(acl_table, acl_rule, "ICMP_TYPE", "8") + + self.verify_acl_rule(dvs, "SAI_ACL_ENTRY_ATTR_FIELD_ICMP_TYPE", "8&mask:0xff") + + self.remove_acl_rule(acl_table, acl_rule) + + self.create_acl_rule(acl_table, acl_rule, "ICMP_CODE", "9") + + self.verify_acl_rule(dvs, "SAI_ACL_ENTRY_ATTR_FIELD_ICMP_CODE", "9&mask:0xff") + + self.remove_acl_rule(acl_table, acl_rule) + + self.remove_acl_table(acl_table) + + def test_AclRuleIcmpV6(self, dvs, testlog): + self.setup_db(dvs) + + acl_table = "TEST_TABLE" + acl_rule = "TEST_RULE" + + self.create_acl_table(acl_table, "L3V6", ["Ethernet0", "Ethernet4"]) + + self.create_acl_rule(acl_table, acl_rule, "ICMPV6_TYPE", "8") + + self.verify_acl_rule(dvs, "SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_TYPE", "8&mask:0xff") + + self.remove_acl_rule(acl_table, acl_rule) + + self.create_acl_rule(acl_table, acl_rule, "ICMPV6_CODE", "9") + + self.verify_acl_rule(dvs, "SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_CODE", "9&mask:0xff") + + self.remove_acl_rule(acl_table, acl_rule) + + self.remove_acl_table(acl_table) diff --git a/tests/test_crm.py b/tests/test_crm.py index eeba6f5a4b0..ff73e744f6d 100644 --- a/tests/test_crm.py +++ b/tests/test_crm.py @@ -15,6 +15,8 @@ def getCrmCounterValue(dvs, key, counter): if k[0] == counter: return int(k[1]) + return 0 + def setReadOnlyAttr(dvs, obj, attr, val): @@ -554,6 +556,8 @@ def test_CrmAcl(dvs, testlog): bind_ports = ["Ethernet0", "Ethernet4"] + old_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + # create ACL table ttbl = swsscommon.Table(db, "ACL_TABLE") fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) @@ -565,8 +569,9 @@ def test_CrmAcl(dvs, testlog): rtbl.set("test|acl_test_rule", fvs) time.sleep(2) - - table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + + new_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + table_used_counter = new_table_used_counter - old_table_used_counter assert table_used_counter == 1 # get ACL table key @@ -596,6 +601,7 @@ def test_CrmAcl(dvs, testlog): time.sleep(2) - table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + new_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + table_used_counter = new_table_used_counter - old_table_used_counter assert table_used_counter == 0 diff --git a/tests/test_mirror.py b/tests/test_mirror.py index d1a29840406..4836465f482 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -15,7 +15,7 @@ def setup_db(self, dvs): 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): + def set_interface_status(self, dvs, interface, admin_status): if interface.startswith("PortChannel"): tbl_name = "PORTCHANNEL" elif interface.startswith("Vlan"): @@ -27,6 +27,12 @@ def set_interface_status(self, interface, admin_status): tbl.set(interface, fvs) time.sleep(1) + # when using FRR, route cannot be inserted if the neighbor is not + # connected. thus it is mandatory to force the interface up manually + if interface.startswith("PortChannel"): + dvs.runcmd("bash -c 'echo " + ("1" if admin_status == "up" else "0") +\ + " > /sys/class/net/" + interface + "/carrier'") + def add_ip_address(self, interface, ip): if interface.startswith("PortChannel"): tbl_name = "PORTCHANNEL_INTERFACE" @@ -118,7 +124,7 @@ def test_MirrorAddRemove(self, dvs, testlog): assert self.get_mirror_session_state(session)["status"] == "inactive" # bring up Ethernet16 - self.set_interface_status("Ethernet16", "up") + self.set_interface_status(dvs, "Ethernet16", "up") assert self.get_mirror_session_state(session)["status"] == "inactive" # add IP address to Ethernet16 @@ -183,7 +189,7 @@ def test_MirrorAddRemove(self, dvs, testlog): assert self.get_mirror_session_state(session)["status"] == "inactive" # bring down Ethernet16 - self.set_interface_status("Ethernet16", "down") + self.set_interface_status(dvs, "Ethernet16", "down") assert self.get_mirror_session_state(session)["status"] == "inactive" # remove mirror session @@ -253,8 +259,8 @@ def test_MirrorToVlanAddRemove(self, dvs, testlog): self.create_vlan_member("6", "Ethernet4") # bring up vlan and member - self.set_interface_status("Vlan6", "up") - self.set_interface_status("Ethernet4", "up") + self.set_interface_status(dvs, "Vlan6", "up") + self.set_interface_status(dvs, "Ethernet4", "up") # add ip address to vlan 6 self.add_ip_address("Vlan6", "6.6.6.0/24") @@ -325,8 +331,8 @@ def test_MirrorToVlanAddRemove(self, dvs, testlog): assert self.get_mirror_session_state(session)["status"] == "inactive" # bring down vlan and member - self.set_interface_status("Ethernet4", "down") - self.set_interface_status("Vlan6", "down") + self.set_interface_status(dvs, "Ethernet4", "down") + self.set_interface_status(dvs, "Vlan6", "down") # remove vlan member; remove vlan self.remove_vlan_member("6", "Ethernet4") @@ -390,8 +396,8 @@ def test_MirrorToLagAddRemove(self, dvs, testlog): 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") + self.set_interface_status(dvs, "PortChannel008", "up") + self.set_interface_status(dvs, "Ethernet88", "up") # add ip address to port channel 008 self.add_ip_address("PortChannel008", "11.11.11.0/24") @@ -422,8 +428,8 @@ def test_MirrorToLagAddRemove(self, dvs, testlog): assert self.get_mirror_session_state(session)["status"] == "inactive" # bring down port channel and port channel member - self.set_interface_status("PortChannel008", "down") - self.set_interface_status("Ethernet88", "down") + self.set_interface_status(dvs, "PortChannel008", "down") + self.set_interface_status(dvs, "Ethernet88", "down") # remove port channel member; remove port channel self.remove_port_channel_member("008", "Ethernet88") @@ -458,7 +464,7 @@ def test_MirrorDestMoveVlan(self, dvs, testlog): assert self.get_mirror_session_state(session)["status"] == "inactive" # bring up port; add ip; add neighbor; add route - self.set_interface_status("Ethernet32", "up") + self.set_interface_status(dvs, "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") @@ -479,8 +485,8 @@ def test_MirrorDestMoveVlan(self, dvs, testlog): # 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") + self.set_interface_status(dvs, "Vlan9", "up") + self.set_interface_status(dvs, "Ethernet48", "up") assert self.get_mirror_session_state(session)["status"] == "active" # add ip address to vlan 9 @@ -539,8 +545,8 @@ def test_MirrorDestMoveVlan(self, dvs, testlog): assert fv[1] == "false" # bring down vlan and member; remove vlan member; remove vlan - self.set_interface_status("Ethernet48", "down") - self.set_interface_status("Vlan9", "down") + self.set_interface_status(dvs, "Ethernet48", "down") + self.set_interface_status(dvs, "Vlan9", "down") self.remove_vlan_member("9", "Ethernet48") self.remove_vlan("9") @@ -548,7 +554,7 @@ def test_MirrorDestMoveVlan(self, dvs, testlog): 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") + self.set_interface_status(dvs, "Ethernet32", "down") # remove mirror session self.remove_mirror_session(session) @@ -576,7 +582,7 @@ def test_MirrorDestMoveLag(self, dvs, testlog): assert self.get_mirror_session_state(session)["status"] == "inactive" # bring up port; add ip; add neighbor; add route - self.set_interface_status("Ethernet64", "up") + self.set_interface_status(dvs, "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") @@ -597,8 +603,8 @@ def test_MirrorDestMoveLag(self, dvs, testlog): # 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") + self.set_interface_status(dvs, "PortChannel080", "up") + self.set_interface_status(dvs, "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") @@ -663,8 +669,8 @@ def test_MirrorDestMoveLag(self, dvs, testlog): 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.set_interface_status(dvs, "Ethernet32", "down") + self.set_interface_status(dvs, "PortChannel080", "down") self.remove_port_channel_member("080", "Ethernet32") self.remove_port_channel(dvs, "080") assert self.get_mirror_session_state(session)["status"] == "active" @@ -673,7 +679,7 @@ def test_MirrorDestMoveLag(self, dvs, testlog): 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") + self.set_interface_status(dvs, "Ethernet64", "down") assert self.get_mirror_session_state(session)["status"] == "inactive" # remove mirror session @@ -719,7 +725,7 @@ def test_AclBindMirror(self, dvs, testlog): acl_rule = "MIRROR_RULE" # bring up port; assign ip; create neighbor; create route - self.set_interface_status("Ethernet32", "up") + self.set_interface_status(dvs, "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") @@ -788,4 +794,4 @@ def test_AclBindMirror(self, dvs, testlog): 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") + self.set_interface_status(dvs, "Ethernet32", "down") diff --git a/tests/test_mirror_ipv6_combined.py b/tests/test_mirror_ipv6_combined.py new file mode 100644 index 00000000000..f97315836a1 --- /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 00000000000..839e79c018a --- /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") + + diff --git a/tests/test_mirror_policer.py b/tests/test_mirror_policer.py new file mode 100644 index 00000000000..3226fbb1661 --- /dev/null +++ b/tests/test_mirror_policer.py @@ -0,0 +1,239 @@ +# 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 + + +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_port_status(self, port, admin_status): + tbl = swsscommon.Table(self.cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("admin_status", "up")]) + tbl.set(port, fvs) + time.sleep(1) + + def add_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set(interface + "|" + ip, fvs) + time.sleep(1) + + def remove_ip_address(self, interface, ip): + tbl = swsscommon.Table(self.cdb, "INTERFACE") + 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, policer): + 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), + ("policer", policer)]) + 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 create_policer(self, name): + tbl = swsscommon.Table(self.cdb, "POLICER") + fvs = swsscommon.FieldValuePairs([("meter_type", "packets"), + ("mode", "sr_tcm"), + ("cir", "600"), + ("cbs", "600"), + ("red_packet_action", "drop")]) + tbl.set(name, fvs) + time.sleep(1) + + def remove_policer(self, name): + tbl = swsscommon.Table(self.cdb, "POLICER") + tbl._del(name) + time.sleep(1) + + def test_MirrorPolicer(self, dvs, testlog): + self.setup_db(dvs) + + session = "MIRROR_SESSION" + policer= "POLICER" + + # create policer + self.create_policer(policer) + + # create mirror session + self.create_mirror_session(session, "3.3.3.3", "2.2.2.2", "0x6558", "8", "100", "0", policer) + self.set_port_status("Ethernet16", "up") + self.add_ip_address("Ethernet16", "10.0.0.0/31") + self.add_neighbor("Ethernet16", "10.0.0.1", "02:04:06:08:10:12") + self.add_route(dvs, "2.2.2.2", "10.0.0.1") + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + policer_entries = tbl.getKeys() + assert len(policer_entries) == 1 + policer_oid = policer_entries[0] + + # 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) == 12 + 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] == "3.3.3.3" + 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 + elif fv[0] == "SAI_MIRROR_SESSION_ATTR_POLICER": + assert fv[1] == policer_oid + else: + assert False + + # remove mirror session + self.remove_route(dvs, "2.2.2.2") + self.remove_neighbor("Ethernet16", "10.0.0.1") + self.remove_ip_address("Ethernet16", "10.0.0.0/31") + self.set_port_status("Ethernet16", "down") + self.remove_mirror_session(session) + + # remove policer + self.remove_policer(policer) + + + 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) + + 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) + + def remove_mirror_acl_dscp_rule(self, table, rule): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table + "|" + rule) + time.sleep(1) + + def test_MirrorPolicerWithAcl(self, dvs, testlog): + self.setup_db(dvs) + + session = "MIRROR_SESSION" + policer= "POLICER" + acl_table = "MIRROR_TABLE" + acl_rule = "MIRROR_RULE" + + # create policer + self.create_policer(policer) + + # create mirror session + self.create_mirror_session(session, "3.3.3.3", "2.2.2.2", "0x6558", "8", "100", "0", policer) + self.set_port_status("Ethernet16", "up") + self.add_ip_address("Ethernet16", "10.0.0.0/31") + self.add_neighbor("Ethernet16", "10.0.0.1", "02:04:06:08:10:12") + self.add_route(dvs, "2.2.2.2", "10.0.0.1") + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + mirror_entries = tbl.getKeys() + assert len(mirror_entries) == 1 + mirror_oid = mirror_entries[0] + + # create acl table + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"]) + + # create acl rule with dscp value and mask + self.create_mirror_acl_dscp_rule(acl_table, acl_rule, "8/56", 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] == "8&mask:0x38" + if fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": + assert fv[1] == "1:" + mirror_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_route(dvs, "2.2.2.2") + self.remove_neighbor("Ethernet16", "10.0.0.1") + self.remove_ip_address("Ethernet16", "10.0.0.0/31") + self.set_port_status("Ethernet16", "down") + self.remove_mirror_session(session) + + # remove policer + self.remove_policer(policer) diff --git a/tests/test_vlan.py b/tests/test_vlan.py index 49e9b154924..907f26267f5 100644 --- a/tests/test_vlan.py +++ b/tests/test_vlan.py @@ -360,3 +360,32 @@ def test_AddVlanWithIncorrectValueType(self, dvs, testlog, test_input, expected) else: #remove vlan self.remove_vlan(vlan) + + @pytest.mark.skip(reason="AddMaxVlan take too long to execute") + def test_AddMaxVlan(self, dvs, testlog): + self.setup_db(dvs) + + min_vid = 2 + max_vid = 4094 + + # create max vlan + vlan = min_vid + while vlan <= max_vid: + self.create_vlan(str(vlan)) + vlan += 1 + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == (4094-1) + + # remove all vlan + vlan = min_vid + while vlan <= max_vid: + self.remove_vlan(str(vlan)) + vlan += 1 + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 0 diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 1c9aac342c3..5ca4f167b1e 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -799,8 +799,6 @@ def get_vnet_obj(self): ''' Test 1 - Create Vlan Interface, Tunnel and Vnet ''' - # TODO: Please fix this test case. - @pytest.mark.skip(reason="Starting Vxlanmgr to be merged") def test_vnet_orch_1(self, dvs, testlog): vnet_obj = self.get_vnet_obj() @@ -850,8 +848,6 @@ def test_vnet_orch_1(self, dvs, testlog): ''' Test 2 - Two VNets, One HSMs per VNet ''' - # TODO: Please fix this test case. - @pytest.mark.skip(reason="Starting Vxlanmgr to be merged") def test_vnet_orch_2(self, dvs, testlog): vnet_obj = self.get_vnet_obj() @@ -905,8 +901,6 @@ def test_vnet_orch_2(self, dvs, testlog): ''' Test 3 - Two VNets, One HSMs per VNet, Peering ''' - # TODO: Please fix this test case. - @pytest.mark.skip(reason="Starting Vxlanmgr to be merged") def test_vnet_orch_3(self, dvs, testlog): vnet_obj = self.get_vnet_obj() diff --git a/tests/test_watermark.py b/tests/test_watermark.py index 80602929c9a..79b8fa98810 100644 --- a/tests/test_watermark.py +++ b/tests/test_watermark.py @@ -188,8 +188,9 @@ def test_clear(self, dvs): # clear pg shared watermark, and verify that headroom watermark and persistent watermarks are not affected - dvs.runcmd("sonic-clear priority-group watermark shared") - + exitcode, output = dvs.runcmd("sonic-clear priority-group watermark shared") + time.sleep(1) + assert exitcode == 0, "CLI failure: %s" % output # make sure it cleared self.verify_value(dvs, self.pgs, WmTables.user, SaiWmStats.pg_shared, "0") @@ -201,7 +202,9 @@ def test_clear(self, dvs): # clear queue unicast persistent watermark, and verify that multicast watermark and user watermarks are not affected - dvs.runcmd("sonic-clear queue persistent-watermark unicast") + exitcode, output = dvs.runcmd("sonic-clear queue persistent-watermark unicast") + time.sleep(1) + assert exitcode == 0, "CLI failure: %s" % output # make sure it cleared self.verify_value(dvs, self.uc_q, WmTables.persistent, SaiWmStats.queue_shared, "0")