diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 0bb5cc0f37c..fedd7ef4aef 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -93,7 +93,8 @@ orchagent_SOURCES = \ lagid.cpp \ bfdorch.cpp \ srv6orch.cpp \ - response_publisher.cpp + response_publisher.cpp \ + nvgreorch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/nvgreorch.cpp b/orchagent/nvgreorch.cpp new file mode 100644 index 00000000000..38f8c19874a --- /dev/null +++ b/orchagent/nvgreorch.cpp @@ -0,0 +1,582 @@ +#include "orch.h" +#include "nvgreorch.h" +#include "request_parser.h" +#include "swssnet.h" +#include "directory.h" + +#define NVGRE_VSID_MAX_VALUE 16777214 + +extern Directory gDirectory; +extern PortsOrch* gPortsOrch; +extern sai_object_id_t gSwitchId; +extern sai_object_id_t gUnderlayIfId; +extern sai_object_id_t gVirtualRouterId; +extern sai_tunnel_api_t *sai_tunnel_api; + +static const std::vector nvgreMapTypes = { + MAP_T_VLAN, + MAP_T_BRIDGE +}; + +static const std::map nvgreEncapTunnelMap = { + { MAP_T_VLAN, SAI_TUNNEL_MAP_TYPE_VLAN_ID_TO_VSID }, + { MAP_T_BRIDGE, SAI_TUNNEL_MAP_TYPE_BRIDGE_IF_TO_VSID } +}; + +static inline sai_tunnel_map_type_t get_encap_nvgre_mapper(map_type_t map) +{ + return nvgreEncapTunnelMap.at(map); +} + +static const std::map nvgreDecapTunnelMap = { + { MAP_T_VLAN, SAI_TUNNEL_MAP_TYPE_VSID_TO_VLAN_ID }, + { MAP_T_BRIDGE, SAI_TUNNEL_MAP_TYPE_VSID_TO_BRIDGE_IF } +}; + +static inline sai_tunnel_map_type_t get_decap_nvgre_mapper(map_type_t map) +{ + return nvgreDecapTunnelMap.at(map); +} + +static const map> nvgreEncapTunnelMapKeyVal = +{ + { MAP_T_VLAN, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_VALUE } + }, + { MAP_T_BRIDGE, + { SAI_TUNNEL_MAP_ENTRY_ATTR_BRIDGE_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_VALUE } + } +}; + +static inline sai_tunnel_map_entry_attr_t get_encap_nvgre_map_key(map_type_t map) +{ + return nvgreEncapTunnelMapKeyVal.at(map).first; +} + +static inline sai_tunnel_map_entry_attr_t get_encap_nvgre_map_val(map_type_t map) +{ + return nvgreEncapTunnelMapKeyVal.at(map).second; +} + +static const map> nvgreDecapTunnelMapKeyVal = +{ + { MAP_T_VLAN, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE } + }, + { MAP_T_BRIDGE, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_BRIDGE_ID_VALUE } + } +}; + +static inline sai_tunnel_map_entry_attr_t get_decap_nvgre_map_key(map_type_t map) +{ + return nvgreDecapTunnelMapKeyVal.at(map).first; +} + +static inline sai_tunnel_map_entry_attr_t get_decap_nvgre_map_val(map_type_t map) +{ + return nvgreDecapTunnelMapKeyVal.at(map).second; +} + +/** @brief Creates tunnel mapper in SAI. + * + * @param sai_tunnel_map_type SAI tunnel map type e.g. VSID_TO_VLAN + * + * @return Tunnel map SAI identifier. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel_map(sai_tunnel_map_type_t sai_tunnel_map_type) +{ + sai_attribute_t attr; + std::vector tunnel_map_attrs; + + attr.id = SAI_TUNNEL_MAP_ATTR_TYPE; + attr.value.u32 = sai_tunnel_map_type; + + tunnel_map_attrs.push_back(attr); + + sai_object_id_t tunnel_map_id; + sai_status_t status = sai_tunnel_api->create_tunnel_map( + &tunnel_map_id, + gSwitchId, + static_cast(tunnel_map_attrs.size()), + tunnel_map_attrs.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create the NVGRE tunnel map object"); + } + + return tunnel_map_id; +} + +/** @brief Removes tunnel mapper in SAI. + * + * @param sai_tunnel_map_type SAI tunnel map identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel_map(sai_object_id_t tunnel_map_id) +{ + sai_status_t status = sai_tunnel_api->remove_tunnel_map(tunnel_map_id); + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove the NVGRE tunnel map object"); + } +} + + +/** @brief Creates tunnel in SAI. + * + * @param ids Pointer to structure where stored tunnel and tunnel mappers identifiers. + * @param src_ip Pointer to source IP address. + * + * @return SAI tunnel identifier. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel(struct tunnel_sai_ids_t &ids, const sai_ip_address_t &src_ip, sai_object_id_t underlay_rif) +{ + sai_attribute_t attr; + std::vector tunnel_attrs; + + attr.id = SAI_TUNNEL_ATTR_TYPE; + attr.value.s32 = SAI_TUNNEL_TYPE_NVGRE; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE; + attr.value.oid = underlay_rif; + tunnel_attrs.push_back(attr); + + sai_object_id_t decap_map_list[MAP_T_MAX]; + uint8_t num_decap_map = 0; + + for (auto map_type : nvgreMapTypes) + { + decap_map_list[num_decap_map] = ids.tunnel_decap_id.at(map_type); + num_decap_map++; + } + + attr.id = SAI_TUNNEL_ATTR_DECAP_MAPPERS; + attr.value.objlist.count = num_decap_map; + attr.value.objlist.list = decap_map_list; + tunnel_attrs.push_back(attr); + + sai_object_id_t encap_map_list[MAP_T_MAX]; + uint8_t num_encap_map = 0; + + for (auto map_type : nvgreMapTypes) + { + encap_map_list[num_encap_map] = ids.tunnel_encap_id.at(map_type); + num_encap_map++; + } + + attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; + attr.value.objlist.count = num_encap_map; + attr.value.objlist.list = encap_map_list; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + attr.value.ipaddr = src_ip; + tunnel_attrs.push_back(attr); + + sai_object_id_t tunnel_id; + sai_status_t status = sai_tunnel_api->create_tunnel( + &tunnel_id, + gSwitchId, + static_cast(tunnel_attrs.size()), + tunnel_attrs.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create the NVGRE tunnel object"); + } + + return tunnel_id; +} + +/** @brief Removes tunnel in SAI. + * + * @param tunnel_id Pointer to tunnel identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel(sai_object_id_t tunnel_id) +{ + sai_status_t status = sai_tunnel_api->remove_tunnel(tunnel_id); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove the NVGRE tunnel object"); + } +} + +/** @brief Creates tunnel termination in SAI. + * + * @param tunnel_id Tunnel identifier. + * @param src_ip Pointer to source IP address. + * @param default_vrid Virtual router identifier. + * + * @return SAI tunnel termination identifier. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel_termination(sai_object_id_t tunnel_id, const sai_ip_address_t &src_ip, sai_object_id_t default_vrid) +{ + sai_attribute_t attr; + std::vector tunnel_attrs; + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID; + attr.value.oid = default_vrid; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP; + attr.value.ipaddr = src_ip; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE; + attr.value.s32 = SAI_TUNNEL_TYPE_NVGRE; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID; + attr.value.oid = tunnel_id; + tunnel_attrs.push_back(attr); + + sai_object_id_t term_table_id; + sai_status_t status = sai_tunnel_api->create_tunnel_term_table_entry( + &term_table_id, + gSwitchId, + static_cast(tunnel_attrs.size()), + tunnel_attrs.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create a tunnel term table object"); + } + + return term_table_id; +} + +/** @brief Removes tunnel termination in SAI. + * + * @param tunnel_id Pointer to tunnel termination identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel_termination(sai_object_id_t tunnel_term_id) +{ + sai_status_t status = sai_tunnel_api->remove_tunnel_term_table_entry(tunnel_term_id); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove a tunnel term object"); + } +} + +void NvgreTunnel::createNvgreMappers() +{ + for (auto map_type : nvgreMapTypes) + { + tunnel_ids_.tunnel_encap_id.insert( + make_pair(map_type, sai_create_tunnel_map(get_encap_nvgre_mapper(map_type))) + ); + } + + for (auto map_type : nvgreMapTypes) + { + tunnel_ids_.tunnel_decap_id.insert( + make_pair(map_type, sai_create_tunnel_map(get_decap_nvgre_mapper(map_type))) + ); + } +} + +void NvgreTunnel::removeNvgreMappers() +{ + for (auto map_type : nvgreMapTypes) + { + sai_remove_tunnel_map(getEncapMapId(map_type)); + } + + for (auto map_type : nvgreMapTypes) + { + sai_remove_tunnel_map(getDecapMapId(map_type)); + } + + tunnel_ids_.tunnel_encap_id.clear(); + tunnel_ids_.tunnel_decap_id.clear(); +} + +void NvgreTunnel::createNvgreTunnel() +{ + sai_ip_address_t ip_addr; + swss::copy(ip_addr, src_ip_); + + tunnel_ids_.tunnel_id = sai_create_tunnel(tunnel_ids_, ip_addr, gUnderlayIfId); + tunnel_ids_.tunnel_term_id = sai_create_tunnel_termination(tunnel_ids_.tunnel_id, ip_addr, gVirtualRouterId); + + SWSS_LOG_INFO("NVGRE tunnel '%s' was created", tunnel_name_.c_str()); +} + +void NvgreTunnel::removeNvgreTunnel() +{ + try + { + sai_remove_tunnel_termination(tunnel_ids_.tunnel_term_id); + sai_remove_tunnel(tunnel_ids_.tunnel_id); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error while removing tunnel entry. Tunnel: %s. Error: %s", tunnel_name_.c_str(), error.what()); + } + + SWSS_LOG_INFO("NVGRE tunnel '%s' was removed", tunnel_name_.c_str()); + + tunnel_ids_.tunnel_id = SAI_NULL_OBJECT_ID; + tunnel_ids_.tunnel_term_id = SAI_NULL_OBJECT_ID; +} + +NvgreTunnel::NvgreTunnel(std::string tunnelName, IpAddress srcIp) : + tunnel_name_(tunnelName), + src_ip_(srcIp) +{ + createNvgreMappers(); + createNvgreTunnel(); +} + +NvgreTunnel::~NvgreTunnel() +{ + removeNvgreTunnel(); + removeNvgreMappers(); +} + +bool NvgreTunnelOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto src_ip = request.getAttrIP("src_ip"); + const auto& tunnel_name = request.getKeyString(0); + + if (isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("NVGRE tunnel '%s' already exists", tunnel_name.c_str()); + return true; + } + + nvgre_tunnel_table_[tunnel_name] = std::unique_ptr(new NvgreTunnel(tunnel_name, src_ip)); + + return true; +} + +bool NvgreTunnelOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + const auto& tunnel_name = request.getKeyString(0); + + if (!isTunnelExists(tunnel_name)) + { + SWSS_LOG_ERROR("NVGRE tunnel '%s' doesn't exist", tunnel_name.c_str()); + return true; + } + + nvgre_tunnel_table_.erase(tunnel_name); + + SWSS_LOG_INFO("NVGRE tunnel '%s' was removed", tunnel_name.c_str()); + + return true; +} + +/** @brief Creates tunnel map entry in SAI. + * + * @param map_type map type - VLAN or BRIDGE. + * @param vsid Virtual Subnet ID value. + * @param vlan_id VLAN ID value. + * @param bridge_obj_id SAI bridge object. + * @param encap encapsulation flag. + * + * @return SAI tunnel map entry ID. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel_map_entry( + map_type_t map_type, + sai_uint32_t vsid, + sai_vlan_id_t vlan_id, + sai_object_id_t bridge_obj_id, + bool encap) +{ + sai_attribute_t attr; + sai_object_id_t tunnel_map_entry_id; + std::vector tunnel_map_entry_attrs; + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE; + attr.value.u32 = (encap) ? get_encap_nvgre_mapper(map_type) : get_decap_nvgre_mapper(map_type); + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP; + attr.value.oid = (encap) ? getEncapMapId(map_type) : getDecapMapId(map_type); + tunnel_map_entry_attrs.push_back(attr); + + attr.id = (encap) ? get_encap_nvgre_map_key(map_type) : get_decap_nvgre_map_val(map_type); + if (bridge_obj_id != SAI_NULL_OBJECT_ID) + { + attr.value.oid = bridge_obj_id; + } + else + { + attr.value.u16 = vlan_id; + } + + tunnel_map_entry_attrs.push_back(attr); + + attr.id = (encap) ? get_encap_nvgre_map_val(map_type) : get_decap_nvgre_map_key(map_type); + attr.value.u32 = vsid; + tunnel_map_entry_attrs.push_back(attr); + + sai_status_t status = sai_tunnel_api->create_tunnel_map_entry(&tunnel_map_entry_id, gSwitchId, + static_cast (tunnel_map_entry_attrs.size()), + tunnel_map_entry_attrs.data()); + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create the NVGRE tunnel map entry object"); + } + + return tunnel_map_entry_id; +} + + +bool NvgreTunnel::addDecapMapperEntry( + map_type_t map_type, + uint32_t vsid, + sai_vlan_id_t vlan_id, + std::string tunnel_map_entry_name, + sai_object_id_t bridge_obj) +{ + auto tunnel_map_entry_id = sai_create_tunnel_map_entry(map_type, vsid, vlan_id, bridge_obj); + + nvgre_tunnel_map_table_[tunnel_map_entry_name].map_entry_id = tunnel_map_entry_id; + nvgre_tunnel_map_table_[tunnel_map_entry_name].vlan_id = vlan_id; + nvgre_tunnel_map_table_[tunnel_map_entry_name].vsid = vsid; + + SWSS_LOG_INFO("NVGRE decap tunnel map entry '%s' for tunnel '%s' was created", + tunnel_map_entry_name.c_str(), tunnel_name_.c_str()); + + return true; +} + +bool NvgreTunnelMapOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto tunnel_name = request.getKeyString(0); + NvgreTunnelOrch* tunnel_orch = gDirectory.get(); + + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("NVGRE tunnel '%s' doesn't exist", tunnel_name.c_str()); + return true; + } + + auto tunnel_obj = tunnel_orch->getNvgreTunnel(tunnel_name); + const auto full_tunnel_map_entry_name = request.getFullKey(); + + if (tunnel_obj->isTunnelMapExists(full_tunnel_map_entry_name)) + { + SWSS_LOG_WARN("NVGRE tunnel map '%s' already exist", full_tunnel_map_entry_name.c_str()); + return true; + } + + sai_vlan_id_t vlan_id = (sai_vlan_id_t) request.getAttrVlan("vlan_id"); + Port port; + + if (!gPortsOrch->getVlanByVlanId(vlan_id, port)) + { + SWSS_LOG_WARN("VLAN ID doesn't exist: %d", vlan_id); + return true; + } + + auto vsid = static_cast(request.getAttrUint("vsid")); + if (vsid > NVGRE_VSID_MAX_VALUE) + { + SWSS_LOG_WARN("VSID is invalid: %d", vsid); + return true; + } + + if (!tunnel_obj->addDecapMapperEntry(MAP_T_VLAN, vsid, vlan_id, full_tunnel_map_entry_name)) + { + return true; + } + + return true; +} + +/** @brief Removes tunnel map entry in SAI. + * + * @param obj_id SAI tunnel map identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel_map_entry(sai_object_id_t obj_id) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + + if (obj_id != SAI_NULL_OBJECT_ID) + { + status = sai_tunnel_api->remove_tunnel_map_entry(obj_id); + } + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't delete the NVGRE tunnel map entry object"); + } +} + +bool NvgreTunnel::delMapperEntry(std::string tunnel_map_entry_name) +{ + auto tunnel_map_entry_id = getMapEntryId(tunnel_map_entry_name); + + try + { + sai_remove_tunnel_map_entry(tunnel_map_entry_id); + } + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error while removing decap tunnel map %s: %s", + tunnel_map_entry_name.c_str(), error.what()); + return false; + } + + nvgre_tunnel_map_table_.erase(tunnel_map_entry_name); + + SWSS_LOG_INFO("NVGRE tunnel map entry '%s' for tunnel '%s' was removed", + tunnel_map_entry_name.c_str(), tunnel_name_.c_str()); + + return true; +} + +bool NvgreTunnelMapOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + const auto& tunnel_name = request.getKeyString(0); + NvgreTunnelOrch* tunnel_orch = gDirectory.get(); + auto tunnel_obj = tunnel_orch->getNvgreTunnel(tunnel_name); + const auto& full_tunnel_map_entry_name = request.getFullKey(); + + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("NVGRE tunnel '%s' does not exist", tunnel_name.c_str()); + return true; + } + + if (!tunnel_obj->isTunnelMapExists(full_tunnel_map_entry_name)) + { + SWSS_LOG_WARN("NVGRE tunnel map '%s' does not exist", + full_tunnel_map_entry_name.c_str()); + return true; + } + + if (!tunnel_obj->delMapperEntry(full_tunnel_map_entry_name)) + { + return true; + } + + return true; +} diff --git a/orchagent/nvgreorch.h b/orchagent/nvgreorch.h new file mode 100644 index 00000000000..82092565ac7 --- /dev/null +++ b/orchagent/nvgreorch.h @@ -0,0 +1,167 @@ +#pragma once + +#include + +#include "sai.h" +#include "orch.h" +#include "request_parser.h" +#include "portsorch.h" + +typedef enum { + MAP_T_VLAN = 0, + MAP_T_BRIDGE = 1, + MAP_T_MAX = 2 +} map_type_t; + +struct tunnel_sai_ids_t +{ + std::map tunnel_encap_id; + std::map tunnel_decap_id; + sai_object_id_t tunnel_id; + sai_object_id_t tunnel_term_id; +}; + +typedef struct nvgre_tunnel_map_entry_s +{ + sai_object_id_t map_entry_id; + sai_vlan_id_t vlan_id; + uint32_t vsid; +} nvgre_tunnel_map_entry_t; + +const request_description_t nvgre_tunnel_request_description = { + { REQ_T_STRING }, + { + { "src_ip", REQ_T_IP }, + }, + { "src_ip" } +}; + +typedef std::map NvgreTunnelMapTable; + +class NvgreTunnel +{ +public: + NvgreTunnel(std::string tunnelName, IpAddress srcIp); + ~NvgreTunnel(); + + bool isTunnelMapExists(const std::string& name) const + { + return nvgre_tunnel_map_table_.find(name) != std::end(nvgre_tunnel_map_table_); + } + + sai_object_id_t getDecapMapId(map_type_t type) const + { + return tunnel_ids_.tunnel_decap_id.at(type); + } + + sai_object_id_t getEncapMapId(map_type_t type) const + { + return tunnel_ids_.tunnel_encap_id.at(type); + } + + sai_object_id_t getMapEntryId(std::string tunnel_map_entry_name) + { + return nvgre_tunnel_map_table_.at(tunnel_map_entry_name).map_entry_id; + } + + sai_object_id_t getMapEntryVlanId(std::string tunnel_map_entry_name) + { + return nvgre_tunnel_map_table_.at(tunnel_map_entry_name).vlan_id; + } + + sai_object_id_t getMapEntryVsid(std::string tunnel_map_entry_name) + { + return nvgre_tunnel_map_table_.at(tunnel_map_entry_name).vsid; + } + + bool addDecapMapperEntry(map_type_t map_type, uint32_t vsid, sai_vlan_id_t vlan_id, std::string tunnel_map_entry_name, sai_object_id_t bridge_obj=SAI_NULL_OBJECT_ID); + + bool delMapperEntry(std::string tunnel_map_entry_name); + +private: + void createNvgreMappers(); + void removeNvgreMappers(); + + void createNvgreTunnel(); + void removeNvgreTunnel(); + + sai_object_id_t sai_create_tunnel_map(sai_tunnel_map_type_t sai_tunnel_map_type); + void sai_remove_tunnel_map(sai_object_id_t tunnel_map_id); + + sai_object_id_t sai_create_tunnel(struct tunnel_sai_ids_t &ids, const sai_ip_address_t &src_ip, sai_object_id_t underlay_rif); + void sai_remove_tunnel(sai_object_id_t tunnel_id); + + sai_object_id_t sai_create_tunnel_termination(sai_object_id_t tunnel_id, const sai_ip_address_t &src_ip, sai_object_id_t default_vrid); + void sai_remove_tunnel_termination(sai_object_id_t tunnel_term_id); + + sai_object_id_t sai_create_tunnel_map_entry(map_type_t map_type, sai_uint32_t vsid, sai_vlan_id_t vlan_id, sai_object_id_t bridge_obj_id, bool encap=false); + void sai_remove_tunnel_map_entry(sai_object_id_t obj_id); + + std::string tunnel_name_; + IpAddress src_ip_; + tunnel_sai_ids_t tunnel_ids_; + + NvgreTunnelMapTable nvgre_tunnel_map_table_; +}; + +typedef std::map> NvgreTunnelTable; + +class NvgreTunnelRequest : public Request +{ +public: + NvgreTunnelRequest() : Request(nvgre_tunnel_request_description, '|') { } +}; + +class NvgreTunnelOrch : public Orch2 +{ +public: + NvgreTunnelOrch(DBConnector *db, const std::string& tableName) : + Orch2(db, tableName, request_) + { } + + bool isTunnelExists(const std::string& tunnelName) const + { + return nvgre_tunnel_table_.find(tunnelName) != std::end(nvgre_tunnel_table_); + } + + NvgreTunnel* getNvgreTunnel(const std::string& tunnelName) + { + return nvgre_tunnel_table_.at(tunnelName).get(); + } + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + NvgreTunnelRequest request_; + NvgreTunnelTable nvgre_tunnel_table_; +}; + +const request_description_t nvgre_tunnel_map_request_description = { + { REQ_T_STRING, REQ_T_STRING }, + { + { "vsid", REQ_T_UINT }, + { "vlan_id", REQ_T_VLAN }, + }, + { "vsid", "vlan_id" } +}; + +class NvgreTunnelMapRequest : public Request +{ +public: + NvgreTunnelMapRequest() : Request(nvgre_tunnel_map_request_description, '|') { } +}; + +class NvgreTunnelMapOrch : public Orch2 +{ +public: + NvgreTunnelMapOrch(DBConnector *db, const std::string& tableName) : + Orch2(db, tableName, request_) + {} + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + NvgreTunnelMapRequest request_; +}; \ No newline at end of file diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 03f16dc2767..d4fe4828b4f 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -201,6 +201,10 @@ bool OrchDaemon::init() EvpnNvoOrch* evpn_nvo_orch = new EvpnNvoOrch(m_applDb, APP_VXLAN_EVPN_NVO_TABLE_NAME); gDirectory.set(evpn_nvo_orch); + NvgreTunnelOrch *nvgre_tunnel_orch = new NvgreTunnelOrch(m_configDb, CFG_NVGRE_TUNNEL_TABLE_NAME); + gDirectory.set(nvgre_tunnel_orch); + NvgreTunnelMapOrch *nvgre_tunnel_map_orch = new NvgreTunnelMapOrch(m_configDb, CFG_NVGRE_TUNNEL_MAP_TABLE_NAME); + gDirectory.set(nvgre_tunnel_map_orch); vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, @@ -425,6 +429,8 @@ bool OrchDaemon::init() m_orchList.push_back(gIsoGrpOrch); m_orchList.push_back(gFgNhgOrch); m_orchList.push_back(mux_st_orch); + m_orchList.push_back(nvgre_tunnel_orch); + m_orchList.push_back(nvgre_tunnel_map_orch); if (m_fabricEnabled) { diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index ea49affbfc1..35e531aa15c 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -43,6 +43,7 @@ #include "p4orch/p4orch.h" #include "bfdorch.h" #include "srv6orch.h" +#include "nvgreorch.h" using namespace swss; diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index b51d5ab4721..42b9f17110f 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -90,7 +90,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/macsecorch.cpp \ $(top_srcdir)/orchagent/lagid.cpp \ $(top_srcdir)/orchagent/bfdorch.cpp \ - $(top_srcdir)/orchagent/srv6orch.cpp + $(top_srcdir)/orchagent/srv6orch.cpp \ + $(top_srcdir)/orchagent/nvgreorch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/test_nvgre_tunnel.py b/tests/test_nvgre_tunnel.py new file mode 100644 index 00000000000..90fe560141c --- /dev/null +++ b/tests/test_nvgre_tunnel.py @@ -0,0 +1,381 @@ +import time +import json +import random +import time +import pytest + + +from swsscommon import swsscommon +from pprint import pprint + + +NVGRE_TUNNEL = 'NVGRE_TUNNEL' +NVGRE_TUNNEL_MAP = 'NVGRE_TUNNEL_MAP' + + +SAI_OBJECT_TYPE_TUNNEL = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL' +SAI_OBJECT_TYPE_TUNNEL_MAP = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP' +SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY' + + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + +def create_entry_tbl(db, table, separator, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + + +def delete_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + + +def get_all_created_entries(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) >= 0, "DB entries was't created" + new_entries.sort() + return new_entries + + +def get_created_entries(db, table, existed_entries, count): + new_entries = get_all_created_entries(db, table, existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + return new_entries + + +def get_exist_entries(dvs, table): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + +def get_created_entry(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == 1, "Wrong number of created entries." + return new_entries[0] + + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + +def get_lo(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE') + + entries = tbl.getKeys() + lo_id = None + for entry in entries: + status, fvs = tbl.get(entry) + assert status, "Got an error when get a key" + for key, value in fvs: + if key == 'SAI_ROUTER_INTERFACE_ATTR_TYPE' and value == 'SAI_ROUTER_INTERFACE_TYPE_LOOPBACK': + lo_id = entry + break + else: + assert False, 'Don\'t found loopback id' + + return lo_id + + +def check_object(db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key in keys, "The desired key is not presented" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) == len(expected_attributes), "Unexpected number of attributes" + + attr_keys = {entry[0] for entry in fvs} + + for name, value in fvs: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + + +loopback_id = 0 + + +class NvgreTunnel(object): + tunnel_ids = set() + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_map_map = {} + tunnel = {} + + + def fetch_exist_entries(self, dvs): + self.tunnel_ids = get_exist_entries(dvs, SAI_OBJECT_TYPE_TUNNEL) + self.tunnel_map_ids = get_exist_entries(dvs, SAI_OBJECT_TYPE_TUNNEL_MAP) + self.tunnel_map_entry_ids = get_exist_entries(dvs, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) + + global loopback_id + if not loopback_id: + loopback_id = get_lo(dvs) + + + def create_nvgre_tunnel(self, dvs, tunnel_name, src_ip): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + create_entry_tbl(conf_db, NVGRE_TUNNEL, '|', tunnel_name, [ ('src_ip', src_ip) ]) + time.sleep(1) + + + def check_nvgre_tunnel(self, dvs, tunnel_name, src_ip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + global loopback_id + + tunnel_id = get_created_entry(asic_db, SAI_OBJECT_TYPE_TUNNEL, self.tunnel_ids) + tunnel_map_ids = get_created_entries(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, self.tunnel_map_ids, 4) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) == len(self.tunnel_map_entry_ids), "The TUNNEL_MAP_ENTRY is created too early" + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" + + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[0], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VLAN_ID_TO_VSID' }) + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[1], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_BRIDGE_IF_TO_VSID' }) + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[2], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VSID_TO_VLAN_ID' }) + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[3], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VSID_TO_BRIDGE_IF' }) + + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL, tunnel_id, + { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_NVGRE', + 'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': f'2:{tunnel_map_ids[2]},{tunnel_map_ids[3]}', + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': f'2:{tunnel_map_ids[0]},{tunnel_map_ids[1]}', + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip + } + ) + + self.tunnel_map_ids.update(tunnel_map_ids) + self.tunnel_ids.add(tunnel_id) + self.tunnel_map_map[tunnel_name] = tunnel_map_ids + self.tunnel[tunnel_name] = tunnel_id + + + def check_invalid_nvgre_tunnel(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL) == len(self.tunnel_ids), "Invalid TUNNEL was created" + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP) == len(self.tunnel_map_ids), "Invalid TUNNEL_MAP was created" + + + def remove_nvgre_tunnel(self, dvs, tunnel_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + delete_entry_tbl(conf_db, NVGRE_TUNNEL, tunnel_name) + time.sleep(1) + + + def check_remove_nvgre_tunnel(self, dvs, tunnel_name): + self.fetch_exist_entries(dvs) + self.tunnel.pop(tunnel_name, None) + self.tunnel_map_map.pop(tunnel_name, None) + + + def create_nvgre_tunnel_map_entry(self, dvs, tunnel_name, tunnel_map_entry_name, vlan_id, vsid): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + create_entry_tbl( + conf_db, + NVGRE_TUNNEL_MAP, '|', f'{tunnel_name}|{tunnel_map_entry_name}', + [ + ('vsid', vsid), + ('vlan_id', f'Vlan{vlan_id}'), + ], + ) + time.sleep(1) + + + def check_nvgre_tunnel_map_entry(self, dvs, tunnel_name, vlan_id, vsid): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_ids = get_created_entries(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, self.tunnel_map_ids, 4) + else: + tunnel_map_ids = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = get_created_entries(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 1) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 1), "The TUNNEL_MAP_ENTRY is created too early" + + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VSID_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_ids[2], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_KEY': vsid, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vlan_id, + } + ) + + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + + def check_invalid_nvgre_tunnel_map_entry(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) == len(self.tunnel_map_entry_ids), "Invalid TUNNEL_MAP_ENTRY was created" + + + def remove_nvgre_tunnel_map_entry(self, dvs, tunnel_name, tunnel_map_entry_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + delete_entry_tbl(conf_db, NVGRE_TUNNEL_MAP, f'{tunnel_name}|{tunnel_map_entry_name}') + time.sleep(1) + + + def check_remove_nvgre_tunnel_map_entry(self, dvs): + self.fetch_exist_entries(dvs) + + +@pytest.mark.usefixtures('dvs_vlan_manager') +class TestNvgreTunnel(object): + + def get_nvgre_tunnel_obj(self): + return NvgreTunnel() + + + def test_nvgre_create_tunnel_map_entry(self, dvs, testlog): + try: + tunnel_name = 'tunnel_1' + tunnel_map_entry_name = 'entry_1' + src_ip = '10.0.0.1' + vlan_id = '500' + vsid = '850' + + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + self.dvs_vlan.create_vlan(vlan_id) + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name, src_ip) + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name, src_ip) + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name, tunnel_map_entry_name, vlan_id, vsid) + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name, vlan_id, vsid) + finally: + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name, tunnel_map_entry_name) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name) + + self.dvs_vlan.remove_vlan(vlan_id) + + + def test_multiple_nvgre_tunnels_entries(self, dvs, testlog): + try: + tunnel_name_1 = 'tunnel_1' + tunnel_name_2 = 'tunnel_2' + tunnel_name_3 = 'tunnel_3' + entry_1 = 'entry_1' + entry_2 = 'entry_2' + entry_3 = 'entry_3' + entry_4 = 'entry_4' + + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + self.dvs_vlan.create_vlan('501') + self.dvs_vlan.create_vlan('502') + self.dvs_vlan.create_vlan('503') + self.dvs_vlan.create_vlan('504') + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name_1, '10.0.0.1') + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name_1, '10.0.0.1') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_1, entry_1, '501', '801') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_1, '501', '801') + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name_2, '10.0.0.2') + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name_2, '10.0.0.2') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_2, entry_2, '502', '802') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_2, '502', '802') + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name_3, '10.0.0.3') + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name_3, '10.0.0.3') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_3, '503', '803') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_3, '503', '803') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_4, '504', '804') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_3, '504', '804') + finally: + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_1, entry_1) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name_1) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name_1) + + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_2, entry_2) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name_2) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name_2) + + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_3) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_4) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name_3) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name_3) + + self.dvs_vlan.remove_vlan('501') + self.dvs_vlan.remove_vlan('502') + self.dvs_vlan.remove_vlan('503') + self.dvs_vlan.remove_vlan('504') + + + def test_invalid_nvgre_tunnel(self, dvs, testlog): + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + nvgre_obj.create_nvgre_tunnel(dvs, 'tunnel_1', '1111.1111.1111.1111') + nvgre_obj.check_invalid_nvgre_tunnel(dvs) + + + def test_invalid_nvgre_tunnel_map_entry(self, dvs, testlog): + try: + tunnel_name = 'tunnel_1' + tunnel_map_entry_name = 'entry_1' + src_ip = '10.0.0.1' + vlan_id = '500' + vsid = 'INVALID' + + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + self.dvs_vlan.create_vlan(vlan_id) + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name, src_ip) + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name, src_ip) + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name, tunnel_map_entry_name, vlan_id, vsid) + nvgre_obj.check_invalid_nvgre_tunnel_map_entry(dvs) + finally: + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name) + + self.dvs_vlan.remove_vlan(vlan_id) + + +# Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass