diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index feff1877ab..95f780d76c 100755 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "aclorch.h" #include "logger.h" #include "schema.h" @@ -641,7 +642,7 @@ void AclRule::TunnelNH::load(const std::string& target) void AclRule::TunnelNH::parse(const std::string& target) { - /* Supported Format: endpoint_ip@tunnel_name */ + /* Expected Format: endpoint_ip@tunnel_name[,vni][,mac] */ auto at_pos = target.find('@'); if (at_pos == std::string::npos) { @@ -649,7 +650,29 @@ void AclRule::TunnelNH::parse(const std::string& target) } endpoint_ip = swss::IpAddress(target.substr(0, at_pos)); - tunnel_name = target.substr(at_pos + 1); + std::stringstream ss(target.substr(at_pos + 1)); + + vector components; + while (ss.good()) + { + std::string substr; + getline(ss, substr, ','); + components.push_back(substr); + } + if (components.empty()) + { + throw std::logic_error("Invalid format for Tunnel Next Hop"); + } + + tunnel_name = components[0]; + if (components.size() >= 2) + { + vni = static_cast(std::stoul(components[1])); + } + if (components.size() == 3) + { + mac = swss::MacAddress(components[2]); + } } void AclRule::TunnelNH::clear() diff --git a/orchagent/dash/dashenifwdinfo.cpp b/orchagent/dash/dashenifwdinfo.cpp index 5f808c6108..636c67c5a1 100644 --- a/orchagent/dash/dashenifwdinfo.cpp +++ b/orchagent/dash/dashenifwdinfo.cpp @@ -4,12 +4,6 @@ using namespace swss; using namespace std; const int EniAclRule::BASE_PRIORITY = 9996; -const vector EniAclRule::RULE_NAMES = { - "IN", - "OUT", - "IN_TERM", - "OUT_TERM" -}; unique_ptr EniNH::createNextHop(dpu_type_t type, const IpAddress& ip) { @@ -55,30 +49,33 @@ void RemoteEniNH::resolve(EniInfo& eni) return ; } - if (ctx->handleTunnelNH(tunnel_name_, endpoint_, true)) - { - setStatus(endpoint_status_t::RESOLVED); - } - else + uint64_t vnet_vni; + if (!ctx->findVnetVni(vnet, vnet_vni)) { + SWSS_LOG_ERROR("Couldn't find VNI for Vnet %s", vnet.c_str()); setStatus(endpoint_status_t::UNRESOLVED); + return ; } -} -void RemoteEniNH::destroy(EniInfo& eni) -{ - auto& ctx = eni.getCtx(); - ctx->handleTunnelNH(tunnel_name_, endpoint_, false); + vni_ = std::to_string(vnet_vni); + + /* Note: AclOrch already has logic to create / delete Tunnel NH, no need to create here */ + setStatus(endpoint_status_t::RESOLVED); } string RemoteEniNH::getRedirectVal() { - return endpoint_.to_string() + "@" + tunnel_name_; + /* Format Expected by AclOrch: endpoint_ip@tunnel_name[,vni][,mac] */ + return endpoint_.to_string() + "@" + tunnel_name_ + ',' + vni_; } void EniAclRule::setKey(EniInfo& eni) { - name_ = string(ENI_REDIRECT_TABLE) + ":" + eni.toKey() + "_" + EniAclRule::RULE_NAMES[type_]; + name_ = string(DashEniFwd::TABLE) + ":" + eni.toKey(); + if (type_ == rule_type_t::TUNNEL_TERM) + { + name_ += "_TERM"; + } } update_type_t EniAclRule::processUpdate(EniInfo& eni) @@ -88,9 +85,9 @@ update_type_t EniAclRule::processUpdate(EniInfo& eni) IpAddress primary_endp; dpu_type_t primary_type = LOCAL; update_type_t update_type = PRIMARY_UPDATE; - uint64_t primary_id; + std::string primary_id; - if (type_ == rule_type_t::INBOUND_TERM || type_ == rule_type_t::OUTBOUND_TERM) + if (type_ == rule_type_t::TUNNEL_TERM) { /* Tunnel term entries always use local endpoint regardless of primary id */ if (!eni.findLocalEp(primary_id)) @@ -106,7 +103,7 @@ update_type_t EniAclRule::processUpdate(EniInfo& eni) if (!ctx->dpu_info.getType(primary_id, primary_type)) { - SWSS_LOG_ERROR("No primaryId in DPU Table %" PRIu64 "", primary_id); + SWSS_LOG_ERROR("No primary id %s in DPU Table", primary_id.c_str()); return update_type_t::INVALID; } @@ -182,9 +179,8 @@ void EniAclRule::fire(EniInfo& eni) Delete the complete rule before updating it, ACLOrch Doesn't support incremental updates */ - ctx->rule_table->del(key); + ctx->deleteAclRule(key); setState(rule_state_t::UNINSTALLED); - SWSS_LOG_NOTICE("EniFwd ACL Rule %s deleted", key.c_str()); } if (nh_->getStatus() != endpoint_status_t::RESOLVED) @@ -201,27 +197,17 @@ void EniAclRule::fire(EniInfo& eni) { ACTION_REDIRECT_ACTION, nh_->getRedirectVal() } }; - if (type_ == rule_type_t::INBOUND_TERM || type_ == rule_type_t::OUTBOUND_TERM) + if (type_ == rule_type_t::TUNNEL_TERM) { fv_.push_back({MATCH_TUNNEL_TERM, "true"}); } - - if (type_ == rule_type_t::OUTBOUND || type_ == rule_type_t::OUTBOUND_TERM) - { - fv_.push_back({MATCH_TUNNEL_VNI, to_string(eni.getOutVni())}); - } - ctx->rule_table->set(key, fv_); + ctx->createAclRule(key, fv_); setState(INSTALLED); - SWSS_LOG_NOTICE("EniFwd ACL Rule %s installed", key.c_str()); } string EniAclRule::getMacMatchDirection(EniInfo& eni) { - if (type_ == OUTBOUND || type_ == OUTBOUND_TERM) - { - return eni.getOutMacLookup(); - } return MATCH_INNER_DST_MAC; } @@ -231,7 +217,7 @@ void EniAclRule::destroy(EniInfo& eni) { auto key = getKey(); auto& ctx = eni.getCtx(); - ctx->rule_table->del(key); + ctx->deleteAclRule(key); if (nh_ != nullptr) { nh_->destroy(eni); @@ -292,10 +278,8 @@ bool EniInfo::create(const Request& db_request) SWSS_LOG_ENTER(); auto updates = db_request.getAttrFieldNames(); - auto itr_ep_list = updates.find(ENI_FWD_VDPU_IDS); - auto itr_primary_id = updates.find(ENI_FWD_PRIMARY); - auto itr_out_vni = updates.find(ENI_FWD_OUT_VNI); - auto itr_out_mac_dir = updates.find(ENI_FWD_OUT_MAC_LOOKUP); + auto itr_ep_list = updates.find(DashEniFwd::VDPU_IDS); + auto itr_primary_id = updates.find(DashEniFwd::PRIMARY); /* Validation Checks */ if (itr_ep_list == updates.end() || itr_primary_id == updates.end()) @@ -304,85 +288,26 @@ bool EniInfo::create(const Request& db_request) return false; } - ep_list_ = db_request.getAttrUintList(ENI_FWD_VDPU_IDS); - primary_id_ = db_request.getAttrUint(ENI_FWD_PRIMARY); + ep_list_ = db_request.getAttrStringList(DashEniFwd::VDPU_IDS); + primary_id_ = db_request.getAttrString(DashEniFwd::PRIMARY); - uint64_t local_id; + std::string local_id; bool tunn_term_allow = findLocalEp(local_id); - bool outbound_allow = false; /* Create Rules */ rule_container_.emplace(piecewise_construct, - forward_as_tuple(rule_type_t::INBOUND), - forward_as_tuple(rule_type_t::INBOUND, *this)); - rule_container_.emplace(piecewise_construct, - forward_as_tuple(rule_type_t::OUTBOUND), - forward_as_tuple(rule_type_t::OUTBOUND, *this)); + forward_as_tuple(rule_type_t::NO_TUNNEL_TERM), + forward_as_tuple(rule_type_t::NO_TUNNEL_TERM, *this)); if (tunn_term_allow) { - /* Create rules for tunnel termination if required */ - rule_container_.emplace(piecewise_construct, - forward_as_tuple(rule_type_t::INBOUND_TERM), - forward_as_tuple(rule_type_t::INBOUND_TERM, *this)); + /* Create rule for tunnel termination if required */ rule_container_.emplace(piecewise_construct, - forward_as_tuple(rule_type_t::OUTBOUND_TERM), - forward_as_tuple(rule_type_t::OUTBOUND_TERM, *this)); - } - - /* Infer Direction to check MAC for outbound rules */ - if (itr_out_mac_dir == updates.end()) - { - outbound_mac_lookup_ = MATCH_INNER_SRC_MAC; - } - else - { - auto str = db_request.getAttrString(ENI_FWD_OUT_MAC_LOOKUP); - if (str == OUT_MAC_DIR) - { - outbound_mac_lookup_ = MATCH_INNER_DST_MAC; - } - else - { - outbound_mac_lookup_ = MATCH_INNER_SRC_MAC; - } - } - - /* Infer tunnel_vni for the outbound rules */ - if (itr_out_vni == updates.end()) - { - if (ctx->findVnetVni(vnet_name_, outbound_vni_)) - { - outbound_allow = true; - } - else - { - SWSS_LOG_ERROR("Invalid VNET: No VNI. Cannot install outbound rules: %s", toKey().c_str()); - } - } - else - { - outbound_vni_ = db_request.getAttrUint(ENI_FWD_OUT_VNI); - outbound_allow = true; - } - - fireRule(rule_type_t::INBOUND); - - if (tunn_term_allow) - { - fireRule(rule_type_t::INBOUND_TERM); - } - - if (outbound_allow) - { - fireRule(rule_type_t::OUTBOUND); - } - - if (tunn_term_allow && outbound_allow) - { - fireRule(rule_type_t::OUTBOUND_TERM); + forward_as_tuple(rule_type_t::TUNNEL_TERM), + forward_as_tuple(rule_type_t::TUNNEL_TERM, *this)); } + fireAllRules(); return true; } @@ -408,7 +333,7 @@ bool EniInfo::update(const Request& db_request) /* Only primary_id is expected to change after ENI is created */ auto updates = db_request.getAttrFieldNames(); - auto itr_primary_id = updates.find(ENI_FWD_PRIMARY); + auto itr_primary_id = updates.find(DashEniFwd::PRIMARY); /* Validation Checks */ if (itr_primary_id == updates.end()) @@ -416,26 +341,26 @@ bool EniInfo::update(const Request& db_request) throw logic_error("Invalid DASH_ENI_FORWARD_TABLE update: No primary idx"); } - if (getPrimaryId() == db_request.getAttrUint(ENI_FWD_PRIMARY)) + if (getPrimaryId() == db_request.getAttrString(DashEniFwd::PRIMARY)) { /* No update in the primary id, return true */ return true; } /* Update local primary id and fire the rules */ - primary_id_ = db_request.getAttrUint(ENI_FWD_PRIMARY); + primary_id_ = db_request.getAttrString(DashEniFwd::PRIMARY); fireAllRules(); return true; } -bool EniInfo::findLocalEp(uint64_t& local_endpoint) const +bool EniInfo::findLocalEp(std::string& local_endpoint) const { /* Check if atleast one of the endpoints is local */ bool found = false; for (auto idx : ep_list_) { - dpu_type_t val = dpu_type_t::EXTERNAL; + dpu_type_t val = dpu_type_t::CLUSTER; if (ctx->dpu_info.getType(idx, val) && val == dpu_type_t::LOCAL) { if (!found) @@ -445,8 +370,8 @@ bool EniInfo::findLocalEp(uint64_t& local_endpoint) const } else { - SWSS_LOG_WARN("Multiple Local Endpoints for the ENI %s found, proceeding with %" PRIu64 "" , - mac_.to_string().c_str(), local_endpoint); + SWSS_LOG_WARN("Multiple Local Endpoints for the ENI %s found, proceeding with %s", + mac_.to_string().c_str(), local_endpoint.c_str()); } } } diff --git a/orchagent/dash/dashenifwdorch.cpp b/orchagent/dash/dashenifwdorch.cpp index ad8ae860ff..55038d26fd 100644 --- a/orchagent/dash/dashenifwdorch.cpp +++ b/orchagent/dash/dashenifwdorch.cpp @@ -12,8 +12,6 @@ DashEniFwdOrch::DashEniFwdOrch(DBConnector* cfgDb, DBConnector* applDb, const st : Orch2(applDb, tableName, request_), neighorch_(neighOrch) { SWSS_LOG_ENTER(); - acl_table_type_ = make_unique(applDb, APP_ACL_TABLE_TYPE_TABLE_NAME); - acl_table_ = make_unique(applDb, APP_ACL_TABLE_TABLE_NAME); ctx = make_shared(cfgDb, applDb); if (neighorch_) { @@ -77,51 +75,6 @@ void DashEniFwdOrch::handleNeighUpdate(const NeighborUpdate& update) } } -void DashEniFwdOrch::initAclTableCfg() -{ - vector match_list = { - MATCH_TUNNEL_VNI, - MATCH_DST_IP, - MATCH_INNER_SRC_MAC, - MATCH_INNER_DST_MAC, - MATCH_TUNNEL_TERM - }; - - auto concat = [](const std::string &a, const std::string &b) { return a + "," + b; }; - - std::string matches = std::accumulate( - std::next(match_list.begin()), match_list.end(), match_list[0], - concat); - - string bpoint_types = string(BIND_POINT_TYPE_PORT) + "," + string(BIND_POINT_TYPE_PORTCHANNEL); - - vector fv_ = { - { ACL_TABLE_TYPE_MATCHES, matches}, - { ACL_TABLE_TYPE_ACTIONS, ACTION_REDIRECT_ACTION }, - { ACL_TABLE_TYPE_BPOINT_TYPES, bpoint_types} - }; - - acl_table_type_->set(ENI_REDIRECT_TABLE_TYPE, fv_); - - auto ports = ctx->getBindPoints(); - std::string ports_str; - - if (!ports.empty()) - { - ports_str = std::accumulate(std::next(ports.begin()), ports.end(), ports[0], concat); - } - - /* Write ACL Table */ - vector table_fv_ = { - { ACL_TABLE_DESCRIPTION, "Contains Rule for DASH ENI Based Forwarding"}, - { ACL_TABLE_TYPE, ENI_REDIRECT_TABLE_TYPE }, - { ACL_TABLE_STAGE, STAGE_INGRESS }, - { ACL_TABLE_PORTS, ports_str } - }; - - acl_table_->set(ENI_REDIRECT_TABLE, table_fv_); -} - void DashEniFwdOrch::initLocalEndpoints() { auto ids = ctx->dpu_info.getIds(); @@ -142,7 +95,7 @@ void DashEniFwdOrch::initLocalEndpoints() if (ctx->isNeighborResolved(nh)) { - SWSS_LOG_WARN("Neighbor already populated.. Not Expected"); + SWSS_LOG_INFO("Neighbor already populated for local endpoint %s", local_endp.to_string().c_str()); } ctx->resolveNeighbor(nh); } @@ -150,7 +103,7 @@ void DashEniFwdOrch::initLocalEndpoints() } } -void DashEniFwdOrch::handleEniDpuMapping(uint64_t id, MacAddress mac, bool add) +void DashEniFwdOrch::handleEniDpuMapping(const std::string& id, MacAddress mac, bool add) { /* Make sure id is local */ dpu_type_t primary_type = CLUSTER; @@ -185,11 +138,9 @@ void DashEniFwdOrch::lazyInit() 1. DpuRegistry 2. Other Orch ptrs 3. Internal dpu-id mappings - 4. Write ACL Table Cfg */ ctx->initialize(); ctx->populateDpuRegistry(); - initAclTableCfg(); initLocalEndpoints(); ctx_initialized_ = true; } @@ -216,7 +167,7 @@ bool DashEniFwdOrch::addOperation(const Request& request) if (new_eni) { eni_itr->second.create(request); - uint64_t local_ep; + std::string local_ep; if (eni_itr->second.findLocalEp(local_ep)) { /* Add to the local map if the endpoint is found */ @@ -247,10 +198,9 @@ bool DashEniFwdOrch::delOperation(const Request& request) bool result = eni_itr->second.destroy(request); if (result) { - uint64_t local_ep; + std::string local_ep; if (eni_itr->second.findLocalEp(local_ep)) { - /* Add to the local map if the endpoint is found */ handleEniDpuMapping(local_ep, eni_id, false); } } @@ -259,100 +209,206 @@ bool DashEniFwdOrch::delOperation(const Request& request) } -void DpuRegistry::populate(Table* dpuTable) +void DpuRegistry::populate(const DBConnector* cfg_db) { + /* + Read DPU, VDPU, and Remote DPU tables, they are expected to be populated by the time HA is ready + */ SWSS_LOG_ENTER(); - std::vector keys; - dpuTable->getKeys(keys); + processDpuTable(cfg_db); + processRemoteDpuTable(cfg_db); + processVdpuTable(cfg_db); +} +void DpuRegistry::processDpuTable(const DBConnector* cfg_db) +{ + Table dpuTable(cfg_db, DashEniFwd::DPU_TABLE); + std::vector keys; + dpuTable.getKeys(keys); for (auto key : keys) { try { std::vector values; - dpuTable->get(key, values); + dpuTable.get(key, values); KeyOpFieldsValuesTuple kvo = { key, SET_COMMAND, values }; - processDpuTable(kvo); + + dpu_request_.clear(); + dpu_request_.parse(kvo); + string key = dpu_request_.getKeyString(0); + // Check if STATE is present and if present and value is 'down', skip this DPU + auto updates = dpu_request_.getAttrFieldNames(); + auto itr_state = updates.find(DashEniFwd::STATE); + if (itr_state != updates.end()) + { + auto state_val = dpu_request_.getAttrString(DashEniFwd::STATE); + if (state_val == "down") + { + SWSS_LOG_INFO("Skipping LOCAL DPU %s as its state is down", key.c_str()); + continue; + } + } + + DpuData data; + data.type = dpu_type_t::LOCAL; + data.pa_v4 = dpu_request_.getAttrIP(DashEniFwd::PA_V4); + dpus_name_map_.insert({key, data}); + + SWSS_LOG_INFO("LOCAL DPU %s found, PA_V4: %s", key.c_str(), data.pa_v4.to_string().c_str()); } catch(exception& e) { - SWSS_LOG_ERROR("Failed to parse key:%s in the %s", key.c_str(), CFG_DPU_TABLE); + SWSS_LOG_ERROR("Failed to parse key:%s in the %s", key.c_str(), DashEniFwd::DPU_TABLE); } } - SWSS_LOG_INFO("DPU data read. %zu dpus found", dpus_.size()); } -void DpuRegistry::processDpuTable(const KeyOpFieldsValuesTuple& kvo) +void DpuRegistry::processRemoteDpuTable(const DBConnector* cfg_db) { - DpuData data; - - dpu_request_.clear(); - dpu_request_.parse(kvo); - - uint64_t key = dpu_request_.getKeyUint(0); - string type = dpu_request_.getAttrString(DPU_TYPE); + Table remoteDpuTable(cfg_db, DashEniFwd::REMOTE_DPU_TABLE); + std::vector keys; + remoteDpuTable.getKeys(keys); + for (auto key : keys) + { + try + { + std::vector values; + remoteDpuTable.get(key, values); - dpus_ids_.push_back(key); + KeyOpFieldsValuesTuple kvo = { + key, SET_COMMAND, values + }; + + remote_dpu_request_.clear(); + remote_dpu_request_.parse(kvo); + string key = remote_dpu_request_.getKeyString(0); + + DpuData data; + data.type = dpu_type_t::CLUSTER; + data.pa_v4 = remote_dpu_request_.getAttrIP(DashEniFwd::PA_V4); + data.npu_v4 = remote_dpu_request_.getAttrIP(DashEniFwd::NPU_V4); + dpus_name_map_.insert({key, data}); + + SWSS_LOG_INFO("Remote DPU %s found, PA_V4: %s, NPU_V4: %s", + key.c_str(), + data.pa_v4.to_string().c_str(), + data.npu_v4.to_string().c_str() + ); + } + catch(exception& e) + { + SWSS_LOG_ERROR("Failed to parse key:%s in the %s", key.c_str(), DashEniFwd::REMOTE_DPU_TABLE); + } + } +} - if (type == "local") +void DpuRegistry::processVdpuTable(const DBConnector* cfg_db) +{ + Table vdpuTable(cfg_db, DashEniFwd::VDPU_TABLE); + std::vector keys; + vdpuTable.getKeys(keys); + for (auto key : keys) { - data.type = dpu_type_t::LOCAL; + try + { + std::vector values; + vdpuTable.get(key, values); + + KeyOpFieldsValuesTuple kvo = { + key, SET_COMMAND, values + }; + + vdpu_request_.clear(); + vdpu_request_.parse(kvo); + string key = vdpu_request_.getKeyString(0); + vector dpu_ids = vdpu_request_.getAttrStringList(DashEniFwd::DPU_IDS); + for (auto dpu_id : dpu_ids) + { + /* This method is expected to be called after the DPU/REMOTE_DPU table is populated */ + if (dpus_name_map_.find(dpu_id) != dpus_name_map_.end()) + { + vdpus_map_[key].push_back(dpu_id); + SWSS_LOG_INFO("DPU: %s belongs to VDPU %s", dpu_id.c_str(), key.c_str()); + } + else + { + SWSS_LOG_WARN("Invalid DPU ID: %s, not found in DPU/REMOTE_DPU table", dpu_id.c_str()); + } + } + } + catch(exception& e) + { + SWSS_LOG_ERROR("Failed to parse key:%s in the %s", key.c_str(), DashEniFwd::REMOTE_DPU_TABLE); + } } - else +} + +std::vector DpuRegistry::getIds() +{ + std::vector ids; + for (auto itr = vdpus_map_.begin(); itr != vdpus_map_.end(); itr++) { - // External type is not suported - data.type = dpu_type_t::CLUSTER; + ids.push_back(itr->first); } - - data.pa_v4 = dpu_request_.getAttrIP(DPU_PA_V4); - data.npu_v4 = dpu_request_.getAttrIP(DPU_NPU_V4); - dpus_.insert({key, data}); + return ids; } -std::vector DpuRegistry::getIds() +bool DpuRegistry::getDpuId(const std::string& vdpu_id, std::string& dpu_id) { - return dpus_ids_; + dpu_id.clear(); + auto itr = vdpus_map_.find(vdpu_id); + if (itr == vdpus_map_.end() || itr->second.empty()) return false; + dpu_id = itr->second[0]; + return true; } -bool DpuRegistry::getType(uint64_t id, dpu_type_t& val) +bool DpuRegistry::getType(const std::string& vdpu_id, dpu_type_t& val) { - auto itr = dpus_.find(id); - if (itr == dpus_.end()) return false; + std::string id; + if (!getDpuId(vdpu_id, id)) return false; + auto itr = dpus_name_map_.find(id); + if (itr == dpus_name_map_.end()) return false; val = itr->second.type; return true; } -bool DpuRegistry::getPaV4(uint64_t id, swss::IpAddress& val) +bool DpuRegistry::getPaV4(const std::string& vdpu_id, swss::IpAddress& val) { - auto itr = dpus_.find(id); - if (itr == dpus_.end()) return false; + std::string id; + if (!getDpuId(vdpu_id, id)) return false; + auto itr = dpus_name_map_.find(id); + if (itr == dpus_name_map_.end()) return false; val = itr->second.pa_v4; return true; } -bool DpuRegistry::getNpuV4(uint64_t id, swss::IpAddress& val) +bool DpuRegistry::getNpuV4(const std::string& vdpu_id, swss::IpAddress& val) { - auto itr = dpus_.find(id); - if (itr == dpus_.end()) return false; + std::string id; + if (!getDpuId(vdpu_id, id)) return false; + auto itr = dpus_name_map_.find(id); + if (itr == dpus_name_map_.end()) return false; val = itr->second.npu_v4; return true; } EniFwdCtxBase::EniFwdCtxBase(DBConnector* cfgDb, DBConnector* applDb) { - dpu_tbl_ = make_unique(cfgDb, CFG_DPU_TABLE); + cfg_db_ = make_unique(*cfgDb); port_tbl_ = make_unique
(cfgDb, CFG_PORT_TABLE_NAME); - vip_tbl_ = make_unique
(cfgDb, CFG_VIP_TABLE_TMP); - rule_table = make_unique(applDb, APP_ACL_RULE_TABLE_NAME); + vip_tbl_ = make_unique
(cfgDb, DashEniFwd::VIP_TABLE); + rule_table_ = make_unique(applDb, APP_ACL_RULE_TABLE_NAME); + acl_table_type_ = make_unique(applDb, APP_ACL_TABLE_TYPE_TABLE_NAME); + acl_table_ = make_unique(applDb, APP_ACL_TABLE_TABLE_NAME); vip_inferred_ = false; } void EniFwdCtxBase::populateDpuRegistry() { - dpu_info.populate(dpu_tbl_.get()); + dpu_info.populate(cfg_db_.get()); } std::set EniFwdCtxBase::findInternalPorts() @@ -433,43 +489,6 @@ string EniFwdCtxBase::getNbrAlias(const swss::IpAddress& nh_ip) return alias; } -bool EniFwdCtxBase::handleTunnelNH(const std::string& tunnel_name, swss::IpAddress endpoint, bool create) -{ - SWSS_LOG_ENTER(); - - auto nh_key = endpoint.to_string() + "@" + tunnel_name; - auto nh_itr = remote_nh_map_.find(nh_key); - - /* Delete Tunnel NH if ref_count = 0 */ - if (!create) - { - if (nh_itr != remote_nh_map_.end()) - { - if(!--nh_itr->second.first) - { - remote_nh_map_.erase(nh_key); - return removeNextHopTunnel(tunnel_name, endpoint); - } - } - return true; - } - - if (nh_itr == remote_nh_map_.end()) - { - /* Create a tunnel NH */ - auto nh_oid = createNextHopTunnel(tunnel_name, endpoint); - if (nh_oid == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_ERROR("Failed to create Tunnel Next Hop, name: %s. endpoint %s", tunnel_name.c_str(), - endpoint.to_string().c_str()); - } - remote_nh_map_.insert(make_pair(nh_key, make_pair(0, nh_oid))); - nh_itr = remote_nh_map_.find(nh_key); - } - nh_itr->second.first++; /* Increase the ref count, indicates number of rules using referencing this */ - return true; -} - IpPrefix EniFwdCtxBase::getVip() { SWSS_LOG_ENTER(); @@ -497,7 +516,6 @@ IpPrefix EniFwdCtxBase::getVip() return vip; } - void EniFwdCtx::initialize() { portsorch_ = gDirectory.get(); @@ -553,20 +571,80 @@ std::map& EniFwdCtx::getAllPorts() return portsorch_->getAllPorts(); } -sai_object_id_t EniFwdCtx::createNextHopTunnel(string tunnel_name, IpAddress ip_addr) +void EniFwdCtxBase::createAclRule(const std::string& rule, const std::vector& fv) +{ + if (acl_rule_count_ == 0) + { + addAclTable(); + } + acl_rule_count_++; + SWSS_LOG_INFO("Creating ACL rule: %s, ENI Forwarding rules count: %u", rule.c_str(), acl_rule_count_); + rule_table_->set(rule, fv); +} + +void EniFwdCtxBase::deleteAclRule(const std::string& rule) +{ + rule_table_->del(rule); + if (acl_rule_count_ > 0) + { + acl_rule_count_--; + SWSS_LOG_INFO("Deleted ACL rule: %s, ENI Forwarding rule count: %u", rule.c_str(), acl_rule_count_); + if (acl_rule_count_ == 0) + { + deleteAclTable(); + } + } + else + { + SWSS_LOG_ERROR("Attempted to delete ACL rule %s but rule count is already 0", rule.c_str()); + } +} + +void EniFwdCtxBase::addAclTable() { - return safetyWrapper(vxlanorch_, &VxlanTunnelOrch::createNextHopTunnel, - (sai_object_id_t)SAI_NULL_OBJECT_ID, - (string)tunnel_name, ip_addr, - MacAddress(), - (uint32_t)0); + vector match_list = { + MATCH_DST_IP, + MATCH_INNER_DST_MAC, + MATCH_TUNNEL_TERM + }; + + auto concat = [](const std::string &a, const std::string &b) { return a + "," + b; }; + + std::string matches = std::accumulate( + std::next(match_list.begin()), match_list.end(), match_list[0], + concat); + + string bpoint_types = string(BIND_POINT_TYPE_PORT) + "," + string(BIND_POINT_TYPE_PORTCHANNEL); + + vector fv_ = { + { ACL_TABLE_TYPE_MATCHES, matches}, + { ACL_TABLE_TYPE_ACTIONS, ACTION_REDIRECT_ACTION }, + { ACL_TABLE_TYPE_BPOINT_TYPES, bpoint_types} + }; + + acl_table_type_->set(DashEniFwd::TABLE_TYPE, fv_); + + auto ports = getBindPoints(); + std::string ports_str; + + if (!ports.empty()) + { + ports_str = std::accumulate(std::next(ports.begin()), ports.end(), ports[0], concat); + } + + /* Write ACL Table */ + vector table_fv_ = { + { ACL_TABLE_DESCRIPTION, "Contains Rule for DASH ENI Based Forwarding"}, + { ACL_TABLE_TYPE, DashEniFwd::TABLE_TYPE }, + { ACL_TABLE_STAGE, STAGE_INGRESS }, + { ACL_TABLE_PORTS, ports_str } + }; + + acl_table_->set(DashEniFwd::TABLE, table_fv_); } -bool EniFwdCtx::removeNextHopTunnel(string tunnel_name, IpAddress ip_addr) +void EniFwdCtxBase::deleteAclTable() { - return safetyWrapper(vxlanorch_, &VxlanTunnelOrch::removeNextHopTunnel, - false, - (std::string)tunnel_name, ip_addr, - MacAddress(), - (uint32_t)0); + acl_table_->del(DashEniFwd::TABLE); + acl_table_type_->del(DashEniFwd::TABLE_TYPE); } diff --git a/orchagent/dash/dashenifwdorch.h b/orchagent/dash/dashenifwdorch.h index 4ddba28a88..a513aa1a81 100644 --- a/orchagent/dash/dashenifwdorch.h +++ b/orchagent/dash/dashenifwdorch.h @@ -13,57 +13,10 @@ #include #include -// TODO: remove after the schema is finalized and added to swss-common -#define CFG_VIP_TABLE_TMP "VIP_TABLE" - -#define ENI_REDIRECT_TABLE_TYPE "ENI_REDIRECT" -#define ENI_REDIRECT_TABLE "ENI" -#define ENI_FWD_VDPU_IDS "vdpu_ids" -#define ENI_FWD_PRIMARY "primary_vdpu" -#define ENI_FWD_OUT_VNI "outbound_vni" -#define ENI_FWD_OUT_MAC_LOOKUP "outbound_eni_mac_lookup" - -#define DPU_TYPE "type" -#define DPU_STATE "state" -#define DPU_PA_V4 "pa_ipv4" -#define DPU_PA_V6 "pa_ipv6" -#define DPU_NPU_V4 "npu_ipv4" -#define DPU_NPU_V6 "npu_ipv6" - -#define DPU_LOCAL "local" -#define OUT_MAC_DIR "dst" - - - -const request_description_t eni_dash_fwd_desc = { - { REQ_T_STRING, REQ_T_MAC_ADDRESS }, // VNET_NAME, ENI_ID - { - { ENI_FWD_VDPU_IDS, REQ_T_UINT_LIST }, // DPU ID's - { ENI_FWD_PRIMARY, REQ_T_UINT }, - { ENI_FWD_OUT_VNI, REQ_T_UINT }, - { ENI_FWD_OUT_MAC_LOOKUP, REQ_T_STRING }, - }, - { } -}; - -const request_description_t dpu_table_desc = { - { REQ_T_UINT }, // DPU_ID - { - { DPU_TYPE, REQ_T_STRING }, - { DPU_STATE, REQ_T_STRING }, - { DPU_PA_V4, REQ_T_IP }, - { DPU_PA_V6, REQ_T_IP }, - { DPU_NPU_V4, REQ_T_IP }, - { DPU_NPU_V6, REQ_T_IP }, - }, - { DPU_TYPE, DPU_PA_V4, DPU_NPU_V4 } -}; - typedef enum { LOCAL, - CLUSTER, - EXTERNAL + CLUSTER } dpu_type_t; typedef enum @@ -90,10 +43,8 @@ typedef enum typedef enum { - INBOUND = 0, - OUTBOUND, - INBOUND_TERM, - OUTBOUND_TERM + NO_TUNNEL_TERM = 0, + TUNNEL_TERM } rule_type_t; @@ -106,13 +57,44 @@ class EniInfo; class EniFwdCtxBase; class EniFwdCtx; +namespace DashEniFwd +{ + /* TABLES; Until finalized and added to sonic-swss-common */ + static constexpr const char* DPU_TABLE = "DPU"; + static constexpr const char* REMOTE_DPU_TABLE = "REMOTE_DPU"; + static constexpr const char* VDPU_TABLE = "VDPU"; + static constexpr const char* VIP_TABLE = "VIP_TABLE"; + + /* ENI Registry Fields */ + static constexpr const char* TABLE_TYPE = "ENI_REDIRECT"; + static constexpr const char* TABLE = "ENI"; + static constexpr const char* VDPU_IDS = "vdpu_ids"; + static constexpr const char* PRIMARY = "primary_vdpu"; + + /* DPU Registry Fields */ + static constexpr const char* STATE = "state"; + static constexpr const char* PA_V4 = "pa_ipv4"; + static constexpr const char* PA_V6 = "pa_ipv6"; + static constexpr const char* NPU_V4 = "npu_ipv4"; + static constexpr const char* NPU_V6 = "npu_ipv6"; + static constexpr const char* DPU_IDS = "main_dpu_ids"; +}; + +const request_description_t eni_dash_fwd_desc = { + { REQ_T_STRING, REQ_T_MAC_ADDRESS }, // VNET_NAME, ENI_ID + { + { DashEniFwd::VDPU_IDS, REQ_T_STRING_LIST }, // VDPU ID's + { DashEniFwd::PRIMARY, REQ_T_STRING }, + }, + { DashEniFwd::PRIMARY } +}; class DashEniFwdOrch : public Orch2, public Observer { public: struct EniFwdRequest : public Request { - EniFwdRequest() : Request(eni_dash_fwd_desc, ':') {} + EniFwdRequest() : Request(eni_dash_fwd_desc, ':', true) {} }; DashEniFwdOrch(swss::DBConnector*, swss::DBConnector*, const std::string&, NeighOrch* neigh_orch_); @@ -128,25 +110,51 @@ class DashEniFwdOrch : public Orch2, public Observer private: void lazyInit(); - void initAclTableCfg(); void initLocalEndpoints(); void handleNeighUpdate(const NeighborUpdate& update); - void handleEniDpuMapping(uint64_t id, MacAddress mac, bool add = true); + void handleEniDpuMapping(const std::string& id, MacAddress mac, bool add = true); /* multimap because Multiple ENIs can be mapped to the same DPU */ - std::multimap dpu_eni_map_; + std::multimap dpu_eni_map_; /* Local Endpoint -> DPU mapping */ - std::map neigh_dpu_map_; + std::map neigh_dpu_map_; std::map eni_container_; bool ctx_initialized_ = false; shared_ptr ctx; - unique_ptr acl_table_; - unique_ptr acl_table_type_; NeighOrch* neighorch_; }; +const request_description_t dpu_table_desc = { + { REQ_T_STRING }, + { + { DashEniFwd::STATE, REQ_T_STRING }, + { DashEniFwd::PA_V4, REQ_T_IP }, + { DashEniFwd::PA_V6, REQ_T_IP }, + }, + { DashEniFwd::STATE, DashEniFwd::PA_V4 } +}; + +const request_description_t remote_dpu_table_desc = { + { REQ_T_STRING }, + { + { DashEniFwd::PA_V4, REQ_T_IP }, + { DashEniFwd::PA_V6, REQ_T_IP }, + { DashEniFwd::NPU_V4, REQ_T_IP }, + { DashEniFwd::NPU_V6, REQ_T_IP }, + }, + { DashEniFwd::PA_V4, DashEniFwd::NPU_V4 } +}; + +const request_description_t vdpu_table_desc = { + { REQ_T_STRING }, + { + { DashEniFwd::DPU_IDS, REQ_T_STRING_LIST }, + }, + { DashEniFwd::DPU_IDS } +}; + class DpuRegistry { public: @@ -159,21 +167,37 @@ class DpuRegistry struct DpuRequest : public Request { - DpuRequest() : Request(dpu_table_desc, '|' ) { } + DpuRequest() : Request(dpu_table_desc, '|', true) {} + }; + struct RemoteDpuRequest : public Request + { + RemoteDpuRequest() : Request(remote_dpu_table_desc, '|', true) {} + }; + struct VdpuRequest : public Request + { + VdpuRequest() : Request(vdpu_table_desc, '|', true) {} }; - void populate(Table*); - std::vector getIds(); - bool getType(uint64_t id, dpu_type_t& val); - bool getPaV4(uint64_t id, swss::IpAddress& val); - bool getNpuV4(uint64_t id, swss::IpAddress& val); + void populate(const swss::DBConnector*); + std::vector getIds(); + + bool getDpuId(const std::string& vdpu_id, std::string& dpu_id); + bool getType(const std::string& vdpu_id, dpu_type_t& val); + bool getPaV4(const std::string& vdpu_id, swss::IpAddress& val); + bool getNpuV4(const std::string& vdpu_id, swss::IpAddress& val); private: - void processDpuTable(const KeyOpFieldsValuesTuple& ); + void processDpuTable(const swss::DBConnector*); + void processRemoteDpuTable(const swss::DBConnector*); + void processVdpuTable(const swss::DBConnector*); - std::vector dpus_ids_; DpuRequest dpu_request_; - map dpus_; + RemoteDpuRequest remote_dpu_request_; + VdpuRequest vdpu_request_; + // DPU -> DpuData + unordered_map dpus_name_map_; + // VDPU Name -> [DPU2, DPU3, ...] + unordered_map> vdpus_map_; }; @@ -190,7 +214,7 @@ class EniNH swss::IpAddress getEp() {return endpoint_;} virtual void resolve(EniInfo& eni) = 0; - virtual void destroy(EniInfo& eni) = 0; + virtual void destroy(EniInfo& eni) {}; virtual string getRedirectVal() = 0; protected: @@ -209,7 +233,6 @@ class LocalEniNH : public EniNH setType(dpu_type_t::LOCAL); } void resolve(EniInfo& eni) override; - void destroy(EniInfo& eni) {} string getRedirectVal() override; }; @@ -224,11 +247,11 @@ class RemoteEniNH : public EniNH setType(dpu_type_t::CLUSTER); } void resolve(EniInfo& eni) override; - void destroy(EniInfo& eni) override; string getRedirectVal() override; private: string tunnel_name_; + string vni_; }; @@ -236,7 +259,6 @@ class EniAclRule { public: static const int BASE_PRIORITY; - static const std::vector RULE_NAMES; EniAclRule(rule_type_t type, EniInfo& eni) : type_(type), @@ -272,12 +294,10 @@ class EniInfo string toKey() const; std::shared_ptr& getCtx() {return ctx;} - bool findLocalEp(uint64_t&) const; + bool findLocalEp(std::string&) const; swss::MacAddress getMac() const { return mac_; } // Can only be set during object creation - std::vector getEpList() { return ep_list_; } - uint64_t getPrimaryId() const { return primary_id_; } - uint64_t getOutVni() const { return outbound_vni_; } - std::string getOutMacLookup() const { return outbound_mac_lookup_; } + std::vector getEpList() { return ep_list_; } + std::string getPrimaryId() const { return primary_id_; } std::string getVnet() const { return vnet_name_; } protected: @@ -291,10 +311,8 @@ class EniInfo std::shared_ptr ctx; std::map rule_container_; - std::vector ep_list_; - uint64_t primary_id_; - uint64_t outbound_vni_; - std::string outbound_mac_lookup_; + std::vector ep_list_; + std::string primary_id_; std::string vnet_name_; swss::MacAddress mac_; std::string mac_key_; // Formatted MAC key @@ -311,9 +329,11 @@ class EniFwdCtxBase void populateDpuRegistry(); std::vector getBindPoints(); std::string getNbrAlias(const swss::IpAddress& ip); - bool handleTunnelNH(const std::string&, swss::IpAddress, bool); swss::IpPrefix getVip(); + void createAclRule(const std::string&, const std::vector&); + void deleteAclRule(const std::string&); + virtual void initialize() = 0; /* API's that call other orchagents */ virtual std::map& getAllPorts() = 0; @@ -322,48 +342,32 @@ class EniFwdCtxBase virtual string getRouterIntfsAlias(const IpAddress &, const string & = "") = 0; virtual bool findVnetVni(const std::string&, uint64_t& ) = 0; virtual bool findVnetTunnel(const std::string&, string&) = 0; - virtual sai_object_id_t createNextHopTunnel(string, IpAddress) = 0; - virtual bool removeNextHopTunnel(string, IpAddress) = 0; DpuRegistry dpu_info; - unique_ptr rule_table; + protected: std::set findInternalPorts(); + void addAclTable(); + void deleteAclTable(); + /* Reference counting for ACL rules */ + uint32_t acl_rule_count_ = 0; - /* RemoteNh Key -> {ref_count, sai oid} */ - std::map> remote_nh_map_; /* Mapping between DPU Nbr and Alias */ std::map nh_alias_map_; unique_ptr port_tbl_; unique_ptr vip_tbl_; - unique_ptr dpu_tbl_; + unique_ptr cfg_db_; + unique_ptr rule_table_; + unique_ptr acl_table_; + unique_ptr acl_table_type_; - /* Only one vip is expected per T1 */ + /* Only one vip is expected per T1 cluster */ swss::IpPrefix vip; bool vip_inferred_; }; -/* - Wrapper on API's that are throwable -*/ -template -R safetyWrapper(C* ptr, R (C::*func)(Args...), R defaultValue, Args&&... args) -{ - SWSS_LOG_ENTER(); - try - { - return (ptr->*func)(std::forward(args)...); - } - catch (const std::exception &e) - { - SWSS_LOG_ERROR("Exception thrown.. %s", e.what()); - return defaultValue; - } -} - - /* Implements API's to access other orchagents */ @@ -380,8 +384,6 @@ class EniFwdCtx : public EniFwdCtxBase bool findVnetVni(const std::string&, uint64_t&) override; bool findVnetTunnel(const std::string&, string&) override; std::map& getAllPorts() override; - virtual sai_object_id_t createNextHopTunnel(string, IpAddress) override; - virtual bool removeNextHopTunnel(string, IpAddress) override; private: PortsOrch* portsorch_; diff --git a/orchagent/request_parser.cpp b/orchagent/request_parser.cpp index 665026394f..e2a63c6a73 100644 --- a/orchagent/request_parser.cpp +++ b/orchagent/request_parser.cpp @@ -41,6 +41,7 @@ void Request::clear() attr_item_bools_.clear(); attr_item_mac_addresses_.clear(); attr_item_packet_actions_.clear(); + attr_item_string_list_.clear(); is_parsed_ = false; } @@ -156,8 +157,16 @@ void Request::parseAttrs(const KeyOpFieldsValuesTuple& request) const auto item = request_description_.attr_item_types.find(fvField(*i)); if (item == not_found) { - throw std::invalid_argument(std::string("Unknown attribute name: ") + fvField(*i)); + if (!relaxed_attr_parsing_) + { + throw std::invalid_argument(std::string("Unknown attribute name: ") + fvField(*i)); + } + else + { + continue; + } } + attr_names_.insert(fvField(*i)); switch(item->second) { @@ -200,6 +209,9 @@ void Request::parseAttrs(const KeyOpFieldsValuesTuple& request) case REQ_T_BOOL_LIST: attr_item_bool_list_[fvField(*i)] = parseBoolList(fvValue(*i)); break; + case REQ_T_STRING_LIST: + attr_item_string_list_[fvField(*i)] = parseStringList(fvValue(*i)); + break; default: throw std::logic_error(std::string("Not implemented attribute type parser for attribute:") + fvField(*i)); } @@ -451,3 +463,22 @@ vector Request::parseUintList(const std::string& str) throw std::invalid_argument(std::string("Out of range unsigned integer: ") + str); } } + +vector Request::parseStringList(const std::string& str) +{ + std::vector res; + try + { + std::string token; + std::istringstream iss(str); + while (getline(iss, token, ',')) + { + res.emplace_back(token); + } + } + catch (std::invalid_argument& _) + { + throw std::invalid_argument(std::string("Invalid string list: ") + str); + } + return res; +} diff --git a/orchagent/request_parser.h b/orchagent/request_parser.h index 4b94802123..f63c3fd316 100644 --- a/orchagent/request_parser.h +++ b/orchagent/request_parser.h @@ -23,6 +23,7 @@ typedef enum _request_types_t REQ_T_IP_LIST, REQ_T_UINT_LIST, REQ_T_BOOL_LIST, + REQ_T_STRING_LIST, } request_types_t; typedef struct _request_description @@ -175,11 +176,18 @@ class Request return attr_item_bool_list_.at(attr_name); } + const std::vector& getAttrStringList(const std::string& attr_name) const + { + assert(is_parsed_); + return attr_item_string_list_.at(attr_name); + } + protected: - Request(const request_description_t& request_description, const char key_separator) + Request(const request_description_t& request_description, const char key_separator, bool relaxed_attr_parsing = false) : request_description_(request_description), key_separator_(key_separator), is_parsed_(false), + relaxed_attr_parsing_(relaxed_attr_parsing), number_of_key_items_(request_description.key_item_types.size()) { } @@ -200,6 +208,7 @@ class Request std::vector parseMacAddressList(const std::string& str); std::vector parseUintList(const std::string& str); std::vector parseBoolList(const std::string& str); + std::vector parseStringList(const std::string& str); sai_packet_action_t parsePacketAction(const std::string& str); @@ -207,6 +216,8 @@ class Request char key_separator_; bool is_parsed_; size_t number_of_key_items_; + // Enable if only interested in only a subset of attributes + bool relaxed_attr_parsing_; std::string table_name_; std::string operation_; @@ -231,6 +242,7 @@ class Request std::unordered_map> attr_item_ip_list_; std::unordered_map> attr_item_mac_addresses_list_; std::unordered_map> attr_item_uint_list_; + std::unordered_map> attr_item_string_list_; }; #endif // __REQUEST_PARSER_H diff --git a/tests/mock_tests/aclorch_rule_ut.cpp b/tests/mock_tests/aclorch_rule_ut.cpp index 3268c663e7..277986ade4 100644 --- a/tests/mock_tests/aclorch_rule_ut.cpp +++ b/tests/mock_tests/aclorch_rule_ut.cpp @@ -164,10 +164,10 @@ namespace aclorch_rule_test static_cast(m_VxlanTunnelOrch)->doTask(*consumer.get()); } - void createTunnelNH(string ip) + void createTunnelNH(string ip, uint32_t vni) { IpAddress mock_nh_ip(ip); - ASSERT_EQ(m_VxlanTunnelOrch->createNextHopTunnel(mock_tunnel_name, mock_nh_ip, MacAddress()), nh_oid); + ASSERT_EQ(m_VxlanTunnelOrch->createNextHopTunnel(mock_tunnel_name, mock_nh_ip, MacAddress(), vni), nh_oid); } void populateAclTale() @@ -195,8 +195,14 @@ namespace aclorch_rule_test }); } - void addTunnelNhRule(string ip, string tunnel_name) + void addTunnelNhRule(string ip, string tunnel_name, string vni) { + string redirect_str = ip + "@" + tunnel_name; + if (!vni.empty()) + { + redirect_str = redirect_str + ',' + vni; + } + /* Create a rule */ doAclRuleTask({ { @@ -206,7 +212,7 @@ namespace aclorch_rule_test { RULE_PRIORITY, "9999" }, { MATCH_DST_IP, "10.0.0.1/24" }, { MATCH_TUNNEL_TERM, "true" }, - { ACTION_REDIRECT_ACTION, ip + "@" + tunnel_name } + { ACTION_REDIRECT_ACTION, redirect_str } } } }); @@ -239,7 +245,7 @@ namespace aclorch_rule_test Return(SAI_STATUS_SUCCESS) )); EXPECT_CALL(*mock_sai_acl_api, create_acl_entry).WillOnce(testing::Invoke(aclMockState.get(), &SaiMockState::handleCreate)); - addTunnelNhRule(mock_nh_ip_str, mock_tunnel_name); + addTunnelNhRule(mock_nh_ip_str, mock_tunnel_name, "1000"); /* Verify SAI attributes and if the rule is created */ SaiAttributeList attr_list(SAI_OBJECT_TYPE_ACL_ENTRY, vector({ @@ -268,8 +274,8 @@ namespace aclorch_rule_test Return(SAI_STATUS_SUCCESS) )); EXPECT_CALL(*mock_sai_acl_api, create_acl_entry).WillOnce(testing::Invoke(aclMockState.get(), &SaiMockState::handleCreate)); - createTunnelNH(mock_nh_ip_str); - addTunnelNhRule(mock_nh_ip_str, mock_tunnel_name); + createTunnelNH(mock_nh_ip_str, 1000); + addTunnelNhRule(mock_nh_ip_str, mock_tunnel_name, "1000"); ASSERT_TRUE(gAclOrch->getAclRule(acl_table, acl_rule)); /* ACL Rule is deleted but nexthop is not deleted */ @@ -282,7 +288,7 @@ namespace aclorch_rule_test TEST_F(AclRedirectActionTest, TunnelNH_InvalidTunnel) { EXPECT_CALL(*mock_sai_acl_api, create_acl_entry).Times(0); - addTunnelNhRule(mock_nh_ip_str, mock_invalid_tunnel_name); + addTunnelNhRule(mock_nh_ip_str, mock_invalid_tunnel_name, ""); ASSERT_FALSE(gAclOrch->getAclRule(acl_table, acl_rule)); } @@ -292,7 +298,7 @@ namespace aclorch_rule_test Return(SAI_STATUS_FAILURE) /* create next hop fails */ ); EXPECT_CALL(*mock_sai_acl_api, create_acl_entry).Times(0); - addTunnelNhRule(mock_invalid_nh_ip_str, mock_tunnel_name); + addTunnelNhRule(mock_invalid_nh_ip_str, mock_tunnel_name, ""); ASSERT_FALSE(gAclOrch->getAclRule(acl_table, acl_rule)); } } diff --git a/tests/mock_tests/dashenifwdorch_ut.cpp b/tests/mock_tests/dashenifwdorch_ut.cpp index f3a157f87d..83cda7cfb2 100644 --- a/tests/mock_tests/dashenifwdorch_ut.cpp +++ b/tests/mock_tests/dashenifwdorch_ut.cpp @@ -23,8 +23,6 @@ namespace dashenifwdorch_ut MOCK_METHOD(void, resolveNeighbor, (const NextHopKey&), (override)); MOCK_METHOD(bool, findVnetVni, (const std::string&, uint64_t&), (override)); MOCK_METHOD(bool, findVnetTunnel, (const std::string&, std::string&), (override)); - MOCK_METHOD(sai_object_id_t, createNextHopTunnel, (std::string, IpAddress), (override)); - MOCK_METHOD(bool, removeNextHopTunnel, (std::string, IpAddress), (override)); MOCK_METHOD((std::map&), getAllPorts, (), (override)); }; @@ -35,6 +33,9 @@ namespace dashenifwdorch_ut unique_ptr applDb; unique_ptr chassisApplDb; unique_ptr
dpuTable; + unique_ptr
remoteDpuTable; + unique_ptr
vdpuTable; + unique_ptr
eniFwdTable; unique_ptr
aclRuleTable; unique_ptr eniOrch; @@ -56,38 +57,68 @@ namespace dashenifwdorch_ut string remote_npuv4 = "20.0.0.2"; string remote_2_npuv4 = "20.0.0.3"; - uint64_t test_vni = 1234; - uint64_t test_vni2 = 5678; + std::map allPorts; + uint64_t test_vni = 1000; int BASE_PRIORITY = 9996; void populateDpuTable() { /* Add 1 local and 1 cluster DPU */ - dpuTable->set("1", + dpuTable->set("local_dpu", + { + { DashEniFwd::PA_V4, local_pav4 }, + { DashEniFwd::STATE, "up" }, + { "gnmi_port", "50051" }, + { "local_port", "8080" }, + }, SET_COMMAND); + + dpuTable->set("local_down_dpu", { - { DPU_TYPE, "local" }, - { DPU_PA_V4, local_pav4 }, - { DPU_NPU_V4, local_npuv4 }, + { DashEniFwd::PA_V4, local_pav4 }, + { DashEniFwd::STATE, "down" }, }, SET_COMMAND); - dpuTable->set("2", + remoteDpuTable->set("remote_dpu", { - { DPU_TYPE, "cluster" }, - { DPU_PA_V4, remote_pav4 }, - { DPU_NPU_V4, remote_npuv4 }, + { DashEniFwd::PA_V4, remote_pav4 }, + { DashEniFwd::NPU_V4, remote_npuv4 }, }, SET_COMMAND); - dpuTable->set("3", + remoteDpuTable->set("remote_dpu2", { - { DPU_TYPE, "cluster" }, - { DPU_PA_V4, remote_2_pav4 }, - { DPU_NPU_V4, remote_2_npuv4 }, + { DashEniFwd::PA_V4, remote_2_pav4 }, + { DashEniFwd::NPU_V4, remote_2_npuv4 }, + }, SET_COMMAND); + + vdpuTable->set("vdpu0", + { + { DashEniFwd::DPU_IDS, "local_dpu" }, + }, SET_COMMAND); + + vdpuTable->set("vdpu1", + { + { DashEniFwd::DPU_IDS, "remote_dpu" }, + }, SET_COMMAND); + + vdpuTable->set("vdpu2", + { + { DashEniFwd::DPU_IDS, "remote_dpu2" }, + }, SET_COMMAND); + + vdpuTable->set("vdpu3", + { + { DashEniFwd::DPU_IDS, "invalid_dpu" }, + }, SET_COMMAND); + + vdpuTable->set("vdpu4", + { + { DashEniFwd::DPU_IDS, "local_down_dpu" }, }, SET_COMMAND); } void populateVip() { - Table vipTable(cfgDb.get(), CFG_VIP_TABLE_TMP); + Table vipTable(cfgDb.get(), DashEniFwd::VIP_TABLE); vipTable.set(test_vip, {{}}); } @@ -121,13 +152,29 @@ namespace dashenifwdorch_ut << key << ": Still Exist"; } + void checkNoKeyExists(Table* m_table, string expected_key) + { + std::string val; + std::vector keys; + m_table->getKeys(keys); + for (auto& key : keys) { + if (key == expected_key) + { + EXPECT_FALSE(true) << expected_key << ": Still Exist"; + } + } + } + void SetUp() override { testing_db::reset(); cfgDb = make_unique("CONFIG_DB", 0); applDb = make_unique("APPL_DB", 0); chassisApplDb = make_unique("CHASSIS_APP_DB", 0); /* Initialize tables */ - dpuTable = make_unique
(cfgDb.get(), CFG_DPU_TABLE); + dpuTable = make_unique
(cfgDb.get(), DashEniFwd::DPU_TABLE); + remoteDpuTable = make_unique
(cfgDb.get(), DashEniFwd::REMOTE_DPU_TABLE); + vdpuTable = make_unique
(cfgDb.get(), DashEniFwd::VDPU_TABLE); + eniFwdTable = make_unique
(applDb.get(), APP_DASH_ENI_FORWARD_TABLE); aclRuleTable = make_unique
(applDb.get(), APP_ACL_RULE_TABLE_NAME); /* Populate DPU Configuration */ @@ -137,6 +184,17 @@ namespace dashenifwdorch_ut /* Clear the default context and Patch with the Mock */ ctx = make_shared(cfgDb.get(), applDb.get()); + /* Create a set of ports */ + allPorts["Ethernet0"] = Port("Ethernet0", Port::PHY); + allPorts["Ethernet4"] = Port("Ethernet4", Port::PHY); + allPorts["Ethernet8"] = Port("Ethernet8", Port::PHY); + allPorts["Ethernet16"] = Port("Ethernet16", Port::PHY); + allPorts["PortChannel1011"] = Port("PortChannel1012", Port::LAG); + allPorts["PortChannel1012"] = Port("Ethernet16", Port::LAG); + allPorts["PortChannel1011"].m_members.insert("Ethernet8"); + allPorts["PortChannel1012"].m_members.insert("Ethernet16"); + ON_CALL(*ctx, getAllPorts()).WillByDefault(ReturnRef(allPorts)); + eniOrch->ctx.reset(); eniOrch->ctx = ctx; eniOrch->ctx->populateDpuRegistry(); @@ -149,30 +207,42 @@ namespace dashenifwdorch_ut */ TEST_F(DashEniFwdOrchTest, TestDpuRegistry) { - dpu_type_t type = dpu_type_t::EXTERNAL; + dpu_type_t type; swss::IpAddress pa_v4; swss::IpAddress npu_v4; EniFwdCtx ctx(cfgDb.get(), applDb.get()); ctx.populateDpuRegistry(); - EXPECT_TRUE(ctx.dpu_info.getType(1, type)); + EXPECT_TRUE(ctx.dpu_info.getType("vdpu0", type)); EXPECT_EQ(type, dpu_type_t::LOCAL); - EXPECT_TRUE(ctx.dpu_info.getType(2, type)); - EXPECT_EQ(type, dpu_type_t::CLUSTER); - - EXPECT_TRUE(ctx.dpu_info.getPaV4(1, pa_v4)); + EXPECT_TRUE(ctx.dpu_info.getPaV4("vdpu0", pa_v4)); EXPECT_EQ(pa_v4.to_string(), local_pav4); - EXPECT_TRUE(ctx.dpu_info.getPaV4(2, pa_v4)); + + EXPECT_TRUE(ctx.dpu_info.getType("vdpu1", type)); + EXPECT_EQ(type, dpu_type_t::CLUSTER); + EXPECT_TRUE(ctx.dpu_info.getPaV4("vdpu1", pa_v4)); EXPECT_EQ(pa_v4.to_string(), remote_pav4); - - EXPECT_TRUE(ctx.dpu_info.getNpuV4(1, npu_v4)); - EXPECT_EQ(npu_v4.to_string(), local_npuv4); - EXPECT_TRUE(ctx.dpu_info.getNpuV4(2, npu_v4)); + EXPECT_TRUE(ctx.dpu_info.getNpuV4("vdpu1", npu_v4)); EXPECT_EQ(npu_v4.to_string(), remote_npuv4); + + EXPECT_TRUE(ctx.dpu_info.getNpuV4("vdpu2", npu_v4)); + EXPECT_EQ(npu_v4.to_string(), remote_2_npuv4); - vector ids = {1, 2, 3}; - EXPECT_EQ(ctx.dpu_info.getIds(), ids); + /* Invalid DPU */ + EXPECT_FALSE(ctx.dpu_info.getNpuV4("vdpu3", npu_v4)); + EXPECT_FALSE(ctx.dpu_info.getType("vdpu3", type)); + EXPECT_FALSE(ctx.dpu_info.getPaV4("vdpu3", pa_v4)); + + /* Down DPU */ + EXPECT_FALSE(ctx.dpu_info.getNpuV4("vdpu4", npu_v4)); + EXPECT_FALSE(ctx.dpu_info.getType("vdpu4", type)); + EXPECT_FALSE(ctx.dpu_info.getPaV4("vdpu4", pa_v4)); + + vector exp_ids = {"vdpu0", "vdpu1", "vdpu2"}; + auto ids = ctx.dpu_info.getIds(); + std::sort(ids.begin(), ids.end()); + EXPECT_EQ(ids, exp_ids); } /* @@ -185,7 +255,7 @@ namespace dashenifwdorch_ut /* Mock calls to intfsOrch and neighOrch If neighbor is already resolved, resolveNeighbor is not called */ EXPECT_CALL(*ctx, getRouterIntfsAlias(nh_ip, _)).WillOnce(Return(alias_dpu)); /* Once per local endpoint */ - EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(4).WillRepeatedly(Return(true)); + EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(2).WillRepeatedly(Return(true)); EXPECT_CALL(*ctx, resolveNeighbor(nh)).Times(0); doDashEniFwdTableTask(applDb.get(), @@ -195,9 +265,8 @@ namespace dashenifwdorch_ut vnet_name + ":" + test_mac, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "1" }, // Local endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni) } + { DashEniFwd::VDPU_IDS, "vdpu0,vdpu1" }, + { DashEniFwd::PRIMARY, "vdpu0" }, // Local endpoint is the primary } } } @@ -205,113 +274,19 @@ namespace dashenifwdorch_ut ); /* Check ACL Rules */ - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN", { + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key, { { ACTION_REDIRECT_ACTION , local_pav4 }, { MATCH_DST_IP, test_vip }, - { RULE_PRIORITY, to_string(BASE_PRIORITY + INBOUND) }, + { RULE_PRIORITY, to_string(BASE_PRIORITY) }, { MATCH_INNER_DST_MAC, test_mac } }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT", { - { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_DST_IP, test_vip }, - { RULE_PRIORITY, to_string(BASE_PRIORITY + OUTBOUND) }, - { MATCH_INNER_SRC_MAC, test_mac }, { MATCH_TUNNEL_VNI, to_string(test_vni) } - }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN_TERM", { + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_TERM", { { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_DST_IP, test_vip }, - { RULE_PRIORITY, to_string(BASE_PRIORITY + INBOUND_TERM) }, + { RULE_PRIORITY, to_string(BASE_PRIORITY + rule_type_t::TUNNEL_TERM) }, { MATCH_INNER_DST_MAC, test_mac }, { MATCH_TUNNEL_TERM, "true"} }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT_TERM", { - { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_DST_IP, test_vip }, - { RULE_PRIORITY, to_string(BASE_PRIORITY + OUTBOUND_TERM) }, - { MATCH_INNER_SRC_MAC, test_mac }, { MATCH_TUNNEL_VNI, to_string(test_vni) }, - { MATCH_TUNNEL_TERM, "true"} - }); - } - - /* - Infer VNI by reading from the VnetOrch, resolved Neighbor - */ - TEST_F(DashEniFwdOrchTest, LocalNeighbor_NoVNI) - { - auto nh_ip = swss::IpAddress(local_pav4); - NextHopKey nh = {nh_ip, alias_dpu}; - - EXPECT_CALL(*ctx, findVnetVni(vnet_name, _)).Times(1) // Called once per ENI - .WillRepeatedly(DoAll( - SetArgReferee<1>(test_vni2), - Return(true) - )); - - EXPECT_CALL(*ctx, getRouterIntfsAlias(nh_ip, _)).WillOnce(Return(alias_dpu)); - EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(4).WillRepeatedly(Return(true)); - - doDashEniFwdTableTask(applDb.get(), - deque( - { - { - vnet_name + ":" + test_mac2, - SET_COMMAND, - { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "1" }, // Local endpoint is the primary - // No Explicit VNI from the DB, should be inferred from the VNetOrch - } - } - } - ) - ); - - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT", { - { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_DST_IP, test_vip }, - { RULE_PRIORITY, to_string(BASE_PRIORITY + OUTBOUND) }, - { MATCH_INNER_SRC_MAC, test_mac2 }, { MATCH_TUNNEL_VNI, to_string(test_vni2) } - }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT_TERM", { - { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_DST_IP, test_vip }, - { RULE_PRIORITY, to_string(BASE_PRIORITY + OUTBOUND_TERM) }, - { MATCH_INNER_SRC_MAC, test_mac2 }, { MATCH_TUNNEL_VNI, to_string(test_vni2) }, - { MATCH_TUNNEL_TERM, "true"} - }); } - /* - Verify MAC direction - */ - TEST_F(DashEniFwdOrchTest, LocalNeighbor_MacDirection) - { - auto nh_ip = swss::IpAddress(local_pav4); - NextHopKey nh = {nh_ip, alias_dpu}; - - EXPECT_CALL(*ctx, getRouterIntfsAlias(nh_ip, _)).WillOnce(Return(alias_dpu)); - EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(4).WillRepeatedly(Return(true)); - - doDashEniFwdTableTask(applDb.get(), - deque( - { - { - vnet_name + ":" + test_mac2, - SET_COMMAND, - { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "1" }, // Local endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni2) }, - { ENI_FWD_OUT_MAC_LOOKUP, "dst" } - } - } - } - ) - ); - - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT", { - { MATCH_INNER_DST_MAC, test_mac2 }, - }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT_TERM", { - { MATCH_INNER_DST_MAC, test_mac2 }, { MATCH_TUNNEL_TERM, "true"} - }); - } - - /* VNI is provided by HaMgrd, UnResolved Neighbor */ @@ -323,9 +298,9 @@ namespace dashenifwdorch_ut EXPECT_CALL(*ctx, getRouterIntfsAlias(nh_ip, _)).WillOnce(Return(alias_dpu)); /* Neighbor is not resolved, 1 per rule + 1 for initLocalEndpoints */ - EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(9).WillRepeatedly(Return(false)); + EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(5).WillRepeatedly(Return(false)); /* resolveNeighbor is called because the neigh is not resolved */ - EXPECT_CALL(*ctx, resolveNeighbor(nh)).Times(9); /* 1 per rule + 1 for initLocalEndpoints */ + EXPECT_CALL(*ctx, resolveNeighbor(nh)).Times(5); /* 1 per rule + 1 for initLocalEndpoints */ eniOrch->initLocalEndpoints(); @@ -337,70 +312,61 @@ namespace dashenifwdorch_ut vnet_name + ":" + test_mac, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "1" }, // Local endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni) } + { DashEniFwd::VDPU_IDS, "vdpu0,vdpu1" }, + { DashEniFwd::PRIMARY, "vdpu0" }, // Local endpoint is the primary } }, { vnet_name + ":" + test_mac2, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "1" }, // Local endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni2) } + { DashEniFwd::VDPU_IDS, "vdpu0,vdpu1" }, + { DashEniFwd::PRIMARY, "vdpu0" }, // Local endpoint is the primary } } } ) ); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_IN"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_IN_TERM"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_OUT"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_OUT_TERM"); - + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key); + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_TERM"); + /* Neighbor is resolved, Trigger a nexthop update (1 for Neigh Update) * 4 for Types of Rules */ - EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(8).WillRepeatedly(Return(true)); + EXPECT_CALL(*ctx, isNeighborResolved(nh)).Times(4).WillRepeatedly(Return(true)); NeighborEntry temp_entry = nh; NeighborUpdate update = { temp_entry, MacAddress(), true }; eniOrch->update(SUBJECT_TYPE_NEIGH_CHANGE, static_cast(&update)); /* Check ACL Rules */ - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN", { + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key, { { ACTION_REDIRECT_ACTION, local_pav4 } }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN_TERM", { + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_TERM", { { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_TUNNEL_TERM, "true"} }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT", { - { ACTION_REDIRECT_ACTION, local_pav4 }, - }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT_TERM", { - { ACTION_REDIRECT_ACTION, local_pav4 }, { MATCH_TUNNEL_TERM, "true"}, - { MATCH_INNER_SRC_MAC, test_mac2 }, - }); } /* Remote Endpoint */ TEST_F(DashEniFwdOrchTest, RemoteNeighbor) - { - IpAddress remote_npuv4_ip = IpAddress(remote_npuv4); + { EXPECT_CALL(*ctx, getRouterIntfsAlias(_, _)).WillOnce(Return(alias_dpu)); /* calls to neighOrch expected for tunn termination entries */ - EXPECT_CALL(*ctx, isNeighborResolved(_)).Times(4).WillRepeatedly(Return(true)); + EXPECT_CALL(*ctx, isNeighborResolved(_)).Times(2).WillRepeatedly(Return(true)); - EXPECT_CALL(*ctx, findVnetTunnel(vnet_name, _)).Times(4) // Once per non-tunnel term rules + EXPECT_CALL(*ctx, findVnetTunnel(vnet_name, _)).Times(2) // Once per non-tunnel term rules .WillRepeatedly(DoAll( SetArgReferee<1>(tunnel_name), Return(true) )); - EXPECT_CALL(*ctx, createNextHopTunnel(tunnel_name, remote_npuv4_ip)).Times(1) - .WillRepeatedly(Return(0x400000000064d)); // Only once since same NH object will be re-used + EXPECT_CALL(*ctx, findVnetVni(vnet_name, _)).Times(2) // Called once per ENI + .WillRepeatedly(DoAll( + SetArgReferee<1>(test_vni), + Return(true) + )); doDashEniFwdTableTask(applDb.get(), deque( @@ -409,18 +375,16 @@ namespace dashenifwdorch_ut vnet_name + ":" + test_mac, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "2" }, // Remote endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni) } + { DashEniFwd::VDPU_IDS, "vdpu0,vdpu1" }, + { DashEniFwd::PRIMARY, "vdpu1" }, // Remote endpoint is the primary } }, { vnet_name + ":" + test_mac2, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "2" }, // Remote endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni2) } + { DashEniFwd::VDPU_IDS, "vdpu0,vdpu1" }, + { DashEniFwd::PRIMARY, "vdpu1" }, // Remote endpoint is the primary } } } @@ -428,26 +392,10 @@ namespace dashenifwdorch_ut ); /* Check ACL Rules */ - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN", { - { ACTION_REDIRECT_ACTION, remote_npuv4 + "@" + tunnel_name } - }); - /* Tunnel termiantion rule should be local endpoint */ - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT_TERM", { - { ACTION_REDIRECT_ACTION, local_pav4 } - }); - - /* Rules for second ENI */ - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac2_key + "_OUT", { - { ACTION_REDIRECT_ACTION, remote_npuv4 + "@" + tunnel_name } + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key, { + { ACTION_REDIRECT_ACTION, remote_npuv4 + "@" + tunnel_name + "," + to_string(test_vni) } }); - /* Check Ref count */ - ASSERT_TRUE(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name) != ctx->remote_nh_map_.end()); - EXPECT_EQ(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name)->second.first, 4); /* 4 rules using this NH */ - EXPECT_EQ(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name)->second.second, 0x400000000064d); - - EXPECT_CALL(*ctx, removeNextHopTunnel(tunnel_name, remote_npuv4_ip)).WillOnce(Return(true)); - /* Delete all ENI's */ doDashEniFwdTableTask(applDb.get(), deque( @@ -465,16 +413,10 @@ namespace dashenifwdorch_ut } ) ); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_IN"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_IN_TERM"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_OUT"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_OUT_TERM"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_IN"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_IN_TERM"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_OUT"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_OUT_TERM"); - /* Check the tunnel is removed */ - ASSERT_TRUE(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name) == ctx->remote_nh_map_.end()); + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key ); + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac2_key + "_TERM"); + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key); + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_TERM"); } /* @@ -482,17 +424,19 @@ namespace dashenifwdorch_ut */ TEST_F(DashEniFwdOrchTest, RemoteNeighbor_SwitchToLocal) { - IpAddress remote_npuv4_ip = IpAddress(remote_npuv4); EXPECT_CALL(*ctx, getRouterIntfsAlias(_, _)).WillOnce(Return(alias_dpu)); - /* 2 calls made for tunnel termination rules */ - EXPECT_CALL(*ctx, isNeighborResolved(_)).Times(2).WillRepeatedly(Return(true)); - EXPECT_CALL(*ctx, findVnetTunnel(vnet_name, _)).Times(2) // Once per non-tunnel term rules + /* 1 calls made for tunnel termination rules */ + EXPECT_CALL(*ctx, isNeighborResolved(_)).Times(1).WillRepeatedly(Return(true)); + EXPECT_CALL(*ctx, findVnetTunnel(vnet_name, _)).Times(1) // Once per non-tunnel term rules .WillRepeatedly(DoAll( SetArgReferee<1>(tunnel_name), Return(true) )); - EXPECT_CALL(*ctx, createNextHopTunnel(tunnel_name, remote_npuv4_ip)).Times(1) - .WillRepeatedly(Return(0x400000000064d)); // Only once since same NH object will be re-used + EXPECT_CALL(*ctx, findVnetVni(vnet_name, _)).Times(1) // Called once per ENI + .WillRepeatedly(DoAll( + SetArgReferee<1>(test_vni), + Return(true) + )); doDashEniFwdTableTask(applDb.get(), deque( @@ -501,28 +445,20 @@ namespace dashenifwdorch_ut vnet_name + ":" + test_mac, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "1,2" }, - { ENI_FWD_PRIMARY, "2" }, // Remote endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni) } + { DashEniFwd::VDPU_IDS, "vdpu0,vdpu1" }, + { DashEniFwd::PRIMARY, "vdpu1" }, // Remote endpoint is the primary } } } ) ); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN", { - { ACTION_REDIRECT_ACTION, remote_npuv4 + "@" + tunnel_name } - }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT", { - { ACTION_REDIRECT_ACTION, remote_npuv4 + "@" + tunnel_name } + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key, { + { ACTION_REDIRECT_ACTION, remote_npuv4 + "@" + tunnel_name + ',' + to_string(test_vni) } }); - ASSERT_TRUE(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name) != ctx->remote_nh_map_.end()); - EXPECT_EQ(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name)->second.first, 2); /* 4 rules using this NH */ - EXPECT_EQ(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name)->second.second, 0x400000000064d); - /* 2 calls will be made for non tunnel termination rules after primary switch */ - EXPECT_CALL(*ctx, isNeighborResolved(_)).Times(2).WillRepeatedly(Return(true)); - EXPECT_CALL(*ctx, removeNextHopTunnel(tunnel_name, remote_npuv4_ip)).WillOnce(Return(true)); + /* 1 calls will be made for non tunnel termination rules after primary switch */ + EXPECT_CALL(*ctx, isNeighborResolved(_)).Times(1).WillRepeatedly(Return(true)); doDashEniFwdTableTask(applDb.get(), deque( @@ -531,22 +467,12 @@ namespace dashenifwdorch_ut vnet_name + ":" + test_mac, SET_COMMAND, { - { ENI_FWD_PRIMARY, "1" }, // Primary is Local now + { DashEniFwd::PRIMARY, "vdpu0" }, // Primary is Local now } } } ) ); - - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT", { - { ACTION_REDIRECT_ACTION, local_pav4 } - }); - /* Check ACL Rules */ - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT_TERM", { - { ACTION_REDIRECT_ACTION, local_pav4 } - }); - /* Check the tunnel is removed */ - ASSERT_TRUE(ctx->remote_nh_map_.find(remote_npuv4 + "@" + tunnel_name) == ctx->remote_nh_map_.end()); } /* @@ -554,16 +480,17 @@ namespace dashenifwdorch_ut No Tunnel Termination Rules expected */ TEST_F(DashEniFwdOrchTest, RemoteNeighbor_NoTunnelTerm) - { - IpAddress remote_npuv4_ip = IpAddress(remote_2_npuv4); - EXPECT_CALL(*ctx, findVnetTunnel(vnet_name, _)).Times(2) // Only two rules are created + { + EXPECT_CALL(*ctx, findVnetTunnel(vnet_name, _)).Times(1) // Only 1 rule is created .WillRepeatedly(DoAll( SetArgReferee<1>(tunnel_name), Return(true) )); - - EXPECT_CALL(*ctx, createNextHopTunnel(tunnel_name, remote_npuv4_ip)).Times(1) - .WillRepeatedly(Return(0x400000000064d)); // Only once since same NH object will be re-used + EXPECT_CALL(*ctx, findVnetVni(vnet_name, _)).Times(1) // Called once per ENI + .WillRepeatedly(DoAll( + SetArgReferee<1>(test_vni), + Return(true) + )); doDashEniFwdTableTask(applDb.get(), deque( @@ -572,44 +499,27 @@ namespace dashenifwdorch_ut vnet_name + ":" + test_mac, SET_COMMAND, { - { ENI_FWD_VDPU_IDS, "2,3" }, - { ENI_FWD_PRIMARY, "3" }, // Remote endpoint is the primary - { ENI_FWD_OUT_VNI, to_string(test_vni) } + { DashEniFwd::VDPU_IDS, "vdpu1,vdpu2" }, + { DashEniFwd::PRIMARY, "vdpu2" }, // Remote endpoint is the primary } } } ) ); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_IN", { - { ACTION_REDIRECT_ACTION, remote_2_npuv4 + "@" + tunnel_name } - }); - checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key+ "_OUT", { - { ACTION_REDIRECT_ACTION, remote_2_npuv4 + "@" + tunnel_name } + checkKFV(aclRuleTable.get(), "ENI:" + vnet_name + "_" + test_mac_key, { + { ACTION_REDIRECT_ACTION, remote_2_npuv4 + "@" + tunnel_name + ',' + to_string(test_vni) } }); /* Tunnel termination rules are not installed */ - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_IN_TERM"); - checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_OUT_TERM"); + checkRuleUninstalled("ENI:" + vnet_name + "_" + test_mac_key+ "_TERM"); } /* - Test ACL Table and Table Type config + Test ACL Table and Table Type config with reference counting */ TEST_F(DashEniFwdOrchTest, TestAclTableConfig) { - /* Create a set of ports */ - std::map allPorts; - allPorts["Ethernet0"] = Port("Ethernet0", Port::PHY); - allPorts["Ethernet4"] = Port("Ethernet4", Port::PHY); - allPorts["Ethernet8"] = Port("Ethernet8", Port::PHY); - allPorts["Ethernet16"] = Port("Ethernet16", Port::PHY); - allPorts["PortChannel1011"] = Port("PortChannel1012", Port::LAG); - allPorts["PortChannel1012"] = Port("Ethernet16", Port::LAG); - allPorts["PortChannel1011"].m_members.insert("Ethernet8"); - allPorts["PortChannel1012"].m_members.insert("Ethernet16"); - EXPECT_CALL(*ctx, getAllPorts()).WillOnce(ReturnRef(allPorts)); - Table aclTableType(applDb.get(), APP_ACL_TABLE_TYPE_TABLE_NAME); Table aclTable(applDb.get(), APP_ACL_TABLE_TABLE_NAME); Table portTable(cfgDb.get(), CFG_PORT_TABLE_NAME); @@ -625,10 +535,22 @@ namespace dashenifwdorch_ut { PORT_ROLE, PORT_ROLE_DPC } }, SET_COMMAND); - eniOrch->initAclTableCfg(); + // Initially no ACL table should exist + checkNoKeyExists(&aclTable, "ENI"); + checkNoKeyExists(&aclTableType, "ENI_REDIRECT"); + // Create first ACL rule - should create the table + vector fv1 = { + { RULE_PRIORITY, "9996" }, + { MATCH_DST_IP, test_vip }, + { MATCH_INNER_DST_MAC, test_mac }, + { ACTION_REDIRECT_ACTION, local_pav4 } + }; + eniOrch->ctx->createAclRule("ENI:rule1", fv1); + + // Verify ACL table and table type were created after first rule checkKFV(&aclTableType, "ENI_REDIRECT", { - { ACL_TABLE_TYPE_MATCHES, "TUNNEL_VNI,DST_IP,INNER_SRC_MAC,INNER_DST_MAC,TUNNEL_TERM" }, + { ACL_TABLE_TYPE_MATCHES, "DST_IP,INNER_DST_MAC,TUNNEL_TERM" }, { ACL_TABLE_TYPE_ACTIONS, "REDIRECT_ACTION" }, { ACL_TABLE_TYPE_BPOINT_TYPES, "PORT,PORTCHANNEL" } }); @@ -638,6 +560,46 @@ namespace dashenifwdorch_ut { ACL_TABLE_STAGE, STAGE_INGRESS }, { ACL_TABLE_PORTS, "Ethernet0,PortChannel1011,PortChannel1012" } }); + + // Create second and third ACL rules - table should still exist + vector fv2 = { + { RULE_PRIORITY, "9997" }, + { MATCH_DST_IP, test_vip }, + { MATCH_INNER_DST_MAC, test_mac2 }, + { ACTION_REDIRECT_ACTION, local_pav4 } + }; + eniOrch->ctx->createAclRule("ENI:rule2", fv2); + + vector fv3 = { + { RULE_PRIORITY, "9998" }, + { MATCH_DST_IP, test_vip }, + { MATCH_INNER_DST_MAC, test_mac }, + { ACTION_REDIRECT_ACTION, remote_pav4 } + }; + eniOrch->ctx->createAclRule("ENI:rule3", fv3); + + // Verify rule count is 3 + EXPECT_EQ(eniOrch->ctx->acl_rule_count_, 3); + + // Delete first two rules - table should still exist + eniOrch->ctx->deleteAclRule("ENI:rule1"); + EXPECT_EQ(eniOrch->ctx->acl_rule_count_, 2); + + eniOrch->ctx->deleteAclRule("ENI:rule2"); + EXPECT_EQ(eniOrch->ctx->acl_rule_count_, 1); + + // Table should still exist + checkKFV(&aclTable, "ENI", { + { ACL_TABLE_TYPE, "ENI_REDIRECT" } + }); + + // Delete last rule - table should be removed + eniOrch->ctx->deleteAclRule("ENI:rule3"); + EXPECT_EQ(eniOrch->ctx->acl_rule_count_, 0); + + // Verify ACL table and table type were deleted after last rule + checkNoKeyExists(&aclTable, "ENI"); + checkNoKeyExists(&aclTableType, "ENI_REDIRECT"); } } @@ -658,7 +620,5 @@ namespace mock_orch_test string tunnel; ASSERT_NO_THROW(ctx.findVnetTunnel("Vnet_1000", tunnel)); ASSERT_NO_THROW(ctx.getAllPorts()); - ASSERT_NO_THROW(ctx.createNextHopTunnel("tunnel0", IpAddress("10.0.0.1"))); - ASSERT_NO_THROW(ctx.removeNextHopTunnel("tunnel0", IpAddress("10.0.0.1"))); } } diff --git a/tests/mock_tests/mock_dbconnector.cpp b/tests/mock_tests/mock_dbconnector.cpp index 7cabdc2224..74a289907c 100644 --- a/tests/mock_tests/mock_dbconnector.cpp +++ b/tests/mock_tests/mock_dbconnector.cpp @@ -11,6 +11,10 @@ namespace swss { + + DBConnector::DBConnector(const DBConnector& other) : DBConnector(other.m_dbName, 0, false) + {} + DBConnector::DBConnector(int dbId, const std::string &hostname, int port, unsigned int timeout) : m_dbId(dbId) { diff --git a/tests/request_parser_ut.cpp b/tests/request_parser_ut.cpp index 042ff98108..6bf7ad2030 100644 --- a/tests/request_parser_ut.cpp +++ b/tests/request_parser_ut.cpp @@ -30,6 +30,12 @@ class TestRequest1 : public Request TestRequest1() : Request(request_description1, '|') { } }; +class TestRequest1_Relaxed : public Request +{ +public: + TestRequest1_Relaxed() : Request(request_description1, '|', true) { } +}; + const request_description_t request_description2 = { { REQ_T_STRING, REQ_T_MAC_ADDRESS, REQ_T_STRING }, { @@ -110,6 +116,37 @@ TEST(request_parser, simpleKey) } } +TEST(request_parser, relaxedAttrParsing) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "v4", "true" }, + { "v6", "true" }, + { "src_mac", "02:03:04:05:06:07" }, + { "ttl_action", "copy" }, + { "ip_opt_action", "drop" }, + { "l3_mc_action", "log" }, + { "nlist", "name1" }, + { "random", "whatever" } + } + }; + + try + { + TestRequest1_Relaxed request; + + EXPECT_NO_THROW(request.parse(t)); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + TEST(request_parser, simpleKeyEmptyAttrs) { KeyOpFieldsValuesTuple t {"key1", "SET", @@ -1842,3 +1879,148 @@ TEST(request_parser, mac_key_parse_checker) FAIL() << "Got unexpected exception"; } } + +/* +Check STRING_LIST attribute type +*/ +const request_description_t test_string_list = { + { REQ_T_STRING }, + { + { "main_dpu_ids", REQ_T_STRING_LIST }, + }, + { } +}; + +class TestRequestStringList: public Request +{ +public: + TestRequestStringList() : Request(test_string_list, '|') { } +}; + +TEST(request_parser, string_list_basic) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "main_dpu_ids", "dpu0,dpu1,dpu3" }, + } + }; + + try + { + TestRequestStringList request; + + EXPECT_NO_THROW(request.parse(t)); + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{"main_dpu_ids"})); + + auto main_dpu_list = request.getAttrStringList("main_dpu_ids"); + std::vector expected_main{"dpu0", "dpu1", "dpu3"}; + EXPECT_EQ(main_dpu_list.size(), 3); + for (size_t idx = 0; idx < main_dpu_list.size(); idx++) + { + EXPECT_STREQ(main_dpu_list[idx].c_str(), expected_main[idx].c_str()); + } + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, string_list_single_item) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "main_dpu_ids", "dpu0" }, + } + }; + + try + { + TestRequestStringList request; + EXPECT_NO_THROW(request.parse(t)); + auto main_dpu_list = request.getAttrStringList("main_dpu_ids"); + EXPECT_EQ(main_dpu_list.size(), 1); + EXPECT_STREQ(main_dpu_list[0].c_str(), "dpu0"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, string_list_empty) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "main_dpu_ids", "" }, + } + }; + + try + { + TestRequestStringList request; + EXPECT_NO_THROW(request.parse(t)); + auto main_dpu_list = request.getAttrStringList("main_dpu_ids"); + EXPECT_EQ(main_dpu_list.size(), 0); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, string_list_clear_test) +{ + KeyOpFieldsValuesTuple t1 {"key1", "SET", + { + { "main_dpu_ids", "dpu0,dpu1,dpu3" }, + } + }; + + KeyOpFieldsValuesTuple t2 {"key2", "SET", + { + { "main_dpu_ids", "dpu4,dpu5" }, + } + }; + + try + { + TestRequestStringList request; + + // Parse first request + EXPECT_NO_THROW(request.parse(t1)); + auto main_dpu_list1 = request.getAttrStringList("main_dpu_ids"); + EXPECT_EQ(main_dpu_list1.size(), 3); + EXPECT_STREQ(main_dpu_list1[0].c_str(), "dpu0"); + + // Clear and parse second request + EXPECT_NO_THROW(request.clear()); + EXPECT_NO_THROW(request.parse(t2)); + auto main_dpu_list2 = request.getAttrStringList("main_dpu_ids"); + EXPECT_EQ(main_dpu_list2.size(), 2); + EXPECT_STREQ(main_dpu_list2[0].c_str(), "dpu4"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +}