diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 12347d954f..153e91f127 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -110,6 +110,8 @@ orchagent_SOURCES = \ dash/dashrouteorch.cpp \ dash/dashvnetorch.cpp \ dash/dashaclorch.cpp \ + dash/dashaclgroupmgr.cpp \ + dash/dashtagmgr.cpp \ dash/pbutils.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp diff --git a/orchagent/dash/dashaclgroupmgr.cpp b/orchagent/dash/dashaclgroupmgr.cpp new file mode 100644 index 0000000000..f0131dfda5 --- /dev/null +++ b/orchagent/dash/dashaclgroupmgr.cpp @@ -0,0 +1,736 @@ +#include + +#include + +#include "dashaclgroupmgr.h" + +#include "crmorch.h" +#include "dashorch.h" +#include "dashaclorch.h" +#include "saihelper.h" +#include "pbutils.h" + +extern sai_dash_acl_api_t* sai_dash_acl_api; +extern sai_dash_eni_api_t* sai_dash_eni_api; +extern sai_object_id_t gSwitchId; +extern CrmOrch *gCrmOrch; + +using namespace std; +using namespace swss; +using namespace dash::acl_in; +using namespace dash::acl_out; +using namespace dash::acl_rule; +using namespace dash::acl_group; +using namespace dash::tag; +using namespace dash::types; + +const static vector all_protocols(boost::counting_iterator(0), boost::counting_iterator(UINT8_MAX + 1)); +const static vector all_ports = {{numeric_limits::min(), numeric_limits::max()}}; + +bool from_pb(const AclRule& data, DashAclRule& rule) +{ + rule.m_priority = data.priority(); + rule.m_action = (data.action() == ACTION_PERMIT) ? DashAclRule::Action::ALLOW : DashAclRule::Action::DENY; + rule.m_terminating = data.terminating(); + + if (data.protocol_size()) + { + rule.m_protocols.reserve(data.protocol_size()); + rule.m_protocols.assign(data.protocol().begin(), data.protocol().end()); + } + + if (!to_sai(data.src_addr(), rule.m_src_prefixes)) + { + return false; + } + + if (!to_sai(data.dst_addr(), rule.m_dst_prefixes)) + { + return false; + } + + if (data.src_tag_size()) + { + rule.m_src_tags.insert(data.src_tag().begin(), data.src_tag().end()); + } + + if (data.dst_tag_size()) + { + rule.m_dst_tags.insert(data.dst_tag().begin(), data.dst_tag().end()); + } + + if (!data.src_port_size()) + { + rule.m_src_ports = all_ports; + } + else if (!to_sai(data.src_port(), rule.m_src_ports)) + { + return false; + } + + if (!data.dst_port_size()) + { + rule.m_dst_ports = all_ports; + } + else if (!to_sai(data.dst_port(), rule.m_dst_ports)) + { + return false; + } + + return true; +} + +bool from_pb(const dash::acl_group::AclGroup &data, DashAclGroup& group) +{ + if (!to_sai(data.ip_version(), group.m_ip_version)) + { + return false; + } + + return true; +} + +sai_attr_id_t getSaiStage(DashAclDirection d, sai_ip_addr_family_t f, DashAclStage s) +{ + const static map, sai_attr_id_t> StageMaps = + { + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE1}, SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE2}, SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE3}, SAI_ENI_ATTR_INBOUND_V4_STAGE3_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE4}, SAI_ENI_ATTR_INBOUND_V4_STAGE4_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE5}, SAI_ENI_ATTR_INBOUND_V4_STAGE5_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE1}, SAI_ENI_ATTR_INBOUND_V6_STAGE1_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE2}, SAI_ENI_ATTR_INBOUND_V6_STAGE2_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE3}, SAI_ENI_ATTR_INBOUND_V6_STAGE3_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE4}, SAI_ENI_ATTR_INBOUND_V6_STAGE4_DASH_ACL_GROUP_ID}, + {{DashAclDirection::IN, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE5}, SAI_ENI_ATTR_INBOUND_V6_STAGE5_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE1}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE1_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE2}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE2_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE3}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE3_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE4}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE4_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV4, DashAclStage::STAGE5}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE5_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE1}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE1_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE2}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE2_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE3}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE3_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE4}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE4_DASH_ACL_GROUP_ID}, + {{DashAclDirection::OUT, SAI_IP_ADDR_FAMILY_IPV6, DashAclStage::STAGE5}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE5_DASH_ACL_GROUP_ID}, + }; + + auto stage = StageMaps.find({d, f, s}); + if (stage == StageMaps.end()) + { + SWSS_LOG_ERROR("Invalid stage %d %d %d", static_cast(d), f, static_cast(s)); + throw runtime_error("Invalid stage"); + } + + return stage->second; +} + +DashAclGroupMgr::DashAclGroupMgr(DashOrch *dashorch, DashAclOrch *aclorch) : + m_dash_orch(dashorch), + m_dash_acl_orch(aclorch) +{ + SWSS_LOG_ENTER(); +} + +void DashAclGroupMgr::init(DashAclGroup& group) +{ + SWSS_LOG_ENTER(); + group.m_dash_acl_group_id = SAI_NULL_OBJECT_ID; + + for (auto& rule: group.m_dash_acl_rule_table) + { + rule.second.m_dash_acl_rule_id = SAI_NULL_OBJECT_ID; + } +} + +void DashAclGroupMgr::create(DashAclGroup& group) +{ + SWSS_LOG_ENTER(); + + vector attrs; + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY; + attrs.back().value.s32 = group.m_ip_version; + + auto status = sai_dash_acl_api->create_dash_acl_group(&group.m_dash_acl_group_id, gSwitchId, static_cast(attrs.size()), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create ACL group: %d, %s", status, sai_serialize_status(status).c_str()); + handleSaiCreateStatus((sai_api_t)SAI_API_DASH_ACL, status); + } + + CrmResourceType crm_rtype = (group.m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? + CrmResourceType::CRM_DASH_IPV4_ACL_GROUP : CrmResourceType::CRM_DASH_IPV6_ACL_GROUP; + gCrmOrch->incCrmDashAclUsedCounter(crm_rtype, group.m_dash_acl_group_id); +} + +task_process_status DashAclGroupMgr::create(const string& group_id, DashAclGroup& group) +{ + SWSS_LOG_ENTER(); + + if (exists(group_id)) + { + return task_failed; + } + + create(group); + + m_groups_table.emplace(group_id, group); + + SWSS_LOG_INFO("Created ACL group %s", group_id.c_str()); + + return task_success; +} + +void DashAclGroupMgr::remove(DashAclGroup& group) +{ + SWSS_LOG_ENTER(); + + if (group.m_dash_acl_group_id == SAI_NULL_OBJECT_ID) + { + return; + } + + sai_status_t status = sai_dash_acl_api->remove_dash_acl_group(group.m_dash_acl_group_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove ACL group: %d, %s", status, sai_serialize_status(status).c_str()); + handleSaiRemoveStatus((sai_api_t)SAI_API_DASH_ACL, status); + } + + CrmResourceType crm_rtype = (group.m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? + CrmResourceType::CRM_DASH_IPV4_ACL_GROUP : CrmResourceType::CRM_DASH_IPV6_ACL_GROUP; + gCrmOrch->decCrmDashAclUsedCounter(crm_rtype, group.m_dash_acl_group_id); + + group.m_dash_acl_group_id = SAI_NULL_OBJECT_ID; +} + +task_process_status DashAclGroupMgr::remove(const string& group_id) +{ + SWSS_LOG_ENTER(); + + auto group_it = m_groups_table.find(group_id); + if (group_it == m_groups_table.end()) + { + SWSS_LOG_INFO("ACL group %s doesn't exist", group_id.c_str()); + return task_success; + } + + auto& group = group_it->second; + + if (!group.m_dash_acl_rule_table.empty()) + { + SWSS_LOG_ERROR("ACL group %s still has %lu rules", group_id.c_str(), group.m_dash_acl_rule_table.size()); + return task_need_retry; + } + + if (isBound(group)) + { + SWSS_LOG_ERROR("ACL group %s still has %lu references", group_id.c_str(), group.m_in_tables.size() + group.m_out_tables.size()); + return task_need_retry; + } + + remove(group); + + m_groups_table.erase(group_id); + SWSS_LOG_INFO("Removed ACL group %s", group_id.c_str()); + + return task_success; +} + +bool DashAclGroupMgr::exists(const string& group_id) const +{ + SWSS_LOG_ENTER(); + + return m_groups_table.find(group_id) != m_groups_table.end(); +} + +void DashAclGroupMgr::onUpdate(const string& group_id, const string& tag_id, const DashTag& tag) +{ + SWSS_LOG_ENTER(); + + auto group_it = m_groups_table.find(group_id); + if (group_it == m_groups_table.end()) + { + return; + } + + auto& group = group_it->second; + if (isBound(group)) + { + // If the group is bound to at least one ENI refresh the full group to update the affected rules. + // When the group is bound to the ENI we need to make sure that the update of the affected rules will be atomic. + SWSS_LOG_INFO("Update full ACL group %s", group_id.c_str()); + + refreshAclGroupFull(group_id); + } + else + { + // If the group is not bound to ENI update the rule immediately. + SWSS_LOG_INFO("Update ACL group %s", group_id.c_str()); + for (auto& rule_it: group.m_dash_acl_rule_table) + { + auto& rule = rule_it.second; + if (rule.m_src_tags.find(tag_id) != rule.m_src_tags.end() || rule.m_dst_tags.find(tag_id) != rule.m_dst_tags.end()) + { + removeRule(group, rule); + createRule(group, rule); + } + } + } +} + +void DashAclGroupMgr::refreshAclGroupFull(const string &group_id) +{ + SWSS_LOG_ENTER(); + + auto& group = m_groups_table[group_id]; + + DashAclGroup new_group = group; + init(new_group); + create(new_group); + + for (auto& rule: new_group.m_dash_acl_rule_table) + { + createRule(new_group, rule.second); + } + + for (const auto& table: new_group.m_in_tables) + { + const auto& eni_id = table.first; + const auto& stages = table.second; + + const auto eni = m_dash_orch->getEni(eni_id); + ABORT_IF_NOT(eni != nullptr, "Failed to get ENI %s", eni_id.c_str()); + + for (const auto& stage: stages) + { + bind(new_group, *eni, DashAclDirection::IN, stage); + } + } + + for (const auto& table: new_group.m_out_tables) + { + const auto& eni_id = table.first; + const auto& stages = table.second; + + const auto eni = m_dash_orch->getEni(eni_id); + ABORT_IF_NOT(eni != nullptr, "Failed to get ENI %s", eni_id.c_str()); + + for (const auto& stage: stages) + { + bind(new_group, *eni, DashAclDirection::OUT, stage); + } + } + + removeAclGroupFull(group); + + group = new_group; +} + +void DashAclGroupMgr::removeAclGroupFull(DashAclGroup& group) +{ + SWSS_LOG_ENTER(); + + for (auto& rule: group.m_dash_acl_rule_table) + { + removeRule(group, rule.second); + } + + remove(group); +} + +void DashAclGroupMgr::createRule(DashAclGroup& group, DashAclRule& rule) +{ + SWSS_LOG_ENTER(); + + vector attrs; + vector src_prefixes = {}; + vector dst_prefixes = {}; + + auto any_ip = [] (const auto& g) + { + sai_ip_prefix_t ip_prefix = {}; + ip_prefix.addr_family = g.isIpV4() ? SAI_IP_ADDR_FAMILY_IPV4 : SAI_IP_ADDR_FAMILY_IPV6; + return ip_prefix; + }; + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_PRIORITY; + attrs.back().value.u32 = rule.m_priority; + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_ACTION; + + if (rule.m_action == DashAclRule::Action::ALLOW) + { + attrs.back().value.s32 = rule.m_terminating ? + SAI_DASH_ACL_RULE_ACTION_PERMIT : SAI_DASH_ACL_RULE_ACTION_PERMIT_AND_CONTINUE; + } + else + { + attrs.back().value.s32 = rule.m_terminating ? + SAI_DASH_ACL_RULE_ACTION_DENY : SAI_DASH_ACL_RULE_ACTION_DENY_AND_CONTINUE; + } + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_PROTOCOL; + + if (rule.m_protocols.size()) + { + attrs.back().value.u8list.count = static_cast(rule.m_protocols.size()); + attrs.back().value.u8list.list = rule.m_protocols.data(); + } + else + { + auto protocols = all_protocols; + attrs.back().value.u8list.count = static_cast(protocols.size()); + attrs.back().value.u8list.list = protocols.data(); + } + + if (!rule.m_src_prefixes.empty()) + { + src_prefixes.insert(src_prefixes.end(), + rule.m_src_prefixes.begin(), rule.m_src_prefixes.end()); + } + + if (!rule.m_dst_prefixes.empty()) + { + dst_prefixes.insert(dst_prefixes.end(), + rule.m_dst_prefixes.begin(), rule.m_dst_prefixes.end()); + } + + for (const auto &tag : rule.m_src_tags) + { + const auto& prefixes = m_dash_acl_orch->getDashAclTagMgr().getPrefixes(tag); + + src_prefixes.insert(src_prefixes.end(), + prefixes.begin(), prefixes.end()); + } + + for (const auto &tag : rule.m_dst_tags) + { + const auto& prefixes = m_dash_acl_orch->getDashAclTagMgr().getPrefixes(tag); + + dst_prefixes.insert(dst_prefixes.end(), + prefixes.begin(), prefixes.end()); + } + + if (src_prefixes.empty()) + { + src_prefixes.push_back(any_ip(group)); + } + + if (dst_prefixes.empty()) + { + dst_prefixes.push_back(any_ip(group)); + } + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_SIP; + attrs.back().value.ipprefixlist.count = static_cast(src_prefixes.size()); + attrs.back().value.ipprefixlist.list = src_prefixes.data(); + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_DIP; + attrs.back().value.ipprefixlist.count = static_cast(dst_prefixes.size()); + attrs.back().value.ipprefixlist.list = dst_prefixes.data(); + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_SRC_PORT; + attrs.back().value.u16rangelist.count = static_cast(rule.m_src_ports.size()); + attrs.back().value.u16rangelist.list = rule.m_src_ports.data(); + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_DST_PORT; + attrs.back().value.u16rangelist.count = static_cast(rule.m_dst_ports.size()); + attrs.back().value.u16rangelist.list = rule.m_dst_ports.data(); + + attrs.emplace_back(); + attrs.back().id = SAI_DASH_ACL_RULE_ATTR_DASH_ACL_GROUP_ID; + attrs.back().value.oid = group.m_dash_acl_group_id; + + auto status = sai_dash_acl_api->create_dash_acl_rule(&rule.m_dash_acl_rule_id, gSwitchId, static_cast(attrs.size()), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create ACL rule: %d, %s", status, sai_serialize_status(status).c_str()); + handleSaiCreateStatus((sai_api_t)SAI_API_DASH_ACL, status); + } + + CrmResourceType crm_rtype = (group.m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? + CrmResourceType::CRM_DASH_IPV4_ACL_RULE : CrmResourceType::CRM_DASH_IPV6_ACL_RULE; + gCrmOrch->incCrmDashAclUsedCounter(crm_rtype, group.m_dash_acl_group_id); +} + +task_process_status DashAclGroupMgr::createRule(const string& group_id, const string& rule_id, DashAclRule& rule) +{ + SWSS_LOG_ENTER(); + + auto group_it = m_groups_table.find(group_id); + if (group_it == m_groups_table.end()) + { + SWSS_LOG_INFO("ACL group %s doesn't exist, waiting for group creating before creating rule %s", group_id.c_str(), rule_id.c_str()); + return task_need_retry; + } + auto& group = group_it->second; + + auto acl_rule_it = group.m_dash_acl_rule_table.find(rule_id); + ABORT_IF_NOT(acl_rule_it == group.m_dash_acl_rule_table.end(), "Failed to create ACL rule %s. Rule already exist in ACL group %s", rule_id.c_str(), group_id.c_str()); + + createRule(group, rule); + + group.m_dash_acl_rule_table.emplace(rule_id, rule); + attachTags(group_id, rule.m_src_tags); + attachTags(group_id, rule.m_dst_tags); + + SWSS_LOG_INFO("Created ACL rule %s:%s", group_id.c_str(), rule_id.c_str()); + + return task_success; +} + +task_process_status DashAclGroupMgr::updateRule(const string& group_id, const string& rule_id, DashAclRule& rule) +{ + SWSS_LOG_ENTER(); + + if (isBound(group_id)) + { + SWSS_LOG_INFO("Failed to update dash ACL rule %s:%s, ACL group is bound to the ENI", group_id.c_str(), rule_id.c_str()); + return task_failed; + } + + if (ruleExists(group_id, rule_id)) + { + removeRule(group_id, rule_id); + } + + createRule(group_id, rule_id, rule); + + return task_success; +} + +void DashAclGroupMgr::removeRule(DashAclGroup& group, DashAclRule& rule) +{ + SWSS_LOG_ENTER(); + + if (rule.m_dash_acl_rule_id == SAI_NULL_OBJECT_ID) + { + return; + } + + // Remove the ACL group + auto status = sai_dash_acl_api->remove_dash_acl_rule(rule.m_dash_acl_rule_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove ACL rule: %d, %s", status, sai_serialize_status(status).c_str()); + handleSaiRemoveStatus((sai_api_t)SAI_API_DASH_ACL, status); + } + + CrmResourceType crm_resource = (group.m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? + CrmResourceType::CRM_DASH_IPV4_ACL_RULE : CrmResourceType::CRM_DASH_IPV6_ACL_RULE; + gCrmOrch->decCrmDashAclUsedCounter(crm_resource, group.m_dash_acl_group_id); + + rule.m_dash_acl_rule_id = SAI_NULL_OBJECT_ID; +} + +task_process_status DashAclGroupMgr::removeRule(const string& group_id, const string& rule_id) +{ + SWSS_LOG_ENTER(); + + if (!exists(group_id) || !ruleExists(group_id, rule_id)) + { + SWSS_LOG_INFO("ACL rule %s:%s does not exists", group_id.c_str(), rule_id.c_str()); + return task_success; + } + + auto& group = m_groups_table[group_id]; + if (isBound(group)) + { + SWSS_LOG_INFO("Failed to remove dash ACL rule %s:%s, ACL group is bound to the ENI", group_id.c_str(), rule_id.c_str()); + return task_need_retry; + } + + auto& rule = group.m_dash_acl_rule_table[rule_id]; + + removeRule(group, rule); + + detachTags(group_id, rule.m_src_tags); + detachTags(group_id, rule.m_dst_tags); + + group.m_dash_acl_rule_table.erase(rule_id); + + SWSS_LOG_INFO("Removed ACL rule %s:%s", group_id.c_str(), rule_id.c_str()); + + return task_success; +} + +void DashAclGroupMgr::bind(const DashAclGroup& group, const EniEntry& eni, DashAclDirection direction, DashAclStage stage) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = getSaiStage(direction, group.m_ip_version, stage); + attr.value.oid = group.m_dash_acl_group_id; + + auto status = sai_dash_eni_api->set_eni_attribute(eni.eni_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to bind ACL group to ENI: %d", status); + handleSaiSetStatus((sai_api_t)SAI_API_DASH_ENI, status); + } +} + +bool DashAclGroupMgr::ruleExists(const string& group_id, const string& rule_id) const +{ + SWSS_LOG_ENTER(); + + auto group_it = m_groups_table.find(group_id); + if (group_it == m_groups_table.end()) + { + return false; + } + + return group_it->second.m_dash_acl_rule_table.find(rule_id) != group_it->second.m_dash_acl_rule_table.end(); +} + +task_process_status DashAclGroupMgr::bind(const string& group_id, const string& eni_id, DashAclDirection direction, DashAclStage stage) +{ + SWSS_LOG_ENTER(); + + auto group_it = m_groups_table.find(group_id); + if (group_it == m_groups_table.end()) + { + SWSS_LOG_INFO("Failed to bind ACL group %s to ENI %s. ACL group does not exist", group_id.c_str(), eni_id.c_str()); + return task_need_retry; + } + + auto& group = group_it->second; + + if (group.m_dash_acl_rule_table.empty()) + { + SWSS_LOG_INFO("ACL group %s has no rules attached. Waiting for ACL rules creation", group_id.c_str()); + return task_need_retry; + } + + auto eni = m_dash_orch->getEni(eni_id); + if (!eni) + { + SWSS_LOG_INFO("eni %s cannot be found", eni_id.c_str()); + return task_need_retry; + } + + bind(group, *eni, direction, stage); + + auto& table = (direction == DashAclDirection::IN) ? group.m_in_tables : group.m_out_tables; + auto& eni_stages = table[eni_id]; + + eni_stages.insert(stage); + + SWSS_LOG_INFO("Bound ACL group %s to ENI %s", group_id.c_str(), eni_id.c_str()); + + return task_success; +} + +void DashAclGroupMgr::unbind(const DashAclGroup& group, const EniEntry& eni, DashAclDirection direction, DashAclStage stage) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = getSaiStage(direction, group.m_ip_version, stage); + attr.value.oid = SAI_NULL_OBJECT_ID; + + auto status = sai_dash_eni_api->set_eni_attribute(eni.eni_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to unbind ACL group from ENI: %d", status); + handleSaiSetStatus((sai_api_t)SAI_API_DASH_ENI, status); + } +} + +task_process_status DashAclGroupMgr::unbind(const string& group_id, const string& eni_id, DashAclDirection direction, DashAclStage stage) +{ + SWSS_LOG_ENTER(); + + auto group_it = m_groups_table.find(group_id); + if (group_it == m_groups_table.end()) + { + SWSS_LOG_INFO("ACL group %s does not exist", group_id.c_str()); + return task_success; + } + + auto& group = group_it->second; + + auto eni_entry = m_dash_orch->getEni(eni_id); + if (!eni_entry) + { + SWSS_LOG_INFO("eni %s cannot be found", eni_id.c_str()); + return task_success; + } + + auto& table = (direction == DashAclDirection::IN) ? group.m_in_tables : group.m_out_tables; + auto eni_it = table.find(eni_id); + if (eni_it == table.end()) + { + SWSS_LOG_INFO("ACL group %s is not bound to ENI %s", group_id.c_str(), eni_id.c_str()); + return task_success; + } + + auto& eni_stages = eni_it->second; + if (eni_stages.find(stage) == eni_stages.end()) + { + SWSS_LOG_INFO("ACL group %s is not bound to ENI %s stage %d", group_id.c_str(), eni_id.c_str(), static_cast(stage)); + return task_success; + } + + unbind(group, *eni_entry, direction, stage); + + eni_stages.erase(stage); + if (eni_stages.empty()) + { + table.erase(eni_it); + } + + return task_success; +} + +bool DashAclGroupMgr::isBound(const string &group_id) +{ + SWSS_LOG_ENTER(); + + if (!exists(group_id)) + { + return false; + } + + return isBound(m_groups_table[group_id]); +} + +bool DashAclGroupMgr::isBound(const DashAclGroup& group) +{ + SWSS_LOG_ENTER(); + + return !group.m_in_tables.empty() || !group.m_out_tables.empty(); +} + +void DashAclGroupMgr::attachTags(const string &group_id, const unordered_set& tags) +{ + SWSS_LOG_ENTER(); + + for (const auto& tag_id : tags) + { + m_dash_acl_orch->getDashAclTagMgr().attach(tag_id, group_id); + } +} + +void DashAclGroupMgr::detachTags(const string &group_id, const unordered_set& tags) +{ + SWSS_LOG_ENTER(); + + for (const auto& tag_id : tags) + { + m_dash_acl_orch->getDashAclTagMgr().detach(tag_id, group_id); + } +} diff --git a/orchagent/dash/dashaclgroupmgr.h b/orchagent/dash/dashaclgroupmgr.h new file mode 100644 index 0000000000..6ef9498cd2 --- /dev/null +++ b/orchagent/dash/dashaclgroupmgr.h @@ -0,0 +1,124 @@ +#pragma once + +#include + +#include +#include +#include + +#include "dashorch.h" +#include "dashtagmgr.h" + +#include "dash_api/acl_group.pb.h" +#include "dash_api/acl_rule.pb.h" +#include "dash_api/acl_in.pb.h" +#include "dash_api/acl_out.pb.h" + +enum class DashAclStage +{ + STAGE1, + STAGE2, + STAGE3, + STAGE4, + STAGE5, +}; + +enum class DashAclDirection +{ + IN, + OUT, +}; + +struct DashAclRule +{ + sai_object_id_t m_dash_acl_rule_id = SAI_NULL_OBJECT_ID; + + enum class Action + { + ALLOW, + DENY, + }; + + sai_uint32_t m_priority; + Action m_action; + bool m_terminating; + std::vector m_protocols; + std::vector m_src_prefixes; + std::vector m_dst_prefixes; + std::unordered_set m_src_tags; + std::unordered_set m_dst_tags; + std::vector m_src_ports; + std::vector m_dst_ports; +}; + +struct DashAclGroup +{ + using EniTable = std::unordered_map>; + using RuleTable = std::unordered_map; + using RuleKeys = std::unordered_set; + sai_object_id_t m_dash_acl_group_id = SAI_NULL_OBJECT_ID; + + std::string m_guid; + sai_ip_addr_family_t m_ip_version; + RuleTable m_dash_acl_rule_table; + + EniTable m_in_tables; + EniTable m_out_tables; + + bool isIpV4() const + { + return m_ip_version == SAI_IP_ADDR_FAMILY_IPV4; + } + + bool isIpV6() const + { + return m_ip_version == SAI_IP_ADDR_FAMILY_IPV6; + } +}; + +bool from_pb(const dash::acl_rule::AclRule& data, DashAclRule& rule); +bool from_pb(const dash::acl_group::AclGroup &data, DashAclGroup& group); + +class DashAclOrch; + +class DashAclGroupMgr +{ + DashOrch *m_dash_orch; + DashAclOrch *m_dash_acl_orch; + std::unordered_map m_groups_table; + +public: + DashAclGroupMgr(DashOrch *dashorch, DashAclOrch *aclorch); + + task_process_status create(const std::string& group_id, DashAclGroup& group); + task_process_status remove(const std::string& group_id); + bool exists(const std::string& group_id) const; + + void onUpdate(const std::string& group_id, const std::string& tag_id,const DashTag& tag); + + task_process_status createRule(const std::string& group_id, const std::string& rule_id, DashAclRule& rule); + task_process_status updateRule(const std::string& group_id, const std::string& rule_id, DashAclRule& rule); + task_process_status removeRule(const std::string& group_id, const std::string& rule_id); + bool ruleExists(const std::string& group_id, const std::string& rule_id) const; + + task_process_status bind(const std::string& group_id, const std::string& eni_id, DashAclDirection direction, DashAclStage stage); + task_process_status unbind(const std::string& group_id, const std::string& eni_id, DashAclDirection direction, DashAclStage stage); + +private: + void init(DashAclGroup& group); + void create(DashAclGroup& group); + void remove(DashAclGroup& group); + + void createRule(DashAclGroup& group, DashAclRule& rule); + void removeRule(DashAclGroup& group, DashAclRule& rule); + + void bind(const DashAclGroup& group, const EniEntry& eni, DashAclDirection direction, DashAclStage stage); + void unbind(const DashAclGroup& group, const EniEntry& eni, DashAclDirection direction, DashAclStage stage); + bool isBound(const std::string &group_id); + bool isBound(const DashAclGroup& group); + void attachTags(const std::string &group_id, const std::unordered_set& tags); + void detachTags(const std::string &group_id, const std::unordered_set& tags); + + void refreshAclGroupFull(const std::string &group_id); + void removeAclGroupFull(DashAclGroup& group); +}; diff --git a/orchagent/dash/dashaclorch.cpp b/orchagent/dash/dashaclorch.cpp index 31022dc4a4..38d9c5a083 100644 --- a/orchagent/dash/dashaclorch.cpp +++ b/orchagent/dash/dashaclorch.cpp @@ -5,14 +5,12 @@ #include -#include -#include - #include "dashaclorch.h" #include "taskworker.h" #include "pbutils.h" #include "crmorch.h" -#include "dashaclorch.h" +#include "dashaclgroupmgr.h" +#include "dashtagmgr.h" #include "saihelper.h" using namespace std; @@ -21,18 +19,12 @@ using namespace dash::acl_in; using namespace dash::acl_out; using namespace dash::acl_rule; using namespace dash::acl_group; +using namespace dash::tag; using namespace dash::types; -extern sai_dash_acl_api_t* sai_dash_acl_api; -extern sai_dash_eni_api_t* sai_dash_eni_api; -extern sai_object_id_t gSwitchId; -extern CrmOrch *gCrmOrch; - template static bool extractVariables(const string &input, char delimiter, T &output, Args &... args) { - SWSS_LOG_ENTER(); - const auto tokens = swss::tokenize(input, delimiter); try { @@ -45,49 +37,63 @@ static bool extractVariables(const string &input, char delimiter, T &output, Arg } } -sai_attr_id_t getSaiStage(DashAclDirection d, sai_ip_addr_family_t f, uint32_t s) +namespace swss { + +template<> +inline void lexical_convert(const string &buffer, DashAclStage &stage) { - const static map, sai_attr_id_t> StageMaps = - { - {{IN, SAI_IP_ADDR_FAMILY_IPV4, 1}, SAI_ENI_ATTR_INBOUND_V4_STAGE1_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV4, 2}, SAI_ENI_ATTR_INBOUND_V4_STAGE2_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV4, 3}, SAI_ENI_ATTR_INBOUND_V4_STAGE3_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV4, 4}, SAI_ENI_ATTR_INBOUND_V4_STAGE4_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV4, 5}, SAI_ENI_ATTR_INBOUND_V4_STAGE5_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV6, 1}, SAI_ENI_ATTR_INBOUND_V6_STAGE1_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV6, 2}, SAI_ENI_ATTR_INBOUND_V6_STAGE2_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV6, 3}, SAI_ENI_ATTR_INBOUND_V6_STAGE3_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV6, 4}, SAI_ENI_ATTR_INBOUND_V6_STAGE4_DASH_ACL_GROUP_ID}, - {{IN, SAI_IP_ADDR_FAMILY_IPV6, 5}, SAI_ENI_ATTR_INBOUND_V6_STAGE5_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV4, 1}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE1_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV4, 2}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE2_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV4, 3}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE3_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV4, 4}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE4_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV4, 5}, SAI_ENI_ATTR_OUTBOUND_V4_STAGE5_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV6, 1}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE1_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV6, 2}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE2_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV6, 3}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE3_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV6, 4}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE4_DASH_ACL_GROUP_ID}, - {{OUT, SAI_IP_ADDR_FAMILY_IPV6, 5}, SAI_ENI_ATTR_OUTBOUND_V6_STAGE5_DASH_ACL_GROUP_ID}, - }; - - auto stage = StageMaps.find({d, f, s}); - if (stage == StageMaps.end()) + SWSS_LOG_ENTER(); + + if (buffer == "1") + { + stage = DashAclStage::STAGE1; + } + else if (buffer == "2") + { + stage = DashAclStage::STAGE2; + } + else if (buffer == "3") { - SWSS_LOG_WARN("Invalid stage %d %d %d", d, f, s); - throw runtime_error("Invalid stage"); + stage = DashAclStage::STAGE3; } + else if (buffer == "4") + { + stage = DashAclStage::STAGE4; + } + else if (buffer == "5") + { + stage = DashAclStage::STAGE5; + } + else + { + SWSS_LOG_ERROR("Invalid stage : %s", buffer.c_str()); + throw invalid_argument("Invalid stage"); + } + +} - return stage->second; } DashAclOrch::DashAclOrch(DBConnector *db, const vector &tables, DashOrch *dash_orch, ZmqServer *zmqServer) : ZmqOrch(db, tables, zmqServer), - m_dash_orch(dash_orch) + m_dash_orch(dash_orch), + m_group_mgr(dash_orch, this), + m_tag_mgr(this) + { SWSS_LOG_ENTER(); +} - assert(m_dash_orch); +DashAclGroupMgr& DashAclOrch::getDashAclGroupMgr() +{ + SWSS_LOG_ENTER(); + return m_group_mgr; +} + +DashTagMgr& DashAclOrch::getDashAclTagMgr() +{ + SWSS_LOG_ENTER(); + return m_tag_mgr; } void DashAclOrch::doTask(ConsumerBase &consumer) @@ -103,7 +109,9 @@ void DashAclOrch::doTask(ConsumerBase &consumer) KeyOnlyWorker::makeMemberTask(APP_DASH_ACL_GROUP_TABLE_NAME, DEL_COMMAND, &DashAclOrch::taskRemoveDashAclGroup, this), PbWorker::makeMemberTask(APP_DASH_ACL_RULE_TABLE_NAME, SET_COMMAND, &DashAclOrch::taskUpdateDashAclRule, this), KeyOnlyWorker::makeMemberTask(APP_DASH_ACL_RULE_TABLE_NAME, DEL_COMMAND, &DashAclOrch::taskRemoveDashAclRule, this), - }; + PbWorker::makeMemberTask(APP_DASH_PREFIX_TAG_TABLE_NAME, SET_COMMAND, &DashAclOrch::taskUpdateDashPrefixTag, this), + KeyOnlyWorker::makeMemberTask(APP_DASH_PREFIX_TAG_TABLE_NAME, DEL_COMMAND, &DashAclOrch::taskRemoveDashPrefixTag, this), + }; const string &table_name = consumer.getTableName(); auto itr = consumer.m_toSync.begin(); @@ -161,23 +169,19 @@ task_process_status DashAclOrch::taskUpdateDashAclIn( { SWSS_LOG_ENTER(); - task_process_status v4_status = task_success, v6_status = task_success; - if (!data.v4_acl_group_id().empty()) - { - v4_status = bindAclToEni(m_dash_acl_in_table, key, data.v4_acl_group_id()); - } - if (v4_status != task_success) - { - return v4_status; - } - if (!data.v6_acl_group_id().empty()) + for (const auto& gid: { data.v4_acl_group_id(), data.v6_acl_group_id() }) { - v6_status = bindAclToEni(m_dash_acl_in_table, key, data.v6_acl_group_id()); - } - if (v6_status != task_success) - { - return v6_status; + if (gid.empty()) + { + continue; + } + auto status = bindAclToEni(DashAclDirection::IN, key, gid); + if (status != task_success) + { + return status; + } } + return task_success; } @@ -186,7 +190,7 @@ task_process_status DashAclOrch::taskRemoveDashAclIn( { SWSS_LOG_ENTER(); - return unbindAclFromEni(m_dash_acl_in_table, key); + return unbindAclFromEni(DashAclDirection::IN, key); } task_process_status DashAclOrch::taskUpdateDashAclOut( @@ -195,23 +199,19 @@ task_process_status DashAclOrch::taskUpdateDashAclOut( { SWSS_LOG_ENTER(); - task_process_status v4_status = task_success, v6_status = task_success; - if (!data.v4_acl_group_id().empty()) - { - v4_status = bindAclToEni(m_dash_acl_out_table, key, data.v4_acl_group_id()); - } - if (v4_status != task_success) - { - return v4_status; - } - if (!data.v6_acl_group_id().empty()) - { - v6_status = bindAclToEni(m_dash_acl_out_table, key, data.v6_acl_group_id()); - } - if (v6_status != task_success) + for (const auto& gid: { data.v4_acl_group_id(), data.v6_acl_group_id() }) { - return v6_status; + if (gid.empty()) + { + continue; + } + auto status = bindAclToEni(DashAclDirection::OUT, key, gid); + if (status != task_success) + { + return status; + } } + return task_success; } @@ -220,7 +220,7 @@ task_process_status DashAclOrch::taskRemoveDashAclOut( { SWSS_LOG_ENTER(); - return unbindAclFromEni(m_dash_acl_out_table, key); + return unbindAclFromEni(DashAclDirection::OUT, key); } task_process_status DashAclOrch::taskUpdateDashAclGroup( @@ -229,40 +229,19 @@ task_process_status DashAclOrch::taskUpdateDashAclGroup( { SWSS_LOG_ENTER(); - if (m_dash_acl_group_table.find(key) != m_dash_acl_group_table.end()) + if (m_group_mgr.exists(key)) { - // Update the ACL group's attributes SWSS_LOG_WARN("Cannot update attributes of ACL group %s", key.c_str()); return task_failed; } - sai_ip_addr_family_t ip_version = data.ip_version() == IpVersion::IP_VERSION_IPV4 ? SAI_IP_ADDR_FAMILY_IPV4 : SAI_IP_ADDR_FAMILY_IPV6; - vector attrs; - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY; - attrs.back().value.s32 = ip_version; - - // Guid wasn't mapping to any SAI attributes - - // Create a new ACL group - DashAclGroupEntry acl_group; - sai_status_t status = sai_dash_acl_api->create_dash_acl_group(&acl_group.m_dash_acl_group_id, gSwitchId, static_cast(attrs.size()), attrs.data()); - if (status != SAI_STATUS_SUCCESS) + DashAclGroup group = {}; + if (!from_pb(data, group)) { - SWSS_LOG_WARN("Failed to create ACL group %s, rv: %s", key.c_str(), sai_serialize_status(status).c_str()); return task_failed; } - acl_group.m_rule_count = 0; - acl_group.m_ref_count = 0; - acl_group.m_ip_version = ip_version; - m_dash_acl_group_table.emplace(key, acl_group); - SWSS_LOG_NOTICE("Created ACL group %s", key.c_str()); - - CrmResourceType crm_rtype = (acl_group.m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? - CrmResourceType::CRM_DASH_IPV4_ACL_GROUP : CrmResourceType::CRM_DASH_IPV6_ACL_GROUP; - gCrmOrch->incCrmDashAclUsedCounter(crm_rtype, acl_group.m_dash_acl_group_id); - return task_success; + return m_group_mgr.create(key, group); } task_process_status DashAclOrch::taskRemoveDashAclGroup( @@ -270,43 +249,7 @@ task_process_status DashAclOrch::taskRemoveDashAclGroup( { SWSS_LOG_ENTER(); - auto acl_group = getAclGroup(key); - - if (acl_group == nullptr || acl_group->m_dash_acl_group_id == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_WARN("ACL group %s doesn't exist", key.c_str()); - return task_success; - } - - // The member rules of group should be removed first - if (acl_group->m_rule_count != 0) - { - SWSS_LOG_INFO("ACL group %s still has %d rules", key.c_str(), acl_group->m_rule_count); - return task_need_retry; - } - - // The refer count of group should be cleaned first - if (acl_group->m_ref_count != 0) - { - SWSS_LOG_INFO("ACL group %s still has %d references", key.c_str(), acl_group->m_ref_count); - return task_need_retry; - } - - // Remove the ACL group - sai_status_t status = sai_dash_acl_api->remove_dash_acl_group(acl_group->m_dash_acl_group_id); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_WARN("Failed to remove ACL group %s, rv: %s", key.c_str(), sai_serialize_status(status).c_str()); - return task_failed; - } - CrmResourceType crm_rtype = (acl_group->m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? - CrmResourceType::CRM_DASH_IPV4_ACL_GROUP : CrmResourceType::CRM_DASH_IPV6_ACL_GROUP; - gCrmOrch->decCrmDashAclUsedCounter(crm_rtype, acl_group->m_dash_acl_group_id); - - m_dash_acl_group_table.erase(key); - SWSS_LOG_NOTICE("Removed ACL group %s", key.c_str()); - - return task_success; + return m_group_mgr.remove(key); } task_process_status DashAclOrch::taskUpdateDashAclRule( @@ -318,183 +261,25 @@ task_process_status DashAclOrch::taskUpdateDashAclRule( string group_id, rule_id; if (!extractVariables(key, ':', group_id, rule_id)) { - SWSS_LOG_WARN("Failed to parse key %s", key.c_str()); + SWSS_LOG_ERROR("Failed to parse key %s", key.c_str()); return task_failed; } - auto acl_group = getAclGroup(group_id); - if (acl_group == nullptr) - { - SWSS_LOG_INFO("ACL group %s doesn't exist, waiting for group creating before creating rule %s", group_id.c_str(), rule_id.c_str()); - return task_need_retry; - } - - if (m_dash_acl_rule_table.find(key) != m_dash_acl_rule_table.end()) - { - // Remove the old ACL rule - auto status = taskRemoveDashAclRule(key); - if (status != task_success) - { - return status; - } - } - - vector attrs; - - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_PRIORITY; - attrs.back().value.u32 = data.priority(); + DashAclRule rule = {}; - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_ACTION; - if (data.action() == Action::ACTION_PERMIT) - { - if (data.terminating()) - { - attrs.back().value.s32 = SAI_DASH_ACL_RULE_ACTION_PERMIT; - } - else - { - attrs.back().value.s32 = SAI_DASH_ACL_RULE_ACTION_PERMIT_AND_CONTINUE; - } - } - else + if (!from_pb(data, rule)) { - if (data.terminating()) - { - attrs.back().value.s32 = SAI_DASH_ACL_RULE_ACTION_DENY; - } - else - { - attrs.back().value.s32 = SAI_DASH_ACL_RULE_ACTION_DENY_AND_CONTINUE; - } + return task_failed; } - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_PROTOCOL; - vector protocols; - if (data.protocol_size() == 0) + if (m_group_mgr.ruleExists(group_id, rule_id)) { - const static vector ALL_PROTOCOLS = [](){ - vector protocols; - for (uint16_t i = 0; i <= 255; i++) - { - protocols.push_back(static_cast(i)); - } - return protocols; - }(); - attrs.back().value.u8list.count = static_cast(ALL_PROTOCOLS.size()); - attrs.back().value.u8list.list = const_cast(ALL_PROTOCOLS.data()); + return m_group_mgr.updateRule(group_id, rule_id, rule); } else { - protocols.reserve(data.protocol_size()); - protocols.assign(data.protocol().begin(), data.protocol().end()); - attrs.back().value.u8list.count = static_cast(protocols.size()); - attrs.back().value.u8list.list = protocols.data(); + return m_group_mgr.createRule(group_id, rule_id, rule); } - - - const static sai_ip_prefix_t SAI_ALL_IPV4_PREFIX = [](){ - sai_ip_prefix_t ip_prefix; - ip_prefix.addr_family = SAI_IP_ADDR_FAMILY_IPV4; - ip_prefix.addr.ip4 = 0; - ip_prefix.mask.ip4 = 0; - return ip_prefix; - }(); - - const static sai_ip_prefix_t SAI_ALL_IPV6_PREFIX = [](){ - sai_ip_prefix_t ip_prefix; - ip_prefix.addr_family = SAI_IP_ADDR_FAMILY_IPV6; - memset(ip_prefix.addr.ip6, 0, sizeof(ip_prefix.addr.ip6)); - memset(ip_prefix.mask.ip6, 0, sizeof(ip_prefix.mask.ip6)); - return ip_prefix; - }(); - - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_SIP; - vector src_prefixes; - if (data.src_addr_size() == 0) - { - src_prefixes.push_back( - acl_group->m_ip_version == SAI_IP_ADDR_FAMILY_IPV4 ? - SAI_ALL_IPV4_PREFIX : - SAI_ALL_IPV6_PREFIX); - } - else if (!to_sai(data.src_addr(), src_prefixes)) - { - return task_invalid_entry; - } - attrs.back().value.ipprefixlist.count = static_cast(src_prefixes.size()); - attrs.back().value.ipprefixlist.list = src_prefixes.data(); - - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_DIP; - vector dst_prefixes; - if (data.src_addr_size() == 0) - { - dst_prefixes.push_back( - acl_group->m_ip_version == SAI_IP_ADDR_FAMILY_IPV4 ? - SAI_ALL_IPV4_PREFIX : - SAI_ALL_IPV6_PREFIX); - } - else if (!to_sai(data.src_addr(), dst_prefixes)) - { - return task_invalid_entry; - } - attrs.back().value.ipprefixlist.count = static_cast(dst_prefixes.size()); - attrs.back().value.ipprefixlist.list = dst_prefixes.data(); - - const static sai_u16_range_t SAI_ALL_PORTS{numeric_limits::min(), numeric_limits::max()}; - - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_SRC_PORT; - vector src_ports; - if (data.src_port_size() == 0) - { - src_ports.push_back(SAI_ALL_PORTS); - } - else if (!to_sai(data.src_port(), src_ports)) - { - return task_invalid_entry; - } - attrs.back().value.u16rangelist.count = static_cast(src_ports.size()); - attrs.back().value.u16rangelist.list = src_ports.data(); - - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_DST_PORT; - vector dst_ports; - if (data.dst_port_size() == 0) - { - dst_ports.push_back(SAI_ALL_PORTS); - } - else if (!to_sai(data.dst_port(), dst_ports)) - { - return task_invalid_entry; - } - attrs.back().value.u16rangelist.count = static_cast(dst_ports.size()); - attrs.back().value.u16rangelist.list = dst_ports.data(); - - attrs.emplace_back(); - attrs.back().id = SAI_DASH_ACL_RULE_ATTR_DASH_ACL_GROUP_ID; - attrs.back().value.oid = acl_group->m_dash_acl_group_id; - - DashAclRuleEntry acl_rule; - sai_status_t status = sai_dash_acl_api->create_dash_acl_rule(&acl_rule.m_dash_acl_rule_id, gSwitchId, static_cast(attrs.size()), attrs.data()); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_WARN("Failed to create dash ACL rule %s, rv: %s", key.c_str(), sai_serialize_status(status).c_str()); - return task_failed; - } - m_dash_acl_rule_table.emplace(key, acl_rule); - acl_group->m_rule_count++; - SWSS_LOG_NOTICE("Created ACL rule %s", key.c_str()); - - CrmResourceType crm_rtype = (acl_group->m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? - CrmResourceType::CRM_DASH_IPV4_ACL_RULE : CrmResourceType::CRM_DASH_IPV6_ACL_RULE; - gCrmOrch->incCrmDashAclUsedCounter(crm_rtype, acl_group->m_dash_acl_group_id); - - return task_success; } task_process_status DashAclOrch::taskRemoveDashAclRule( @@ -505,197 +290,100 @@ task_process_status DashAclOrch::taskRemoveDashAclRule( string group_id, rule_id; if (!extractVariables(key, ':', group_id, rule_id)) { - SWSS_LOG_WARN("Failed to parse key %s", key.c_str()); + SWSS_LOG_ERROR("Failed to parse key %s", key.c_str()); return task_failed; } - auto &acl_group = m_dash_acl_group_table[group_id]; + return m_group_mgr.removeRule(group_id, rule_id); +} + +task_process_status DashAclOrch::taskUpdateDashPrefixTag( + const std::string &tag_id, + const PrefixTag &data) +{ + SWSS_LOG_ENTER(); - auto itr = m_dash_acl_rule_table.find(key); + DashTag tag = {}; - if (itr == m_dash_acl_rule_table.end()) + if (!from_pb(data, tag)) { - SWSS_LOG_WARN("ACL rule %s doesn't exist", key.c_str()); - return task_success; + return task_failed; } - auto &acl_rule = itr->second; - - bool is_existing = acl_rule.m_dash_acl_rule_id != SAI_NULL_OBJECT_ID; - - if (!is_existing) + if (m_tag_mgr.exists(tag_id)) { - SWSS_LOG_WARN("ACL rule %s doesn't exist", key.c_str()); - return task_success; + return m_tag_mgr.update(tag_id, tag); } - - // Remove the ACL group - sai_status_t status = sai_dash_acl_api->remove_dash_acl_rule(acl_rule.m_dash_acl_rule_id); - if (status != SAI_STATUS_SUCCESS) + else { - SWSS_LOG_WARN("Failed to remove dash ACL rule %s, rv: %s", key.c_str(), sai_serialize_status(status).c_str()); - return task_failed; + return m_tag_mgr.create(tag_id, tag); } - m_dash_acl_rule_table.erase(itr); - --acl_group.m_rule_count; - - CrmResourceType crm_resource = (acl_group.m_ip_version == SAI_IP_ADDR_FAMILY_IPV4) ? - CrmResourceType::CRM_DASH_IPV4_ACL_RULE : CrmResourceType::CRM_DASH_IPV6_ACL_RULE; - gCrmOrch->decCrmDashAclUsedCounter(crm_resource, acl_group.m_dash_acl_group_id); - - SWSS_LOG_NOTICE("Removed ACL rule %s", key.c_str()); - - return task_success; } -DashAclGroupEntry* DashAclOrch::getAclGroup(const string &group_id) +task_process_status DashAclOrch::taskRemoveDashPrefixTag( + const std::string &key) { SWSS_LOG_ENTER(); - auto itr = m_dash_acl_group_table.find(group_id); - - if (itr != m_dash_acl_group_table.end() && itr->second.m_dash_acl_group_id != SAI_NULL_OBJECT_ID) - { - return &itr->second; - } - else - { - return nullptr; - } + return m_tag_mgr.remove(key); } -task_process_status DashAclOrch::bindAclToEni(DashAclBindTable &acl_bind_table, const string &key, const string &acl_group_id) +task_process_status DashAclOrch::bindAclToEni(DashAclDirection direction, const std::string table_id, const std::string &acl_group_id) { SWSS_LOG_ENTER(); - assert(&acl_bind_table == &m_dash_acl_in_table || &acl_bind_table == &m_dash_acl_out_table); - DashAclDirection direction = ((&acl_bind_table == &m_dash_acl_in_table) ? DashAclDirection::IN : DashAclDirection::OUT); - string eni; - uint32_t stage; - if (!extractVariables(key, ':', eni, stage)) - { - SWSS_LOG_WARN("Invalid key : %s", key.c_str()); - return task_failed; - } + DashAclStage stage; - if (acl_group_id.empty()) + if (!extractVariables(table_id, ':', eni, stage)) { - SWSS_LOG_WARN("Empty group id in the key : %s", key.c_str()); + SWSS_LOG_ERROR("Invalid key : %s", table_id.c_str()); return task_failed; } - auto eni_entry = m_dash_orch->getEni(eni); - if (eni_entry == nullptr) - { - SWSS_LOG_INFO("eni %s cannot be found", eni.c_str()); - // The ENI may not be created yet, so we will wait for the ENI to be created - return task_need_retry; - } - - auto &acl_bind = acl_bind_table[key]; - - auto acl_group = getAclGroup(acl_group_id); - if (acl_group == nullptr) - { - SWSS_LOG_INFO("acl group %s cannot be found, wait for create", acl_group_id.c_str()); - return task_need_retry; - } - - if (acl_group->m_rule_count <= 0) - { - SWSS_LOG_INFO("acl group %s contains 0 rules, waiting for rule creation", acl_group_id.c_str()); - return task_need_retry; - } - - if (acl_bind.m_acl_group_id == acl_group_id) - { - SWSS_LOG_INFO("acl group %s is already bound to %s", acl_group_id.c_str(), key.c_str()); - return task_success; - } - else if (!acl_bind.m_acl_group_id.empty()) - { - auto old_acl_group = getAclGroup(acl_bind.m_acl_group_id); - if (old_acl_group != nullptr) - { - old_acl_group->m_ref_count--; - } - else - { - SWSS_LOG_WARN("Failed to find old acl group %s", acl_bind.m_acl_group_id.c_str()); - } - } - acl_bind.m_acl_group_id = acl_group_id; - - sai_attribute_t attr; - attr.id = getSaiStage(direction, acl_group->m_ip_version, stage); - attr.value.oid = acl_group->m_dash_acl_group_id; + DashAclEntry table = { .m_acl_group_id = acl_group_id }; - sai_status_t status = sai_dash_eni_api->set_eni_attribute(eni_entry->eni_id, &attr); - if (status != SAI_STATUS_SUCCESS) + auto rv = m_group_mgr.bind(table.m_acl_group_id, eni, direction, stage); + if (rv != task_success) { - SWSS_LOG_WARN("Failed to bind ACL %s to eni %s attribute, status : %s", key.c_str(), acl_group_id.c_str(), sai_serialize_status(status).c_str()); - return task_failed; + return rv; } - acl_group->m_ref_count++; - SWSS_LOG_NOTICE("Bind ACL group %s to %s", acl_group_id.c_str(), key.c_str()); + DashAclTable& tables = (direction == DashAclDirection::IN) ? m_dash_acl_in_table : m_dash_acl_out_table; + tables[table_id] = table; - return task_success; + return rv; } -task_process_status DashAclOrch::unbindAclFromEni(DashAclBindTable &acl_bind_table, const string &key) +task_process_status DashAclOrch::unbindAclFromEni(DashAclDirection direction, const std::string table_id) { SWSS_LOG_ENTER(); - assert(&acl_bind_table == &m_dash_acl_in_table || &acl_bind_table == &m_dash_acl_out_table); - DashAclDirection direction = ((&acl_bind_table == &m_dash_acl_in_table) ? DashAclDirection::IN : DashAclDirection::OUT); - string eni; - uint32_t stage; - if (!extractVariables(key, ':', eni, stage)) + DashAclStage stage; + if (!extractVariables(table_id, ':', eni, stage)) { - SWSS_LOG_WARN("Invalid key : %s", key.c_str()); + SWSS_LOG_ERROR("Invalid key : %s", table_id.c_str()); return task_failed; } - auto eni_entry = m_dash_orch->getEni(eni); - if (eni_entry == nullptr) - { - SWSS_LOG_WARN("eni %s cannot be found", eni.c_str()); - return task_failed; - } + DashAclTable& acl_table = (direction == DashAclDirection::IN) ? m_dash_acl_in_table : m_dash_acl_out_table; - auto itr = acl_bind_table.find(key); - if (itr == acl_bind_table.end() || itr->second.m_acl_group_id.empty()) + auto itr = acl_table.find(table_id); + if (itr == acl_table.end()) { - SWSS_LOG_WARN("ACL %s doesn't exist", key.c_str()); + SWSS_LOG_WARN("ACL %s doesn't exist", table_id.c_str()); return task_success; } - auto acl_bind = itr->second; - acl_bind_table.erase(itr); - - auto acl_group = getAclGroup(acl_bind.m_acl_group_id); - if (acl_group == nullptr) - { - SWSS_LOG_WARN("Invalid acl group id : %s", acl_bind.m_acl_group_id.c_str()); - return task_failed; - } - - sai_attribute_t attr; - attr.id = getSaiStage(direction, acl_group->m_ip_version, stage); - attr.value.oid = SAI_NULL_OBJECT_ID; + auto acl = itr->second; - sai_status_t status = sai_dash_eni_api->set_eni_attribute(eni_entry->eni_id, &attr); - if (status != SAI_STATUS_SUCCESS) + auto rv = m_group_mgr.unbind(acl.m_acl_group_id, eni, direction, stage); + if (rv != task_success) { - SWSS_LOG_WARN("Failed to unbind ACL %s to eni %s attribute, status : %s", key.c_str(), acl_bind.m_acl_group_id.c_str(), sai_serialize_status(status).c_str()); - return task_failed; + return rv; } - acl_group->m_ref_count--; - - SWSS_LOG_NOTICE("Unbind ACL group %s from %s", acl_bind.m_acl_group_id.c_str(), key.c_str()); + acl_table.erase(itr); - return task_success; + return rv; } diff --git a/orchagent/dash/dashaclorch.h b/orchagent/dash/dashaclorch.h index b3d417e9cd..b3859c5c2d 100644 --- a/orchagent/dash/dashaclorch.h +++ b/orchagent/dash/dashaclorch.h @@ -17,35 +17,18 @@ #include "zmqserver.h" #include "dashorch.h" +#include "dashaclgroupmgr.h" +#include "dashtagmgr.h" #include "dash_api/acl_group.pb.h" #include "dash_api/acl_rule.pb.h" #include "dash_api/acl_in.pb.h" #include "dash_api/acl_out.pb.h" -typedef enum _DashAclDirection -{ - IN, - OUT, -} DashAclDirection; - -struct DashAclBindEntry { +struct DashAclEntry { std::string m_acl_group_id; }; -struct DashAclGroupEntry { - sai_object_id_t m_dash_acl_group_id; - size_t m_ref_count; - size_t m_rule_count; - sai_ip_addr_family_t m_ip_version; -}; - -struct DashAclRuleEntry { - sai_object_id_t m_dash_acl_rule_id; -}; - -using DashAclBindTable = std::unordered_map; -using DashAclGroupTable = std::unordered_map; -using DashAclRuleTable = std::unordered_map; +using DashAclTable = std::unordered_map; class DashAclOrch : public ZmqOrch { @@ -53,6 +36,8 @@ class DashAclOrch : public ZmqOrch using TaskArgs = std::vector; DashAclOrch(swss::DBConnector *db, const std::vector &tables, DashOrch *dash_orch, swss::ZmqServer *zmqServer); + DashAclGroupMgr& getDashAclGroupMgr(); + DashTagMgr& getDashAclTagMgr(); private: void doTask(ConsumerBase &consumer); @@ -81,20 +66,26 @@ class DashAclOrch : public ZmqOrch task_process_status taskRemoveDashAclRule( const std::string &key); - DashAclGroupEntry* getAclGroup(const std::string &group_id); + task_process_status taskUpdateDashPrefixTag( + const std::string &key, + const dash::tag::PrefixTag &data); + + task_process_status taskRemoveDashPrefixTag( + const std::string &key); task_process_status bindAclToEni( - DashAclBindTable &acl_table, - const std::string &key, + DashAclDirection direction, + const std::string table_id, const std::string &acl_group_id); task_process_status unbindAclFromEni( - DashAclBindTable &acl_table, - const std::string &key); + DashAclDirection direction, + const std::string table_id); + + DashAclTable m_dash_acl_in_table; + DashAclTable m_dash_acl_out_table; - DashAclBindTable m_dash_acl_in_table; - DashAclBindTable m_dash_acl_out_table; - DashAclGroupTable m_dash_acl_group_table; - DashAclRuleTable m_dash_acl_rule_table; + DashAclGroupMgr m_group_mgr; + DashTagMgr m_tag_mgr; DashOrch *m_dash_orch; }; diff --git a/orchagent/dash/dashtagmgr.cpp b/orchagent/dash/dashtagmgr.cpp new file mode 100644 index 0000000000..834442395b --- /dev/null +++ b/orchagent/dash/dashtagmgr.cpp @@ -0,0 +1,150 @@ +#include "dashtagmgr.h" + +#include "dashaclorch.h" +#include "saihelper.h" + +using namespace std; +using namespace swss; + +bool from_pb(const dash::tag::PrefixTag& data, DashTag& tag) +{ + if (!to_sai(data.ip_version(), tag.m_ip_version)) + { + return false; + } + + if(!to_sai(data.prefix_list(), tag.m_prefixes)) + { + return false; + } + + return true; +} + +DashTagMgr::DashTagMgr(DashAclOrch *aclorch) : + m_dash_acl_orch(aclorch) +{ + SWSS_LOG_ENTER(); +} + +task_process_status DashTagMgr::create(const string& tag_id, const DashTag& tag) +{ + SWSS_LOG_ENTER(); + + if (exists(tag_id)) + { + return task_failed; + } + + m_tag_table.emplace(tag_id, tag); + + SWSS_LOG_INFO("Created prefix tag %s", tag_id.c_str()); + + return task_success; +} + +task_process_status DashTagMgr::update(const string& tag_id, const DashTag& new_tag) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("Updating existing prefix tag %s", tag_id.c_str()); + + auto tag_it = m_tag_table.find(tag_id); + if (tag_it == m_tag_table.end()) + { + SWSS_LOG_ERROR("Prefix tag %s does not exist ", tag_id.c_str()); + return task_failed; + } + + auto& tag = tag_it->second; + + if (tag.m_ip_version != new_tag.m_ip_version) + { + SWSS_LOG_WARN("'ip_version' changing is not supported for tag %s", tag_id.c_str()); + return task_failed; + } + + // Update tag prefixes + tag.m_prefixes = new_tag.m_prefixes; + + for (auto& group_it: tag.m_group_refcnt) + { + const auto& group_id = group_it.first; + m_dash_acl_orch->getDashAclGroupMgr().onUpdate(group_id, tag_id, tag); + } + + return task_success; +} + +task_process_status DashTagMgr::remove(const string& tag_id) +{ + SWSS_LOG_ENTER(); + + auto tag_it = m_tag_table.find(tag_id); + if (tag_it == m_tag_table.end()) + { + SWSS_LOG_WARN("Prefix tag %s does not exist ", tag_id.c_str()); + return task_success; + } + + if (!tag_it->second.m_group_refcnt.empty()) + { + SWSS_LOG_WARN("Prefix tag %s is still in use by ACL rule(s)", tag_id.c_str()); + return task_need_retry; + } + + m_tag_table.erase(tag_it); + + return task_success; +} + +bool DashTagMgr::exists(const string& tag_id) const +{ + SWSS_LOG_ENTER(); + + return m_tag_table.find(tag_id) != m_tag_table.end(); +} + +const vector& DashTagMgr::getPrefixes(const string& tag_id) const +{ + SWSS_LOG_ENTER(); + + auto tag_it = m_tag_table.find(tag_id); + ABORT_IF_NOT(tag_it != m_tag_table.end(), "Tag %s does not exist", tag_id.c_str()); + + return tag_it->second.m_prefixes; +} + +task_process_status DashTagMgr::attach(const string& tag_id, const string& group_id) +{ + SWSS_LOG_ENTER(); + + auto tag_it = m_tag_table.find(tag_id); + ABORT_IF_NOT(tag_it != m_tag_table.end(), "Tag %s does not exist", tag_id.c_str()); + auto& tag = tag_it->second; + + ++tag.m_group_refcnt[group_id]; + + SWSS_LOG_NOTICE("Tag %s is used by ACL group %s refcnt: %u", tag_id.c_str(), group_id.c_str(), tag.m_group_refcnt[group_id]); + return task_success; +} + +task_process_status DashTagMgr::detach(const string& tag_id, const string& group_id) +{ + SWSS_LOG_ENTER(); + + auto tag_it = m_tag_table.find(tag_id); + ABORT_IF_NOT(tag_it != m_tag_table.end(), "Tag %s does not exist", tag_id.c_str()); + auto& tag = tag_it->second; + auto group_it = tag.m_group_refcnt.find(group_id); + ABORT_IF_NOT(group_it != tag.m_group_refcnt.end(), "Group %s is not attached to the tag %s", group_id.c_str(), tag_id.c_str()); + + --group_it->second; + if (!group_it->second) + { + tag.m_group_refcnt.erase(group_it); + SWSS_LOG_NOTICE("Tag %s is no longer used by ACL group %s", tag_id.c_str(), group_id.c_str()); + } + + return task_success; +} diff --git a/orchagent/dash/dashtagmgr.h b/orchagent/dash/dashtagmgr.h new file mode 100644 index 0000000000..4b69efdaa8 --- /dev/null +++ b/orchagent/dash/dashtagmgr.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "dashorch.h" +#include "pbutils.h" + +#include "dash_api/prefix_tag.pb.h" + +struct DashTag { + sai_ip_addr_family_t m_ip_version; + std::vector m_prefixes; + std::unordered_map m_group_refcnt; +}; + +bool from_pb(const dash::tag::PrefixTag& data, DashTag& tag); + +class DashAclOrch; + +class DashTagMgr +{ +public: + + DashTagMgr(DashAclOrch *aclorch); + + task_process_status create(const std::string& tag_id, const DashTag& tag); + task_process_status update(const std::string& tag_id, const DashTag& tag); + task_process_status remove(const std::string& tag_id); + bool exists(const std::string& tag_id) const; + + const std::vector& getPrefixes(const std::string& tag_id) const; + + task_process_status attach(const std::string& tag_id, const std::string& group_id); + task_process_status detach(const std::string& tag_id, const std::string& group_id); + +private: + DashAclOrch *m_dash_acl_orch; + std::unordered_map m_tag_table; +}; diff --git a/orchagent/dash/pbutils.cpp b/orchagent/dash/pbutils.cpp index e030382531..e8cd98f9e8 100644 --- a/orchagent/dash/pbutils.cpp +++ b/orchagent/dash/pbutils.cpp @@ -5,6 +5,23 @@ using namespace std; using namespace swss; using namespace google::protobuf; +bool to_sai(const dash::types::IpVersion &pb_version, sai_ip_addr_family_t &sai_ip_family) +{ + switch (pb_version) + { + case dash::types::IP_VERSION_IPV4: + sai_ip_family = SAI_IP_ADDR_FAMILY_IPV4; + break; + case dash::types::IP_VERSION_IPV6: + sai_ip_family = SAI_IP_ADDR_FAMILY_IPV6; + break; + default: + return false; + } + + return true; +} + bool to_sai(const dash::types::IpAddress &pb_address, sai_ip_address_t &sai_address) { SWSS_LOG_ENTER(); diff --git a/orchagent/dash/pbutils.h b/orchagent/dash/pbutils.h index daae486b4a..080cac4666 100644 --- a/orchagent/dash/pbutils.h +++ b/orchagent/dash/pbutils.h @@ -10,6 +10,8 @@ #include "dash_api/types.pb.h" +bool to_sai(const dash::types::IpVersion &pb_version, sai_ip_addr_family_t &sai_ip_family); + bool to_sai(const dash::types::IpAddress &pb_address, sai_ip_address_t &sai_address); bool to_sai(const dash::types::IpPrefix &pb_prefix, sai_ip_prefix_t &sai_prefix); @@ -40,7 +42,7 @@ bool to_sai(const dash::types::ValueOrRange &pb_range, RangeType &sai_range) { if (pb_range.value() < std::numeric_limits::min() || pb_range.value() > std::numeric_limits::max()) { - SWSS_LOG_WARN("The value %s is invalid", pb_range.value()); + SWSS_LOG_WARN("The value %s is invalid", std::to_string(pb_range.value()).c_str()); return false; } sai_range.min = static_cast(pb_range.value()); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 6787fdf846..8968df70e6 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -257,6 +257,7 @@ bool OrchDaemon::init() gDirectory.set(dash_route_orch); vector dash_acl_tables = { + APP_DASH_PREFIX_TAG_TABLE_NAME, APP_DASH_ACL_IN_TABLE_NAME, APP_DASH_ACL_OUT_TABLE_NAME, APP_DASH_ACL_GROUP_TABLE_NAME, diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 83510452a8..95e7104985 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -119,6 +119,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/zmqorch.cpp \ $(top_srcdir)/orchagent/dash/dashaclorch.cpp \ $(top_srcdir)/orchagent/dash/dashorch.cpp \ + $(top_srcdir)/orchagent/dash/dashaclgroupmgr.cpp \ + $(top_srcdir)/orchagent/dash/dashtagmgr.cpp \ $(top_srcdir)/orchagent/dash/dashrouteorch.cpp \ $(top_srcdir)/orchagent/dash/dashvnetorch.cpp \ $(top_srcdir)/cfgmgr/buffermgrdyn.cpp \ diff --git a/tests/test_dash_acl.py b/tests/test_dash_acl.py index a0cc1b2ccf..32e7716e47 100644 --- a/tests/test_dash_acl.py +++ b/tests/test_dash_acl.py @@ -1,3 +1,5 @@ +from collections import namedtuple + from swsscommon import swsscommon from dvslib.dvs_database import DVSDatabase @@ -8,6 +10,7 @@ from dash_api.acl_rule_pb2 import * from dash_api.acl_in_pb2 import * from dash_api.acl_out_pb2 import * +from dash_api.prefix_tag_pb2 import * from dash_api.types_pb2 import * from typing import Union @@ -23,14 +26,21 @@ ACL_GROUP_1 = "acl_group_1" ACL_GROUP_2 = "acl_group_2" +ACL_GROUP_3 = "acl_group_3" ACL_RULE_1 = "1" ACL_RULE_2 = "2" ACL_RULE_3 = "3" ACL_STAGE_1 = "1" ACL_STAGE_2 = "2" +ACL_STAGE_3 = "3" +TAG_1 = "tag_1" +TAG_2 = "tag_2" +TAG_3 = "tag_3" SAI_NULL_OID = "oid:0x0" +PortRange = namedtuple('PortRange', ['min', 'max']) + def to_string(value): if isinstance(value, bool): return "true" if value else "false" @@ -45,6 +55,23 @@ def get_sai_stage(outbound, v4, stage_num): return "SAI_ENI_ATTR_{}_{}_STAGE{}_DASH_ACL_GROUP_ID".format(direction, ip_version, stage_num) +def prefix_list_to_set(prefix_list: str): + count, prefixes = prefix_list.split(":") + + ps = set(prefixes.split(",")) + assert len(ps) == int(count) + + return ps + + +def to_ip_prefix(prefix): + net = ipaddress.IPv4Network(prefix, False) + pfx = IpPrefix() + pfx.ip.ipv4 = socket.htonl(int(net.network_address)) + pfx.mask.ipv4 = socket.htonl(int(net.netmask)) + return pfx + + class ProduceStateTable(object): def __init__(self, database, table_name: str): self.table = swsscommon.ProducerStateTable( @@ -104,6 +131,7 @@ def __getitem__(self, key: str): APPL_DB_TABLE_LIST = [ + swsscommon.APP_DASH_PREFIX_TAG_TABLE_NAME, swsscommon.APP_DASH_ACL_IN_TABLE_NAME, swsscommon.APP_DASH_ACL_OUT_TABLE_NAME, swsscommon.APP_DASH_ACL_GROUP_TABLE_NAME, @@ -127,7 +155,7 @@ def __init__(self, dvs): self.dvs.get_app_db(), table ) table_variable_name = "app_{}".format(table.lower()) - # Based on swsscommon convention for table names, assume + # Based on swsscommon convention for table names, assume # e.g. swsscommon.APP_DASH_ENI_TABLE_NAME == "DASH_ENI_TABLE", therefore # the ProducerStateTable object for swsscommon.APP_DASH_ENI_TABLE_NAME # will be accessible as `self.app_dash_eni_table` @@ -153,14 +181,67 @@ def __init__(self, dvs): self.asic_vnet_table ] - def create_acl_rule(self, group_id, rule_id, pb): - self.app_dash_acl_rule_table[str( - group_id) + ":" + str(rule_id)] = {"pb": pb.SerializeToString()} + def create_prefix_tag(self, name, ip_version, prefixes): + pb = PrefixTag() + pb.ip_version = ip_version + for prefix in prefixes: + pb.prefix_list.append(to_ip_prefix(prefix)) + self.app_dash_prefix_tag_table[str(name)] = {"pb": pb.SerializeToString()} + + def remove_prefix_tag(self, tag_id): + del self.app_dash_prefix_tag_table[str(tag_id)] + + def create_acl_rule(self, group_id, rule_id, action, terminating, priority, protocol=None, + src_addr=None, dst_addr=None, + src_tag=None, dst_tag=None, + src_port=None, dst_port=None): + pb = AclRule() + pb.priority = priority + pb.action = action + pb.terminating = terminating + + if protocol: + map(pb.protocol.append, protocol) + + if src_addr: + for addr in src_addr: + pb.src_addr.append(to_ip_prefix(addr)) + + if dst_addr: + for addr in dst_addr: + pb.dst_addr.append(to_ip_prefix(addr)) + + if src_tag: + for tag in src_tag: + pb.src_tag.append(tag.encode()) + + if dst_tag: + for tag in dst_tag: + pb.dst_tag.append(tag.encode()) + + if src_port: + for pr in src_port: + vr = ValueOrRange() + vr.range.min = pr.min + vr.range.max = pr.max + pb.src_port.append(vr) + + if dst_port: + for pr in dst_port: + vr = ValueOrRange() + vr.range.min = pr.min + vr.range.max = pr.max + pb.dst_port.append(vr) + + self.app_dash_acl_rule_table[str(group_id) + ":" + str(rule_id)] = {"pb": pb.SerializeToString()} + def remove_acl_rule(self, group_id, rule_id): del self.app_dash_acl_rule_table[str(group_id) + ":" + str(rule_id)] - def create_acl_group(self, group_id, pb): + def create_acl_group(self, group_id, ip_version): + pb = AclGroup() + pb.ip_version = IpVersion.IP_VERSION_IPV4 self.app_dash_acl_group_table[str(group_id)] = {"pb": pb.SerializeToString()} def remove_acl_group(self, group_id): @@ -209,6 +290,7 @@ def unbind_acl_out(self, eni, stage): del self.app_dash_acl_out_table[str(eni) + ":" + str(stage)] + class TestAcl(object): @pytest.fixture def ctx(self, dvs): @@ -252,32 +334,25 @@ def ctx(self, dvs): for table in acl_context.asic_db_tables: table.wait_for_n_keys(num_keys=0) + def bind_acl_group(self, ctx, stage_id, group_id, group_oid): + ctx.bind_acl_in(self.eni_name, stage_id, group_id) + self.verify_group_is_bound_to_eni(ctx, stage_id, group_oid) + + def verify_group_is_bound_to_eni(self, ctx, stage_id, group_oid): + eni_key = ctx.asic_eni_table.get_keys()[0] + sai_stage = get_sai_stage(outbound=False, v4=True, stage_num=stage_id) + ctx.asic_eni_table.wait_for_field_match(key=eni_key, expected_fields={sai_stage: group_oid}) + assert sai_stage in ctx.asic_eni_table[eni_key] + assert ctx.asic_eni_table[eni_key][sai_stage] == group_oid + def test_acl_flow(self, ctx): - pb = AclGroup() - pb.ip_version = IpVersion.IP_VERSION_IPV4 - ctx.create_acl_group(ACL_GROUP_1, pb) - pb = AclRule() - pb.priority = 1 - pb.action = Action.ACTION_PERMIT - pb.terminating = False - net = ipaddress.IPv4Network("192.168.0.1/32", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - net = ipaddress.IPv4Network("192.168.1.2/30", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - vr = ValueOrRange() - vr.range.min = 0 - vr.range.max = 1 - pb.src_port.append(vr) - pb.dst_port.append(vr) - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, pb) + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) + + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] group1_id= ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] @@ -293,9 +368,18 @@ def test_acl_flow(self, ctx): assert group1_attr["SAI_DASH_ACL_GROUP_ATTR_IP_ADDR_FAMILY"] == "SAI_IP_ADDR_FAMILY_IPV4" # Create multiple rules - pb.priority = 2 - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, pb) - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_3, pb) + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, + priority=2, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, + priority=2, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_3, + priority=3, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=3) ctx.unbind_acl_in(self.eni_name, ACL_STAGE_1) ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_1) @@ -306,31 +390,13 @@ def test_acl_flow(self, ctx): ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=0) def test_acl_group(self, ctx): - pb = AclGroup() - pb.ip_version = IpVersion.IP_VERSION_IPV6 - ctx.create_acl_group(ACL_GROUP_1, pb) - pb = AclRule() - pb.priority = 1 - pb.action = Action.ACTION_PERMIT - pb.terminating = False - net = ipaddress.IPv4Network("192.168.0.1/32", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - net = ipaddress.IPv4Network("192.168.1.2/30", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - vr = ValueOrRange() - vr.range.min = 0 - vr.range.max = 1 - pb.src_port.append(vr) - pb.dst_port.append(vr) - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, pb) + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV6) + + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1) # Remove group before removing its rule @@ -350,38 +416,18 @@ def test_empty_acl_group_binding(self, ctx): eni_key = ctx.asic_eni_table.get_keys()[0] sai_stage = get_sai_stage(outbound=False, v4=True, stage_num=ACL_STAGE_1) - pb = AclGroup() - pb.ip_version = IpVersion.IP_VERSION_IPV4 - ctx.create_acl_group(ACL_GROUP_1, pb) + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) acl_group_key = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] ctx.bind_acl_in(self.eni_name, ACL_STAGE_1, v4_group_id = ACL_GROUP_1) time.sleep(3) # Binding should not happen yet because the ACL group is empty assert sai_stage not in ctx.asic_eni_table[eni_key] - pb = AclRule() - pb.priority = 1 - pb.action = Action.ACTION_PERMIT - pb.terminating = False - net = ipaddress.IPv4Network("192.168.0.1/32", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - net = ipaddress.IPv4Network("192.168.1.2/30", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - vr = ValueOrRange() - vr.range.min = 0 - vr.range.max = 1 - pb.src_port.append(vr) - pb.dst_port.append(vr) - - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, pb) + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + # Now that the group contains a rule, expect binding to occur ctx.asic_eni_table.wait_for_field_match(key=eni_key, expected_fields={sai_stage: acl_group_key}) @@ -393,34 +439,14 @@ def test_acl_group_binding(self, ctx): eni_key = ctx.asic_eni_table.get_keys()[0] sai_stage = get_sai_stage(outbound=False, v4=True, stage_num=ACL_STAGE_2) - pb = AclGroup() - pb.ip_version = IpVersion.IP_VERSION_IPV4 - ctx.create_acl_group(ACL_GROUP_2, pb) + ctx.create_acl_group(ACL_GROUP_2, IpVersion.IP_VERSION_IPV4) acl_group_key = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] - pb = AclRule() - pb.priority = 1 - pb.action = Action.ACTION_PERMIT - pb.terminating = False - net = ipaddress.IPv4Network("192.168.0.1/32", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - net = ipaddress.IPv4Network("192.168.1.2/30", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - vr = ValueOrRange() - vr.range.min = 0 - vr.range.max = 1 - pb.src_port.append(vr) - pb.dst_port.append(vr) - - ctx.create_acl_rule(ACL_GROUP_2, ACL_RULE_1, pb) + ctx.create_acl_rule(ACL_GROUP_2, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + ctx.bind_acl_in(self.eni_name, ACL_STAGE_2, v4_group_id = ACL_GROUP_2) # Binding should occurr immediately since we added a rule to the group prior to binding ctx.asic_eni_table.wait_for_field_match(key=eni_key, expected_fields={sai_stage: acl_group_key}) @@ -430,70 +456,28 @@ def test_acl_group_binding(self, ctx): def test_acl_rule(self, ctx): # Create acl rule before acl group - pb = AclRule() - pb.priority = 1 - pb.action = Action.ACTION_PERMIT - pb.terminating = False - net = ipaddress.IPv4Network("192.168.0.1/32", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - net = ipaddress.IPv4Network("192.168.1.2/30", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.src_addr.append(pfx) - pb.dst_addr.append(pfx) - vr = ValueOrRange() - vr.range.min = 0 - vr.range.max = 1 - pb.src_port.append(vr) - pb.dst_port.append(vr) - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, pb) + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) time.sleep(3) ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=0) - pb = AclGroup() - pb.ip_version = IpVersion.IP_VERSION_IPV4 - ctx.create_acl_group(ACL_GROUP_1, pb) + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1) # Create acl rule with nonexistent acl group, which should never get programmed to ASIC_DB - ctx.create_acl_rule("0", "0", pb) + ctx.create_acl_rule("0", "0", + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) time.sleep(3) ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1) -# # Create acl with invalid attribute -# ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, {"priority": "abc"}) -# time.sleep(3) -# ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1) - -# # Create acl without some mandatory attributes at first -# ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, {"priority": "1", "action": "allow", "terminating": "false"}) -# time.sleep(3) -# ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1) - - pb = AclRule() - net = ipaddress.IPv4Network("192.168.0.1/32", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.dst_addr.append(pfx) - net = ipaddress.IPv4Network("192.168.1.2/30", False) - pfx = IpPrefix() - pfx.ip.ipv4 = socket.htonl(int(net.network_address)) - pfx.mask.ipv4 = socket.htonl(int(net.netmask)) - pb.dst_addr.append(pfx) - vr = ValueOrRange() - vr.range.min = 0 - vr.range.max = 1 - pb.dst_port.append(vr) - - # Expect the rule to be created only after all the mandatory attributes are added - ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, pb) - time.sleep(3) + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_2, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_addr=["192.168.0.1/32", "192.168.1.2/30"], dst_addr=["192.168.0.1/32", "192.168.1.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=2) ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_1) @@ -503,6 +487,292 @@ def test_acl_rule(self, ctx): ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=0) + @pytest.mark.parametrize("bind_group", [True, False]) + def test_prefix_single_tag(self, ctx, bind_group): + tag1_prefixes = {"1.1.1.0/24", "2.2.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + tag2_prefixes = {"192.168.1.0/30", "192.168.2.0/30", "192.168.3.0/30"} + ctx.create_prefix_tag(TAG_2, IpVersion.IP_VERSION_IPV4, tag2_prefixes) + + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) + group1_id = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_tag=[TAG_1], dst_tag=[TAG_2], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == tag2_prefixes + + if bind_group: + self.bind_acl_group(ctx, ACL_STAGE_1, ACL_GROUP_1, group1_id) + + tag1_prefixes = {"1.1.2.0/24", "2.3.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + time.sleep(3) + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + if bind_group: + new_group1_id = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + assert new_group1_id != group1_id + self.verify_group_is_bound_to_eni(ctx, ACL_STAGE_1, new_group1_id) + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == tag2_prefixes + + tag2_prefixes = {"192.168.2.0/30", "192.168.3.0/30"} + ctx.create_prefix_tag(TAG_2, IpVersion.IP_VERSION_IPV4, tag2_prefixes) + + time.sleep(3) + + ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1) + rule1_id = ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == tag2_prefixes + + if bind_group: + ctx.unbind_acl_in(self.eni_name, ACL_STAGE_1) + + ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_1) + ctx.remove_acl_group(ACL_GROUP_1) + ctx.remove_prefix_tag(TAG_1) + ctx.remove_prefix_tag(TAG_2) + + @pytest.mark.parametrize("bind_group", [True, False]) + def test_multiple_tags(self, ctx, bind_group): + tag1_prefixes = {"1.1.1.0/24", "2.2.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + tag2_prefixes = {"192.168.1.0/30", "192.168.2.0/30", "192.168.1.0/30"} + ctx.create_prefix_tag(TAG_2, IpVersion.IP_VERSION_IPV4, tag2_prefixes) + + tag3_prefixes = {"3.3.0.0/16", "3.4.0.0/16", "4.4.4.0/24", "5.5.5.0/24"} + ctx.create_prefix_tag(TAG_3, IpVersion.IP_VERSION_IPV4, tag3_prefixes) + + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) + group1_id = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + + # Create acl rule before acl group + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_tag=[TAG_1, TAG_2], dst_tag=[TAG_2, TAG_3], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes.union(tag2_prefixes) + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == tag2_prefixes.union(tag3_prefixes) + + if bind_group: + self.bind_acl_group(ctx, ACL_STAGE_1, ACL_GROUP_1, group1_id) + + tag2_prefixes = {"192.168.10.0/30", "192.168.11.0/30", "192.168.12.0/30"} + ctx.create_prefix_tag(TAG_2, IpVersion.IP_VERSION_IPV4, tag2_prefixes) + + tag3_prefixes = {"3.13.0.0/16", "3.14.0.0/16", "4.14.4.0/24", "5.15.5.0/24"} + ctx.create_prefix_tag(TAG_3, IpVersion.IP_VERSION_IPV4, tag3_prefixes) + + time.sleep(3) + + if bind_group: + new_group1_id = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + assert new_group1_id != group1_id + + self.verify_group_is_bound_to_eni(ctx, ACL_STAGE_1, new_group1_id) + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes.union(tag2_prefixes) + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == tag2_prefixes.union(tag3_prefixes) + + if bind_group: + ctx.unbind_acl_in(self.eni_name, ACL_STAGE_1) + + ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_1) + ctx.remove_acl_group(ACL_GROUP_1) + ctx.remove_prefix_tag(TAG_1) + ctx.remove_prefix_tag(TAG_2) + ctx.remove_prefix_tag(TAG_3) + + @pytest.mark.parametrize("bind_group", [True, False]) + def test_multiple_tags_and_prefixes(self, ctx, bind_group): + tag1_prefixes = {"1.1.1.0/24", "2.2.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + tag2_prefixes = {"192.168.1.0/30", "192.168.2.0/30", "192.168.3.0/30"} + ctx.create_prefix_tag(TAG_2, IpVersion.IP_VERSION_IPV4, tag2_prefixes) + + tag3_prefixes = {"3.3.0.0/16", "3.4.0.0/16", "4.4.4.0/24", "5.5.5.0/24"} + ctx.create_prefix_tag(TAG_3, IpVersion.IP_VERSION_IPV4, tag3_prefixes) + + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) + group1_id = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + + prefix_list = {"10.0.0.0/8", "11.1.1.0/24", "11.1.2.0/24"} + + # Create acl rule before acl group + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_tag=[TAG_1, TAG_2, TAG_3], dst_addr=prefix_list, + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + super_set = set() + super_set.update(tag1_prefixes, tag2_prefixes, tag3_prefixes) + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == super_set + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == prefix_list + + if bind_group: + self.bind_acl_group(ctx, ACL_STAGE_1, ACL_GROUP_1, group1_id) + + tag1_prefixes = {"1.1.1.0/24", "2.2.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + tag2_prefixes = {"192.168.1.2/32", "192.168.2.2/32", "192.168.1.2/32"} + ctx.create_prefix_tag(TAG_2, IpVersion.IP_VERSION_IPV4, tag2_prefixes) + + tag3_prefixes = {"3.3.0.0/16", "3.4.0.0/16", "4.4.4.0/24", "5.5.5.0/24"} + ctx.create_prefix_tag(TAG_3, IpVersion.IP_VERSION_IPV4, tag3_prefixes) + + time.sleep(3) + + if bind_group: + new_group1_id = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + assert new_group1_id != group1_id + self.verify_group_is_bound_to_eni(ctx, ACL_STAGE_1, new_group1_id) + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + super_set = set() + super_set.update(tag1_prefixes, tag2_prefixes, tag3_prefixes) + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == super_set + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_DIP"]) == prefix_list + + if bind_group: + ctx.unbind_acl_in(self.eni_name, ACL_STAGE_1) + + ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_1) + ctx.remove_acl_group(ACL_GROUP_1) + ctx.remove_prefix_tag(TAG_1) + ctx.remove_prefix_tag(TAG_2) + ctx.remove_prefix_tag(TAG_3) + + @pytest.mark.parametrize("bind_group", [True, False]) + def test_multiple_groups_prefix_single_tag(self, ctx, bind_group): + groups = [ACL_GROUP_1, ACL_GROUP_2, ACL_GROUP_3] + stages = [ACL_STAGE_1, ACL_STAGE_2, ACL_STAGE_3] + + tag1_prefixes = {"1.1.1.0/24", "2.2.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + for group in groups: + ctx.create_acl_group(group, IpVersion.IP_VERSION_IPV4) + ctx.create_acl_rule(group, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_tag=[TAG_1], dst_addr=["192.168.1.2/30", "192.168.2.2/30", "192.168.3.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + + group_ids = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=3) + rule_ids = ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=3) + + for rid in rule_ids: + rule_attrs = ctx.asic_dash_acl_rule_table[rid] + assert prefix_list_to_set(rule_attrs["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + + if bind_group: + eni_stages = [] + eni_key = ctx.asic_eni_table.get_keys()[0] + for stage, group in zip(stages, groups): + ctx.bind_acl_in(self.eni_name, stage, group) + eni_stages.append(get_sai_stage(outbound=False, v4=True, stage_num=stage)) + + ctx.asic_eni_table.wait_for_fields(key=eni_key, expected_fields=eni_stages) + for stage in eni_stages: + assert ctx.asic_eni_table[eni_key][stage] in group_ids + + tag1_prefixes = {"1.1.2.0/24", "2.3.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + time.sleep(3) + + rule_ids = ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=3) + + for rid in rule_ids: + rule_attrs = ctx.asic_dash_acl_rule_table[rid] + assert prefix_list_to_set(rule_attrs["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + + if bind_group: + new_group_ids = ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=3) + + ctx.asic_eni_table.wait_for_fields(key=eni_key, expected_fields=eni_stages) + for stage in eni_stages: + assert ctx.asic_eni_table[eni_key][stage] in new_group_ids + + for stage in stages: + ctx.unbind_acl_in(self.eni_name, stage) + + for group in groups: + ctx.remove_acl_rule(group, ACL_RULE_1) + ctx.remove_acl_group(group) + + ctx.remove_prefix_tag(TAG_1) + ctx.remove_prefix_tag(TAG_2) + + def test_tag_remove(self, ctx): + tag1_prefixes = {"1.1.1.0/24", "2.2.0.0/16"} + ctx.create_prefix_tag(TAG_1, IpVersion.IP_VERSION_IPV4, tag1_prefixes) + + ctx.create_acl_group(ACL_GROUP_1, IpVersion.IP_VERSION_IPV4) + ctx.asic_dash_acl_group_table.wait_for_n_keys(num_keys=1)[0] + + # Create acl rule before acl group + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=1, action=Action.ACTION_PERMIT, terminating=False, + src_tag=[TAG_1], dst_addr=["192.168.1.2/30", "192.168.2.2/30", "192.168.3.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + + + rule1_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule1_attr = ctx.asic_dash_acl_rule_table[rule1_id] + + assert prefix_list_to_set(rule1_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + + ctx.remove_prefix_tag(TAG_1) + time.sleep(1) + + ctx.create_acl_rule(ACL_GROUP_1, ACL_RULE_1, + priority=2, action=Action.ACTION_DENY, terminating=False, + src_tag=[TAG_1], dst_addr=["192.168.1.2/30", "192.168.2.2/30", "192.168.3.2/30"], + src_port=[PortRange(0,1)], dst_port=[PortRange(0,1)]) + + rule2_id= ctx.asic_dash_acl_rule_table.wait_for_n_keys(num_keys=1)[0] + rule2_attr = ctx.asic_dash_acl_rule_table[rule2_id] + + assert prefix_list_to_set(rule2_attr["SAI_DASH_ACL_RULE_ATTR_SIP"]) == tag1_prefixes + + ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_1) + ctx.remove_acl_rule(ACL_GROUP_1, ACL_RULE_2) + ctx.remove_acl_group(ACL_GROUP_1) + ctx.remove_prefix_tag(TAG_1) + ctx.remove_prefix_tag(TAG_2) + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down # before retrying