diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index ef9391ae42..8a4ebbecc7 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -34,6 +34,7 @@ extern string gMySwitchType; #define MIN_VLAN_ID 1 // 0 is a reserved VLAN ID #define MAX_VLAN_ID 4095 // 4096 is a reserved VLAN ID + #define STATE_DB_ACL_ACTION_FIELD_IS_ACTION_LIST_MANDATORY "is_action_list_mandatory" #define STATE_DB_ACL_ACTION_FIELD_ACTION_LIST "action_list" #define STATE_DB_ACL_L3V4V6_SUPPORTED "supported_L3V4V6" @@ -42,6 +43,10 @@ extern string gMySwitchType; #define ACL_COUNTER_DEFAULT_POLLING_INTERVAL_MS 10000 // ms #define ACL_COUNTER_DEFAULT_ENABLED_STATE false + +#define EGR_SET_DSCP_TABLE_ID "EgressSetDSCP" +#define MAX_META_DATA_VALUE 4095 + const int TCP_PROTOCOL_NUM = 6; // TCP protocol number acl_rule_attr_lookup_t aclMatchLookup = @@ -76,7 +81,8 @@ acl_rule_attr_lookup_t aclMatchLookup = { MATCH_INNER_L4_DST_PORT, SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_DST_PORT }, { MATCH_BTH_OPCODE, SAI_ACL_ENTRY_ATTR_FIELD_BTH_OPCODE}, { MATCH_AETH_SYNDROME, SAI_ACL_ENTRY_ATTR_FIELD_AETH_SYNDROME}, - { MATCH_TUNNEL_TERM, SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_TERMINATED} + { MATCH_TUNNEL_TERM, SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_TERMINATED}, + { MATCH_METADATA, SAI_ACL_ENTRY_ATTR_FIELD_ACL_USER_META} }; static acl_range_type_lookup_t aclRangeTypeLookup = @@ -126,6 +132,12 @@ static acl_packet_action_lookup_t aclPacketActionLookup = { PACKET_ACTION_COPY, SAI_PACKET_ACTION_COPY }, }; +static acl_rule_attr_lookup_t aclMetadataDscpActionLookup = +{ + { ACTION_META_DATA, SAI_ACL_ENTRY_ATTR_ACTION_SET_ACL_META_DATA}, + { ACTION_DSCP, SAI_ACL_ENTRY_ATTR_ACTION_SET_DSCP} +}; + static acl_dtel_flow_op_type_lookup_t aclDTelFlowOpTypeLookup = { { DTEL_FLOW_OP_NOP, SAI_ACL_DTEL_FLOW_OP_NOP }, @@ -353,6 +365,42 @@ static acl_table_action_list_lookup_t defaultAclActionList = } } } + }, + { + // MARK_META + TABLE_TYPE_MARK_META, + { + { + ACL_STAGE_INGRESS, + { + SAI_ACL_ACTION_TYPE_SET_ACL_META_DATA + } + } + } + }, + { + // MARK_METAV6 + TABLE_TYPE_MARK_META_V6, + { + { + ACL_STAGE_INGRESS, + { + SAI_ACL_ACTION_TYPE_SET_ACL_META_DATA + } + } + } + }, + { + // EGR_SET_DSCP + TABLE_TYPE_EGR_SET_DSCP, + { + { + ACL_STAGE_EGRESS, + { + SAI_ACL_ACTION_TYPE_SET_DSCP + } + } + } } }; @@ -418,6 +466,18 @@ static acl_table_match_field_lookup_t stageMandatoryMatchFields = } } } + }, + { + // EGR_SET_DSCP + TABLE_TYPE_EGR_SET_DSCP, + { + { + ACL_STAGE_EGRESS, + { + SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META + } + } + } } }; @@ -710,7 +770,7 @@ bool AclTableTypeParser::parseAclTableTypeActions(const std::string& value, AclT auto mirrorAction = aclMirrorStageLookup.find(action); auto dtelAction = aclDTelActionLookup.find(action); auto otherAction = aclOtherActionLookup.find(action); - + auto metadataAction = aclMetadataDscpActionLookup.find(action); if (l3Action != aclL3ActionLookup.end()) { saiActionAttr = l3Action->second; @@ -727,6 +787,10 @@ bool AclTableTypeParser::parseAclTableTypeActions(const std::string& value, AclT { saiActionAttr = otherAction->second; } + else if (metadataAction != aclMetadataDscpActionLookup.end()) + { + saiActionAttr = metadataAction->second; + } else { SWSS_LOG_ERROR("Unknown action %s", action.c_str()); @@ -1057,6 +1121,18 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) return false; } } + else if (attr_name == MATCH_METADATA) + { + matchData.data.u32 = to_uint(attr_value); + matchData.mask.u32 = 0xFFFFFFFF; + + if (matchData.data.u32 < m_pAclOrch->getAclMetaDataMin() || matchData.data.u32 > m_pAclOrch->getAclMetaDataMax()) + { + SWSS_LOG_ERROR("Invalid MATCH_METADATA configuration: %s, expected value between %d - %d", attr_value.c_str(), + m_pAclOrch->getAclMetaDataMin(), m_pAclOrch->getAclMetaDataMax()); + return false; + } + } } catch (exception &e) { @@ -1624,7 +1700,7 @@ bool AclRule::getCreateCounter() const return m_createCounter; } -shared_ptr AclRule::makeShared(AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple& data) +shared_ptr AclRule::makeShared(AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple& data, MetaDataMgr * m_metadataMgr) { shared_ptr aclRule; @@ -1640,6 +1716,10 @@ shared_ptr AclRule::makeShared(AclOrch *acl, MirrorOrch *mirror, DTelOr { return make_shared(acl, rule, table); } + else if (acl->isUsingEgrSetDscp(table) || table == EGR_SET_DSCP_TABLE_ID) + { + return make_shared(acl, rule, table, m_metadataMgr); + } else if (aclDTelActionLookup.find(action) != aclDTelActionLookup.cend()) { if (!dtel) @@ -2188,6 +2268,105 @@ void AclRuleMirror::onUpdate(SubjectType type, void *cntx) } } +AclRuleUnderlaySetDscp::AclRuleUnderlaySetDscp(AclOrch *aclOrch, string rule, string table, MetaDataMgr* m_metaDataMgr, bool createCounter): + AclRule(aclOrch, rule, table, createCounter), + table_id(table), + m_metaDataMgr(m_metaDataMgr) +{ +} + +uint32_t AclRuleUnderlaySetDscp::getDscpValue() const +{ + return cachedDscpValue; +} + +uint32_t AclRuleUnderlaySetDscp::getMetadata() const +{ + return cachedMetadata; +} + +bool AclRuleUnderlaySetDscp::validateAddAction(string attr_name, string _attr_value) +{ + SWSS_LOG_ENTER(); + + string attr_value = to_upper(_attr_value); + + sai_object_id_t table_oid = m_pAclOrch->getTableById(table_id); + auto aclTable = m_pAclOrch->getTableByOid(table_oid); + string type = aclTable->type.getName(); + string key = table_id + ":" + m_id; + // we handle the allocation of metadata for here. based on SET_DSCP action, we check if a metadata is already allocated then we reuse it + // otherwise we allocate a new metadata. This metadata is then set an the action for the Rule of this table. We also cache the SET_DSCP + // value and the allocated metadata in a the rule structure itself so that when we go to addRule we can use these to add the + // egr_set_dscp rule + if (attr_name == ACTION_DSCP && (type == TABLE_TYPE_MARK_META || type == TABLE_TYPE_MARK_META_V6)) + { + if (!m_pAclOrch->isUsingEgrSetDscp(table_id)) + { + + SWSS_LOG_ERROR("Unexpected Error. Table %s not asssociated with EGR_SET_DSCP table", table_id.c_str()); + return false; + } + if (m_pAclOrch->hasMetaDataRefCount(key) > 0) + { + SWSS_LOG_ERROR("Metadata already allocated for Rule %s in table %s. Remove and Re-add the rule.", m_id.c_str(), table_id.c_str()); + return false; + } + u_int8_t actionDscpValue = uint8_t(std::stoi(attr_value)); + cachedDscpValue = actionDscpValue; + auto metadata = m_metaDataMgr->getFreeMetaData(actionDscpValue); + + if (!m_metaDataMgr->isValidMetaData(metadata)) + { + SWSS_LOG_ERROR("Failed to get free metadata for DSCP value %d", actionDscpValue); + return false; + } + SWSS_LOG_ERROR("Allocated metadata %d for DSCP value %d", metadata, actionDscpValue); + cachedMetadata = metadata; + attr_name = ACTION_META_DATA; + attr_value = std::to_string(metadata); + m_pAclOrch->addMetaDataRef(key, metadata); + } + + + sai_acl_action_data_t actionData; + actionData.parameter.u32 = 0; + + SWSS_LOG_INFO("attr_name: %s, attr_value: %s int val %d", attr_name.c_str(), attr_value.c_str(), to_uint(attr_value)); + // we only handle DSCP and META_DATA actions for now. + if (attr_name == ACTION_DSCP || attr_name == ACTION_META_DATA) + { + actionData.parameter.u32 = to_uint(attr_value); + if (attr_name == ACTION_META_DATA && (actionData.parameter.u32 < m_pAclOrch->getAclMetaDataMin() || actionData.parameter.u32 > m_pAclOrch->getAclMetaDataMax())) + { + return false; + } + } + else + { + return false; + } + + actionData.enable = true; + return setAction(aclMetadataDscpActionLookup[attr_name], actionData); +} + +bool AclRuleUnderlaySetDscp::validate() +{ + SWSS_LOG_ENTER(); + if ( m_actions.size() != 1) + { + return false; + } + + return true; +} + +void AclRuleUnderlaySetDscp::onUpdate(SubjectType, void *) +{ + // Do nothing +} + AclTable::AclTable(AclOrch *pAclOrch, string id) noexcept : m_pAclOrch(pAclOrch), id(id) { @@ -3195,6 +3374,108 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr } + if (platform == VS_PLATFORM_SUBSTRING) + { + // For testing on VS the following values will be used. + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_RANGE_CAPABLE] = "true"; + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_MIN] = "1"; + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_MAX] = "7"; + m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ATTR_META_CAPABLE] = "true"; + m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ACTION_META_CAPABLE] = "true"; + m_metaDataMgr.populateRange(1,7); + } + else + { + // check switch capability of Metadata attribute, action and range. + // SAI_SWITCH_ATTR_ACL_USER_META_DATA_RANGE support and range values. + // SAI_ACL_ENTRY_ATTR_ACTION_SET_ACL_META_DATA + // SAI_ACL_ENTRY_ATTR_FIELD_ACL_USER_META + + sai_status_t status = SAI_STATUS_SUCCESS; + sai_attr_capability_t capability; + uint16_t metadataMin = 0; + uint16_t metadataMax = 0; + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_MIN] = "0"; + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_MAX] = "0"; + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_RANGE_CAPABLE] = "false"; + m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ATTR_META_CAPABLE] = "false"; + m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ACTION_META_CAPABLE] = "false"; + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, SAI_SWITCH_ATTR_ACL_USER_META_DATA_RANGE, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query ACL_USER_META_DATA_RANGE %d", status); + } + else + { + if (capability.get_implemented) + { + sai_attribute_t attrs[1]; + attrs[0].id = SAI_SWITCH_ATTR_ACL_USER_META_DATA_RANGE; + sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, attrs); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not get range for ACL_USER_META_DATA_RANGE %d", status); + } + else + { + SWSS_LOG_NOTICE("ACL_USER_META_DATA_RANGE, min: %u, max: %u", attrs[0].value.u32range.min, attrs[0].value.u32range.max); + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_RANGE_CAPABLE] = "true"; + if (attrs[0].value.u32range.min > MAX_META_DATA_VALUE) + { + SWSS_LOG_ERROR("Unsupported ACL_USER_META_DATA_RANGE min value"); + metadataMin = 0; + } + else + { + metadataMin = uint16_t(attrs[0].value.u32range.min); + } + if (attrs[0].value.u32range.max > MAX_META_DATA_VALUE) + { + metadataMax = MAX_META_DATA_VALUE; + } + else + { + metadataMax = uint16_t(attrs[0].value.u32range.max); + } + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_MIN] = std::to_string(metadataMin); + m_switchMetaDataCapabilities[TABLE_ACL_USER_META_DATA_MAX] = std::to_string(metadataMax); + } + + } + SWSS_LOG_NOTICE("ACL_USER_META_DATA_RANGE capability %d", capability.get_implemented); + } + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_ACL_ENTRY, SAI_ACL_ENTRY_ATTR_FIELD_ACL_USER_META, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query ACL_ENTRY_ATTR_FIELD_ACL_USER_META %d", status); + } + else + { + if (capability.set_implemented) + { + m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ATTR_META_CAPABLE] = "true"; + } + SWSS_LOG_NOTICE("ACL_ENTRY_ATTR_FIELD_ACL_USER_META capability %d", capability.set_implemented); + } + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_ACL_ENTRY, SAI_ACL_ENTRY_ATTR_ACTION_SET_ACL_META_DATA, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Could not query ACL_ENTRY_ATTR_ACTION_SET_ACL_META_DATA %d", status); + } + else + { + if (capability.set_implemented) + { + m_switchMetaDataCapabilities[TABLE_ACL_ENTRY_ACTION_META_CAPABLE] = "true"; + } + SWSS_LOG_NOTICE("ACL_ENTRY_ATTR_ACTION_SET_ACL_META_DATA capability %d", capability.set_implemented); + } + + m_metaDataMgr.populateRange(metadataMin, metadataMax); + + } // Store the capabilities in state database // TODO: Move this part of the code into syncd vector fvVector; @@ -3463,7 +3744,44 @@ void AclOrch::initDefaultTableTypes(const string& platform, const string& sub_pl .build() ); } + if (isAclMetaDataSupported()) + { + addAclTableType( + builder.withName(TABLE_TYPE_MARK_META) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_PORT) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_LAG) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_SRC_IP)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_DST_IP)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_DSCP)) + .build() + ); + + addAclTableType( + builder.withName(TABLE_TYPE_MARK_META_V6) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_PORT) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_LAG) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_SRC_IPV6)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_IPV6_NEXT_HEADER)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_DSCP)) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS)) + .build() + ); + addAclTableType( + builder.withName(TABLE_TYPE_EGR_SET_DSCP) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_PORT) + .withBindPointType(SAI_ACL_BIND_POINT_TYPE_LAG) + .withMatch(make_shared(SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META)) + .build() + ); + } // Placeholder for control plane tables addAclTableType(builder.withName(TABLE_TYPE_CTRLPLANE).build()); } @@ -3561,8 +3879,12 @@ void AclOrch::putAclActionCapabilityInDB(acl_stage_type_t stage) string delimiter; ostringstream acl_action_value_stream; ostringstream is_action_list_mandatory_stream; - - for (const auto& action_map: {aclL3ActionLookup, aclMirrorStageLookup, aclDTelActionLookup}) + acl_rule_attr_lookup_t metadataActionLookup = {}; + if (isAclMetaDataSupported()) + { + metadataActionLookup = aclMetadataDscpActionLookup; + } + for (const auto& action_map: {aclL3ActionLookup, aclMirrorStageLookup, aclDTelActionLookup, metadataActionLookup}) { for (const auto& it: action_map) { @@ -3802,6 +4124,27 @@ void AclOrch::getAddDeletePorts(AclTable &newT, newPortSet.insert(p); } + // if the table type is TABLE_TYPE_EGR_SET_DSCP we use a single instance of this + // table with all the tables of type TABLE_TYPE_MARK_META/v6 therefoere we need to + // to collect all the ports from the tables of type TABLE_TYPE_MARK_META/v6 and + // put them in the newPortSet. + if (curT.id == EGR_SET_DSCP_TABLE_ID) + { + for(auto iter : m_egrSetDscpRef) + { + auto tableOid = getTableById(iter); + auto existingtable = m_AclTables.at(tableOid); + for (auto p : existingtable.pendingPortSet) + { + newPortSet.insert(p); + } + for (auto p : existingtable.portSet) + { + newPortSet.insert(p); + } + } + } + // Collect current ports for (auto p : curT.pendingPortSet) { @@ -3897,7 +4240,6 @@ bool AclOrch::updateAclTablePorts(AclTable &newTable, AclTable &curTable) bool AclOrch::updateAclTable(AclTable ¤tTable, AclTable &newTable) { SWSS_LOG_ENTER(); - currentTable.description = newTable.description; if (!updateAclTablePorts(newTable, currentTable)) { @@ -3908,10 +4250,193 @@ bool AclOrch::updateAclTable(AclTable ¤tTable, AclTable &newTable) return true; } -bool AclOrch::updateAclTable(string table_id, AclTable &table) +EgressSetDscpTableStatus AclOrch::addEgrSetDscpTable(string table_id, AclTable &table, string orignalTableTypeName) { SWSS_LOG_ENTER(); + EgressSetDscpTableStatus status = EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_NOT_REQUIRED; + AclTable egrSetDscpTable(this); + // we only add the EGR_SET_DSCP table if the table type is TABLE_TYPE_UNDERLAY_SET_DSCP or TABLE_TYPE_UNDERLAY_SET_DSCPV6 + // otherwise we return EGRESS_SET_DSCP_TABLE_NOT_REQUIRED. + if (orignalTableTypeName == TABLE_TYPE_UNDERLAY_SET_DSCP || orignalTableTypeName == TABLE_TYPE_UNDERLAY_SET_DSCPV6) + { + if (!isAclMetaDataSupported()) + { + SWSS_LOG_ERROR("Platform does not support MARK_META/MARK_METAV6 tables."); + return EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_NOT_SUPPORTED; + } + AclTable egrSetDscpTable(this); + + // copy ports from the TABLE_TYPE_UNDERLAY_SET_DSCP/v6 to the egrSetDscpTable. + std::set ports; + ports.insert(table.portSet.begin(), table.portSet.end()); + ports.insert(table.pendingPortSet.begin(), table.pendingPortSet.end()); + + for (auto alias : ports) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_INFO("Add unready port %s to pending list for ACL table %s", + alias.c_str(), EGR_SET_DSCP_TABLE_ID); + egrSetDscpTable.pendingPortSet.emplace(alias); + continue; + } + + sai_object_id_t bind_port_id; + if (!getAclBindPortId(port, bind_port_id)) + { + SWSS_LOG_ERROR("Failed to get port %s bind port ID for ACL table %s", + alias.c_str(), EGR_SET_DSCP_TABLE_ID); + return EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_FAILED; + } + egrSetDscpTable.link(bind_port_id); + egrSetDscpTable.portSet.emplace(alias); + } + + egrSetDscpTable.id = EGR_SET_DSCP_TABLE_ID; + egrSetDscpTable.stage = ACL_STAGE_EGRESS; + auto egrSetDscpTableType = getAclTableType(TABLE_TYPE_EGR_SET_DSCP); + sai_object_id_t egrSetDscp_oid = getTableById(EGR_SET_DSCP_TABLE_ID); + // create the EGR_SET_DSCP fisrt time if not present. Otherwise update the existing table. + if (m_egrSetDscpRef.empty()) + { + // Create EGR_SET_DSCP table + egrSetDscpTable.validateAddType(*egrSetDscpTableType); + egrSetDscpTable.addMandatoryActions(); + if (!egrSetDscpTable.validate()) + { + SWSS_LOG_ERROR("Failed to validate ACL table %s", + EGR_SET_DSCP_TABLE_ID); + // since we failed to create the table, there is no need for rollback. + return EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_FAILED; + } + if (!addAclTable(egrSetDscpTable)) + { + SWSS_LOG_ERROR("Failed to create ACL table EgressSetDSCP"); + // since we failed to create the table, there is no need for rollback. + return EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_FAILED; + } + } + else + { + if (updateAclTable(m_AclTables[egrSetDscp_oid], egrSetDscpTable)) + { + SWSS_LOG_NOTICE("Successfully updated existing ACL table EgressSetDSCP"); + // We do not set the status here as we still have to update + // TABLE_TYPE_MARK_META/V6 table. + } + else + { + SWSS_LOG_ERROR("Failed to update existing ACL table EgressSetDSCP"); + // there is no need for roollback as we have not made any changes to the MARK_META/V6 tables. + // We can simply return false. + return EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_FAILED; + } + } + // keep track of the fact that this table is now associated with the EGR_SET_DSCP table. + + m_egrSetDscpRef.insert(table_id); + SWSS_LOG_NOTICE("Added ACL table %s to EgrSetDscpRef", table_id.c_str()); + status = EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_SUCCESS; + + } + + return status; +} + +bool AclOrch::removeEgrSetDscpTable(string table_id) +{ + m_egrSetDscpRef.erase(table_id); + SWSS_LOG_INFO("attempting to remove %s reference", table_id.c_str()); + if (m_egrSetDscpRef.size() == 0) + { + if (!removeAclTable(EGR_SET_DSCP_TABLE_ID)) + { + m_egrSetDscpRef.insert(table_id); + SWSS_LOG_ERROR("Failed to remove ACL table %s", EGR_SET_DSCP_TABLE_ID); + return false; + } + } + else + { + //create a dummy table with no ports. The updateAclTable will remove the + // unique ports which were associated with table_id. + // The way this works is as follows. + // The getAddDeletePorts function collects all the ports of the tables which + // are in m_egrSetDscpRef set and adds those ports to the EGR_SET_DSCP. + // As a result the EGR_SET_DSCP is associated with all the ports to which the + // TABLE_TYPE_UNDERLAY_SET_DSCP/V6 tables are attached. + // + // when we want to remove one of the tables referencing the EGR_SET_DSCP. + // we remove it from m_egrSetDscpRef, then send a updateAclTable with a + // EGR_SET_DSCP table with no assiciated ports. + // The getAddDeletePorts collects all the ports except for the one assocated + // with the table we just removed from m_egrSetDscpRef and updated the EGR_SET_DSCP + // with new port set. + AclTable dummyTable(this); + dummyTable.id = EGR_SET_DSCP_TABLE_ID; + dummyTable.stage = ACL_STAGE_EGRESS; + if (!updateAclTable(EGR_SET_DSCP_TABLE_ID, dummyTable, "")) + { + m_egrSetDscpRef.insert(table_id); + SWSS_LOG_ERROR("Failed to remove ACL table %s", EGR_SET_DSCP_TABLE_ID); + return false; + } + SWSS_LOG_NOTICE("Successfully removed the %s table from the reference of %s", table_id.c_str(), EGR_SET_DSCP_TABLE_ID); + } + return true; +} + +bool AclOrch::addEgrSetDscpRule(string key, string dscpAction) +{ + auto metadata = m_egrDscpRuleMetadata[key]; + + if (m_metadataEgrDscpRule[metadata].size() == 1) + { + // Create EGR_SET_DSCP rule. set the match criteria to metadata value and action to dscpAction. + auto egrSetDscpRule = make_shared(this, std::to_string(metadata), EGR_SET_DSCP_TABLE_ID, &m_metaDataMgr); + egrSetDscpRule->validateAddMatch(MATCH_METADATA, std::to_string(metadata)); + egrSetDscpRule->validateAddAction(ACTION_DSCP, dscpAction); + + if (egrSetDscpRule->validate()) + { + if (!addAclRule(egrSetDscpRule, EGR_SET_DSCP_TABLE_ID)) + { + SWSS_LOG_ERROR("Failed to create ACL rule %d in table %s", metadata, EGR_SET_DSCP_TABLE_ID); + return false; + } + } + else + { + SWSS_LOG_ERROR("Failed to validate ACL rule %d in table %s", metadata, EGR_SET_DSCP_TABLE_ID); + return false; + } + } + return true; +} + +bool AclOrch::removeEgrSetDscpRule(string key) +{ + auto metadata = m_egrDscpRuleMetadata[key]; + if (getMetaDataRefCount(metadata) == 1) + { + if(!removeAclRule(EGR_SET_DSCP_TABLE_ID, std::to_string(metadata))) + { + SWSS_LOG_ERROR("Failed to remove ACL rule %s in table %s", key.c_str(), EGR_SET_DSCP_TABLE_ID); + return false; + } + } + removeMetaDataRef(key, metadata); + m_metaDataMgr.recycleMetaData(metadata); + SWSS_LOG_ERROR("Freeing metadata %d for Rule %s", metadata, key.c_str()); + + return true; +} + +bool AclOrch::updateAclTable(string table_id, AclTable &table) +{ + SWSS_LOG_ENTER(); auto tableOid = getTableById(table_id); if (tableOid == SAI_NULL_OBJECT_ID) { @@ -3928,6 +4453,34 @@ bool AclOrch::updateAclTable(string table_id, AclTable &table) return true; } +bool AclOrch::updateAclTable(string table_id, AclTable &table, string orignalTableTypeName) +{ + SWSS_LOG_ENTER(); + + // we call the addEgrSetDscpTable to add the EGR_SET_DSCP table if the table type is TABLE_TYPE_UNDERLAY_SET_DSCP or TABLE_TYPE_UNDERLAY_SET_DSCPV6 + // for other tables it simply retuns EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_NOT_REQUIRED + EgressSetDscpTableStatus egrSetDscpStatus = addEgrSetDscpTable(table_id, table, orignalTableTypeName); + bool status = false; + if (egrSetDscpStatus == EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_FAILED || + egrSetDscpStatus == EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_NOT_SUPPORTED) + { + SWSS_LOG_INFO("Failed to add/update ACL table %s with %s",table_id.c_str(), orignalTableTypeName.c_str()); + return false; + } + + status = updateAclTable(table_id,table); + // if we have not updated the EGR_SET_DSCP, we simply need to return the status. + // otherewise we need to undo the changes we made to the EGR_SET_DSCP if the update + // of the MARK_META table failed. + if (egrSetDscpStatus == EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_SUCCESS && !status) + { + // This is the scenario where we have successfully updated the EGR_SET_DSCP but failed to update the MARK_META table. + SWSS_LOG_ERROR("Reverting changes to EGR_SET_DSCP because update of %s failed", table_id.c_str()); + removeEgrSetDscpTable(table_id); + } + return status; +} + bool AclOrch::addAclTable(AclTable &newTable) { SWSS_LOG_ENTER(); @@ -4057,10 +4610,44 @@ bool AclOrch::addAclTable(AclTable &newTable) } } +bool AclOrch::addAclTable(string table_id, AclTable &newTable, string orignalTableTypeName) +{ + SWSS_LOG_ENTER(); + // we call the addEgrSetDscpTable to add the EGR_SET_DSCP table if the table type is TABLE_TYPE_UNDERLAY_SET_DSCP + // or TABLE_TYPE_UNDERLAY_SET_DSCPV6. For other tables it simply retuns EGRESS_SET_DSCP_TABLE_NOT_REQUIRED. + EgressSetDscpTableStatus egrSetDscpStatus = addEgrSetDscpTable(table_id, newTable, orignalTableTypeName); + bool status = false; + if (egrSetDscpStatus == EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_FAILED || + egrSetDscpStatus == EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_NOT_SUPPORTED) + { + return false; + } + status = addAclTable(newTable); + // if we have not updated the EGR_SET_DSCP, we simply need to return the status. + // otherewise we need to undo the changes we made to the EGR_SET_DSCP if the update + // of the MARK_META table failed. + if (egrSetDscpStatus == EgressSetDscpTableStatus::EGRESS_SET_DSCP_TABLE_SUCCESS && !status) + { + // This is the scenario where we have successfully updated the EGR_SET_DSCP but failed to update the MARK_META table. + SWSS_LOG_ERROR("Reverting changes to EGR_SET_DSCP because update of %s failed", table_id.c_str()); + removeEgrSetDscpTable(table_id); + } + return status; +} + bool AclOrch::removeAclTable(string table_id) { SWSS_LOG_ENTER(); + if (m_egrSetDscpRef.find(table_id) != m_egrSetDscpRef.end()) + { + if (!removeEgrSetDscpTable(table_id)) + { + SWSS_LOG_ERROR("Failed to remove Egress Set DSCP table associated with ACL table %s", table_id.c_str()); + return false; + } + } + sai_object_id_t table_oid = getTableById(table_id); if (table_oid == SAI_NULL_OBJECT_ID) { @@ -4070,8 +4657,11 @@ bool AclOrch::removeAclTable(string table_id) /* If ACL rules associate with this table, remove the rules first.*/ bool suc = m_AclTables[table_oid].clear(); - if (!suc) return false; - + if (!suc) + { + SWSS_LOG_ERROR("Failed to remove ACL rules in table %s", table_id.c_str()); + return false; + } // Unbind table from switch if needed. AclTable &table = m_AclTables.at(table_oid); if (table.bindToSwitch) @@ -4080,6 +4670,7 @@ bool AclOrch::removeAclTable(string table_id) assert(table->stage == ACL_STAGE_EGRESS); if(!gSwitchOrch->unbindAclTableFromSwitch(ACL_STAGE_EGRESS, table.getOid())) { + SWSS_LOG_ERROR("Failed to unbind ACL table %s from switch", table_id.c_str()); return false; } } @@ -4167,28 +4758,56 @@ bool AclOrch::removeAclTableType(const string& tableTypeName) bool AclOrch::addAclRule(shared_ptr newRule, string table_id) { + SWSS_LOG_ENTER(); + bool needsEgrSetDscp = false; + string key = table_id + ":" + newRule->getId(); + // if the table is using EGR_SET_DSCP, we need to add the EGR_SET_DSCP rule. + if (isUsingEgrSetDscp(table_id)) + { + needsEgrSetDscp = true; + string dscpAction = std::to_string(std::static_pointer_cast(newRule)->getDscpValue()); + if (!addEgrSetDscpRule(key, dscpAction)) + { + SWSS_LOG_ERROR("Failed to add Egress Set Dscp rule for Rule %s in table %s.", + newRule->getId().c_str(), table_id.c_str()); + return false; + } + } + // add the regular rule. + bool status = true; sai_object_id_t table_oid = getTableById(table_id); if (table_oid == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("Failed to add ACL rule in ACL table %s. Table doesn't exist", table_id.c_str()); - return false; + status = false; } - - if (!m_AclTables[table_oid].add(newRule)) + if (status && !m_AclTables[table_oid].add(newRule)) { - return false; + status = false; } - if (newRule->hasCounter()) + if (status && newRule->hasCounter()) { registerFlexCounter(*newRule); } - - return true; + if(!status && needsEgrSetDscp) + { + removeEgrSetDscpRule(key); + return false; + } + return status; } bool AclOrch::removeAclRule(string table_id, string rule_id) { + string key = table_id + ":" + rule_id; + if (m_egrDscpRuleMetadata.find(key) != m_egrDscpRuleMetadata.end()) + { + if (!removeEgrSetDscpRule(key)) + { + return false; + } + } sai_object_id_t table_oid = getTableById(table_id); if (table_oid == SAI_NULL_OBJECT_ID) { @@ -4445,6 +5064,94 @@ bool AclOrch::isAclActionEnumValueSupported(sai_acl_action_type_t action, sai_ac return it->second.find(param.s32) != it->second.cend(); } +bool AclOrch::isAclMetaDataSupported() const +{ + if (m_switchMetaDataCapabilities.at(TABLE_ACL_USER_META_DATA_RANGE_CAPABLE) == "true" && + m_switchMetaDataCapabilities.at(TABLE_ACL_ENTRY_ATTR_META_CAPABLE) == "true" && + m_switchMetaDataCapabilities.at(TABLE_ACL_ENTRY_ACTION_META_CAPABLE) == "true") + { + return true; + } + return false; +} + +uint16_t AclOrch::getAclMetaDataMin() const +{ + if (m_switchMetaDataCapabilities.at(TABLE_ACL_USER_META_DATA_RANGE_CAPABLE) == "true") + { + return uint16_t(std::stoi(m_switchMetaDataCapabilities.at(TABLE_ACL_USER_META_DATA_MIN))); + } + return 0; +} + +uint16_t AclOrch::getAclMetaDataMax() const +{ + if (m_switchMetaDataCapabilities.at(TABLE_ACL_USER_META_DATA_RANGE_CAPABLE) == "true") + { + return uint16_t(std::stoi(m_switchMetaDataCapabilities.at(TABLE_ACL_USER_META_DATA_MAX))); + } + return 0; +} + +bool AclOrch::isUsingEgrSetDscp(const string& table) const +{ + if (m_egrSetDscpRef.find(table) != m_egrSetDscpRef.end()) + { + return true; + } + return false; +} + +string AclOrch::translateUnderlaySetDscpTableTypeName(const string& tableTypeName) const +{ + // The TABLE_TYPE_UNDERLAY_SET_DSCP/V6 is translated to table translates into TABLE_TYPE_MARK_META/V6 + if (tableTypeName == TABLE_TYPE_UNDERLAY_SET_DSCP) + { + return TABLE_TYPE_MARK_META; + } + else if(tableTypeName == TABLE_TYPE_UNDERLAY_SET_DSCPV6) + { + return TABLE_TYPE_MARK_META_V6; + } + return tableTypeName; +} + +void AclOrch::addMetaDataRef(string key, uint16_t metadata) +{ + m_egrDscpRuleMetadata[key] = metadata; + if (m_metadataEgrDscpRule.find(metadata) == m_metadataEgrDscpRule.end()) + { + m_metadataEgrDscpRule[metadata] = set(); + } + m_metadataEgrDscpRule[metadata].insert(key); + +} + +void AclOrch::removeMetaDataRef(string key, uint16_t metadata) +{ + m_metadataEgrDscpRule[metadata].erase(key); + m_egrDscpRuleMetadata.erase(key); +} + +uint32_t AclOrch::getMetaDataRefCount(uint16_t metadata) +{ + if (m_metadataEgrDscpRule.find(metadata) != m_metadataEgrDscpRule.end()) + { + return uint32_t(m_metadataEgrDscpRule[metadata].size()); + } + return 0; +} + +uint32_t AclOrch::hasMetaDataRefCount(string key) +{ + if (m_egrDscpRuleMetadata.find(key) != m_egrDscpRuleMetadata.end()) + { + auto md = m_egrDscpRuleMetadata[key]; + return uint32_t(m_metadataEgrDscpRule[md].size()); + } + return 0; +} + void AclOrch::doAclTableTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -4522,6 +5229,15 @@ void AclOrch::doAclTableTask(Consumer &consumer) } } + // For the case of Table type TABLE_TYPE_UNDERLAY_SET_DSCP/V6 we need to translate + // it to TABLE_TYPE_MARK_META/V6. We retain the original table type name in orignalTableTypeName + // and pass it ot the updateAclTable/ addAclTable functions. There based on the orignalTableTypeName + // we create/update the EgrSetDscp table. + string firstTableTypeName; + string unused; + string orignalTableTypeName = tableTypeName; + tableTypeName = translateUnderlaySetDscpTableTypeName(tableTypeName); + auto tableType = getAclTableType(tableTypeName); if (!tableType) { @@ -4547,7 +5263,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) m_AclTables[table_oid])) { // Update the existing table using the info in newTable - if (updateAclTable(m_AclTables[table_oid], newTable)) + if (updateAclTable(table_id, newTable, orignalTableTypeName)) { SWSS_LOG_NOTICE("Successfully updated existing ACL table %s", table_id.c_str()); @@ -4564,7 +5280,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) } else { - if (addAclTable(newTable)) + if (addAclTable(table_id, newTable, orignalTableTypeName)) { // Mark ACL table as ACTIVE setAclTableStatus(table_id, AclObjectStatus::ACTIVE); @@ -4572,6 +5288,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) } else { + //we have failed to create the MarkMeta table, we need to remove the EgrSetDscp table setAclTableStatus(table_id, AclObjectStatus::PENDING_CREATION); it++; } @@ -4667,7 +5384,7 @@ void AclOrch::doAclRuleTask(Consumer &consumer) try { - newRule = AclRule::makeShared(this, m_mirrorOrch, m_dTelOrch, rule_id, table_id, t); + newRule = AclRule::makeShared(this, m_mirrorOrch, m_dTelOrch, rule_id, table_id, t, &m_metaDataMgr); } catch (exception &e) { @@ -4746,13 +5463,13 @@ void AclOrch::doAclRuleTask(Consumer &consumer) } if (bHasIPV4 && bHasIPV6) - { - if (type == TABLE_TYPE_L3V4V6) - { - SWSS_LOG_ERROR("Rule '%s' is invalid since it has both v4 and v6 matchfields.", rule_id.c_str()); - bAllAttributesOk = false; - } - } + { + if (type == TABLE_TYPE_L3V4V6) + { + SWSS_LOG_ERROR("Rule '%s' is invalid since it has both v4 and v6 matchfields.", rule_id.c_str()); + bAllAttributesOk = false; + } + } // validate and create ACL rule if (bAllAttributesOk && newRule->validate()) @@ -5197,3 +5914,89 @@ void AclOrch::removeAllAclRuleStatus() m_aclRuleStateTable.del(key); } } + +void MetaDataMgr::populateRange(uint16_t min, uint16_t max) +{ + metaMin = min; + metaMax = max; + SWSS_LOG_INFO("Metadata range %d to %d", metaMin,metaMax); + for (uint16_t i = metaMin; i <= metaMax; i++) + { + m_freeMetadata.push_back(i); + } + initComplete = true; +} + +bool MetaDataMgr::isValidMetaData(uint16_t metadata) +{ + if (metadata >= metaMin && metadata <= metaMax) + { + return true; + } + return false; +} + +uint16_t MetaDataMgr::getFreeMetaData(uint8_t dscp) +{ + uint16_t metadata = metaMax + 1; + SWSS_LOG_INFO("Metadata Request for dscp %d", dscp); + + if (initComplete) + { + if (m_dscpMetadata.find(dscp) != m_dscpMetadata.end()) + { + // dscp value has a metadata value assigned to it. + metadata = m_dscpMetadata[dscp]; + SWSS_LOG_INFO("Metadata %d has already been allocated for dscp %d, refcount %d", metadata, dscp, m_MetadataRef[metadata]+1); + + } + else + { + if (m_freeMetadata.empty()) + { + SWSS_LOG_ERROR("Metadata Value not available for allocation."); + return metadata; + } + metadata = m_freeMetadata.front(); + m_freeMetadata.erase(m_freeMetadata.begin()); + m_dscpMetadata[dscp] = metadata; + SWSS_LOG_INFO("New Metadata %d allocated for dscp %d", metadata, dscp); + } + m_MetadataRef[metadata] += 1; + } + else + { + SWSS_LOG_ERROR("Metadata request before Initialization complete."); + } + return metadata; +} + +void MetaDataMgr::recycleMetaData(uint16_t metadata) +{ + if (initComplete) + { + m_MetadataRef[metadata] -= 1; + SWSS_LOG_INFO("Freeing Metadata %d refcount %d", metadata, m_MetadataRef[metadata]); + if (m_MetadataRef[metadata] == 0) + { + + for (auto iter = m_dscpMetadata.begin(); iter != m_dscpMetadata.end();) + { + if ( iter->second == metadata) + { + m_dscpMetadata.erase(iter++); + m_freeMetadata.push_front(metadata); + break; + } + else + { + ++iter; + } + } + } + } + else + { + SWSS_LOG_ERROR("Unexpected: Metadata free before Initialization complete."); + } +} diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 4dcc450173..9e27be2ac0 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -53,6 +53,7 @@ #define MATCH_BTH_OPCODE "BTH_OPCODE" #define MATCH_AETH_SYNDROME "AETH_SYNDROME" #define MATCH_TUNNEL_TERM "TUNNEL_TERM" +#define MATCH_METADATA "META_DATA" #define BIND_POINT_TYPE_PORT "PORT" #define BIND_POINT_TYPE_PORTCHANNEL "PORTCHANNEL" @@ -70,6 +71,8 @@ #define ACTION_DTEL_FLOW_SAMPLE_PERCENT "FLOW_SAMPLE_PERCENT" #define ACTION_DTEL_REPORT_ALL_PACKETS "REPORT_ALL_PACKETS" #define ACTION_COUNTER "COUNTER" +#define ACTION_META_DATA "META_DATA_ACTION" +#define ACTION_DSCP "DSCP_ACTION" #define PACKET_ACTION_FORWARD "FORWARD" #define PACKET_ACTION_DROP "DROP" @@ -104,6 +107,12 @@ #define ACL_COUNTER_FLEX_COUNTER_GROUP "ACL_STAT_COUNTER" +#define TABLE_ACL_USER_META_DATA_RANGE_CAPABLE "ACL_USER_META_DATA_RANGE_CAPABLE" +#define TABLE_ACL_USER_META_DATA_MIN "ACL_USER_META_DATA_MIN" +#define TABLE_ACL_USER_META_DATA_MAX "ACL_USER_META_DATA_MAX" +#define TABLE_ACL_ENTRY_ATTR_META_CAPABLE "ACL_ENTRY_ATTR_META_CAPABLE" +#define TABLE_ACL_ENTRY_ACTION_META_CAPABLE "ACL_ENTRY_ACTION_META_CAPABLE" + enum AclObjectStatus { ACTIVE = 0, @@ -112,6 +121,14 @@ enum AclObjectStatus PENDING_REMOVAL }; +enum EgressSetDscpTableStatus +{ + EGRESS_SET_DSCP_TABLE_FAILED = 0, + EGRESS_SET_DSCP_TABLE_SUCCESS, + EGRESS_SET_DSCP_TABLE_NOT_REQUIRED, + EGRESS_SET_DSCP_TABLE_NOT_SUPPORTED +}; + struct AclActionCapabilities { set actionList; @@ -168,6 +185,24 @@ class AclTableRangeMatch: public AclTableMatchInterface private: vector m_rangeList; }; + +class MetaDataMgr +{ +public: + void populateRange(uint16_t min, uint16_t max); + uint16_t getFreeMetaData(uint8_t dscp); + void recycleMetaData(uint16_t metadata); + bool isValidMetaData(uint16_t metadata); + +private: + bool initComplete = false; + uint16_t metaMin = 0; + uint16_t metaMax = 0; + list m_freeMetadata; + map m_dscpMetadata; + map m_MetadataRef; +}; + class AclTableType { public: @@ -281,7 +316,13 @@ class AclRule bool getCreateCounter() const; const vector& getRangeConfig() const; - static shared_ptr makeShared(AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple&); + static shared_ptr makeShared(AclOrch *acl, + MirrorOrch *mirror, + DTelOrch *dtel, + const string& rule, + const string& table, + const KeyOpFieldsValuesTuple&, + MetaDataMgr * m_metadataMgr); virtual ~AclRule() {} protected: @@ -381,6 +422,23 @@ class AclRuleDTelWatchListEntry: public AclRule bool INT_session_valid; }; +class AclRuleUnderlaySetDscp: public AclRule +{ +public: + AclRuleUnderlaySetDscp(AclOrch *m_pAclOrch, string rule, string table, MetaDataMgr* m_metaDataMgr, bool createCounter = true); + + bool validateAddAction(string attr_name, string attr_value); + bool validate(); + void onUpdate(SubjectType, void *) override; + uint32_t getDscpValue() const; + uint32_t getMetadata() const; +protected: + uint32_t cachedDscpValue; + uint32_t cachedMetadata; + string table_id; + MetaDataMgr* m_metaDataMgr; +}; + class AclTable { public: @@ -494,6 +552,14 @@ class AclOrch : public Orch, public Observer bool addAclTable(AclTable &aclTable); bool removeAclTable(string table_id); + bool addAclTable(string table_id, AclTable &aclTable, string orignalTableTypeName); + bool updateAclTable(string table_id, AclTable &table, string orignalTableTypeName); + EgressSetDscpTableStatus addEgrSetDscpTable(string table_id, AclTable &table, string orignalTableTypeName); + + bool removeEgrSetDscpTable(string table_id); + bool addEgrSetDscpRule(string key, string dscpAction); + bool removeEgrSetDscpRule(string key); + bool addAclTableType(const AclTableType& tableType); bool removeAclTableType(const string& tableTypeName); bool updateAclTable(AclTable ¤tTable, AclTable &newTable); @@ -513,11 +579,22 @@ class AclOrch : public Orch, public Observer bool isAclActionListMandatoryOnTableCreation(acl_stage_type_t stage) const; bool isAclActionSupported(acl_stage_type_t stage, sai_acl_action_type_t action) const; bool isAclActionEnumValueSupported(sai_acl_action_type_t action, sai_acl_action_parameter_t param) const; + bool isUsingEgrSetDscp(const string& table) const; + string translateUnderlaySetDscpTableTypeName(const string& tableTypeName) const; + bool isAclMetaDataSupported() const; + uint16_t getAclMetaDataMin() const; + uint16_t getAclMetaDataMax() const; + + void addMetaDataRef(string key, uint16_t metadata); + void removeMetaDataRef(string key, uint16_t metadata); + uint32_t getMetaDataRefCount(uint16_t metadata); + uint32_t hasMetaDataRefCount(string key); bool m_isCombinedMirrorV6Table = true; map m_mirrorTableCapabilities; map m_L3V4V6Capability; - + map m_switchMetaDataCapabilities; + void registerFlexCounter(const AclRule& rule); void deregisterFlexCounter(const AclRule& rule); @@ -593,8 +670,12 @@ class AclOrch : public Orch, public Observer Table m_aclTableStateTable; Table m_aclRuleStateTable; + MetaDataMgr m_metaDataMgr; map m_mirrorTableId; map m_mirrorV6TableId; + set m_egrSetDscpRef; + map> m_metadataEgrDscpRule; + map m_egrDscpRuleMetadata; acl_capabilities_t m_aclCapabilities; acl_action_enum_values_capabilities_t m_aclEnumActionCapabilities; diff --git a/orchagent/acltable.h b/orchagent/acltable.h index 1b1cdeb29a..8f51fd9a09 100644 --- a/orchagent/acltable.h +++ b/orchagent/acltable.h @@ -35,6 +35,11 @@ extern "C" { #define TABLE_TYPE_MCLAG "MCLAG" #define TABLE_TYPE_MUX "MUX" #define TABLE_TYPE_DROP "DROP" +#define TABLE_TYPE_MARK_META "MARK_META" +#define TABLE_TYPE_MARK_META_V6 "MARK_METAV6" +#define TABLE_TYPE_EGR_SET_DSCP "EGR_SET_DSCP" +#define TABLE_TYPE_UNDERLAY_SET_DSCP "UNDERLAY_SET_DSCP" +#define TABLE_TYPE_UNDERLAY_SET_DSCPV6 "UNDERLAY_SET_DSCPV6" typedef enum { diff --git a/tests/dash/test_dash_route_group.py b/tests/dash/test_dash_route_group.py index 459507aaa1..dbc8f31eeb 100644 --- a/tests/dash/test_dash_route_group.py +++ b/tests/dash/test_dash_route_group.py @@ -44,7 +44,11 @@ def test_rebind_eni_route_group(dash_db: DashDB): dash_db.set_app_db_entry(APP_DASH_ROUTE_GROUP_TABLE_NAME, ROUTE_GROUP2, ROUTE_GROUP2_CONFIG) dash_db.set_app_db_entry(APP_DASH_ROUTE_TABLE_NAME, ROUTE_GROUP2, OUTBOUND_ROUTE_PREFIX1, ROUTE_VNET_CONFIG_UNDERLAY_SIP) - rg2_oid = dash_db.wait_for_asic_db_keys("ASIC_STATE:SAI_OBJECT_TYPE_OUTBOUND_ROUTING_GROUP", min_keys=2)[1] + oids = dash_db.wait_for_asic_db_keys("ASIC_STATE:SAI_OBJECT_TYPE_OUTBOUND_ROUTING_GROUP", min_keys=2) + for oid in oids: + if oid != rg1_oid: + rg2_oid = oid + break dash_db.set_app_db_entry(APP_DASH_ENI_ROUTE_TABLE_NAME, ENI_ID, ENI_ROUTE_GROUP1_CONFIG) diff --git a/tests/dvslib/dvs_acl.py b/tests/dvslib/dvs_acl.py index 236ccaa0fc..2197c034b8 100644 --- a/tests/dvslib/dvs_acl.py +++ b/tests/dvslib/dvs_acl.py @@ -331,6 +331,30 @@ def verify_acl_table_action_list( for action in expected_action_list: assert action in action_list + def create_dscp_acl_rule( + self, + table_name: str, + rule_name: str, + qualifiers: Dict[str, str], + action: str, + priority: str = "2020" + ) -> None: + """Create a new DSCP ACL rule in the given table. + Args: + table_name: The name of the ACL table to add the rule to. + rule_name: The name of the ACL rule. + qualifiers: The list of qualifiers to add to the rule. + action: DSCP value. + priority: The priority of the rule. + """ + fvs = { + "priority": priority, + "DSCP_ACTION": action + } + + for k, v in qualifiers.items(): + fvs[k] = v + self.config_db.create_entry("ACL_RULE", "{}|{}".format(table_name, rule_name), fvs) def create_acl_rule( self, diff --git a/tests/test_acl_mark.py b/tests/test_acl_mark.py new file mode 100644 index 0000000000..aa5b5ec7ed --- /dev/null +++ b/tests/test_acl_mark.py @@ -0,0 +1,447 @@ +import pytest +from requests import request + +OVERLAY_TABLE_TYPE = "UNDERLAY_SET_DSCP" +OVERLAY_TABLE_NAME = "OVERLAY_MARK_META_TEST" +OVERLAY_BIND_PORTS = ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"] +OVERLAY_RULE_NAME = "OVERLAY_TEST_RULE" + +OVERLAY_TABLE_TYPE6 = "UNDERLAY_SET_DSCPV6" +OVERLAY_TABLE_NAME6 = "OVERLAY_MARK_META_TEST6" +OVERLAY_BIND_PORTS6 = ["Ethernet20", "Ethernet24", "Ethernet28", "Ethernet32"] +OVERLAY_RULE_NAME6 = "OVERLAY_TEST_RULE6" + +# tests for UNDERLAY_SET_DSCP table + + +class TestAclMarkMeta: + @pytest.fixture + def overlay_acl_table(self, dvs_acl): + try: + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME, + OVERLAY_TABLE_TYPE, + OVERLAY_BIND_PORTS) + yield dvs_acl.get_acl_table_ids(2) + finally: + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME) + dvs_acl.verify_acl_table_count(0) + + @pytest.fixture + def overlay6_acl_table(self, dvs_acl): + try: + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME6, + OVERLAY_TABLE_TYPE6, + OVERLAY_BIND_PORTS6) + yield dvs_acl.get_acl_table_ids(2) + finally: + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME6) + dvs_acl.verify_acl_table_count(0) + + def verify_acl_table_group_members_multitable(self, dvs_acl, acl_table_id, acl_table_group_ids, member_count): + members = dvs_acl.asic_db.wait_for_n_keys(dvs_acl.ADB_ACL_GROUP_MEMBER_TABLE_NAME, + member_count) + + member_groups = [] + table_member_map = {} + for member in members: + fvs = dvs_acl.asic_db.wait_for_entry(dvs_acl.ADB_ACL_GROUP_MEMBER_TABLE_NAME, member) + group_id = fvs.get("SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID") + table_id = fvs.get("SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID") + + if group_id in acl_table_group_ids and table_id in acl_table_id: + member_groups.append(group_id) + if table_id not in table_member_map: + table_member_map[table_id] = [] + table_member_map[table_id].append(group_id) + + assert set(member_groups) == set(acl_table_group_ids) + return table_member_map + + def get_table_stage(self, dvs_acl, acl_table_id, v4_ports, v6_ports): + stages = [] + names = [] + ports = [] + for table in acl_table_id: + fvs = dvs_acl.asic_db.wait_for_entry(dvs_acl.ADB_ACL_TABLE_NAME, table) + stage = fvs.get("SAI_ACL_TABLE_ATTR_ACL_STAGE") + if stage == "SAI_ACL_STAGE_INGRESS": + stages.append("ingress") + elif stage == "SAI_ACL_STAGE_EGRESS": + stages.append("egress") + qual = fvs.get("SAI_ACL_TABLE_ATTR_FIELD_ACL_USER_META") + if qual == "true": + names.append("EGR_SET_DSCP") + ports.append(v4_ports+v6_ports) + qual = fvs.get("SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6") + if qual == "true": + names.append("MARK_META6") + ports.append(v6_ports) + qual = fvs.get("SAI_ACL_TABLE_ATTR_FIELD_DST_IP") + if qual == "true": + names.append("MARK_META") + ports.append(v4_ports) + return stages, names, ports + + def verify_acl_table_port_binding_multi(self, dvs_acl, table_member_map, bind_ports, stages, acl_table_id): + for i in range(0, len(stages)): + stage = stages[i] + table = acl_table_id[i] + port_groups = [] + for port in bind_ports[i]: + port_oid = dvs_acl.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "").get(port) + fvs = dvs_acl.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid) + acl_table_group_id = fvs.pop(dvs_acl.ADB_PORT_ATTR_LOOKUP[stage], None) + assert acl_table_group_id in table_member_map[table] + port_groups.append(acl_table_group_id) + + assert len(port_groups) == len(bind_ports[i]) + assert set(port_groups) == set(table_member_map[table]) + + + def get_acl_rules_with_action(self, dvs_acl, total_rules): + """Verify that there are N rules in the ASIC DB.""" + members = dvs_acl.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY", + total_rules) + + member_groups = [] + table_member_map = {} + for member in members: + fvs = dvs_acl.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY", member) + table_id = fvs.get("SAI_ACL_ENTRY_ATTR_TABLE_ID") + entry = {} + entry['id'] = member + action = fvs.get("SAI_ACL_ENTRY_ATTR_ACTION_SET_DSCP") + if action: + entry['action_type'] = "dscp" + entry['action_value'] = action + meta = fvs.get("SAI_ACL_ENTRY_ATTR_FIELD_ACL_USER_META") + entry['match_meta'] = meta.split('&')[0] + action = fvs.get("SAI_ACL_ENTRY_ATTR_ACTION_SET_ACL_META_DATA") + if action: + entry['action_type'] = "meta" + entry['action_value'] = action + + if table_id not in table_member_map: + table_member_map[table_id] = [] + table_member_map[table_id].append(entry) + return table_member_map + + def verify_acl_rules_with_action(self, table_names, acl_table_id, table_rules, meta, dscp): + for i in range(0, len(table_names)): + if acl_table_id[i] in table_rules: + for j in range(0, len(table_rules[acl_table_id[i]])): + if table_names[i] == "MARK_META" or table_names[i] == "MARK_META6": + assert table_rules[acl_table_id[i]][j]['action_type'] == "meta" + assert table_rules[acl_table_id[i]][j]['action_value'] in meta + else: + assert table_rules[acl_table_id[i]][j]['action_type'] == "dscp" + assert table_rules[acl_table_id[i]][j]['action_value'] in dscp + assert table_rules[acl_table_id[i]][j]['match_meta'] in meta + + def test_OverlayTableCreationDeletion(self, dvs_acl): + try: + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME, OVERLAY_TABLE_TYPE, OVERLAY_BIND_PORTS) + # this should create 2 tables. MARK_META and EGR_SET_DSCP Verify the table count. + acl_table_id = dvs_acl.get_acl_table_ids(2) + stages, names, ports = self.get_table_stage(dvs_acl, acl_table_id, OVERLAY_BIND_PORTS, []) + + acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(OVERLAY_BIND_PORTS)*2) + table_member_map = self.verify_acl_table_group_members_multitable(dvs_acl, acl_table_id, acl_table_group_ids, 8) + + self.verify_acl_table_port_binding_multi(dvs_acl, table_member_map, ports, stages, acl_table_id) + + # Verify status is written into STATE_DB + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME, "Active") + finally: + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME) + dvs_acl.verify_acl_table_count(0) + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME, None) + + def test_Overlay6TableCreationDeletion(self, dvs_acl): + try: + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME6, OVERLAY_TABLE_TYPE6, OVERLAY_BIND_PORTS6) + # this should create 2 tables. MARK_META and EGR_SET_DSCP Verify the table count. + acl_table_id = dvs_acl.get_acl_table_ids(2) + stages, names, ports = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS6) + acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(OVERLAY_BIND_PORTS6)*2) + table_member_map = self.verify_acl_table_group_members_multitable(dvs_acl, acl_table_id, acl_table_group_ids, 8) + + self.verify_acl_table_port_binding_multi(dvs_acl, table_member_map, ports, stages, acl_table_id) + + # Verify status is written into STATE_DB + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME6, "Active") + finally: + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME6) + dvs_acl.verify_acl_table_count(0) + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME6, None) + + def test_OverlayBothv4v6TableCreationDeletion(self, dvs_acl): + try: + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME, OVERLAY_TABLE_TYPE, OVERLAY_BIND_PORTS) + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME6, OVERLAY_TABLE_TYPE6, OVERLAY_BIND_PORTS6) + # this should create 2 tables. MARK_META and EGR_SET_DSCP Verify the table count. + acl_table_id = dvs_acl.get_acl_table_ids(3) + stages, names, ports = self.get_table_stage(dvs_acl, acl_table_id,OVERLAY_BIND_PORTS, OVERLAY_BIND_PORTS6) + acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(OVERLAY_BIND_PORTS6)*4) + table_member_map = self.verify_acl_table_group_members_multitable(dvs_acl, acl_table_id, acl_table_group_ids, 16) + + self.verify_acl_table_port_binding_multi(dvs_acl, table_member_map, ports, stages, acl_table_id) + + # Verify status is written into STATE_DB + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME, "Active") + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME6, "Active") + finally: + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME) + dvs_acl.verify_acl_table_count(2) + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME, None) + acl_table_id = dvs_acl.get_acl_table_ids(2) + + stages, names, ports = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS6) + acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(OVERLAY_BIND_PORTS6)*2) + table_member_map = self.verify_acl_table_group_members_multitable(dvs_acl, acl_table_id, acl_table_group_ids, 8) + + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME6) + dvs_acl.verify_acl_table_count(0) + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME6, None) + + def test_OverlayBothv4v6TableSameintfCreationDeletion(self, dvs_acl): + try: + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME, OVERLAY_TABLE_TYPE, OVERLAY_BIND_PORTS) + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME6, OVERLAY_TABLE_TYPE6, OVERLAY_BIND_PORTS) + # this should create 2 tables. MARK_META and EGR_SET_DSCP Verify the table count. + acl_table_id = dvs_acl.get_acl_table_ids(3) + stages, names, ports = self.get_table_stage(dvs_acl, acl_table_id,OVERLAY_BIND_PORTS, OVERLAY_BIND_PORTS) + acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(OVERLAY_BIND_PORTS)*2) + table_member_map = self.verify_acl_table_group_members_multitable(dvs_acl, acl_table_id, acl_table_group_ids, 12) + + self.verify_acl_table_port_binding_multi(dvs_acl, table_member_map, ports, stages, acl_table_id) + + # Verify status is written into STATE_DB + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME, "Active") + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME6, "Active") + finally: + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME) + dvs_acl.verify_acl_table_count(2) + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME, None) + acl_table_id = dvs_acl.get_acl_table_ids(2) + + stages, names, ports = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(OVERLAY_BIND_PORTS)*2) + table_member_map = self.verify_acl_table_group_members_multitable(dvs_acl, acl_table_id, acl_table_group_ids, 8) + + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME6) + dvs_acl.verify_acl_table_count(0) + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_table_status(OVERLAY_TABLE_NAME6, None) + + def test_OverlayEntryCreationDeletion(self, dvs_acl, overlay_acl_table): + config_qualifiers = {"DST_IP": "20.0.0.1/32", + "SRC_IP": "10.0.0.0/32"} + acl_table_id = dvs_acl.get_acl_table_ids(2) + _, names, _ = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "VALID_RULE", config_qualifiers,action="12") + # Verify status is written into STATE_DB + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "VALID_RULE", "Active") + table_rules = self.get_acl_rules_with_action(dvs_acl, 2) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1"], ["12"]) + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "VALID_RULE") + # Verify the STATE_DB entry is removed + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "VALID_RULE", None) + dvs_acl.verify_no_acl_rules() + + def test_OverlayEntryMultiRuleRef(self, dvs_acl, overlay_acl_table): + config_qualifiers = {"DST_IP": "20.0.0.1/32", + "SRC_IP": "10.0.0.0/32", + "DSCP": "1" + } + acl_table_id = dvs_acl.get_acl_table_ids(2) + _, names, _ = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + #create 1st Rule + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "1", config_qualifiers, action="12") + #create 2nd Rule + config_qualifiers["DSCP"] = "2" + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "2", config_qualifiers, action="12") + #create 3rd Rule + config_qualifiers["DSCP"] = "3" + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "3", config_qualifiers, action="12") + + #This should create 4 rules 3 for MARK_META and 1 for EGR_SET_DSCP + # Verify status is written into STATE_DB + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", "Active") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "2", "Active") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "3", "Active") + table_rules = self.get_acl_rules_with_action(dvs_acl, 4) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1"], ["12"]) + + # remove first rule. We should still have 3 rules, 2 for MARK_META and 1 for EGR_SET_DSCP + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "1") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", None) + table_rules = self.get_acl_rules_with_action(dvs_acl, 3) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1"], ["12"]) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", None) + + # remove 2nd rule. We should still have 2 rules, 1 for MARK_META and 1 for EGR_SET_DSCP + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "2") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "2", None) + table_rules = self.get_acl_rules_with_action(dvs_acl, 2) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1"], ["12"]) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "2", None) + + # Verify the STATE_DB entry is removed + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "3") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "3", None) + + dvs_acl.verify_no_acl_rules() + + def test_OverlayEntryMultiTableRules(self, dvs_acl): + config_qualifiers = {"DST_IP": "20.0.0.1/32", + "SRC_IP": "10.0.0.0/32", + "DSCP": "1"} + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME, + OVERLAY_TABLE_TYPE, + OVERLAY_BIND_PORTS) + dvs_acl.create_acl_table(OVERLAY_TABLE_NAME6, + OVERLAY_TABLE_TYPE6, + OVERLAY_BIND_PORTS6) + acl_table_id = dvs_acl.get_acl_table_ids(3) + _, names, _ = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + #create 1st Rule + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "1", config_qualifiers, action="12") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", "Active") + + #create 2nd Rule ipv6 + config_qualifiers6 = {"SRC_IPV6": "2777::0/64", + "DST_IPV6": "2788::0/64", + "DSCP" : "1"}; + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME6, "1", config_qualifiers6, action="12") + + # Verify status of both rules. + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", "Active") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME6, "1", "Active") + table_rules = self.get_acl_rules_with_action(dvs_acl, 3) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1"], ["12"]) + + # remove first rule. We should still have 1 rule, 1 for MARK_META + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME6, "1") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME6, "1", None) + table_rules = self.get_acl_rules_with_action(dvs_acl, 2) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1"], ["12"]) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", "Active") + + # remove 2nd rule. We should still have 2 rules, 1 for MARK_META and 1 for EGR_SET_DSCP + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "1") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", None) + dvs_acl.verify_no_acl_rules() + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME) + dvs_acl.remove_acl_table(OVERLAY_TABLE_NAME6) + dvs_acl.verify_acl_table_count(0) + + def test_OverlayEntryMultiMetaRule(self, dvs_acl, overlay_acl_table): + config_qualifiers = {"DST_IP": "20.0.0.1/32", + "SRC_IP": "10.0.0.0/32", + "DSCP": "1" + } + + acl_table_id = dvs_acl.get_acl_table_ids(2) + _, names, _ = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + #create 1st Rule + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "1", config_qualifiers, action="12") + #create 2nd Rule + config_qualifiers["DSCP"] = "2" + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "2", config_qualifiers, action="13") + #create 3rd Rule + config_qualifiers["DSCP"] = "3" + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, "3", config_qualifiers, action="14") + + #This should create 4 rules 3 for MARK_META and 1 for EGR_SET_DSCP + # Verify status is written into STATE_DB + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", "Active") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "2", "Active") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "3", "Active") + table_rules = self.get_acl_rules_with_action(dvs_acl, 6) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1", "2", "3"], ["12", "13", "14"]) + + # remove first rule. We should still have 3 rules, 2 for MARK_META and 1 for EGR_SET_DSCP + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "1") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", None) + table_rules = self.get_acl_rules_with_action(dvs_acl, 4) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1", "2", "3"], ["12", "13", "14"]) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "1", None) + + # remove 2nd rule. We should still have 2 rules, 1 for MARK_META and 1 for EGR_SET_DSCP + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "2") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "2", None) + table_rules = self.get_acl_rules_with_action(dvs_acl, 2) + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, ["1", "2", "3"], ["12", "13", "14"]) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "2", None) + + # Verify the STATE_DB entry is removed + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, "3") + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, "3", None) + + dvs_acl.verify_no_acl_rules() + + def test_OverlayEntryExhaustMeta(self, dvs_acl, overlay_acl_table): + config_qualifiers = {"DST_IP": "20.0.0.1/32", + "SRC_IP": "10.0.0.0/32", + "DSCP": "1" + } + acl_table_id = dvs_acl.get_acl_table_ids(2) + _, names, _ = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + #create 8 rules. 8th one should fail. + for i in range(1, 9): + config_qualifiers["DSCP"] = str(i) + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, str(i), config_qualifiers, action=str(i+10)) + if i < 8: + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, str(i), "Active") + else: + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, str(i), None) + + table_rules = self.get_acl_rules_with_action(dvs_acl, 14) + meta = [str(i) for i in range(1, 8)] + dscps = [str(i) for i in range(11, 18)] + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, meta, dscps) + + for i in range(1, 9): + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, str(i)) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, str(i), None) + dvs_acl.verify_no_acl_rules() + + def test_OverlayEntryTestMetaDataMgr(self, dvs_acl, overlay_acl_table): + # allocate all 7 metadata values and free them multiple times. + # At the end there should be no rules allocated. + for i in range(1, 4): + config_qualifiers = {"DST_IP": "20.0.0.1/32", + "SRC_IP": "10.0.0.0/32", + "DSCP": "1" + } + acl_table_id = dvs_acl.get_acl_table_ids(2) + _, names, _ = self.get_table_stage(dvs_acl, acl_table_id, [], OVERLAY_BIND_PORTS) + #create 8 rules. 8th one should fail. + for i in range(1, 9): + config_qualifiers["DSCP"] = str(i) + dvs_acl.create_dscp_acl_rule(OVERLAY_TABLE_NAME, str(i), config_qualifiers, action=str(i+10)) + if i < 8: + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, str(i), "Active") + else: + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, str(i), None) + + table_rules = self.get_acl_rules_with_action(dvs_acl, 14) + meta = [str(i) for i in range(1, 8)] + dscps = [str(i) for i in range(11, 18)] + self.verify_acl_rules_with_action(names, acl_table_id, table_rules, meta, dscps) + + for i in range(1, 9): + dvs_acl.remove_acl_rule(OVERLAY_TABLE_NAME, str(i)) + dvs_acl.verify_acl_rule_status(OVERLAY_TABLE_NAME, str(i), None) + dvs_acl.verify_no_acl_rules() + + # Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass \ No newline at end of file