From e86b900dc0598f0b50679eee5fc37b65710cbdad Mon Sep 17 00:00:00 2001 From: Ann Pokora <44511240+qbdwlr@users.noreply.github.com> Date: Mon, 28 Jun 2021 14:49:26 -0400 Subject: [PATCH] [MPLS] sonic-swss changes for MPLS (#1686) SONiC swss support for MPLS: * RouteOrch support for SAI MPLS inseg * NeighOrch support for MPLS NHs. * CrmOrch support for MPLS inseg and NH accounting. * New sonic-swss/tests/test_mpls.py for verification. Unit-tests in sonic-swss/tests/test_mpls.py and sonic-utilities/tests --- cfgmgr/intfmgr.cpp | 43 ++ cfgmgr/intfmgr.h | 1 + orchagent/Makefile.am | 1 + orchagent/bulker.h | 41 ++ orchagent/crmorch.cpp | 74 ++- orchagent/crmorch.h | 2 + orchagent/intfsorch.cpp | 50 ++ orchagent/intfsorch.h | 1 + orchagent/label.h | 113 +++++ orchagent/mplsrouteorch.cpp | 841 ++++++++++++++++++++++++++++++++ orchagent/neighorch.cpp | 134 ++++- orchagent/neighorch.h | 3 +- orchagent/nexthopkey.h | 89 +++- orchagent/orchdaemon.cpp | 8 +- orchagent/port.h | 1 + orchagent/routeorch.cpp | 179 ++++--- orchagent/routeorch.h | 46 +- orchagent/saihelper.cpp | 3 + tests/mock_tests/Makefile.am | 1 + tests/mock_tests/aclorch_ut.cpp | 10 +- tests/test_mpls.py | 647 ++++++++++++++++++++++++ 21 files changed, 2148 insertions(+), 140 deletions(-) create mode 100644 orchagent/label.h create mode 100644 orchagent/mplsrouteorch.cpp create mode 100644 tests/test_mpls.py diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index fa2207b32b17..b40f55692abc 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -115,6 +115,33 @@ void IntfMgr::setIntfVrf(const string &alias, const string &vrfName) } } +bool IntfMgr::setIntfMpls(const string &alias, const string& mpls) +{ + stringstream cmd; + string res; + + if (mpls == "enable") + { + cmd << "sysctl -w net.mpls.conf." << alias << ".input=1"; + } + else if ((mpls == "disable") || mpls.empty()) + { + cmd << "sysctl -w net.mpls.conf." << alias << ".input=0"; + } + else + { + SWSS_LOG_ERROR("MPLS state is invalid: \"%s\"", mpls.c_str()); + return false; + } + int ret = swss::exec(cmd.str(), res); + // Don't return error unless MPLS is explicitly set + if (ret && !mpls.empty()) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } + return true; +} + void IntfMgr::addLoopbackIntf(const string &alias) { stringstream cmd; @@ -448,6 +475,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string nat_zone = ""; string proxy_arp = ""; string grat_arp = ""; + string mpls = ""; for (auto idx : data) { @@ -474,6 +502,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { grat_arp = value; } + else if (field == "mpls") + { + mpls = value; + } if (field == "nat_zone") { @@ -519,6 +551,17 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, FieldValueTuple fvTuple("nat_zone", nat_zone); data.push_back(fvTuple); } + /* Set mpls */ + if (!setIntfMpls(alias, mpls)) + { + SWSS_LOG_ERROR("Failed to set MPLS to \"%s\" for the \"%s\" interface", mpls.c_str(), alias.c_str()); + return false; + } + if (!mpls.empty()) + { + FieldValueTuple fvTuple("mpls", mpls); + data.push_back(fvTuple); + } } if (!parentAlias.empty()) diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index e72a10be969b..655fb4deeb62 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -29,6 +29,7 @@ class IntfMgr : public Orch void setIntfIp(const std::string &alias, const std::string &opCmd, const IpPrefix &ipPrefix); void setIntfVrf(const std::string &alias, const std::string &vrfName); void setIntfMac(const std::string &alias, const std::string &macAddr); + bool setIntfMpls(const std::string &alias, const std::string &mpls); bool doIntfGeneralTask(const std::vector& keys, std::vector data, const std::string& op); bool doIntfAddrTask(const std::vector& keys, const std::vector& data, const std::string& op); diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 05a689f168f4..05abdfbcec74 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -33,6 +33,7 @@ orchagent_SOURCES = \ orch.cpp \ notifications.cpp \ routeorch.cpp \ + mplsrouteorch.cpp \ neighorch.cpp \ intfsorch.cpp \ portsorch.cpp \ diff --git a/orchagent/bulker.h b/orchagent/bulker.h index cd1812861b14..a4a49b105da1 100644 --- a/orchagent/bulker.h +++ b/orchagent/bulker.h @@ -41,6 +41,13 @@ static inline bool operator==(const sai_route_entry_t& a, const sai_route_entry_ ; } +static inline bool operator==(const sai_inseg_entry_t& a, const sai_inseg_entry_t& b) +{ + return a.switch_id == b.switch_id + && a.label == b.label + ; +} + static inline std::size_t hash_value(const sai_ip_prefix_t& a) { size_t seed = 0; @@ -85,6 +92,18 @@ namespace std return seed; } }; + + template <> + struct hash + { + size_t operator()(const sai_inseg_entry_t& a) const noexcept + { + size_t seed = 0; + boost::hash_combine(seed, a.switch_id); + boost::hash_combine(seed, a.label); + return seed; + } + }; } // SAI typedef which is not available in SAI 1.5 @@ -151,6 +170,19 @@ struct SaiBulkerTraits //using bulk_set_entry_attribute_fn = sai_bulk_object_set_attribute_fn; }; +template<> +struct SaiBulkerTraits +{ + using entry_t = sai_inseg_entry_t; + using api_t = sai_mpls_api_t; + using create_entry_fn = sai_create_inseg_entry_fn; + using remove_entry_fn = sai_remove_inseg_entry_fn; + using set_entry_attribute_fn = sai_set_inseg_entry_attribute_fn; + using bulk_create_entry_fn = sai_bulk_create_inseg_entry_fn; + using bulk_remove_entry_fn = sai_bulk_remove_inseg_entry_fn; + using bulk_set_entry_attribute_fn = sai_bulk_set_inseg_entry_attribute_fn; +}; + template class EntityBulker { @@ -550,6 +582,15 @@ inline EntityBulker::EntityBulker(sai_fdb_api_t *api, size_t max_ */ } +template <> +inline EntityBulker::EntityBulker(sai_mpls_api_t *api, size_t max_bulk_size) : + max_bulk_size(max_bulk_size) +{ + create_entries = api->create_inseg_entries; + remove_entries = api->remove_inseg_entries; + set_entries_attribute = api->set_inseg_entries_attribute; +} + template class ObjectBulker { diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 14b0cab4d159..e0eb24239bd7 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -40,7 +40,9 @@ const map crmResTypeNameMap = { CrmResourceType::CRM_FDB_ENTRY, "FDB_ENTRY" }, { CrmResourceType::CRM_IPMC_ENTRY, "IPMC_ENTRY" }, { CrmResourceType::CRM_SNAT_ENTRY, "SNAT_ENTRY" }, - { CrmResourceType::CRM_DNAT_ENTRY, "DNAT_ENTRY" } + { CrmResourceType::CRM_DNAT_ENTRY, "DNAT_ENTRY" }, + { CrmResourceType::CRM_MPLS_INSEG, "MPLS_INSEG" }, + { CrmResourceType::CRM_MPLS_NEXTHOP, "MPLS_NEXTHOP" }, }; const map crmResSaiAvailAttrMap = @@ -60,7 +62,9 @@ const map crmResSaiAvailAttrMap = { CrmResourceType::CRM_FDB_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY }, { CrmResourceType::CRM_IPMC_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_IPMC_ENTRY}, { CrmResourceType::CRM_SNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY }, - { CrmResourceType::CRM_DNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY } + { CrmResourceType::CRM_DNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY }, + { CrmResourceType::CRM_MPLS_INSEG, SAI_OBJECT_TYPE_INSEG_ENTRY }, + { CrmResourceType::CRM_MPLS_NEXTHOP, SAI_OBJECT_TYPE_NEXT_HOP }, }; const map crmThreshTypeResMap = @@ -80,7 +84,9 @@ const map crmThreshTypeResMap = { "fdb_entry_threshold_type", CrmResourceType::CRM_FDB_ENTRY }, { "ipmc_entry_threshold_type", CrmResourceType::CRM_IPMC_ENTRY }, { "snat_entry_threshold_type", CrmResourceType::CRM_SNAT_ENTRY }, - { "dnat_entry_threshold_type", CrmResourceType::CRM_DNAT_ENTRY } + { "dnat_entry_threshold_type", CrmResourceType::CRM_DNAT_ENTRY }, + { "mpls_inseg_threshold_type", CrmResourceType::CRM_MPLS_INSEG }, + { "mpls_nexthop_threshold_type", CrmResourceType::CRM_MPLS_NEXTHOP }, }; const map crmThreshLowResMap = @@ -100,7 +106,9 @@ const map crmThreshLowResMap = {"fdb_entry_low_threshold", CrmResourceType::CRM_FDB_ENTRY }, {"ipmc_entry_low_threshold", CrmResourceType::CRM_IPMC_ENTRY }, {"snat_entry_low_threshold", CrmResourceType::CRM_SNAT_ENTRY }, - {"dnat_entry_low_threshold", CrmResourceType::CRM_DNAT_ENTRY } + {"dnat_entry_low_threshold", CrmResourceType::CRM_DNAT_ENTRY }, + {"mpls_inseg_low_threshold", CrmResourceType::CRM_MPLS_INSEG }, + {"mpls_nexthop_low_threshold", CrmResourceType::CRM_MPLS_NEXTHOP }, }; const map crmThreshHighResMap = @@ -120,7 +128,9 @@ const map crmThreshHighResMap = {"fdb_entry_high_threshold", CrmResourceType::CRM_FDB_ENTRY }, {"ipmc_entry_high_threshold", CrmResourceType::CRM_IPMC_ENTRY }, {"snat_entry_high_threshold", CrmResourceType::CRM_SNAT_ENTRY }, - {"dnat_entry_high_threshold", CrmResourceType::CRM_DNAT_ENTRY } + {"dnat_entry_high_threshold", CrmResourceType::CRM_DNAT_ENTRY }, + {"mpls_inseg_high_threshold", CrmResourceType::CRM_MPLS_INSEG }, + {"mpls_nexthop_high_threshold", CrmResourceType::CRM_MPLS_NEXTHOP }, }; const map crmThreshTypeMap = @@ -147,7 +157,9 @@ const map crmAvailCntsTableMap = { "crm_stats_fdb_entry_available", CrmResourceType::CRM_FDB_ENTRY }, { "crm_stats_ipmc_entry_available", CrmResourceType::CRM_IPMC_ENTRY }, { "crm_stats_snat_entry_available", CrmResourceType::CRM_SNAT_ENTRY }, - { "crm_stats_dnat_entry_available", CrmResourceType::CRM_DNAT_ENTRY } + { "crm_stats_dnat_entry_available", CrmResourceType::CRM_DNAT_ENTRY }, + { "crm_stats_mpls_inseg_available", CrmResourceType::CRM_MPLS_INSEG }, + { "crm_stats_mpls_nexthop_available", CrmResourceType::CRM_MPLS_NEXTHOP }, }; const map crmUsedCntsTableMap = @@ -167,7 +179,9 @@ const map crmUsedCntsTableMap = { "crm_stats_fdb_entry_used", CrmResourceType::CRM_FDB_ENTRY }, { "crm_stats_ipmc_entry_used", CrmResourceType::CRM_IPMC_ENTRY }, { "crm_stats_snat_entry_used", CrmResourceType::CRM_SNAT_ENTRY }, - { "crm_stats_dnat_entry_used", CrmResourceType::CRM_DNAT_ENTRY } + { "crm_stats_dnat_entry_used", CrmResourceType::CRM_DNAT_ENTRY }, + { "crm_stats_mpls_inseg_used", CrmResourceType::CRM_MPLS_INSEG }, + { "crm_stats_mpls_nexthop_used", CrmResourceType::CRM_MPLS_NEXTHOP }, }; CrmOrch::CrmOrch(DBConnector *db, string tableName): @@ -463,10 +477,10 @@ void CrmOrch::getResAvailableCounters() sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - if((status == SAI_STATUS_NOT_SUPPORTED) || - (status == SAI_STATUS_NOT_IMPLEMENTED) || - SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || - SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + if ((status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED) || + SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || + SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) { // mark unsupported resources res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; @@ -536,8 +550,44 @@ void CrmOrch::getResAvailableCounters() break; } + case CrmResourceType::CRM_MPLS_INSEG: + { + sai_object_type_t objType = static_cast(crmResSaiAvailAttrMap.at(res.first)); + uint64_t availCount = 0; + sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 0, nullptr, &availCount); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); + break; + } + + res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); + + break; + } + + case CrmResourceType::CRM_MPLS_NEXTHOP: + { + sai_object_type_t objType = static_cast(crmResSaiAvailAttrMap.at(res.first)); + sai_attribute_t attr; + uint64_t availCount = 0; + + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + attr.value.s32 = SAI_NEXT_HOP_TYPE_MPLS; + sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 1, &attr, &availCount); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); + break; + } + + res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); + + break; + } + default: - SWSS_LOG_ERROR("Failed to get CRM resource type %u. Unknown resource type.\n", (uint32_t)res.first); + SWSS_LOG_ERROR("Failed to get CRM resource type %u. Unknown resource type.\n", static_cast(res.first)); return; } } diff --git a/orchagent/crmorch.h b/orchagent/crmorch.h index 7c093ffb24a6..d8b5522d3a87 100644 --- a/orchagent/crmorch.h +++ b/orchagent/crmorch.h @@ -28,6 +28,8 @@ enum class CrmResourceType CRM_IPMC_ENTRY, CRM_SNAT_ENTRY, CRM_DNAT_ENTRY, + CRM_MPLS_INSEG, + CRM_MPLS_NEXTHOP, }; enum class CrmThresholdType diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 27deb78d0473..b270a0c5e59e 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -195,6 +195,31 @@ void IntfsOrch::decreaseRouterIntfsRefCount(const string &alias) alias.c_str(), m_syncdIntfses[alias].ref_count); } +bool IntfsOrch::setRouterIntfsMpls(const Port &port) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_MPLS_STATE; + attr.value.booldata = port.m_mpls; + + sai_status_t status = + sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set router interface %s MPLS to %s, rv:%d", + port.m_alias.c_str(), (port.m_mpls ? "enable" : "disable"), status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + SWSS_LOG_NOTICE("Set router interface %s MPLS to %s", port.m_alias.c_str(), + (port.m_mpls ? "enable" : "disable")); + return true; +} + bool IntfsOrch::setRouterIntfsMtu(const Port &port) { SWSS_LOG_ENTER(); @@ -636,6 +661,7 @@ void IntfsOrch::doTask(Consumer &consumer) uint32_t nat_zone_id = 0; string proxy_arp = ""; string inband_type = ""; + bool mpls = false; for (auto idx : data) { @@ -661,6 +687,10 @@ void IntfsOrch::doTask(Consumer &consumer) continue; } } + else if (field == "mpls") + { + mpls = (value == "enable" ? true : false); + } else if (field == "nat_zone") { try @@ -852,6 +882,14 @@ void IntfsOrch::doTask(Consumer &consumer) } gPortsOrch->setPort(alias, port); } + /* Set MPLS */ + if ((!ip_prefix_in_key) && (port.m_mpls != mpls)) + { + port.m_mpls = mpls; + + setRouterIntfsMpls(port); + gPortsOrch->setPort(alias, port); + } } } @@ -1091,6 +1129,17 @@ bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port) attr.value.u32 = port.m_mtu; attrs.push_back(attr); + if (port.m_mpls) + { + // Default value of ADMIN_MPLS_STATE is disabled and does not need + // to be explicitly included in RIF Create request. + attr.id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_MPLS_STATE; + attr.value.booldata = port.m_mpls; + + SWSS_LOG_INFO("Enabling MPLS on interface %s\n", port.m_alias.c_str()); + attrs.push_back(attr); + } + if (gIsNatSupported) { attr.id = SAI_ROUTER_INTERFACE_ATTR_NAT_ZONE_ID; @@ -1153,6 +1202,7 @@ bool IntfsOrch::removeRouterIntfs(Port &port) port.m_rif_id = 0; port.m_vr_id = 0; port.m_nat_zone_id = 0; + port.m_mpls = false; gPortsOrch->setPort(port.m_alias, port); SWSS_LOG_NOTICE("Remove router interface for port %s", port.m_alias.c_str()); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 89167fb80462..5605abf13381 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -46,6 +46,7 @@ class IntfsOrch : public Orch bool setRouterIntfsMac(const Port &port); bool setRouterIntfsNatZoneId(Port &port); bool setRouterIntfsAdminStatus(const Port &port); + bool setRouterIntfsMpls(const Port &port); std::set getSubnetRoutes(); diff --git a/orchagent/label.h b/orchagent/label.h new file mode 100644 index 000000000000..ae5452c1933f --- /dev/null +++ b/orchagent/label.h @@ -0,0 +1,113 @@ +#ifndef __LABEL__ +#define __LABEL__ + +#include +#include +#include +#include "converter.h" +#include "tokenize.h" + +namespace swss { + +typedef uint32_t Label; + +#define LABEL_DELIMITER '/' +#define LABEL_VALUE_MIN 0 +#define LABEL_VALUE_MAX 0xFFFFF + +struct LabelStack +{ + std::vector