diff --git a/vslib/BitResourcePool.h b/vslib/BitResourcePool.h new file mode 100644 index 000000000..0f6b9e933 --- /dev/null +++ b/vslib/BitResourcePool.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +// TODO refactor and move to cpp +// +// NOTE: this class is probably not needed at all + +namespace saivs +{ + class BitResourcePool { + public: + BitResourcePool(uint16_t size, uint32_t base) : resource_size(size), base_index(base){ + if (size > MAX_RESOURCE_SIZE_BYTES * 8) { + throw std::invalid_argument("Resource size exceeds maximum size"); + } + } + ~BitResourcePool() = default; + int alloc() { + for (uint16_t i = 0; i < resource_size; i++) { + if ((resource_bitmap[i / 8] & (1 << (i % 8))) == 0) { + resource_bitmap[i / 8] |= (uint8_t)(1 << (i % 8)); + return base_index + i; + } + } + return -1; + } + void free(uint32_t index) { + if (index >= resource_size + base_index || index < base_index) { + throw std::invalid_argument("Invalid index"); + } + index -= base_index; + resource_bitmap[index / 8] &= (uint8_t)(~(1 << (index % 8))); + } + private: + static const int MAX_RESOURCE_SIZE_BYTES = 16 * 1024; + uint8_t resource_bitmap[MAX_RESOURCE_SIZE_BYTES] = {0}; + uint16_t resource_size; + uint32_t base_index; + }; +}; diff --git a/vslib/HostInterfaceInfo.cpp b/vslib/HostInterfaceInfo.cpp index c558d2a70..bdb1464df 100644 --- a/vslib/HostInterfaceInfo.cpp +++ b/vslib/HostInterfaceInfo.cpp @@ -40,10 +40,7 @@ HostInterfaceInfo::HostInterfaceInfo( { SWSS_LOG_ENTER(); - m_run_thread = true; - - m_e2t = std::make_shared(&HostInterfaceInfo::veth2tap_fun, this); - m_t2e = std::make_shared(&HostInterfaceInfo::tap2veth_fun, this); + m_run_thread = false; } HostInterfaceInfo::~HostInterfaceInfo() @@ -77,6 +74,21 @@ HostInterfaceInfo::~HostInterfaceInfo() SWSS_LOG_NOTICE("joined threads for hostif: %s", m_name.c_str()); } +void HostInterfaceInfo::runThreads() +{ + SWSS_LOG_ENTER(); + + if (m_run_thread) + { + return; + } + + m_run_thread = true; + + m_e2t = std::make_shared(&HostInterfaceInfo::veth2tap_fun, this); + m_t2e = std::make_shared(&HostInterfaceInfo::tap2veth_fun, this); +} + void HostInterfaceInfo::async_process_packet_for_fdb_event( _In_ const uint8_t *data, _In_ size_t size) const diff --git a/vslib/HostInterfaceInfo.h b/vslib/HostInterfaceInfo.h index 9d6da9a3d..5aee41866 100644 --- a/vslib/HostInterfaceInfo.h +++ b/vslib/HostInterfaceInfo.h @@ -55,6 +55,8 @@ namespace saivs bool uninstallTap2EthFilter( _In_ std::shared_ptr filter); + void runThreads(); + private: void veth2tap_fun(); diff --git a/vslib/IpVrfInfo.h b/vslib/IpVrfInfo.h new file mode 100644 index 000000000..659fbadbf --- /dev/null +++ b/vslib/IpVrfInfo.h @@ -0,0 +1,31 @@ +#pragma once + +extern "C" { +#include "sai.h" +} + +namespace saivs +{ + class IpVrfInfo + { + public: + + IpVrfInfo( + _In_ sai_object_id_t obj_id, + _In_ uint32_t vrf_id, + _In_ std::string &vrf_name, + _In_ bool is_ipv6); + + virtual ~IpVrfInfo(); + + public: + + sai_object_id_t m_obj_id; + + uint32_t m_vrf_id; + + std::string m_vrf_name; + + bool m_is_ipv6; + }; +} diff --git a/vslib/Makefile.am b/vslib/Makefile.am index ab6b68a31..98282236f 100644 --- a/vslib/Makefile.am +++ b/vslib/Makefile.am @@ -1,10 +1,19 @@ AM_CXXFLAGS = $(SAIINC) -I$(top_srcdir)/lib -I/usr/include/libnl3 +# TODO fix multiple paths +AM_CFLAGS = -I/usr/include/vpp_plugins -fPIC -I. #-I.. -I../../ -I../../../ + lib_LTLIBRARIES = libsaivs.la noinst_LIBRARIES = libSaiVS.a +# TODO: move vppxlate to proper lib and namespaces + libSaiVS_a_SOURCES = \ + vppxlate/SaiVppXlate.c \ + vppxlate/SaiVppStats.c \ + vppxlate/SaiAclStats.c \ + vppxlate/SaiIntfStats.c \ Buffer.cpp \ ContextConfigContainer.cpp \ ContextConfig.cpp \ @@ -57,6 +66,18 @@ libSaiVS_a_SOURCES = \ SwitchState.cpp \ TrafficFilterPipes.cpp \ TrafficForwarder.cpp \ + SaiObjectDB.cpp \ + SwitchVpp.cpp \ + SwitchVppAcl.cpp \ + SwitchVppNexthop.cpp \ + SwitchVppUtils.cpp \ + SwitchVppFdb.cpp \ + SwitchVppRif.cpp \ + SwitchVppBfd.cpp \ + SwitchVppHostif.cpp \ + SwitchVppRoute.cpp \ + SwitchVppNbr.cpp \ + TunnelManager.cpp \ VirtualSwitchSaiInterface.cpp \ VirtualSwitchSaiInterfaceFdb.cpp \ VirtualSwitchSaiInterfacePort.cpp @@ -69,6 +90,9 @@ sai_vs.cpp: ../stub.pl $(top_srcdir)/SAI/meta/saimetadata.c clean-local: rm -f sai_vs.cpp +# TODO move to configure? +VPP_LIBS = -lvlibapi -lvapiclient -lvppapiclient -lvlibmemoryclient -lsvm -lvppinfra -lvlib -lvatplugin + libsaivs_la_SOURCES = sai_vs.cpp libSaiVS_a_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS) @@ -82,6 +106,6 @@ bin_PROGRAMS = tests tests_SOURCES = tests.cpp tests_CXXFLAGS = $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON) -tests_LDADD = -lhiredis -lswsscommon -lpthread libsaivs.la -L$(top_srcdir)/meta/.libs -lsaimetadata -lsaimeta -lzmq +tests_LDADD = -lhiredis -lswsscommon -lpthread libsaivs.la -L$(top_srcdir)/meta/.libs -lsaimetadata -lsaimeta -lzmq $(VPP_LIBS) TESTS = tests diff --git a/vslib/SaiObjectDB.cpp b/vslib/SaiObjectDB.cpp new file mode 100644 index 000000000..0f0b9671f --- /dev/null +++ b/vslib/SaiObjectDB.cpp @@ -0,0 +1,393 @@ +#include "SwitchVpp.h" +#include "SwitchVppUtils.h" +#include "SaiObjectDB.h" + +#include "meta/sai_serialize.h" + +#include "swss/logger.h" + +#include +#include + +using namespace saivs; + +/* + * Define the child relation between SAI objects. Child is dependent on parent object. For example, ROUTE_ENTRY is a child of VR. + * Key: child object type + * Value: list of child relations. Each child relation defines the parent object type, the attribute id in child object pointing + * to Parent object and the attribute type. + * Child objects defined in this map will be added to the parent object when the child object is created. From parent object, we + * can get the child object by calling get_child_objs() method with the child object type. + * + * Technically, this definition is not needed. Through SAI meta, we can find which attributes are of type object_id or object_list. + * From the attribute value and RealObjectIdManager::objectTypeQuery, we can find the parent object type. So we can create a complete + * graph of all SAI objects. + */ +std::map> sai_child_relation_defs = { + {SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY, {{SAI_OBJECT_TYPE_TUNNEL_MAP, SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP, SAI_ATTR_VALUE_TYPE_OBJECT_ID}}}, + {SAI_OBJECT_TYPE_TUNNEL, {{SAI_OBJECT_TYPE_TUNNEL_MAP, SAI_TUNNEL_ATTR_DECAP_MAPPERS, SAI_ATTR_VALUE_TYPE_OBJECT_LIST}, + {SAI_OBJECT_TYPE_TUNNEL_MAP, SAI_TUNNEL_ATTR_ENCAP_MAPPERS, SAI_ATTR_VALUE_TYPE_OBJECT_LIST}}}, + {SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY, {{SAI_OBJECT_TYPE_TUNNEL, SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID, SAI_ATTR_VALUE_TYPE_OBJECT_ID}}}, + {SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER, {{SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID, SAI_ATTR_VALUE_TYPE_OBJECT_ID}}}, + {SAI_OBJECT_TYPE_ROUTE_ENTRY, {{SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID, SAI_ATTR_VALUE_TYPE_OBJECT_ID}}}, +}; + +static std::vector +get_parent_oids(const sai_attribute_value_t *attr_val, const SaiChildRelation& child_def) +{ + std::vector parent_ids; + + switch (child_def.child_link_attr_type) { + case SAI_ATTR_VALUE_TYPE_OBJECT_ID: + parent_ids.push_back(sai_serialize_object_id(attr_val->oid)); + break; + case SAI_ATTR_VALUE_TYPE_OBJECT_LIST: + { + auto& linked_obj_list = attr_val->objlist; + for (uint32_t i = 0; i < linked_obj_list.count; i++) { + parent_ids.push_back(sai_serialize_object_id(linked_obj_list.list[i])); + } + break; + } + default: + break; + } + return parent_ids; +} + +static std::vector +get_parent_oids(SwitchVpp* switch_db, sai_object_type_t child_type, const std::string& child_oid, const SaiChildRelation& child_def) +{ + std::vector parent_ids; + sai_status_t status; + sai_attribute_t attr; + sai_object_id_t obj_list[MAX_OBJLIST_LEN]; + attr.id = child_def.child_link_attr; + if (child_def.child_link_attr_type == SAI_ATTR_VALUE_TYPE_OBJECT_LIST) { + attr.value.objlist.count = MAX_OBJLIST_LEN; + attr.value.objlist.list = obj_list; + } + + status = switch_db->get(child_type, child_oid, 1, &attr); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_WARN("get_parent_oids: the child object is not found in switch_db %s", child_oid.c_str()); + return parent_ids; + } + switch (child_def.child_link_attr_type) { + case SAI_ATTR_VALUE_TYPE_OBJECT_ID: + parent_ids.push_back(sai_serialize_object_id(attr.value.oid)); + break; + case SAI_ATTR_VALUE_TYPE_OBJECT_LIST: + { + sai_object_list_t& linked_obj_list = attr.value.objlist; + for (uint32_t i = 0; i < linked_obj_list.count; i++) { + parent_ids.push_back(sai_serialize_object_id(linked_obj_list.list[i])); + } + break; + } + default: + break; + } + return parent_ids; +} + +sai_status_t +SaiObjectDB::create_or_update( + _In_ sai_object_type_t object_type, + _In_ const std::string& id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _In_ bool is_create) +{ + sai_status_t status; + auto child_def_it = sai_child_relation_defs.find(object_type); + if (child_def_it == sai_child_relation_defs.end()) { + // Not an interesting type + return SAI_STATUS_SUCCESS; + } + + //Get Parent object type and OID + const sai_attribute_value_t *attr_val; + uint32_t attr_index; + //iterate multiple child relation for the given parent type + auto& child_defs = child_def_it->second; + for (auto child_def: child_defs) { + status = find_attrib_in_list(attr_count, attr_list, child_def.child_link_attr, + &attr_val, &attr_index); + if (status != SAI_STATUS_SUCCESS) { + // attribute not found. move to next child relation. + continue; + } + + if (!is_create) { + //update the parent-child relationship + remove_child_from_parent(object_type, id, child_def); + } + + auto sai_parents = m_sai_parent_objs[child_def.parent_type]; + std::shared_ptr sai_parent; + std::vector parent_ids = get_parent_oids(attr_val, child_def); + + for (auto parent_id: parent_ids) { + sai_object_id_t parent_oid; + sai_deserialize_object_id(parent_id, parent_oid); + sai_object_type_t parent_type = RealObjectIdManager::objectTypeQuery(parent_oid); + if (parent_type != child_def.parent_type) { + /* + * a child may refer to different type of parents with the same parent OID attribute. For example, + * ROUTE_ENTRY uses SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID to refer to NEXT_HOP or NEXT_HOP_GROUP. + */ + continue; + } + auto parent_it = sai_parents.find(parent_id); + if (parent_it == sai_parents.end()) { + //The Parent object hasn't been created. create the Parent object + sai_parent = std::make_shared(m_switch_db, child_def.parent_type, parent_id); + sai_parents[parent_id] = sai_parent; + } else { + sai_parent = parent_it->second; + } + m_sai_parent_objs[child_def.parent_type] = sai_parents; + /** + * multiple copies of child objects can exist as leaf of different parent objects. There can even + * be a copy as parent object if it is created as child first then another object is added as its child. + * Today SaiDBObject is just a wrapper to the underlaying object in switch_db + * until it becomes a parent object, where parent-child relationship is maintained. When the object is + * deleted, it will be removed from the child list of parent objects and from the SaiObjectDB if it is + * also a parent object. Since SaiDBObject is a simple wrapper if it is a child object, it is ok to have + * multiple copies. If we are going to extend it to keep other information, we need to make sure that + * a single copy exists in the SaiObjectDB. + */ + auto sai_child = std::make_shared(m_switch_db, object_type, id); + sai_parent->add_child(sai_child); + SWSS_LOG_INFO("Add child %s:%s to parent %s:%s", + sai_serialize_object_type(object_type).c_str(), id.c_str(), + sai_serialize_object_type(child_def.parent_type).c_str(), parent_id.c_str()); + } + } + + return SAI_STATUS_SUCCESS; +} + +/** + * @brief Removes a child object from its parent in the SaiObjectDB for the give child-parent relationship definition. + * + * This function removes a child object from its parent in the SaiObjectDB. It takes the object type, the ID of the child object, + * and the child-parent relationship definition as input parameters. + * The function retrieves the parent object IDs using the get_parent_oids() function and iterates over each parent ID. + * For each parent ID, it checks if the parent object exists in the SaiObjectDB. If the parent object is not found, a warning + * message is logged. + * If the parent object is found, the function removes the child object from the parent object using the remove_child() + * function and logs a debug message. + * + * @param object_type The type of the child object. + * @param id The ID of the child object. + * @param child_def The child-parent relationship definition. + */ +void +SaiObjectDB::remove_child_from_parent( + _In_ sai_object_type_t object_type, + _In_ const std::string& id, + _In_ const SaiChildRelation& child_def) +{ + std::vector parent_ids = get_parent_oids(m_switch_db, object_type, id, child_def); + auto sai_parents = m_sai_parent_objs[child_def.parent_type]; + for (auto parent_id: parent_ids) { + sai_object_id_t parent_oid; + sai_deserialize_object_id(parent_id, parent_oid); + sai_object_type_t parent_type = RealObjectIdManager::objectTypeQuery(parent_oid); + if (parent_type != child_def.parent_type) { + /* + * a child may refer to different type of parents with the same parent OID attribute. For example, + * ROUTE_ENTRY uses SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID to refer to NEXT_HOP or NEXT_HOP_GROUP. + */ + continue; + } + auto parent_it = sai_parents.find(parent_id); + if (parent_it == sai_parents.end()) { + SWSS_LOG_WARN("Parent object %s:%s is not found in SaiObjectDB", + sai_serialize_object_type(child_def.parent_type).c_str(), parent_id.c_str()); + return; + } else { + //remove the child from sai_parent + parent_it->second->remove_child(object_type, id); + SWSS_LOG_INFO("Remove child %s:%s from parent %s:%s of type %s", + sai_serialize_object_type(object_type).c_str(), + id.c_str(), + sai_serialize_object_type(child_def.parent_type).c_str(), + parent_id.c_str(), + "unknown"); // TODO + } + } +} +sai_status_t +SaiObjectDB::remove( + _In_ sai_object_type_t object_type, + _In_ const std::string& id) +{ + //remove parent object + auto sai_parent_type_it = m_sai_parent_objs.find(object_type); + if (sai_parent_type_it != m_sai_parent_objs.end()) { + auto sai_parent_it = sai_parent_type_it->second.find(id); + if (sai_parent_it != sai_parent_type_it->second.end()) { + sai_parent_type_it->second.erase(sai_parent_it); + } else { + SWSS_LOG_WARN("The object to be removed is not found in SaiObjectDB %s", id.c_str()); + } + + return SAI_STATUS_SUCCESS; + } + //The object can be a child of a parent object + auto child_def_it = sai_child_relation_defs.find(object_type); + if (child_def_it == sai_child_relation_defs.end()) { + // Not an interesting type + return SAI_STATUS_SUCCESS; + } + + //Get Parent object type and OID + auto& child_defs = child_def_it->second; + for (auto child_def: child_defs) { + remove_child_from_parent(object_type, id, child_def); + } + return SAI_STATUS_SUCCESS; +} + +std::shared_ptr +SaiObjectDB::get( + _In_ sai_object_type_t object_type, + _In_ const std::string& id) +{ + sai_status_t status; + sai_attribute_t attr; + //first check if the object is a stored SaiDBObject + auto sai_parent_type_it = m_sai_parent_objs.find(object_type); + if (sai_parent_type_it != m_sai_parent_objs.end()) { + auto sai_parent_it = sai_parent_type_it->second.find(id); + if (sai_parent_it != sai_parent_type_it->second.end()) { + return sai_parent_it->second; + } + } + // check if the object exists + status = m_switch_db->get(object_type, id, 0, &attr); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_WARN("Object is not found in SwitchVpp %s:%s", sai_serialize_object_type(object_type).c_str(), id.c_str()); + return std::shared_ptr(); + } + /* + return a SaiObject as a wrapper to underlaying object is switch_db. + the object may exists in a sai_parent as an entry but we don't care if + returning a new copy since it is just a wrapper today */ + return std::make_shared(m_switch_db, object_type, id); +} + +std::shared_ptr +SaiObject::get_linked_object( + _In_ sai_object_type_t linked_object_type, + _In_ sai_attr_id_t link_attr_id) const +{ + sai_status_t status; + sai_attribute_t attr; + std::string linked_obj_id; + + attr.id = link_attr_id; + status = get_attr(attr); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to get attribute %d from object %s", link_attr_id, m_id.c_str()); + return std::shared_ptr(); + } + linked_obj_id = sai_serialize_object_id(attr.value.oid); + + return m_switch_db->get_sai_object(linked_object_type, linked_obj_id); +} + +std::vector> +SaiObject::get_linked_objects( + _In_ sai_object_type_t linked_object_type, + _In_ sai_attr_id_t link_attr_id) const +{ + sai_status_t status; + sai_attribute_t attr; + std::string linked_obj_id; + std::vector> linked_objs; + sai_object_id_t obj_list[MAX_OBJLIST_LEN]; + attr.id = link_attr_id; + attr.value.objlist.count = MAX_OBJLIST_LEN; + attr.value.objlist.list = obj_list; + status = get_attr(attr); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to get attribute %s from object %s", get_attr_name(link_attr_id), m_id.c_str()); + return linked_objs; + } + sai_object_list_t& linked_obj_list = attr.value.objlist; + for (uint32_t i = 0; i < linked_obj_list.count; i++) { + linked_obj_id = sai_serialize_object_id(linked_obj_list.list[i]); + auto linked_obj = m_switch_db->get_sai_object(linked_object_type, linked_obj_id); + if (linked_obj) { + linked_objs.push_back(linked_obj); + } + } + return linked_objs; +} +const char* +SaiObject::get_attr_name(_In_ sai_attr_id_t attr_id) const +{ + auto meta = sai_metadata_get_attr_metadata(m_type, attr_id); + if (meta == NULL) { + return "unknown"; + } else { + return meta->attridname; + } +} + +sai_status_t +SaiObject::get_mandatory_attr(sai_attribute_t &attr) const +{ + auto status = get_attr(attr); + if (SAI_STATUS_SUCCESS != status) { + SWSS_LOG_ERROR("Failed to get attribute %s from object %s", get_attr_name(attr.id), m_id.c_str()); + } + return status; +} + +sai_status_t +SaiCachedObject::get_attr(sai_attribute_t &attr) const +{ + for (uint32_t ii = 0; ii < m_attr_count; ii++) { + if (m_attr_list[ii].id == attr.id) { + return transfer_attributes(m_type, 1, &(m_attr_list[ii]), &attr, false); + } + } + return SAI_STATUS_ITEM_NOT_FOUND; +} + +sai_status_t +SaiDBObject::get_attr(sai_attribute_t &attr) const +{ + /* we could make a copy of all the attributes and cache in this object*/ + return m_switch_db->get(m_type, m_id, 1, &attr); +} + +SaiModDBObject::SaiModDBObject(SwitchVpp* switch_db, sai_object_type_t type, const std::string& id, + uint32_t attr_count, const sai_attribute_t *attr_list) : + SaiObject(switch_db, type, id), m_attr_count(attr_count), m_attr_list(attr_list) +{ + m_sai_db_obj = switch_db->get_sai_object(type, id); + if (!m_sai_db_obj) { + SWSS_LOG_ERROR("SaiModDBObject: the object is not found in switch_db %s", id.c_str()); + } +} + +sai_status_t +SaiModDBObject::get_attr(sai_attribute_t &attr) const +{ + for (uint32_t ii = 0; ii < m_attr_count; ii++) { + if (m_attr_list[ii].id == attr.id) { + return transfer_attributes(m_type, 1, &(m_attr_list[ii]), &attr, false); + } + } + if (m_sai_db_obj) { + return m_sai_db_obj->get_attr(attr); + } else { + return SAI_STATUS_ITEM_NOT_FOUND; + } +} diff --git a/vslib/SaiObjectDB.h b/vslib/SaiObjectDB.h new file mode 100644 index 000000000..3b3ddf4b3 --- /dev/null +++ b/vslib/SaiObjectDB.h @@ -0,0 +1,316 @@ +#pragma once + +#include + +extern "C" { +#include "sai.h" +} +#include +#include + +namespace saivs +{ + /* + There is circular dependency between SaiObjectDB and SwitchVpp. To remove it we need major surgery. For example, + move SaiObjectDB to SwitchState. When we need to read an object, such as get_linked_object, get it from m_objectHash in SwitchState + */ + class SwitchVpp; + class SaiDBObject; + + typedef struct _SaiChildRelation + { + sai_object_type_t parent_type; + // the attribute id in child object pointing to Parent object + sai_attr_id_t child_link_attr; + sai_attr_value_type_t child_link_attr_type; + } SaiChildRelation; + + /* + SaiObject is the base class for all objects in the SaiObjectDB. + It represents a generic SAI object with a type, serialized ID, and associated attributes. + Derived classes should implement the get_attr() function to retrieve specific attributes. + */ + class SaiObject { + public: + SaiObject(SwitchVpp* switch_db, sai_object_type_t type, const std::string& id) : m_switch_db(switch_db), m_type(type), m_id(id){} + + virtual ~SaiObject() = default; + + virtual sai_status_t get_attr(_Inout_ sai_attribute_t &attr) const = 0; + + /** + * @brief Retrieves the value of a specific attribute of the SAI object. If the attribute is not found, log an error and + * return SAI_STATUS_FAILURE. + * + * @param attr [out] A reference to the sai_attribute_t structure to store the attribute value. + * @return sai_status_t The status of the attribute retrieval operation. + */ + sai_status_t get_mandatory_attr(_Inout_ sai_attribute_t &attr) const; + + const std::string& get_id() const { return m_id; } + + sai_object_type_t get_type() const { return m_type; } + + std::shared_ptr get_linked_object(_In_ sai_object_type_t linked_object_type, _In_ sai_attr_id_t link_attr_id) const; + + std::vector> get_linked_objects(_In_ sai_object_type_t linked_object_type, _In_ sai_attr_id_t link_attr_id) const; + + // Get the attribute name from the attribute ID + const char* get_attr_name(_In_ sai_attr_id_t attr_id) const; + protected: + SwitchVpp* m_switch_db; + sai_object_type_t m_type; + std::string m_id; + }; + + /** + * @brief The SaiCachedObject class represents a SAI object that has not been created in the SaiObjectDB. This is typically + * used for objects that are being created through the SAI API. + * + * The cached object contains the type, serialized ID, and associated attributes. + * + * Derived classes should implement the get_attr() function to retrieve specific attributes. + */ + class SaiCachedObject : public SaiObject { + public: + /** + * @brief Constructs a SaiCachedObject with the specified parameters. + * + * @param switch_db A pointer to the SwitchVpp object. + * @param type The type of the SAI object. + * @param id The serialized ID of the SAI object. + * @param attr_count The number of attributes associated with the SAI object. + * @param attr_list An array of sai_attribute_t structures representing the attributes of the SAI object. + */ + SaiCachedObject(SwitchVpp* switch_db, sai_object_type_t type, const std::string& id, uint32_t attr_count, const sai_attribute_t *attr_list) : + SaiObject(switch_db, type, id), m_attr_count(attr_count), m_attr_list(attr_list) {} + ~SaiCachedObject() = default; + + /** + * @brief Retrieves the value of a specific attribute of the SAI object. + * + * This function should be implemented in derived classes to provide the specific attribute value. + * + * @param attr [out] A reference to the sai_attribute_t structure to store the attribute value. + * @return sai_status_t The status of the attribute retrieval operation. + */ + sai_status_t get_attr(_Inout_ sai_attribute_t &attr) const override; + + private: + /**< The number of attributes associated with the SAI object. */ + uint32_t m_attr_count; + /**< An array of sai_attribute_t structures representing the attributes of the SAI object. */ + const sai_attribute_t *m_attr_list; + }; + + /** + * @brief The `SaiDBObject` class represents a SaiObject that is aready created through the SAI API. + * + * This class inherits from the `SaiObject` class and provides additional functionality for managing child objects. + * It maintains a map of child objects based on their type and ID. + */ + class SaiDBObject : public SaiObject { + public: + /** + * @brief Constructs a `SaiDBObject` object. + * + * @param switch_db A pointer to the SwitchVpp object. + * @param type The type of the SaiObject. + * @param id The ID of the SaiObject. + */ + SaiDBObject(SwitchVpp* switch_db, sai_object_type_t type, const std::string& id) : SaiObject(switch_db, type, id) {} + + /** + * @brief Default destructor for the `SaiDBObject` class. + */ + ~SaiDBObject() = default; + + /** + * @brief Retrieves the attribute of the SaiObject. + * + * This function is overridden from the base class. + * + * @param attr The attribute to be retrieved. + * @return The status of the operation. + */ + sai_status_t get_attr(_Inout_ sai_attribute_t &attr) const override; + + /** + * @brief Retrieves the child objects of the specified type. + * + * This function returns a pointer to the map of child objects of the specified type. The key of the map + * is the ID of the child object. + * + * @param child_type The type of the child objects. + * @return A pointer to the map of child objects, or nullptr if no child objects of the specified type exist. + */ + const std::unordered_map>* get_child_objs(sai_object_type_t child_type) const { + auto it = m_child_map.find(child_type); + if (it != m_child_map.end()) { + return &(it->second); + } + return nullptr; + } + + /** + * @brief Adds a child object to the `SaiDBObject`. + * + * This function adds the specified child object to the map of child objects. + * + * @param entry The child object to be added. + */ + void add_child(const std::shared_ptr entry) { + if (entry) { + m_child_map[entry->get_type()][entry->get_id()] = entry; + } + } + + /** + * @brief Removes a child object from the `SaiDBObject`. + * + * This function removes the child object with the specified type and ID from the map of child objects. + * + * @param child_type The type of the child object. + * @param id The ID of the child object. + */ + void remove_child(sai_object_type_t child_type, const std::string& id) { + auto child_map_it = m_child_map.find(child_type); + if (child_map_it != m_child_map.end()) { + auto child_map_per_type_it = child_map_it->second.find(id); + if (child_map_per_type_it != child_map_it->second.end()) { + child_map_it->second.erase(child_map_per_type_it); + } + } + } + + private: + /** + * @brief A map of child objects based on their type and ID. + * child-type -> child-oid -> child-object + */ + std::unordered_map>> m_child_map; + }; + + /** + * SaiModDBObject is used to represent a SAI object that has been modified. It has the list of modified attributes of + * the SAI object passed in through set request and backed by original SAI object in DB. get_attribute will first read + * from the modified attribute list and if not found, it will read the object in database. + */ + class SaiModDBObject : public SaiObject { + public: + SaiModDBObject(SwitchVpp* switch_db, sai_object_type_t type, const std::string& id, + uint32_t attr_count, const sai_attribute_t *attr_list); + + ~SaiModDBObject() = default; + + sai_status_t get_attr(_Inout_ sai_attribute_t &attr) const override; + + std::shared_ptr get_db_obj() const { + return m_sai_db_obj; + } + + const std::unordered_map>* get_child_objs(sai_object_type_t child_type) const { + if (m_sai_db_obj) { + return m_sai_db_obj->get_child_objs(child_type); + } else { + return nullptr; + } + } + private: + /**< The number of modified attributes associated with the SAI object. */ + uint32_t m_attr_count; + /**< An array of sai_attribute_t structures representing the modified attributes of the SAI object. */ + const sai_attribute_t *m_attr_list; + std::shared_ptr m_sai_db_obj; + }; + + /** + * @brief The SaiObjectDB class represents a database for managed SAI objects. Only the SAI objects we are interested are + * added to SaiObjectDB. See sai_child_relation_defs in SaiObjectDB.cpp for such objects. + * + * This class provides methods for adding, removing, and retrieving SAI objects from the database. + * Each SAI object is associated with a unique ID and belongs to a specific switch. + * + * @note This class assumes that the switch database (`SwitchVpp`) is already initialized. + */ + class SaiObjectDB { + public: + /** + * @brief Constructs a SaiObjectDB object. + * + * @param switch_db A pointer to the switch database (`SwitchVpp`). + */ + SaiObjectDB(SwitchVpp* switch_db) : m_switch_db(switch_db) {}; + + /** + * @brief Destructs the SaiObjectDB object. + */ + ~SaiObjectDB() = default; + + /** + * @brief Adds a new SAI object to the database or update an existing one if it already exists, + * which includes removing the child from the current parent and adding it to the new parent. + * + * @param object_type The type of the SAI object. + * @param id The ID of the SAI object. + * @param attr_count The number of attributes in the attribute list. + * @param attr_list An array of attributes for the SAI object. + * @param is_create A flag indicating whether the object is being created. + * @return The status of the operation. + */ + sai_status_t create_or_update( + _In_ sai_object_type_t object_type, + _In_ const std::string& id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _In_ bool is_create); + + /** + * @brief Removes an existing SAI object from the database. + * + * @param object_type The type of the SAI object. + * @param id The ID of the SAI object. + * @return The status of the operation. + */ + sai_status_t remove( + _In_ sai_object_type_t object_type, + _In_ const std::string& id); + + /** + * @brief Retrieves a shared pointer to an existing SAI object from the database. + * + * @param object_type The type of the SAI object. + * @param id The ID of the SAI object. + * @return A shared pointer to the SAI object, or nullptr if the object is not found. + */ + std::shared_ptr get( + _In_ sai_object_type_t object_type, + _In_ const std::string& id); + + private: + // Pointer to the switch database + SwitchVpp* m_switch_db; + /** + * @brief A map of SAI parent objects based on their type and ID. + * parent-type -> parent-oid -> parent-object + * a parent object has a map of child objects based on their type and ID. + */ + std::unordered_map>> m_sai_parent_objs; + /** + * @brief Removes a child object from its parent in the SaiObjectDB for the give child-parent relationship definition. + * + * This function removes a child object from its parent in the SaiObjectDB. It takes the object type, the ID of the child object, + * and the child-parent relationship definition as input parameters. + * The function retrieves the parent object IDs using the get_parent_oids() function and iterates over each parent ID. + * For each parent ID, it checks if the parent object exists in the SaiObjectDB. If the parent object is not found, a warning + * message is logged and the function returns SAI_STATUS_SUCCESS. + * If the parent object is found, the function removes the child object from the parent object using the remove_child() + * function and logs a debug message. + * + * @param object_type The type of the child object. + * @param id The ID of the child object. + * @param child_def The child-parent relationship definition. + */ + void remove_child_from_parent(_In_ sai_object_type_t object_type, _In_ const std::string& id, _In_ const SaiChildRelation& child_def); + }; +} diff --git a/vslib/SwitchConfig.cpp b/vslib/SwitchConfig.cpp index 88eeebd52..9018a9d4e 100644 --- a/vslib/SwitchConfig.cpp +++ b/vslib/SwitchConfig.cpp @@ -90,6 +90,10 @@ bool SwitchConfig::parseSwitchType( */ switchType = SAI_VS_SWITCH_TYPE_NVDA_MBF2H536C; } + else if (st == SAI_VALUE_VS_SWITCH_TYPE_VPP) + { + switchType = SAI_VS_SWITCH_TYPE_VPP; + } else { std::vector vals { @@ -99,6 +103,7 @@ bool SwitchConfig::parseSwitchType( SAI_VALUE_VS_SWITCH_TYPE_MLNX2700, SAI_VALUE_VS_SWITCH_TYPE_NVDA_MBF2H536C, SAI_VALUE_VS_SWITCH_TYPE_DPU_SIMU_2P + SAI_VALUE_VS_SWITCH_TYPE_VPP, }; SWSS_LOG_ERROR("unknown switch type: '%s', expected (%s)", diff --git a/vslib/SwitchConfig.h b/vslib/SwitchConfig.h index 8509eabf3..a21009a73 100644 --- a/vslib/SwitchConfig.h +++ b/vslib/SwitchConfig.h @@ -28,6 +28,8 @@ namespace saivs SAI_VS_SWITCH_TYPE_NVDA_MBF2H536C, + SAI_VS_SWITCH_TYPE_VPP, + } sai_vs_switch_type_t; typedef enum _sai_vs_boot_type_t diff --git a/vslib/SwitchState.h b/vslib/SwitchState.h index 5330c0ab0..28df989b0 100644 --- a/vslib/SwitchState.h +++ b/vslib/SwitchState.h @@ -48,7 +48,7 @@ namespace saivs public: - sai_status_t getStatsExt( + virtual sai_status_t getStatsExt( _In_ sai_object_type_t obejct_type, _In_ sai_object_id_t object_id, _In_ uint32_t number_of_counters, diff --git a/vslib/SwitchStateBase.h b/vslib/SwitchStateBase.h index 22e111a34..6c394b6b2 100644 --- a/vslib/SwitchStateBase.h +++ b/vslib/SwitchStateBase.h @@ -111,8 +111,10 @@ namespace saivs _In_ const sai_system_port_config_t *sys_port_cfg_list); sai_status_t create_voqs(); + sai_status_t create_voq_per_sysport( _In_ sai_object_id_t sys_port_id); + sai_status_t set_system_port_list(); public: @@ -294,7 +296,7 @@ namespace saivs _In_ sai_bulk_op_error_mode_t mode, _Out_ sai_status_t *object_statuses); - virtual sai_status_t queryAttrEnumValuesCapability( + virtual sai_status_t queryAttrEnumValuesCapability( _In_ sai_object_id_t switch_id, _In_ sai_object_type_t object_type, _In_ sai_attr_id_t attr_id, @@ -324,7 +326,7 @@ namespace saivs _In_ const std::string &serializedObjectId, _In_ const sai_attribute_t* attr); - private: + protected: sai_object_type_t objectTypeQuery( _In_ sai_object_id_t objectId); @@ -334,7 +336,7 @@ namespace saivs public: - void processFdbEntriesForAging(); + virtual void processFdbEntriesForAging(); private: // fdb related @@ -373,16 +375,16 @@ namespace saivs protected: // custom port - sai_status_t createPort( + virtual sai_status_t createPort( _In_ sai_object_id_t object_id, _In_ sai_object_id_t switch_id, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list); - sai_status_t removePort( + virtual sai_status_t removePort( _In_ sai_object_id_t objectId); - sai_status_t setPort( + virtual sai_status_t setPort( _In_ sai_object_id_t objectId, _In_ const sai_attribute_t* attr); @@ -460,14 +462,14 @@ namespace saivs sai_status_t removeHostif( _In_ sai_object_id_t objectId); - sai_status_t vs_remove_hostif_tap_interface( + virtual sai_status_t vs_remove_hostif_tap_interface( _In_ sai_object_id_t hostif_id); - sai_status_t vs_create_hostif_tap_interface( + virtual sai_status_t vs_create_hostif_tap_interface( _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list); - bool hostif_create_tap_veth_forwarding( + virtual bool hostif_create_tap_veth_forwarding( _In_ const std::string &tapname, _In_ int tapfd, _In_ sai_object_id_t port_id); @@ -502,7 +504,7 @@ namespace saivs _In_ sai_port_oper_status_t status, _In_ bool force); - bool hasIfIndex( + virtual bool hasIfIndex( _In_ int ifIndex) const; bool vs_get_oper_speed( @@ -538,7 +540,7 @@ namespace saivs protected: - sai_status_t setAclEntry( + virtual sai_status_t setAclEntry( _In_ sai_object_id_t entry_id, _In_ const sai_attribute_t* attr); diff --git a/vslib/SwitchStateBaseHostif.cpp b/vslib/SwitchStateBaseHostif.cpp index 855ac43e7..e86d30b05 100644 --- a/vslib/SwitchStateBaseHostif.cpp +++ b/vslib/SwitchStateBaseHostif.cpp @@ -508,6 +508,8 @@ bool SwitchStateBase::hostif_create_tap_veth_forwarding( port_id, m_switchConfig->m_eventQueue); + m_hostif_info_map[tapname]->runThreads(); + SWSS_LOG_NOTICE("setup forward rule for %s succeeded", tapname.c_str()); return true; diff --git a/vslib/SwitchVpp.cpp b/vslib/SwitchVpp.cpp new file mode 100644 index 000000000..56c775155 --- /dev/null +++ b/vslib/SwitchVpp.cpp @@ -0,0 +1,1000 @@ +#include "SwitchVpp.h" + +#include "meta/sai_serialize.h" + +#include "swss/logger.h" + +#include "vppxlate/SaiIntfStats.h" + +using namespace saivs; + +// TODO init vpp + +SwitchVpp::SwitchVpp( + _In_ sai_object_id_t switch_id, + _In_ std::shared_ptr manager, + _In_ std::shared_ptr config): + SwitchStateBase(switch_id, manager, config), + m_object_db(this), + m_tunnel_mgr(this) +{ + SWSS_LOG_ENTER(); + + vpp_dp_initialize(); +} + +SwitchVpp::SwitchVpp( + _In_ sai_object_id_t switch_id, + _In_ std::shared_ptr manager, + _In_ std::shared_ptr config, + _In_ std::shared_ptr warmBootState): + SwitchStateBase(switch_id, manager, config, warmBootState), + m_object_db(this), + m_tunnel_mgr(this) +{ + SWSS_LOG_ENTER(); + + vpp_dp_initialize(); +} + +bool SwitchVpp::port_to_hostif_list( + _In_ sai_object_id_t port_id, + _Inout_ std::string& if_name) +{ + SWSS_LOG_ENTER(); + + // TODO to be removed and inlined + + //sai_object_id_t switch_id = switchIdQuery(port_id); + //if (switch_id == SAI_NULL_OBJECT_ID) { + //return false; + //} + //auto it = m_switchStateMap.find(switch_id); + //if (it == m_switchStateMap.end()) { + //return false; + //} + //auto sw = it->second; + //if (sw == nullptr) { + //return false; + //} + //return( + return getTapNameFromPortId(port_id, if_name); +} + +bool SwitchVpp::port_to_hwifname( + _In_ sai_object_id_t port_id, + _Inout_ std::string& if_name) +{ + SWSS_LOG_ENTER(); + + // TODO to be removed and inlined + + // sai_object_id_t switch_id = switchIdQuery(port_id); + // if (switch_id == SAI_NULL_OBJECT_ID) { + // return false; + // } + // auto it = m_switchStateMap.find(switch_id); + // if (it == m_switchStateMap.end()) { + // return false; + // } + // auto sw = it->second; + // if (sw == nullptr) { + // return false; + // } + + return vpp_get_hwif_name(port_id, 0, if_name); +} + +void SwitchVpp::setPortStats( + _In_ sai_object_id_t oid) +{ + std::map stats; + + std::string if_name; + + if (!port_to_hwifname(oid, if_name)) + { + return; + } + + vpp_interface_stats_t port_stats; + + if (vpp_intf_stats_query(if_name.c_str(), &port_stats) == 0) + { + stats[SAI_PORT_STAT_IF_IN_OCTETS] = port_stats.rx_bytes; + stats[SAI_PORT_STAT_IF_IN_UCAST_PKTS] = port_stats.rx; + stats[SAI_PORT_STAT_IF_IN_BROADCAST_PKTS] = port_stats.rx_broadcast; + stats[SAI_PORT_STAT_IF_IN_MULTICAST_PKTS] = port_stats.rx_multicast; + stats[SAI_PORT_STAT_IF_IN_DISCARDS] = port_stats.drops; + stats[SAI_PORT_STAT_IF_OUT_OCTETS] = port_stats.tx_bytes; + stats[SAI_PORT_STAT_IF_OUT_UCAST_PKTS] = port_stats.tx; + stats[SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS] = port_stats.tx_broadcast; + stats[SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS] = port_stats.tx_multicast; + + stats[SAI_PORT_STAT_IN_DROPPED_PKTS] = port_stats.rx_no_buf; + stats[SAI_PORT_STAT_IF_IN_ERRORS] = port_stats.rx_error; + stats[SAI_PORT_STAT_IF_OUT_ERRORS] = port_stats.tx_error; + stats[SAI_PORT_STAT_IP_IN_RECEIVES] = port_stats.ip4; + stats[SAI_PORT_STAT_IPV6_IN_RECEIVES] = port_stats.ip6; + } + + debugSetStats(oid, stats); +} + +sai_status_t SwitchVpp::queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) +{ + SWSS_LOG_ENTER(); + + // TODO: We should generate this metadata for the virtual switch rather + // than hard-coding it here. + + // in virtual switch by default all apis are implemented for all objects. SUCCESS for all attributes + + capability->create_implemented = true; + capability->set_implemented = true; + capability->get_implemented = true; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) +{ + SWSS_LOG_ENTER(); + + if (object_type == SAI_OBJECT_TYPE_PORT) + { + setPortStats(object_id); + } + + return SwitchStateBase::getStatsExt( + object_type, + object_id, + number_of_counters, + counter_ids, + mode, + counters); +} + +void SwitchVpp::processFdbEntriesForAging() +{ + SWSS_LOG_ENTER(); + + return; +} + +sai_status_t SwitchVpp::create( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + if (object_type == SAI_OBJECT_TYPE_DEBUG_COUNTER) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createDebugCounter(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_PORT) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createPort(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_HOSTIF) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createHostif(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_ROUTER_INTERFACE) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createRouterif(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + return addIpRoute(serializedObjectId, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_NEXT_HOP) + { + return createNexthop(serializedObjectId, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER) + { + return createNexthopGroupMember(serializedObjectId, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_NEIGHBOR_ENTRY) + { + return addIpNbr(serializedObjectId, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_ENTRY) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createAclEntry(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_TABLE) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return aclTableCreate(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createAclGrpMbr(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_MACSEC_PORT) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createMACsecPort(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_MACSEC_SC) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createMACsecSC(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_MACSEC_SA) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createMACsecSA(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_NEIGHBOR_ENTRY && m_system_port_list.size()) + { + // Neighbor entry programming for VOQ systems + return createVoqSystemNeighborEntry(serializedObjectId, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_VLAN_MEMBER) + { + sai_object_id_t object_id; + sai_deserialize_object_id(serializedObjectId, object_id); + return createVlanMember(object_id, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_FDB_ENTRY) + { + return FdbEntryadd(serializedObjectId, switch_id, attr_count, attr_list); + } + + if (object_type == SAI_OBJECT_TYPE_BFD_SESSION) + { + return bfd_session_add(serializedObjectId, switch_id, attr_count, attr_list); + } + + return create_internal(object_type, serializedObjectId, switch_id, attr_count, attr_list); +} + +sai_status_t SwitchVpp::create_internal( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto &objectHash = m_objectHash.at(object_type); + + if (m_switchConfig->m_resourceLimiter) + { + size_t limit = m_switchConfig->m_resourceLimiter->getObjectTypeLimit(object_type); + + if (objectHash.size() >= limit) + { + SWSS_LOG_ERROR("too many %s, created %zu is resource limit", + sai_serialize_object_type(object_type).c_str(), + limit); + + return SAI_STATUS_INSUFFICIENT_RESOURCES; + } + } + + auto it = objectHash.find(serializedObjectId); + + if (object_type != SAI_OBJECT_TYPE_SWITCH) + { + /* + * Switch is special, and object is already created by init. + * + * XXX revisit this. + */ + + if (it != objectHash.end()) + { + SWSS_LOG_ERROR("create failed, object already exists, object type: %s: id: %s", + sai_serialize_object_type(object_type).c_str(), + serializedObjectId.c_str()); + + return SAI_STATUS_ITEM_ALREADY_EXISTS; + } + } + + if (objectHash.find(serializedObjectId) == objectHash.end()) + { + /* + * Number of attributes may be zero, so see if actual entry was created + * with empty hash. + */ + + objectHash[serializedObjectId] = {}; + } + + for (uint32_t i = 0; i < attr_count; ++i) + { + auto a = std::make_shared(object_type, &attr_list[i]); + + objectHash[serializedObjectId][a->getAttrMetadata()->attridname] = a; + } + + m_object_db.create_or_update(object_type, serializedObjectId, attr_count, attr_list, true /*is_create*/); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::createPort( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + UpdatePort(object_id, attr_count, attr_list); + + auto sid = sai_serialize_object_id(object_id); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_PORT, sid, switch_id, attr_count, attr_list)); + + return create_port_dependencies(object_id); +} + + +sai_status_t SwitchVpp::remove( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + if (object_type == SAI_OBJECT_TYPE_DEBUG_COUNTER) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeDebugCounter(objectId); + } + + if (object_type == SAI_OBJECT_TYPE_PORT) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removePort(objectId); + } + + if (object_type == SAI_OBJECT_TYPE_HOSTIF) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeHostif(objectId); + } + + if (object_type == SAI_OBJECT_TYPE_ROUTER_INTERFACE) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeRouterif(objectId); + } + + if (object_type == SAI_OBJECT_TYPE_VIRTUAL_ROUTER) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeVrf(objectId); + } + + if (object_type == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + return removeIpRoute(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_NEXT_HOP) + { + return removeNexthop(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER) + { + return removeNexthopGroupMember(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_NEIGHBOR_ENTRY) + { + return removeIpNbr(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_ENTRY) + { + return removeAclEntry(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_TABLE) + { + return aclTableRemove(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER) + { + return removeAclGrpMbr(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_ACL_TABLE_GROUP) + { + return removeAclGrp(serializedObjectId); + } + + if (object_type == SAI_OBJECT_TYPE_MACSEC_PORT) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeMACsecPort(objectId); + } + else if (object_type == SAI_OBJECT_TYPE_MACSEC_SC) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeMACsecSC(objectId); + } + else if (object_type == SAI_OBJECT_TYPE_MACSEC_SA) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeMACsecSA(objectId); + } + + if (object_type == SAI_OBJECT_TYPE_VLAN_MEMBER) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return removeVlanMember(objectId); + } + else if (object_type == SAI_OBJECT_TYPE_FDB_ENTRY) + { + return FdbEntrydel(serializedObjectId); + } + else if (object_type == SAI_OBJECT_TYPE_BFD_SESSION) + { + return bfd_session_del(serializedObjectId); + } + + return remove_internal(object_type, serializedObjectId); +} + +sai_status_t SwitchVpp::remove_internal( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_INFO("removing object: %s", serializedObjectId.c_str()); + + m_object_db.remove(object_type, serializedObjectId); + + auto &objectHash = m_objectHash.at(object_type); + + auto it = objectHash.find(serializedObjectId); + + if (it == objectHash.end()) + { + SWSS_LOG_ERROR("not found %s:%s", + sai_serialize_object_type(object_type).c_str(), + serializedObjectId.c_str()); + + return SAI_STATUS_ITEM_NOT_FOUND; + } + + objectHash.erase(it); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::setPort( + _In_ sai_object_id_t portId, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + UpdatePort(portId, 1, attr); + + auto sid = sai_serialize_object_id(portId); + + return set_internal(SAI_OBJECT_TYPE_PORT, sid, attr); +} + +sai_status_t SwitchVpp::setAclEntry( + _In_ sai_object_id_t entry_id, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + if (attr && attr->id == SAI_ACL_ENTRY_ATTR_ACTION_MACSEC_FLOW) + { + return setAclEntryMACsecFlowActive(entry_id, attr); + } + + auto sid = sai_serialize_object_id(entry_id); + + set_internal(SAI_OBJECT_TYPE_ACL_ENTRY, sid, attr); + + sai_object_id_t tbl_oid; + + if (getAclTableId(entry_id, &tbl_oid) != SAI_STATUS_SUCCESS) + { + return SAI_STATUS_FAILURE; + } + + auto status = AclAddRemoveCheck(tbl_oid); + + SWSS_LOG_NOTICE("ACL entry %s set in table %s set status %d", + sid.c_str(), + sai_serialize_object_id(tbl_oid).c_str(), + status); + + return status; +} + +sai_status_t SwitchVpp::set( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + if (objectType == SAI_OBJECT_TYPE_PORT) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return setPort(objectId, attr); + } + + if (objectType == SAI_OBJECT_TYPE_ROUTER_INTERFACE) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return vpp_update_router_interface(objectId, 1, attr); + } + + if (objectType == SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return setAclGrpMbr(objectId, attr); + } + + if (objectType == SAI_OBJECT_TYPE_ROUTE_ENTRY) + { + return updateIpRoute(serializedObjectId, attr); + } + + if (objectType == SAI_OBJECT_TYPE_SWITCH) + { + switch(attr->id) + { + case SAI_SWITCH_ATTR_VXLAN_DEFAULT_ROUTER_MAC: + { + m_tunnel_mgr.set_router_mac(attr); + break; + } + case SAI_SWITCH_ATTR_VXLAN_DEFAULT_PORT: + { + m_tunnel_mgr.set_vxlan_port(attr); + break; + } + } + } + + if (objectType == SAI_OBJECT_TYPE_ACL_ENTRY) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return setAclEntry(objectId, attr); + } + + if (objectType == SAI_OBJECT_TYPE_MACSEC_SA) + { + sai_object_id_t objectId; + sai_deserialize_object_id(serializedObjectId, objectId); + return setMACsecSA(objectId, attr); + } + + return set_internal(objectType, serializedObjectId, attr); +} + +sai_status_t SwitchVpp::set_internal( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + //Update child-parent relationship before updating the attribute + m_object_db.create_or_update(objectType, serializedObjectId, 1, attr, false /*is_create*/); + + auto it = m_objectHash.at(objectType).find(serializedObjectId); + + if (it == m_objectHash.at(objectType).end()) + { + SWSS_LOG_ERROR("not found %s:%s", + sai_serialize_object_type(objectType).c_str(), + serializedObjectId.c_str()); + + return SAI_STATUS_ITEM_NOT_FOUND; + } + + auto &attrHash = it->second; + + auto a = std::make_shared(objectType, attr); + + // set have only one attribute + attrHash[a->getAttrMetadata()->attridname] = a; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::get( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ uint32_t attr_count, + _Out_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + if (objectType == SAI_OBJECT_TYPE_ACL_COUNTER) + { + sai_object_id_t object_id; + + sai_deserialize_object_id(serializedObjectId, object_id); + return getAclEntryStats(object_id, attr_count, attr_list); + } + + const auto &objectHash = m_objectHash.at(objectType); + + auto it = objectHash.find(serializedObjectId); + + if (it == objectHash.end()) + { + SWSS_LOG_ERROR("not found %s:%s", + sai_serialize_object_type(objectType).c_str(), + serializedObjectId.c_str()); + + return SAI_STATUS_ITEM_NOT_FOUND; + } + + /* + * We need reference here since we can potentially update attr hash for RO + * object. + */ + + auto& attrHash = it->second; + + /* + * Some of the list query maybe for length, so we can't do + * normal serialize, maybe with count only. + */ + + sai_status_t final_status = SAI_STATUS_SUCCESS; + + for (uint32_t idx = 0; idx < attr_count; ++idx) + { + sai_attr_id_t id = attr_list[idx].id; + + auto meta = sai_metadata_get_attr_metadata(objectType, id); + + if (meta == NULL) + { + SWSS_LOG_ERROR("failed to find attribute %d for %s:%s", id, + sai_serialize_object_type(objectType).c_str(), + serializedObjectId.c_str()); + + return SAI_STATUS_FAILURE; + } + + sai_status_t status; + + if (SAI_HAS_FLAG_READ_ONLY(meta->flags)) + { + /* + * Read only attributes may require recalculation. + * Metadata makes sure that non object id's can't have + * read only attributes. So here is definitely OID. + */ + + sai_object_id_t oid; + sai_deserialize_object_id(serializedObjectId, oid); + + status = refresh_read_only(meta, oid); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("%s read only not implemented on %s", + meta->attridname, + serializedObjectId.c_str()); + + return status; + } + } + + auto ait = attrHash.find(meta->attridname); + + if (ait == attrHash.end()) + { + return SAI_STATUS_ITEM_NOT_FOUND; + + SWSS_LOG_WARN("%s not implemented on %s", + meta->attridname, + serializedObjectId.c_str()); + + return SAI_STATUS_NOT_IMPLEMENTED; + } + + auto attr = ait->second->getAttr(); + + status = transfer_attributes(objectType, 1, attr, &attr_list[idx], false); + + if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + /* + * This is considered partial success, since we get correct list + * length. Note that other items ARE processes on the list. + */ + + SWSS_LOG_NOTICE("BUFFER_OVERFLOW %s: %s", + serializedObjectId.c_str(), + meta->attridname); + + /* + * We still continue processing other attributes for get as long as + * we only will be getting buffer overflow error. + */ + + final_status = status; + continue; + } + + if (status != SAI_STATUS_SUCCESS) + { + // all other errors + + SWSS_LOG_ERROR("get failed %s: %s: %s", + serializedObjectId.c_str(), + meta->attridname, + sai_serialize_status(status).c_str()); + + return status; + } + } + + return final_status; +} + +sai_status_t SwitchVpp::bulkCreate( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + + uint32_t object_count = (uint32_t) serialized_object_ids.size(); + + if (!object_count || !attr_count || !attr_list || !object_statuses) + { + SWSS_LOG_ERROR("Invalid arguments"); + return SAI_STATUS_FAILURE; + } + + sai_status_t status = SAI_STATUS_SUCCESS; + uint32_t it; + + for (it = 0; it < object_count; it++) + { + object_statuses[it] = create_internal(object_type, serialized_object_ids[it], switch_id, attr_count[it], attr_list[it]); + + if (object_statuses[it] != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create object with type = %u", object_type); + + status = SAI_STATUS_FAILURE; + + if (mode == SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR) + { + break; + } + } + } + + while (++it < object_count) + { + object_statuses[it] = SAI_STATUS_NOT_EXECUTED; + } + + return status; +} + +sai_status_t SwitchVpp::bulkRemove( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + + uint32_t object_count = (uint32_t) serialized_object_ids.size(); + + if (!object_count || !object_statuses) + { + SWSS_LOG_ERROR("Invalid arguments"); + return SAI_STATUS_FAILURE; + } + + sai_status_t status = SAI_STATUS_SUCCESS; + uint32_t it; + + for (it = 0; it < object_count; it++) + { + object_statuses[it] = remove_internal(object_type, serialized_object_ids[it]); + + if (object_statuses[it] != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove object with type = %u", object_type); + + status = SAI_STATUS_FAILURE; + + if (mode == SAI_BULK_OP_ERROR_MODE_STOP_ON_ERROR) + { + break; + } + } + } + + while (++it < object_count) + { + object_statuses[it] = SAI_STATUS_NOT_EXECUTED; + } + + return status; +} + +sai_status_t SwitchVpp::get_max( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const uint32_t max_attr_count, + _Out_ uint32_t *attr_count, + _Out_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + *attr_count = 0; + + const auto &objectHash = m_objectHash.at(objectType); + + auto it = objectHash.find(serializedObjectId); + + if (it == objectHash.end()) + { + SWSS_LOG_ERROR("not found %s:%s", + sai_serialize_object_type(objectType).c_str(), + serializedObjectId.c_str()); + + return SAI_STATUS_ITEM_NOT_FOUND; + } + + /* + * We need reference here since we can potentially update attr hash for RO + * object. + */ + + auto& attrHash = it->second; + + /* + * Some of the list query maybe for length, so we can't do + * normal serialize, maybe with count only. + */ + + sai_status_t final_status = SAI_STATUS_SUCCESS, status; + uint32_t idx = 0; + sai_attribute_t *dst_attr; + + for (auto &kvp: attrHash) + { + auto attr = kvp.second->getAttr(); + + dst_attr = &attr_list[idx]; + dst_attr->id = attr->id; + + status = transfer_attributes(objectType, 1, attr, dst_attr, false); + + if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + /* + * This is considered partial success, since we get correct list + * length. Note that other items ARE processes on the list. + */ + + SWSS_LOG_NOTICE("BUFFER_OVERFLOW %s: %d", + serializedObjectId.c_str(), + attr->id); + + /* + * We still continue processing other attributes for get as long as + * we only will be getting buffer overflow error. + */ + + final_status = status; + continue; + } + + if (status != SAI_STATUS_SUCCESS) + { + // all other errors + + SWSS_LOG_ERROR("get failed %s: %d: %s", + serializedObjectId.c_str(), + attr->id, + sai_serialize_status(status).c_str()); + + return status; + } + + ++idx; + + if (idx == max_attr_count) + break; + } + + *attr_count = idx; + + return final_status; +} + +std::shared_ptr SwitchVpp::get_sai_object( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + return m_object_db.get(object_type, serializedObjectId); +} diff --git a/vslib/SwitchVpp.h b/vslib/SwitchVpp.h new file mode 100644 index 000000000..5983fa441 --- /dev/null +++ b/vslib/SwitchVpp.h @@ -0,0 +1,874 @@ +#pragma once + +#include "SwitchStateBase.h" + +#include "IpVrfInfo.h" +#include "SaiObjectDB.h" +#include "BitResourcePool.h" +#include "TunnelManager.h" +#include "SwitchVppNexthop.h" +#include "SwitchVppAcl.h" + +#include "vppxlate/SaiVppXlate.h" + +#include + +#define BFD_MUTEX std::lock_guard lock(bfdMapMutex); + +namespace saivs +{ + class SwitchVpp: + public SwitchStateBase + { + public: + + // name hidding + + using saivs::SwitchStateBase::get; + using saivs::SwitchStateBase::set; + + public: + + SwitchVpp( + _In_ sai_object_id_t switch_id, + _In_ std::shared_ptr manager, + _In_ std::shared_ptr config); + + SwitchVpp( + _In_ sai_object_id_t switch_id, + _In_ std::shared_ptr manager, + _In_ std::shared_ptr config, + _In_ std::shared_ptr warmBootState); + + virtual ~SwitchVpp() = default; + + private: // from vpp VirtualSwitchSaiInterface + + void setPortStats( + _In_ sai_object_id_t oid); + + bool port_to_hostif_list( + _In_ sai_object_id_t oid, + _Inout_ std::string& if_name); + + bool port_to_hwifname( + _In_ sai_object_id_t oid, + _Inout_ std::string& if_name); + + public: // from VirtualSwitchSaiInterface changed functions + + virtual sai_status_t queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) override; + + virtual sai_status_t getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) override; + + virtual void processFdbEntriesForAging() override; + + public: + + virtual sai_status_t create( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t remove( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId) override; + + virtual sai_status_t set( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t* attr) override; + + virtual sai_status_t get( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ uint32_t attr_count, + _Out_ sai_attribute_t *attr_list) override; + + protected: + + virtual sai_status_t create_internal( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t remove_internal( + _In_ sai_object_type_t object_type, + _In_ const std::string &serializedObjectId) override; + + virtual sai_status_t set_internal( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t* attr) override; + + virtual sai_status_t createPort( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t setPort( + _In_ sai_object_id_t portId, + _In_ const sai_attribute_t* attr) override; + + virtual sai_status_t setAclEntry( + _In_ sai_object_id_t entry_id, + _In_ const sai_attribute_t* attr) override; + + virtual sai_status_t bulkCreate( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + virtual sai_status_t bulkRemove( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + protected: // hostif + + static int vs_create_tap_device( + _In_ const char *dev, + _In_ int flags); + + static int vs_set_dev_mac_address( + _In_ const char *dev, + _In_ const sai_mac_t& mac); + + static int promisc( + _In_ const char *dev); + + virtual bool hostif_create_tap_veth_forwarding( + _In_ const std::string &tapname, + _In_ int tapfd, + _In_ sai_object_id_t port_id) override; + + virtual sai_status_t vs_create_hostif_tap_interface( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + virtual sai_status_t vs_remove_hostif_tap_interface( + _In_ sai_object_id_t hostif_id) override; + + virtual bool hasIfIndex( + _In_ int ifIndex) const override; + + private: + + // std::map phMap; // TODO to be removed + + public: // VPP (SwitchStateBase.h) + + sai_status_t vpp_dp_initialize(); + + sai_status_t vpp_create_default_1q_bridge(); + + sai_status_t createVlanMember( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t vpp_create_vlan_member( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeVlanMember( + _In_ sai_object_id_t objectId); + + sai_status_t vpp_remove_vlan_member( + _In_ sai_object_id_t vlan_member_oid); + + sai_status_t vpp_create_bvi_interface( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t vpp_delete_bvi_interface( + _In_ sai_object_id_t bvi_obj_id); + + /* FDB Entry and Flush SAI Objects */ + sai_status_t FdbEntryadd( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t vpp_fdbentry_add( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t FdbEntrydel( + _In_ const std::string &serializedObjectId); + + sai_status_t vpp_fdbentry_del( + _In_ const std::string &serializedObjectId); + + sai_status_t vpp_fdbentry_flush( + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + /* BFD Session */ + sai_status_t bfd_session_add( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t vpp_bfd_session_add( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t bfd_session_del( + _In_ const std::string &serializedObjectId); + + sai_status_t vpp_bfd_session_del( + _In_ const std::string &serializedObjectId); + + private: // VPP // BFD related + + typedef struct _vpp_bfd_info_t // TODO to separate file + { + //uint32_t sw_if_index; + bool multihop; + sai_ip_address_t local_addr; + sai_ip_address_t peer_addr; + + // Define the < operator for comparison + bool operator<(const _vpp_bfd_info_t& other) const + { + // compare local IP address first + int cmp = std::memcmp(&local_addr, &other.local_addr, sizeof(sai_ip_address_t)); + + if (cmp != 0) + { + return cmp < 0; + } + + // compare peer IP address + cmp = std::memcmp(&peer_addr, &other.peer_addr, sizeof(sai_ip_address_t)); + + if (cmp != 0) + { + return cmp < 0; + } + + // compare multihop flag + return multihop < other.multihop; + } + } vpp_bfd_info_t; + + std::map m_bfd_info_map; + std::mutex bfdMapMutex; // TODO fix naming + + void send_bfd_state_change_notification( + _In_ sai_object_id_t bfd_oid, + _In_ sai_bfd_session_state_t state, + _In_ bool force); + + void update_bfd_session_state( + _In_ sai_object_id_t bfd_oid, + _In_ sai_bfd_session_state_t state); + + sai_status_t asyncBfdStateUpdate(vpp_bfd_state_notif_t *bfd_notif); + + public: // VPP + + // TODO wiird function, max attr_count + + sai_status_t get_max( + _In_ sai_object_type_t objectType, + _In_ const std::string &serializedObjectId, + _In_ const uint32_t max_attr_count, + _Out_ uint32_t *attr_count, + _Out_ sai_attribute_t *attr_list); + + public: // VPP + + std::shared_ptr get_sai_object( + _In_ sai_object_type_t object_type, + _In_ const std::string &serialized_object_id); + + static int vpp_promisc( + _In_ const char *dev); + + static int vpp_create_tap_device( + _In_ const char *dev, + _In_ int flags); + + public: // VPP + + friend class TunnelManager; + + private: // VPP + + SaiObjectDB m_object_db; + TunnelManager m_tunnel_mgr; + + private: // VPP + + std::map> vrf_objMap; + bool nbr_env_read = false; + bool nbr_active = false; + std::map m_intf_prefix_map; + std::unordered_map lpbInstMap; + std::unordered_map lpbIpToHostIfMap; + std::unordered_map lpbIpToIfMap; + std::unordered_map lpbHostIfToVppIfMap; + + protected: // VPP + + sai_status_t fillNHGrpMember( + nexthop_grp_member_t *nxt_grp_member, + sai_object_id_t next_hop_oid, + uint32_t next_hop_weight, + uint32_t next_hop_sequence); + + sai_status_t IpRouteNexthopGroupEntry( + _In_ sai_object_id_t next_hop_grp_oid, + _Out_ nexthop_grp_config_t **nxthop_group); + + sai_status_t IpRouteNexthopEntry( + _In_ sai_object_id_t next_hop_oid, + _Out_ nexthop_grp_config_t **nxthop_group_cfg); + + sai_status_t createNexthop( + _In_ const std::string& serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeNexthop( + _In_ const std::string &serializedObjectId); + + sai_status_t createNexthopGroupMember( + _In_ const std::string& serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeNexthopGroupMember( + _In_ const std::string& serializedObjectId); + + protected: // VPP + + sai_status_t createRouterif( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeRouterif( + _In_ sai_object_id_t objectId); + + sai_status_t vpp_create_router_interface( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t vpp_update_router_interface( + _In_ sai_object_id_t object_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t vpp_remove_router_interface( + _In_ sai_object_id_t objectId); + + sai_status_t vpp_router_interface_remove_vrf( + _In_ sai_object_id_t obj_id); + + sai_status_t vpp_add_del_intf_ip_addr ( + _In_ sai_ip_prefix_t& ip_prefix, + _In_ sai_object_id_t nexthop_oid, + _In_ bool is_add); + + sai_status_t vpp_add_del_intf_ip_addr_norif ( + _In_ const std::string& ip_prefix_key, + _In_ sai_route_entry_t& route_entry, + _In_ bool is_add); + + sai_status_t vpp_interface_ip_address_update ( + _In_ const char *hw_ifname, + _In_ const std::string &serializedObjectId, + _In_ bool is_add); + + sai_status_t process_interface_loopback ( + _In_ const std::string &serializedObjectId, + _In_ bool &isLoopback, + _In_ bool is_add); + + sai_status_t vpp_add_lpb_intf_ip_addr ( + _In_ const std::string &serializedObjectId); + + sai_status_t vpp_del_lpb_intf_ip_addr ( + _In_ const std::string &serializedObjectId); + + sai_status_t vpp_get_router_intf_name ( + _In_ sai_ip_prefix_t& ip_prefix, + _In_ sai_object_id_t rif_id, + std::string& nexthop_ifname); + + int getNextLoopbackInstance(); + + void markLoopbackInstanceDeleted( + _In_ int instance); + + bool vpp_intf_get_prefix_entry( + _In_ const std::string& intf_name, + _In_ std::string& ip_prefix); + + void vpp_intf_remove_prefix_entry( + _Inout_ const std::string& intf_name); + + sai_status_t asyncIntfStateUpdate( + _In_ const char *hwif_name, + _In_ bool link_up); + + sai_status_t vpp_set_interface_state ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _In_ bool is_up); + sai_status_t vpp_set_port_mtu ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _In_ uint32_t mtu); + sai_status_t vpp_set_interface_mtu ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _In_ uint32_t mtu, + _In_ int type); + + sai_status_t UpdatePort( + _In_ sai_object_id_t object_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeVrf( + _In_ sai_object_id_t objectId); + + std::shared_ptr vpp_get_ip_vrf( + _In_ sai_object_id_t objectId); + + sai_status_t addRemoveIpNbr( + _In_ const std::string &serializedObjectId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _In_ bool is_add); + + sai_status_t addIpNbr( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeIpNbr( + _In_ const std::string &serializedObjectId); + + bool is_ip_nbr_active(); + + sai_status_t addIpRoute( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + sai_status_t removeIpRoute( + _In_ const std::string &serializedObjectId); + + sai_status_t IpRouteNexthopEntry( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + sai_ip_address_t *ip_address, + sai_object_id_t *next_rif_oid); + + std::string convertIPToString( + _In_ const sai_ip_addr_t &ipAddress); + + std::string convertIPv6ToString( + _In_ const sai_ip_addr_t &ipAddress, + _In_ int ipFamily); + + std::string extractDestinationIP( + _In_ const std::string &serializedObjectId); + + unsigned long ipToInteger( + _In_ const std::string &ipAddress); + + sai_status_t IpRouteAddRemove( + _In_ const SaiObject* route_obj, + _In_ bool is_add); + + sai_status_t updateIpRoute( + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t *attr_list); + + int vpp_add_ip_vrf( + _In_ sai_object_id_t objectId, + _In_ uint32_t vrf_id); + + int vpp_del_ip_vrf( + _In_ sai_object_id_t objectId); + + int vpp_get_vrf_id( + _In_ const char *linux_ifname, + _Out_ uint32_t *vrf_id); + + private: // VPP + + typedef struct vpp_ace_cntr_info_ // TODO to separate file + { + sai_object_id_t tbl_oid; + sai_object_id_t ace_oid; + uint32_t acl_index; + uint32_t ace_index; + + } vpp_ace_cntr_info_t; + + std::map> m_acl_tbl_rules_map; + std::map> m_acl_tbl_hw_ports_map; + std::map m_acl_swindex_map; + std::map m_tunterm_acl_swindex_map; + std::map> m_acl_tbl_grp_mbr_map; + std::map> m_acl_tbl_grp_ports_map; + std::map m_ace_cntr_info_map; + + protected: // VPP + + sai_status_t createAclEntry( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t removeAclEntry( + _In_ const std::string &serializedObjectId); + + sai_status_t addRemoveAclEntrytoMap( + _In_ sai_object_id_t entry_id, + _In_ sai_object_id_t tbl_oid, + _In_ bool is_add); + + sai_status_t getAclTableId( + _In_ sai_object_id_t entry_id, sai_object_id_t *tbl_oid); + + sai_status_t AclTblConfig( + _In_ sai_object_id_t tbl_oid); + + sai_status_t AclTblRemove( + _In_ sai_object_id_t tbl_oid); + + sai_status_t AclAddRemoveCheck( + _In_ sai_object_id_t tbl_oid); + + sai_status_t aclTableRemove( + _In_ const std::string &serializedObjectId); + + /** + * @brief Retrieves ACEs and list of ordered ACE info for a given table. + * + * @param[in] tbl_oid The object ID of the table for which to retrieve ACEs. + * @param[out] n_total_entries The total number of ACEs. + * @param[out] aces Pointer to the array of ACEs. + * @param[out] ordered_aces Ordered ACE info list. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t get_sorted_aces( + _In_ sai_object_id_t tbl_oid, + _Out_ size_t &n_total_entries, + _Out_ acl_tbl_entries_t *&aces, + _Out_ std::list &ordered_aces); + + /** + * @brief Cleans up AclTblConfig variables. + * + * @param[in] aces Pointer to the ACEs to be cleaned up. + * @param[in] ordered_aces Ordered ACE list to be cleaned up. + * @param[in] acl Pointer to the VS ACL to be cleaned up. + * @param[in] tunterm_acl Pointer to the VS tunnel termination ACL to be cleaned up. + */ + void cleanup_acl_tbl_config( + _In_ acl_tbl_entries_t *&aces, + _In_ std::list &ordered_aces, + _In_ vpp_acl_t *&acl, + _In_ vpp_tunterm_acl_t *&tunterm_acl); + + /** + * @brief Fills ACL and tunnel termination ACL rules based on the provided ACEs. + * + * @param[in] aces Pointer to ACEs. + * @param[in] ordered_aces Reference to Ordered ACE list. + * @param[in] acl Pointer to the allocated VS ACL. + * @param[in] tunterm_acl Pointer to the allocated VS tunnel termination ACL. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t fill_acl_rules( + _In_ acl_tbl_entries_t *aces, + _In_ std::list &ordered_aces, + _In_ vpp_acl_t *acl, + _In_ vpp_tunterm_acl_t *tunterm_acl); + + /** + * @brief Creates or replaces the provided ACL in VS. + * + * @param[in] acl Pointer to the VS ACL to be added or replaced. + * @param[in] tbl_oid Object ID of the ACL table where the entry will be added or replaced. + * @param[in] aces Pointer to the ACEs. + * @param[in] ordered_aces Ordered ACE list. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t acl_add_replace( + _In_ vpp_acl_t *&acl, + _In_ sai_object_id_t tbl_oid, + _In_ acl_tbl_entries_t *aces, + _In_ std::list &ordered_aces); + + /** + * @brief Allocates memory for VS ACL. + * + * @param[in] n_entries Number of entries in the ACL. + * @param[in] tbl_oid Object ID of the table to which the ACL belongs. + * @param[out] acl_name Name of the allocated ACL. + * @param[out] acl Pointer to the allocated ACL. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t allocate_acl( + _In_ size_t n_entries, + _In_ sai_object_id_t tbl_oid, + _Out_ char (&acl_name)[64], + _Out_ vpp_acl_t *&acl); + + /** + * @brief Allocates memory for VS tunnel termination ACL. + * + * @param[in] n_tunterm_entries Number of tunnel termination entries to allocate. + * @param[in] tbl_oid Object ID of the ACL table. + * @param[out] acl_name Name of the allocated ACL. + * @param[out] tunterm_acl Pointer to the allocated tunnel termination ACL. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t allocate_tunterm_acl( + _In_ size_t n_tunterm_entries, + _In_ sai_object_id_t tbl_oid, + _Out_ char (&acl_name)[64], + _Out_ vpp_tunterm_acl_t *&tunterm_acl); + + /** + * @brief Counts the total number of ACL rules and tunnel termination ACL rules, and sets is_tunterm in the ordered ACE list. + * + * @param[in] aces Pointer to ACEs. + * @param[in] ordered_aces Reference to the ordered ACE list. + * @param[out] n_entries Reference to the number of ACL entries. + * @param[out] n_tunterm_entries Reference to the number of tunnel termination ACL entries. + */ + void count_tunterm_acl_rules( + _In_ acl_tbl_entries_t *aces, + _In_ std::list &ordered_aces, + _Out_ size_t &n_entries, + _Out_ size_t &n_tunterm_entries); + + /** + * @brief Deletes the hardware ports map associated with the given table object ID. + * + * @param[in] tbl_oid The object ID of the table whose hardware ports map is to be deleted. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t tbl_hw_ports_map_delete( + _In_ sai_object_id_t tbl_oid); + + /** + * @brief Deletes a tunnel termination ACL in VS. + * + * @param[in] tbl_oid The object ID of the ACL table. + * @param[in] table_delete Boolean flag indicating whether the overall ACL table is being deleted. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t tunterm_acl_delete( + _In_ sai_object_id_t tbl_oid, + _In_ bool table_delete); + + /** + * @brief Creates or replaces the provided tunnel termination ACL in VS. + * + * @param[in] acl Pointer to the tunnel termination ACL. + * @param[in] tbl_oid Object ID of the ACL table where the entry will be added or replaced. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t tunterm_acl_add_replace( + _In_ vpp_tunterm_acl_t *acl, + _In_ sai_object_id_t tbl_oid); + + /** + * @brief Sets the redirect action for a tunnel termination ACL rule. + * + * @param[in] attr_id The ID of the SAI attribute to be updated. + * @param[in] value Pointer to the SAI attribute value. + * @param[in] rule Pointer to the tunnel termination ACL rule to be updated. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t tunterm_set_action_redirect( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _In_ vpp_tunterm_acl_rule_t *rule); + + /** + * @brief Updates an ACE field for a tunnel termination ACL rule. + * + * @param[in] attr_id The ID of the SAI attribute to be updated. + * @param[in] value Pointer to the SAI attribute value. + * @param[in] rule Pointer to the tunnel termination ACL rule to be updated. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t tunterm_acl_rule_field_update( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _In_ vpp_tunterm_acl_rule_t *rule); + + /** + * @brief Binds or unbinds a tunnel termination ACL table to/from an interface. + * + * @param[in] tbl_oid The object ID of the ACL table to be bound or unbound. + * @param[in] is_add A boolean flag indicating whether to bind (true) or unbind (false) the ACL table. + * @param[in] hwif_name The name of the hardware interface to which the ACL table is to be bound or from which it is to be unbound. + * @return SAI_STATUS_SUCCESS on success, or an appropriate error code otherwise. + */ + sai_status_t tunterm_acl_bindunbind( + _In_ sai_object_id_t tbl_oid, + _In_ bool is_add, + _In_ std::string hwif_name); + + sai_status_t aclTableCreate( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t aclDefaultAllowConfigure( + _In_ sai_object_id_t tbl_oid); + + sai_status_t acl_rule_range_get( + _In_ const sai_object_list_t *range_list, + _Out_ sai_u32_range_t *range_limit_list, + _Out_ sai_acl_range_type_t *range_type_list, + _Out_ uint32_t *range_count); + + sai_status_t acl_range_attr_get( + _In_ const std::string &serializedObjectId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _Out_ sai_attribute_t *attr_range); + + sai_status_t removeAclGrp( + _In_ const std::string &serializedObjectId); + + sai_status_t setAclGrpMbr( + _In_ sai_object_id_t member_oid, + _In_ const sai_attribute_t* attr); + + sai_status_t removeAclGrpMbr( + _In_ const std::string &serializedObjectId); + + sai_status_t createAclGrpMbr( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + sai_status_t addRemoveAclGrpMbr( + _In_ sai_object_id_t member_id, + _In_ sai_object_id_t tbl_grp_oid, + _In_ bool is_add); + + sai_status_t getAclTableGroupId( + _In_ sai_object_id_t member_id, + _Out_ sai_object_id_t *tbl_grp_oid); + + sai_status_t addRemovePortTblGrp( + _In_ sai_object_id_t port_oid, + _In_ sai_object_id_t tbl_grp_oid, + _In_ bool is_add); + + sai_status_t aclBindUnbindPort( + _In_ sai_object_id_t port_oid, + _In_ sai_object_id_t tbl_grp_oid, + _In_ bool is_input, + _In_ bool is_bind); + + sai_status_t aclBindUnbindPorts( + _In_ sai_object_id_t tbl_grp_oid, + _In_ sai_object_id_t tbl_oid, + _In_ bool is_bind); + + sai_status_t getAclEntryStats( + _In_ sai_object_id_t ace_cntr_oid, + _In_ uint32_t attr_count, + _Out_ sai_attribute_t *attr_list); + + public: // VPP + + sai_status_t aclGetVppIndices( + _In_ sai_object_id_t ace_oid, + _Out_ uint32_t *acl_index, + _Out_ uint32_t *ace_index); + + bool vpp_get_hwif_name ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _Out_ std::string& ifname); + + protected: // VPP + + void populate_if_mapping(); + + const char *tap_to_hwif_name(const char *name); + + const char *hwif_to_tap_name(const char *name); + + void vppProcessEvents (); + + void startVppEventsThread(); + + private: // VPP + + std::map m_hostif_hwif_map; + std::map m_hwif_hostif_map; + int mapping_init = 0; + bool m_run_vpp_events_thread = true; + bool VppEventsThreadStarted = false; + std::shared_ptr m_vpp_thread; + + private: // VPP + + static int currentMaxInstance; + + std::set availableInstances; + + //1-4095 BD are statically allocated for VLAN by VLAN-ID. Use 4K-16K for dynamic allocation + static const uint32_t dynamic_bd_id_base = 4*1024; + static const uint16_t dynamic_bd_id_pool_size = 12*1024; + + BitResourcePool dynamic_bd_id_pool = BitResourcePool(dynamic_bd_id_pool_size, dynamic_bd_id_base); + + // bool m_vpp; // TODO to be removed + }; +} diff --git a/vslib/SwitchVppAcl.cpp b/vslib/SwitchVppAcl.cpp new file mode 100644 index 000000000..6e40c3014 --- /dev/null +++ b/vslib/SwitchVppAcl.cpp @@ -0,0 +1,1719 @@ +#include "SwitchVpp.h" +#include "SwitchVppAcl.h" +#include "SwitchVppUtils.h" + +#include "meta/sai_serialize.h" + +#include "swss/logger.h" +#include "swss/exec.h" +#include "swss/converter.h" + +#include "vppxlate/SaiVppXlate.h" +#include "vppxlate/SaiAclStats.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace saivs; + +static sai_status_t acl_ip_field_to_vpp_acl( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_acl_rule_t *rule) +{ + sai_ip_addr_family_t addr_family; + + assert((SAI_ACL_ENTRY_ATTR_FIELD_SRC_IPV6 == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_DST_IPV6 == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_DST_IP == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_INNER_SRC_IP == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IP == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_INNER_SRC_IPV6 == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IPV6 == attr_id)); + + if (!value->aclfield.enable) { + SWSS_LOG_INFO("aclfield not enabled for ip prefix"); + return SAI_STATUS_SUCCESS; + } + + vpp_ip_addr_t *ip_addr, *ip_mask; + + switch (attr_id) { + case SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_SRC_IP: + ip_addr = &rule->src_prefix; + ip_mask = &rule->src_prefix_mask; + addr_family = SAI_IP_ADDR_FAMILY_IPV4; + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_DST_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IP: + ip_addr = &rule->dst_prefix; + ip_mask = &rule->dst_prefix_mask; + addr_family = SAI_IP_ADDR_FAMILY_IPV4; + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_SRC_IPV6: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_SRC_IPV6: + ip_addr = &rule->src_prefix; + ip_mask = &rule->src_prefix_mask; + addr_family = SAI_IP_ADDR_FAMILY_IPV6; + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_DST_IPV6: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IPV6: + ip_addr = &rule->dst_prefix; + ip_mask = &rule->dst_prefix_mask; + addr_family = SAI_IP_ADDR_FAMILY_IPV6; + break; + + default: + SWSS_LOG_ERROR("Unexpected ip field type (%u)\n", attr_id); + return SAI_STATUS_FAILURE; + } + + if (SAI_IP_ADDR_FAMILY_IPV4 == addr_family) { + struct sockaddr_in *sin = &ip_addr->addr.ip4; + + ip_addr->sa_family = AF_INET; + sin->sin_addr.s_addr = value->aclfield.data.ip4; + + sin = &ip_mask->addr.ip4; + sin->sin_addr.s_addr = value->aclfield.mask.ip4; + SWSS_LOG_INFO("Setting ipv4 subnet %x %x", value->aclfield.data.ip4, + value->aclfield.mask.ip4); + } else { + struct sockaddr_in6 *sin6 = &ip_addr->addr.ip6; + + ip_addr->sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, value->aclfield.data.ip6, sizeof(sin6->sin6_addr.s6_addr)); + + sin6 = &ip_mask->addr.ip6; + memcpy(sin6->sin6_addr.s6_addr, &value->aclfield.mask.ip6, sizeof(value->aclfield.mask.ip6)); + } + + return SAI_STATUS_SUCCESS; +} + +static void set_ipv4any_addr_mask (vpp_ip_addr_t *ip_addr) +{ + struct sockaddr_in *sin = &ip_addr->addr.ip4; + + ip_addr->sa_family = AF_INET; + sin->sin_addr.s_addr = (uint32_t) 0; +} + +static void set_ipv6any_addr_mask (vpp_ip_addr_t *ip_addr) +{ + struct sockaddr_in6 *sin6 = &ip_addr->addr.ip6; + + ip_addr->sa_family = AF_INET6; + + unsigned long long v6_mask = (unsigned long long) 0; + + memcpy(sin6->sin6_addr.s6_addr, &v6_mask, 8); + memcpy(&sin6->sin6_addr.s6_addr[8], &v6_mask, 8); +} + +static sai_status_t acl_ip_type_field_to_vpp_acl_rule( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_acl_rule_t *rule) +{ + sai_acl_ip_type_t ip_type; + + assert(SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE == attr_id); + + if (!value->aclfield.enable) { + return SAI_STATUS_SUCCESS; + } + + ip_type = (sai_acl_ip_type_t) value->aclfield.data.s32; + + switch (ip_type) { + case SAI_ACL_IP_TYPE_ANY: + /* Do nothing */ + break; + + case SAI_ACL_IP_TYPE_IP: + /* Do nothing for now */ + break; + + case SAI_ACL_IP_TYPE_IPV4ANY: + set_ipv4any_addr_mask(&rule->src_prefix); + set_ipv4any_addr_mask(&rule->dst_prefix); + set_ipv4any_addr_mask(&rule->src_prefix_mask); + set_ipv4any_addr_mask(&rule->dst_prefix_mask); + + break; + + case SAI_ACL_IP_TYPE_IPV6ANY: + set_ipv6any_addr_mask(&rule->src_prefix); + set_ipv6any_addr_mask(&rule->dst_prefix); + set_ipv6any_addr_mask(&rule->src_prefix_mask); + set_ipv6any_addr_mask(&rule->dst_prefix_mask); + + break; + + default: + SWSS_LOG_INFO("Unsupported ip type (%d)\n", ip_type); + return SAI_STATUS_SUCCESS; + } + return SAI_STATUS_SUCCESS; +} + +static sai_status_t acl_icmp_field_to_vpp_acl_rule( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_acl_rule_t *rule) +{ + uint16_t data = 0, mask = 0; + uint16_t new_data, new_mask; + + assert((SAI_ACL_ENTRY_ATTR_FIELD_ICMP_CODE == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_ICMP_TYPE == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_CODE == attr_id) || + (SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_TYPE == attr_id)); + + new_data = (value->aclfield.enable) ? value->aclfield.data.u8 : 0; + new_mask = (value->aclfield.enable) ? value->aclfield.mask.u8 : 0; + + + switch (attr_id) { + case SAI_ACL_ENTRY_ATTR_FIELD_ICMP_CODE: + case SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_CODE: + data = (uint16_t) ((data & 0xFF) | (new_data << 8)); + mask = (uint16_t) ((mask & 0xFF) | (new_mask << 8)); + + rule->dstport_or_icmpcode_first = data; + rule->srcport_or_icmptype_last = mask; + + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_ICMP_TYPE: + case SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_TYPE: + data = (uint16_t) ((data & 0xFF00) | new_data); + mask = (uint16_t) ((mask & 0xFF00) | new_mask); + + rule->srcport_or_icmptype_first = data; + rule->srcport_or_icmptype_last = mask; + + break; + + default: + SWSS_LOG_ERROR("Unexpected attr_id %d\n", attr_id); + return SAI_STATUS_FAILURE; + } + + return SAI_STATUS_SUCCESS; +} + +static sai_status_t acl_entry_port_to_vpp_acl_rule( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_acl_rule_t *rule) +{ + if (!value->aclfield.enable) { + SWSS_LOG_INFO("aclfield disabled for port configuration"); + return SAI_STATUS_SUCCESS; + } + + switch (attr_id) { + case SAI_ACL_ENTRY_ATTR_FIELD_L4_SRC_PORT: + rule->srcport_or_icmptype_first = value->aclfield.data.u16; + rule->srcport_or_icmptype_last = value->aclfield.data.u16; + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_L4_DST_PORT: + rule->dstport_or_icmpcode_first = value->aclfield.data.u16; + rule->dstport_or_icmpcode_last = value->aclfield.data.u16; + break; + + default: + break; + } + return SAI_STATUS_SUCCESS; +} + +static sai_status_t acl_rule_port_range_vpp_acl_set( + _In_ sai_acl_range_type_t type, + _In_ const sai_u32_range_t *range, + _Out_ vpp_acl_rule_t *rule) +{ + assert(range); + + switch (type) { + case SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE: + rule->srcport_or_icmptype_first = (uint16_t) range->min; + rule->srcport_or_icmptype_last = (uint16_t) range->max; + SWSS_LOG_INFO("SRC port range %u-%u", range->min, range->max); + break; + + case SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE: + rule->dstport_or_icmpcode_first = (uint16_t) range->min; + rule->dstport_or_icmpcode_last = (uint16_t) range->max; + SWSS_LOG_INFO("DST port range %u-%u", range->min, range->max); + break; + + default: + SWSS_LOG_INFO("Range type %d is not supported\n", type); + break; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::acl_rule_range_get( + _In_ const sai_object_list_t *range_list, + _Out_ sai_u32_range_t *range_limit_list, + _Out_ sai_acl_range_type_t *range_type_list, + _Out_ uint32_t *range_count) +{ + uint32_t idx, count = 0; + + sai_u32_range_t *range = range_limit_list; + sai_acl_range_type_t *range_type = range_type_list; + + for (idx = 0; idx < range_list->count; idx++) { + sai_object_id_t oid; + + oid = range_list->list[idx]; + + if (SAI_OBJECT_TYPE_ACL_RANGE == sai_object_type_query(oid)) { + sai_attribute_t attr; + + attr.id = SAI_ACL_RANGE_ATTR_TYPE; + if (get(SAI_OBJECT_TYPE_ACL_RANGE, oid, 1, &attr) == SAI_STATUS_SUCCESS) { + sai_acl_range_type_t type; + + type = (sai_acl_range_type_t) attr.value.s32; + attr.id = SAI_ACL_RANGE_ATTR_LIMIT; + if (get(SAI_OBJECT_TYPE_ACL_RANGE, oid, 1, &attr) == SAI_STATUS_SUCCESS) { + + *range = attr.value.u32range; + *range_type = type; + + range++; + range_type++; + count++; + + if (count == 2) break; + } + } else { + SWSS_LOG_ERROR("SAI_OBJECT_TYPE_ACL_RANGE not found for ACL_RANGE oid"); + return SAI_STATUS_FAILURE; + } + } + } + + *range_count = count; + + return SAI_STATUS_SUCCESS; +} + +static void acl_rule_set_action( + _In_ const sai_attribute_value_t *value, + _Out_ vpp_acl_rule_t *rule) + +{ + switch (value->aclaction.parameter.s32) { + case SAI_PACKET_ACTION_FORWARD: + rule->action = VPP_ACL_ACTION_API_PERMIT_STFULL; + break; + + case SAI_PACKET_ACTION_DROP: + rule->action = VPP_ACL_ACTION_API_DENY; + break; + } +} + +sai_status_t acl_rule_field_update( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_acl_rule_t *rule) +{ + sai_status_t status; + + assert(NULL != value); + status = SAI_STATUS_SUCCESS; + + switch (attr_id) { + case SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_DST_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_SRC_IPV6: + case SAI_ACL_ENTRY_ATTR_FIELD_DST_IPV6: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_SRC_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_SRC_IPV6: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IPV6: + status = acl_ip_field_to_vpp_acl(attr_id, value, rule); + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE: + status = acl_ip_type_field_to_vpp_acl_rule(attr_id, value, rule); + break; + + + case SAI_ACL_ENTRY_ATTR_FIELD_ICMP_CODE: + case SAI_ACL_ENTRY_ATTR_FIELD_ICMP_TYPE: + case SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_CODE: + case SAI_ACL_ENTRY_ATTR_FIELD_ICMPV6_TYPE: + status = acl_icmp_field_to_vpp_acl_rule(attr_id, + value, rule); + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_L4_SRC_PORT: + case SAI_ACL_ENTRY_ATTR_FIELD_L4_DST_PORT: + status = acl_entry_port_to_vpp_acl_rule(attr_id, value, rule); + break; + + case SAI_ACL_ENTRY_ATTR_FIELD_IP_PROTOCOL: + rule->proto = value->aclfield.data.u8 & value->aclfield.mask.u8; + status = SAI_STATUS_SUCCESS; + break; + + case SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION: + acl_rule_set_action(value, rule); + break; + + default: + break; + } + + return status; +} + +sai_status_t SwitchVpp::tunterm_set_action_redirect( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_tunterm_acl_rule_t *rule) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + sai_object_id_t next_hop_oid; + sai_object_id_t rif_oid; + sai_object_id_t port_oid; + sai_ip_address_t ip_address; + uint32_t next_hop_type; + uint16_t vlan_id = 0; + char nh_ip_str[INET6_ADDRSTRLEN]; + + next_hop_oid = value->aclaction.parameter.oid; + + sai_attribute_t attr; + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + CHECK_STATUS(get(SAI_OBJECT_TYPE_NEXT_HOP, next_hop_oid, 1, &attr)); + next_hop_type = attr.value.s32; + if (next_hop_type!= SAI_NEXT_HOP_TYPE_IP) { + return SAI_STATUS_SUCCESS; + } + attr.id = SAI_NEXT_HOP_ATTR_IP; + if (get(SAI_OBJECT_TYPE_NEXT_HOP, next_hop_oid, 1, &attr) == SAI_STATUS_SUCCESS) + { + ip_address = attr.value.ipaddr; + } else { + SWSS_LOG_ERROR("IP address missing in nexthop %s", sai_serialize_object_id(next_hop_oid).c_str()); + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; + if (get(SAI_OBJECT_TYPE_NEXT_HOP, next_hop_oid, 1, &attr) == SAI_STATUS_SUCCESS) + { + rif_oid = attr.value.oid; + } else { + SWSS_LOG_ERROR("RIF missing in nexthop %s", sai_serialize_object_id(next_hop_oid).c_str()); + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + if (get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_oid, 1, &attr) == SAI_STATUS_SUCCESS) + { + port_oid = attr.value.oid; + } else { + SWSS_LOG_ERROR("Port ID is missing missing in RIF object %s", sai_serialize_object_id(rif_oid).c_str()); + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + if (get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_oid, 1, &attr) == SAI_STATUS_SUCCESS) + { + vlan_id = attr.value.u16; + } + + std::string hwif_name; + if (!vpp_get_hwif_name(port_oid, vlan_id, hwif_name)) { + SWSS_LOG_WARN("VS hwif name not found for port %s", sai_serialize_object_id(port_oid).c_str()); + return SAI_STATUS_SUCCESS; + } + + switch (ip_address.addr_family) { + case SAI_IP_ADDR_FAMILY_IPV4: + { + sai_ip_address_t_to_vpp_ip_addr_t(ip_address, rule->next_hop_ip); + rule->ip_protocol = 1; // IP46_TYPE_IP4=1, IP46_TYPE_IP6=2 + break; + } + case SAI_IP_ADDR_FAMILY_IPV6: + { + sai_ip_address_t_to_vpp_ip_addr_t(ip_address, rule->next_hop_ip); + rule->ip_protocol = 2; // IP46_TYPE_IP4=1, IP46_TYPE_IP6=2 + break; + } + default: + break; + } + + strncpy(rule->hwif_name, hwif_name.c_str(), sizeof(rule->hwif_name) -1); + + SWSS_LOG_INFO("Tunterm rule received: IP Protocol %d, rif_oid %ld, next-hop hwif_name %s", rule->ip_protocol, + rif_oid, rule->hwif_name); + + vpp_ip_addr_t_to_string(&rule->next_hop_ip, nh_ip_str, INET6_ADDRSTRLEN); + SWSS_LOG_INFO("Tunterm acl rule has next-hop IP %s", nh_ip_str); + + return status; +} + +sai_status_t SwitchVpp::tunterm_acl_rule_field_update( + _In_ sai_acl_entry_attr_t attr_id, + _In_ const sai_attribute_value_t *value, + _Out_ vpp_tunterm_acl_rule_t *rule) +{ + sai_status_t status; + char dst_ip_str[INET6_ADDRSTRLEN]; + + assert(NULL != value); + status = SAI_STATUS_SUCCESS; + + switch (attr_id) { + case SAI_ACL_ENTRY_ATTR_FIELD_DST_IP: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IP: + { + vpp_ip_addr_t *ip_addr, *ip_mask; + ip_addr = &rule->dst_prefix; + ip_mask = &rule->dst_prefix_mask; + struct sockaddr_in *sin = &ip_addr->addr.ip4; + ip_addr->sa_family = AF_INET; + sin->sin_addr.s_addr = value->aclfield.data.ip4; + sin = &ip_mask->addr.ip4; + sin->sin_addr.s_addr = value->aclfield.mask.ip4; + vpp_ip_addr_t_to_string(ip_addr, dst_ip_str, INET6_ADDRSTRLEN); + SWSS_LOG_INFO("Tunterm acl rule has dst IP: %s", dst_ip_str); + break; + } + case SAI_ACL_ENTRY_ATTR_FIELD_DST_IPV6: + case SAI_ACL_ENTRY_ATTR_FIELD_INNER_DST_IPV6: + { + vpp_ip_addr_t *ip_addr, *ip_mask; + ip_addr = &rule->dst_prefix; + ip_mask = &rule->dst_prefix_mask; + struct sockaddr_in6 *sin6 = &ip_addr->addr.ip6; + ip_addr->sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, value->aclfield.data.ip6, sizeof(sin6->sin6_addr.s6_addr)); + sin6 = &ip_mask->addr.ip6; + memcpy(sin6->sin6_addr.s6_addr, &value->aclfield.mask.ip6, sizeof(value->aclfield.mask.ip6)); + vpp_ip_addr_t_to_string(ip_addr, dst_ip_str, INET6_ADDRSTRLEN); + SWSS_LOG_INFO("Tunterm acl rule has dst IP: %s", dst_ip_str); + break; + } + case SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT: + status = tunterm_set_action_redirect(attr_id, value, rule); + break; + default: + break; + } + + return status; +} + +sai_status_t SwitchVpp::getAclTableId( + _In_ sai_object_id_t entry_id, sai_object_id_t *tbl_oid) +{ + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_TABLE_ID; + if (get(SAI_OBJECT_TYPE_ACL_ENTRY, entry_id, 1, &attr) != SAI_STATUS_SUCCESS) { + auto sid = sai_serialize_object_id(entry_id); + + SWSS_LOG_ERROR("ACL table for acl entry id %s not found", sid.c_str()); + return SAI_STATUS_FAILURE; + } + + *tbl_oid = attr.value.oid; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::acl_range_attr_get ( + _In_ const std::string &serializedObjectId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _Out_ sai_attribute_t *attr_range) +{ + const sai_attribute_t *attr; + + for (uint32_t i = 0; i < attr_count; i++) { + attr = &attr_list[i]; + if (attr->id == SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE) { + attr_range->id = SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE; + return get(SAI_OBJECT_TYPE_ACL_ENTRY, serializedObjectId, + 1, attr_range); + } + } + + return SAI_STATUS_FAILURE; +} + +sai_status_t acl_priority_attr_get ( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + uint32_t *priority) +{ + const sai_attribute_t *attr; + + for (uint32_t i = 0; i < attr_count; i++) { + attr = &attr_list[i]; + if (attr->id == SAI_ACL_ENTRY_ATTR_PRIORITY) { + *priority = attr->value.u32; + return SAI_STATUS_SUCCESS; + } + } + + return SAI_STATUS_FAILURE; +} + +static bool cmp_priority ( + const ordered_ace_list_t& f, + const ordered_ace_list_t& s) +{ + return (f.priority > s.priority); +} + +sai_status_t SwitchVpp::get_sorted_aces( + sai_object_id_t tbl_oid, + size_t &n_total_entries, + acl_tbl_entries_t *&aces, + std::list &ordered_aces) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + auto it = m_acl_tbl_rules_map.find(tbl_oid); + + if (it == m_acl_tbl_rules_map.end()) { + auto sid = sai_serialize_object_id(tbl_oid); + SWSS_LOG_INFO("No ACL entry list for table id %s", sid.c_str()); + return status; + } + + n_total_entries = it->second.size(); + if (n_total_entries == 0) { + return SAI_STATUS_SUCCESS; + } + + std::list &acl_entries = it->second; + acl_tbl_entries_t *p_ace = NULL; + aces = (acl_tbl_entries_t *) calloc(n_total_entries, sizeof(acl_tbl_entries_t)); + if (!aces) { + SWSS_LOG_ERROR("Failed to allocate memory for aces."); + return SAI_STATUS_FAILURE; + } + p_ace = aces; + + /* Collect ACL entries configuration */ + uint32_t index = 0; + for (auto entry_id: acl_entries) { + SWSS_LOG_INFO("Processing ACL entry %s", sai_serialize_object_id(entry_id).c_str()); + + auto sid = sai_serialize_object_id(entry_id); + + if (get_max(SAI_OBJECT_TYPE_ACL_ENTRY, sid, MAX_ACL_ATTRS, + &p_ace->attrs_count, p_ace->attrs) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to get acl entry."); + status = SAI_STATUS_FAILURE; + break; + } + p_ace->attr_range.value.aclfield.data.objlist.list = p_ace->range_objid_list; + p_ace->attr_range.value.aclfield.data.objlist.count = 2; + + if (acl_range_attr_get(sid, p_ace->attrs_count, + p_ace->attrs, &p_ace->attr_range) == SAI_STATUS_SUCCESS) { + p_ace->range_count = 0; + if (acl_rule_range_get(&p_ace->attr_range.value.aclfield.data.objlist, + p_ace->range_limit, p_ace->range_type, &p_ace->range_count) + != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed acl range get."); + status = SAI_STATUS_FAILURE; + break; + } + } + + p_ace->priority = 0; + acl_priority_attr_get(p_ace->attrs_count, p_ace->attrs, &p_ace->priority); + + ordered_aces.push_back({index, p_ace->priority, entry_id, false}); + p_ace++; + index++; + } + + if (status != SAI_STATUS_SUCCESS) { + free(aces); + ordered_aces.clear(); + return SAI_STATUS_FAILURE; + } + + /* Sort ACL entries on priority */ + ordered_aces.sort(cmp_priority); + + return SAI_STATUS_SUCCESS; +} + +void SwitchVpp::count_tunterm_acl_rules( + acl_tbl_entries_t *aces, + std::list &ordered_aces, + size_t &n_entries, + size_t &n_tunterm_entries) +{ + acl_tbl_entries_t *p_ace = NULL; + const sai_attribute_t *attr = NULL; + bool tunterm_flag_set = false; + + for (auto &ace: ordered_aces) { + p_ace = &aces[ace.index]; + tunterm_flag_set = false; + for (uint32_t i = 0; i < p_ace->attrs_count; i++) { + attr = &p_ace->attrs[i]; + if (attr->id == SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_TERMINATED) { + if (attr->value.aclfield.data.booldata == true) { + tunterm_flag_set = true; + } + break; + } + } + if (tunterm_flag_set) { + n_tunterm_entries++; + ace.is_tunterm = true; + } else { + n_entries++; + } + } +} + +sai_status_t SwitchVpp::allocate_acl( + size_t n_entries, + sai_object_id_t tbl_oid, + char (&acl_name)[64], + vpp_acl_t *&acl) +{ + auto tbl_sid = sai_serialize_object_id(tbl_oid); + + if(n_entries == 0) { + return SAI_STATUS_SUCCESS; + } + + acl = (vpp_acl_t *) calloc(1, sizeof(vpp_acl_t) + (n_entries * sizeof(vpp_acl_rule_t))); + if (!acl) { + SWSS_LOG_ERROR("Failed to allocate memory for acl."); + return SAI_STATUS_FAILURE; + } + snprintf(acl_name, sizeof(acl_name), "sonic_acl_%s", tbl_sid.c_str()); + acl->acl_name = acl_name; + acl->count = (uint32_t) n_entries; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::allocate_tunterm_acl( + size_t n_tunterm_entries, + sai_object_id_t tbl_oid, + char (&acl_name)[64], + vpp_tunterm_acl_t *&tunterm_acl) +{ + auto tbl_sid = sai_serialize_object_id(tbl_oid); + + if(n_tunterm_entries == 0) { + return SAI_STATUS_SUCCESS; + } + + tunterm_acl = (vpp_tunterm_acl_t *) calloc(1, sizeof(vpp_tunterm_acl_t) + (n_tunterm_entries * sizeof(vpp_tunterm_acl_rule_t))); + if (!tunterm_acl) { + SWSS_LOG_ERROR("Failed to allocate memory for tunterm acl."); + return SAI_STATUS_FAILURE; + } + tunterm_acl->count = (uint32_t) n_tunterm_entries; + snprintf(acl_name, sizeof(acl_name), "tunterm_sonic_acl_%s", tbl_sid.c_str()); + tunterm_acl->acl_name = acl_name; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::fill_acl_rules( + acl_tbl_entries_t *aces, + std::list &ordered_aces, + vpp_acl_t *acl, + vpp_tunterm_acl_t *tunterm_acl) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + acl_tbl_entries_t *p_ace = NULL; + vpp_acl_rule_t *rule = NULL; + vpp_tunterm_acl_rule_t *tunterm_rule = NULL; + + if(acl != NULL) { + rule = &acl->rules[0]; + } + if(tunterm_acl != NULL) { + tunterm_rule = &tunterm_acl->rules[0]; + } + + for (auto ace: ordered_aces) { + SWSS_LOG_INFO("Acl entry index %u priority %u", ace.index, ace.priority); + p_ace = &aces[ace.index]; + const sai_attribute_t *attr; + for (uint32_t i = 0; i < p_ace->attrs_count && status == SAI_STATUS_SUCCESS; i++) { + attr = &p_ace->attrs[i]; + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_ACL_ENTRY, attr->id); + + if (meta != NULL) { + SWSS_LOG_INFO("Type %s attrib id %s", + sai_serialize_object_type(SAI_OBJECT_TYPE_ACL_ENTRY).c_str(), + meta->attridname); + } + + if (ace.is_tunterm) { + status = tunterm_acl_rule_field_update((sai_acl_entry_attr_t) attr->id, &attr->value, tunterm_rule); + } else if (attr->id == SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE) { + for (uint32_t jdx = 0; jdx < p_ace->range_count; jdx++) { + status = acl_rule_port_range_vpp_acl_set(p_ace->range_type[jdx], + &p_ace->range_limit[jdx], rule); + } + } else { + status = acl_rule_field_update((sai_acl_entry_attr_t) attr->id, &attr->value, rule); + } + + if(status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to fill acl rule, status: %d", status); + return SAI_STATUS_FAILURE; + } + } + if (ace.is_tunterm) { + tunterm_rule++; + } else { + rule++; + } + } + return SAI_STATUS_SUCCESS; +} + +void SwitchVpp::cleanup_acl_tbl_config( + acl_tbl_entries_t *&aces, + std::list &ordered_aces, + vpp_acl_t *&acl, + vpp_tunterm_acl_t *&tunterm_acl) +{ + if(aces != NULL) { + free(aces); + aces = NULL; + } + if(acl != NULL) { + free(acl); + acl = NULL; + } + if(tunterm_acl != NULL) { + free(tunterm_acl); + tunterm_acl = NULL; + } + ordered_aces.clear(); +} + +sai_status_t SwitchVpp::acl_add_replace( + vpp_acl_t *&acl, + sai_object_id_t tbl_oid, + acl_tbl_entries_t *aces, + std::list &ordered_aces) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + bool acl_replace; + uint32_t acl_swindex; + uint32_t index = 0; + acl_tbl_entries_t *p_ace = NULL; + auto tbl_sid = sai_serialize_object_id(tbl_oid); + auto vpp_idx_it = m_acl_swindex_map.find(tbl_oid); + if (vpp_idx_it == m_acl_swindex_map.end()) { + acl_swindex = 0; + acl_replace = false; + } else if (acl == NULL) { + status = aclDefaultAllowConfigure(tbl_oid); + return status; + } else { + acl_swindex = vpp_idx_it->second; + acl_replace = true; + } + + status = vpp_acl_add_replace(acl, &acl_swindex, acl_replace); + if (status == SAI_STATUS_SUCCESS) { + m_acl_swindex_map[tbl_oid] = acl_swindex; + index = 0; + for (auto ace: ordered_aces) { + p_ace = &aces[ace.index]; + const sai_attribute_t *attr; + sai_object_id_t ace_cntr_oid; + for (uint32_t i = 0; i < p_ace->attrs_count; i++) { + attr = &p_ace->attrs[i]; + if (attr->id == SAI_ACL_ENTRY_ATTR_ACTION_COUNTER) { + ace_cntr_oid = attr->value.aclaction.parameter.oid; + auto ace_it = m_ace_cntr_info_map.find(ace_cntr_oid); + if (ace_it != m_ace_cntr_info_map.end()) { + m_ace_cntr_info_map.erase(ace_it); + } + // For stats we need to find vpp rule index from acl_entry_counter (ace_counter) + m_ace_cntr_info_map[ace_cntr_oid] = { tbl_oid, ace.ace_oid, acl_swindex, index }; + } + } + index++; + } + } else { + SWSS_LOG_ERROR("Vpp acl add replace failed, status: %d", status); + return SAI_STATUS_FAILURE; + } + + SWSS_LOG_INFO("ACL table %s %s ", tbl_sid.c_str(), + acl_replace ? "replaced" : "added"); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::tunterm_acl_bindunbind(sai_object_id_t tbl_oid, bool is_add, std::string hwif_name) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + auto tunterm_idx_it = m_tunterm_acl_swindex_map.find(tbl_oid); + if (tunterm_idx_it != m_tunterm_acl_swindex_map.end()) { + auto tunterm_idx = tunterm_idx_it->second; + status = vpp_tunterm_acl_interface_add_del(tunterm_idx, is_add, hwif_name.c_str()); + } + if(status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Tunterm acl bind/unbind failed to hwif %s, status: %d", + hwif_name.c_str(), status); + } + return status; +} + +sai_status_t SwitchVpp::tunterm_acl_add_replace(vpp_tunterm_acl_t *acl, sai_object_id_t tbl_oid) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + uint32_t tunterm_acl_swindex = 0; + bool do_port_bind = false; + auto tbl_sid = sai_serialize_object_id(tbl_oid); + + auto tunterm_idx_it = m_tunterm_acl_swindex_map.find(tbl_oid); + if ((tunterm_idx_it == m_tunterm_acl_swindex_map.end()) && (acl != NULL)) { + // ADD new tunterm acl + tunterm_acl_swindex = ~tunterm_acl_swindex; + do_port_bind = true; + } else if (tunterm_idx_it != m_tunterm_acl_swindex_map.end() && (acl == NULL)) { + // REPLACE with empty ACL (delete tunterm acl) + return tunterm_acl_delete(tbl_oid, false); + } else if (tunterm_idx_it != m_tunterm_acl_swindex_map.end()) { + // REPLACE with incoming tunterm acl + tunterm_acl_swindex = tunterm_idx_it->second; + } else { + // NO-OP, tunterm acl not configured and no incoming tunterm acl. + return status; + } + + std::list hwif_names; + SWSS_LOG_INFO("Adding tunterm acl rules, tunterm_acl index %u", tunterm_acl_swindex); + + status = vpp_tunterm_acl_add_replace(&tunterm_acl_swindex, acl->count, acl); + + if (status == SAI_STATUS_SUCCESS) { + m_tunterm_acl_swindex_map[tbl_oid] = tunterm_acl_swindex; + } else { + return SAI_STATUS_FAILURE; + } + + /* + * Bind tunterm acl to ports which ACL table is already bound when first + * tunterm ACL rule is received. + */ + if (do_port_bind) { + auto it_hw_ports = m_acl_tbl_hw_ports_map.find(tbl_oid); + if (it_hw_ports == m_acl_tbl_hw_ports_map.end()) { + auto sid = sai_serialize_object_id(tbl_oid); + SWSS_LOG_INFO("Tunterm acl - no ports bound for table id %s", sid.c_str()); + } else { + hwif_names = it_hw_ports->second; + for (auto hwif_name: hwif_names) { + SWSS_LOG_INFO("Tunterm acl - binding to hwif %s", hwif_name.c_str()); + status = tunterm_acl_bindunbind(tbl_oid, true, hwif_name); + if(status != SAI_STATUS_SUCCESS) { + return status; + } + } + } + } + + SWSS_LOG_INFO("Tunterm ACL table %s %s status %d", tbl_sid.c_str(), + do_port_bind ? "add" : "replace", status); + + return status; +} + +sai_status_t SwitchVpp::tbl_hw_ports_map_delete(sai_object_id_t tbl_oid) +{ + auto hw_ports_it = m_acl_tbl_hw_ports_map.find(tbl_oid); + if (hw_ports_it == m_acl_tbl_hw_ports_map.end()) { + return SAI_STATUS_SUCCESS; + } + m_acl_tbl_hw_ports_map.erase(hw_ports_it); + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::tunterm_acl_delete(sai_object_id_t tbl_oid, bool table_delete) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + auto tunterm_idx_it = m_tunterm_acl_swindex_map.find(tbl_oid); + + if (tunterm_idx_it == m_tunterm_acl_swindex_map.end()) { + SWSS_LOG_WARN("No tunterm ACL configured for table %s", + sai_serialize_object_id(tbl_oid).c_str()); + return status; + } + + /* + * In the case where tunterm ACL is deleted by ACL update with empty ACL, + * need to unbind the ports here before tunterm ACL can be deleted. In the + * regular tunterm ACL delete case, the ports are unbound via SAI calls. + */ + if(!table_delete) { + for(auto hwif_name: m_acl_tbl_hw_ports_map[tbl_oid]) { + status = tunterm_acl_bindunbind(tbl_oid, false, hwif_name); + if(status != SAI_STATUS_SUCCESS) { + return status; + } + } + } + + uint32_t tunterm_acl_swindex = tunterm_idx_it->second; + status = vpp_tunterm_acl_del(tunterm_acl_swindex); + if (status == SAI_STATUS_SUCCESS) { + m_tunterm_acl_swindex_map.erase(tunterm_idx_it); + if(table_delete) { + tbl_hw_ports_map_delete(tbl_oid); + } + } + + SWSS_LOG_INFO("Tunterm acl table %s, remove tunterm_acl index %u, " + "status %d", sai_serialize_object_id(tbl_oid).c_str(), + tunterm_acl_swindex, status); + + return status; +} + +sai_status_t SwitchVpp::AclTblConfig( + _In_ sai_object_id_t tbl_oid) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + size_t n_entries = 0; + size_t n_tunterm_entries = 0; + size_t n_total_entries = 0; + acl_tbl_entries_t *aces = NULL; + vpp_acl_t *acl = NULL; + vpp_tunterm_acl_t *tunterm_acl = NULL; + char aclname[64]; + char tunterm_aclname[64]; + std::map acl_aces_index_map; + std::list ordered_aces = {}; + + #define CHECK_STATUS_ACLTBLCONFIG(status) { \ + sai_status_t _status = (status); \ + if (_status != SAI_STATUS_SUCCESS) { \ + cleanup_acl_tbl_config(aces, ordered_aces, acl, tunterm_acl); \ + return _status; \ + } \ + } + + CHECK_STATUS_ACLTBLCONFIG(get_sorted_aces(tbl_oid, n_total_entries, aces, + ordered_aces)); + + count_tunterm_acl_rules(aces, ordered_aces, n_entries, n_tunterm_entries); + + SWSS_LOG_INFO("Tunterm acl count: %ld, regular acl count: %ld, " + "total: %ld", n_tunterm_entries, n_entries, n_total_entries); + + CHECK_STATUS_ACLTBLCONFIG(allocate_acl(n_entries, tbl_oid, aclname, acl)); + + CHECK_STATUS_ACLTBLCONFIG(allocate_tunterm_acl(n_tunterm_entries, tbl_oid, + tunterm_aclname, tunterm_acl)); + + CHECK_STATUS_ACLTBLCONFIG(fill_acl_rules(aces, ordered_aces, acl, tunterm_acl)); + + status = acl_add_replace(acl, tbl_oid, aces, ordered_aces); + + if (status == SAI_STATUS_SUCCESS) { + status = tunterm_acl_add_replace(tunterm_acl, tbl_oid); + } else { + SWSS_LOG_ERROR("ACL plugin operation failed, skipping tunterm " + "configuration. status %d", status); + } + + cleanup_acl_tbl_config(aces, ordered_aces, acl, tunterm_acl); + return status; +} + +sai_status_t SwitchVpp::aclGetVppIndices( + _In_ sai_object_id_t ace_cntr_oid, + _Out_ uint32_t *acl_index, + _Out_ uint32_t *ace_index) +{ + auto vpp_ace_it = m_ace_cntr_info_map.find(ace_cntr_oid); + if (vpp_ace_it == m_ace_cntr_info_map.end()) { + SWSS_LOG_WARN("VS ace entry %s not found in vpp_ace_cntr_info_map", + sai_serialize_object_id(ace_cntr_oid).c_str()); + return SAI_STATUS_FAILURE; + } + auto & ace_info = vpp_ace_it->second; + + *acl_index = ace_info.acl_index; + *ace_index = ace_info.ace_index; + + SWSS_LOG_INFO("VS acl index %u ace index %u for acl_counter %s acl_table %s", + *acl_index, *ace_index, + sai_serialize_object_id(ace_cntr_oid).c_str(), + sai_serialize_object_id(ace_info.tbl_oid).c_str()); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::aclTableCreate( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + auto sid = sai_serialize_object_id(object_id); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_ACL_TABLE, sid, switch_id, attr_count, attr_list)); + + SWSS_LOG_NOTICE("ACL table %s created", sid.c_str()); + + return aclDefaultAllowConfigure(object_id); +} + +sai_status_t SwitchVpp::aclTableRemove( + _In_ const std::string &serializedObjectId) +{ + sai_object_id_t tbl_oid; + + sai_deserialize_object_id(serializedObjectId, tbl_oid); + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_ACL_TABLE, serializedObjectId)); + + return AclTblRemove(tbl_oid); +} + +sai_status_t SwitchVpp::aclDefaultAllowConfigure ( + _In_ sai_object_id_t tbl_oid) +{ + sai_attribute_t attr[2]; + uint32_t acl_swindex = 0; + bool acl_replace = false; + auto vpp_idx_it = m_acl_swindex_map.find(tbl_oid); + if (vpp_idx_it != m_acl_swindex_map.end()) { + acl_swindex = vpp_idx_it->second; + acl_replace = true; + } + + attr[0].id = SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE; + attr[0].value.aclfield.enable = true; + attr[0].value.aclfield.data.s32 = SAI_ACL_IP_TYPE_IPV4ANY; + + attr[1].id = SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE; + attr[1].value.aclfield.enable = true; + attr[1].value.aclfield.data.s32 = SAI_ACL_IP_TYPE_IPV6ANY; + + vpp_acl_t *acl; + + acl = (vpp_acl_t *) calloc(1, sizeof(vpp_acl_t) + (2 * sizeof(vpp_acl_rule_t))); + if (!acl) { + return SAI_STATUS_FAILURE; + } + acl->count = 2; + char aclname[64]; + + auto sid = sai_serialize_object_id(tbl_oid); + + snprintf(aclname, sizeof(aclname), "sonic_acl_%s", sid.c_str()); + acl->acl_name = aclname; + + vpp_acl_rule_t *rule = &acl->rules[0]; + + acl_rule_field_update((sai_acl_entry_attr_t) attr[0].id, &attr[0].value, rule); + rule->action = VPP_ACL_ACTION_API_PERMIT; + + rule = &acl->rules[1]; + + acl_rule_field_update((sai_acl_entry_attr_t) attr[1].id, &attr[1].value, rule); + rule->action = VPP_ACL_ACTION_API_PERMIT; + + sai_status_t status; + + status = vpp_acl_add_replace(acl, &acl_swindex, acl_replace); + if (status == SAI_STATUS_SUCCESS && !acl_replace) { + m_acl_swindex_map[tbl_oid] = acl_swindex; + } + + free(acl); + SWSS_LOG_INFO("Default Allow all ACL for table %s added, status %d swindex %u", + sid.c_str(), status, acl_swindex); + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::AclTblRemove( + _In_ sai_object_id_t tbl_oid) +{ + sai_status_t status; + + auto it = m_acl_tbl_rules_map.find(tbl_oid); + + if (it != m_acl_tbl_rules_map.end()) { + std::list& member_list = it->second; + + if (member_list.size()) { + member_list.clear(); + } + m_acl_tbl_rules_map.erase(it); + } + + status = tunterm_acl_delete(tbl_oid, true); + + auto vpp_idx_it = m_acl_swindex_map.find(tbl_oid); + if (vpp_idx_it == m_acl_swindex_map.end()) { + SWSS_LOG_WARN("No ACL configured for table %s", sai_serialize_object_id(tbl_oid).c_str()); + return SAI_STATUS_FAILURE; + } + uint32_t acl_swindex = vpp_idx_it->second; + + status = vpp_acl_del(acl_swindex); + + if (status == SAI_STATUS_SUCCESS) { + m_acl_swindex_map.erase(vpp_idx_it); + } + SWSS_LOG_NOTICE("ACL table %s remove swindex %u status %d", + sai_serialize_object_id(tbl_oid).c_str(), acl_swindex, status); + + return status; +} + +sai_status_t SwitchVpp::addRemoveAclEntrytoMap( + _In_ sai_object_id_t entry_id, + _In_ sai_object_id_t tbl_oid, + _In_ bool is_add) +{ + SWSS_LOG_ENTER(); + + auto it = m_acl_tbl_rules_map.find(tbl_oid); + + if (it == m_acl_tbl_rules_map.end()) { + + if (!is_add) { + auto sid = sai_serialize_object_id(entry_id); + SWSS_LOG_ERROR("ACL entry with id %s not found in tbl %s", sid.c_str(), + sai_serialize_object_id(tbl_oid).c_str()); + return SAI_STATUS_FAILURE; + } + + std::list member_list; + + member_list = { entry_id }; + m_acl_tbl_rules_map[tbl_oid] = member_list; + + } else { + std::list& member_list = it->second; + + if (!is_add) { + member_list.remove(entry_id); + return SAI_STATUS_SUCCESS; + } + member_list.push_back(entry_id); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::AclAddRemoveCheck( + _In_ sai_object_id_t tbl_oid) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + auto it = m_acl_tbl_rules_map.find(tbl_oid); + + if (it == m_acl_tbl_rules_map.end()) { + return SAI_STATUS_SUCCESS; + } + + status = AclTblConfig(tbl_oid); + return status; +} + +sai_status_t SwitchVpp::createAclEntry( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto sid = sai_serialize_object_id(object_id); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_ACL_ENTRY, sid, switch_id, attr_count, attr_list)); + + sai_object_id_t tbl_oid; + + if (getAclTableId(object_id, &tbl_oid) != SAI_STATUS_SUCCESS) { + return SAI_STATUS_FAILURE; + } + sai_status_t status; + + status = addRemoveAclEntrytoMap(object_id, tbl_oid, true); + if (status == SAI_STATUS_SUCCESS) { + status = AclAddRemoveCheck(tbl_oid); + } + return status; +} + +sai_status_t SwitchVpp::removeAclEntry( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t entry_oid; + + sai_deserialize_object_id(serializedObjectId, entry_oid); + + sai_object_id_t tbl_oid; + + if (getAclTableId(entry_oid, &tbl_oid) != SAI_STATUS_SUCCESS) { + return SAI_STATUS_FAILURE; + } + + sai_status_t status; + + status = addRemoveAclEntrytoMap(entry_oid, tbl_oid, false); + if (status == SAI_STATUS_SUCCESS) { + status = AclAddRemoveCheck(tbl_oid); + } + remove_internal(SAI_OBJECT_TYPE_ACL_ENTRY, serializedObjectId); + + SWSS_LOG_NOTICE("ACL entry %s in table %s remove status %d", + sai_serialize_object_id(entry_oid).c_str(), + sai_serialize_object_id(tbl_oid).c_str(), + status); + + return status; +} + +sai_status_t SwitchVpp::getAclTableGroupId( + _In_ sai_object_id_t member_oid, + _Out_ sai_object_id_t *tbl_grp_oid) +{ + sai_attribute_t attr; + + attr.id = SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID; + if (get(SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER, member_oid, 1, &attr) != SAI_STATUS_SUCCESS) { + auto sid = sai_serialize_object_id(member_oid); + + SWSS_LOG_INFO("ACL table group oid for acl grp member id %s not found", sid.c_str()); + return SAI_STATUS_FAILURE; + } + + *tbl_grp_oid = attr.value.oid; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::addRemoveAclGrpMbr( + _In_ sai_object_id_t member_oid, + _In_ sai_object_id_t tbl_grp_oid, + _In_ bool is_add) +{ + SWSS_LOG_ENTER(); + + auto it = m_acl_tbl_grp_mbr_map.find(tbl_grp_oid); + + if (it == m_acl_tbl_grp_mbr_map.end()) { + + if (!is_add) { + auto sid = sai_serialize_object_id(member_oid); + SWSS_LOG_ERROR("ACL group member with id %s not found in tbl %s", sid.c_str(), + sai_serialize_object_id(tbl_grp_oid).c_str()); + return SAI_STATUS_FAILURE; + } + + std::list member_list; + + member_list = { member_oid }; + m_acl_tbl_grp_mbr_map[tbl_grp_oid] = member_list; + + } else { + std::list& member_list = it->second; + + if (!is_add) { + member_list.remove(member_oid); + } else { + member_list.push_back(member_oid); + } + } + + sai_attribute_t attr; + + attr.id = SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID; + if (get(SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER, member_oid, 1, &attr) != SAI_STATUS_SUCCESS) { + auto sid = sai_serialize_object_id(member_oid); + + SWSS_LOG_INFO("ACL group member %s table id not found", sid.c_str()); + return SAI_STATUS_SUCCESS; + } + + aclBindUnbindPorts(tbl_grp_oid, attr.value.oid, is_add); + + SWSS_LOG_NOTICE("ACL group member %s %s table group %s", + sai_serialize_object_id(member_oid).c_str(), + is_add ? "added to" : "removed from", + sai_serialize_object_id(tbl_grp_oid).c_str()); + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::createAclGrpMbr( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto sid = sai_serialize_object_id(object_id); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER, + sid, switch_id, attr_count, attr_list)); + + sai_object_id_t tbl_grp_oid; + + if (getAclTableGroupId(object_id, &tbl_grp_oid) != SAI_STATUS_SUCCESS) { + return SAI_STATUS_FAILURE; + } + sai_status_t status; + + status = addRemoveAclGrpMbr(object_id, tbl_grp_oid, true); + + return status; +} + +sai_status_t SwitchVpp::removeAclGrpMbr( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t member_oid; + + sai_deserialize_object_id(serializedObjectId, member_oid); + + sai_object_id_t tbl_grp_oid; + + if (getAclTableGroupId(member_oid, &tbl_grp_oid) != SAI_STATUS_SUCCESS) { + return SAI_STATUS_FAILURE; + } + + sai_status_t status; + + status = addRemoveAclGrpMbr(member_oid, tbl_grp_oid, false); + + SWSS_LOG_NOTICE("Remove Acl grp member %s status %d", + serializedObjectId.c_str(), status); + + remove_internal(SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER, serializedObjectId); + + return status; +} + +sai_status_t SwitchVpp::setAclGrpMbr( + _In_ sai_object_id_t member_oid, + _In_ const sai_attribute_t* attr) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_SUCCESS; + + sai_object_id_t tbl_grp_oid = SAI_NULL_OBJECT_ID; + + getAclTableGroupId(member_oid, &tbl_grp_oid); + + if (attr->id == SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID) { + if (tbl_grp_oid == SAI_NULL_OBJECT_ID) { + status = addRemoveAclGrpMbr(member_oid, tbl_grp_oid, true); + } else { + status = addRemoveAclGrpMbr(member_oid, tbl_grp_oid, false); + if (status == SAI_STATUS_SUCCESS) { + status = addRemoveAclGrpMbr(member_oid, attr->value.oid, true); + } + } + } + auto sid = sai_serialize_object_id(member_oid); + + SWSS_LOG_NOTICE("ACL grp member %s set attr %d", sid.c_str(), attr->id); + + set_internal(SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER, sid, attr); + + return status; +} + +sai_status_t SwitchVpp::removeAclGrp( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t tbl_grp_oid; + + sai_deserialize_object_id(serializedObjectId, tbl_grp_oid); + + auto it = m_acl_tbl_grp_mbr_map.find(tbl_grp_oid); + + if (it != m_acl_tbl_grp_mbr_map.end()) { + + std::list& member_list = it->second; + sai_status_t status; + + for (sai_object_id_t member_oid: member_list) { + + status = addRemoveAclGrpMbr(member_oid, tbl_grp_oid, false); + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_WARN("Failed to delete ACL tbl grp member %s from group %s", + sai_serialize_object_id(member_oid).c_str(), + serializedObjectId.c_str()); + } + } + m_acl_tbl_grp_mbr_map.erase(it); + } + SWSS_LOG_NOTICE("Remove ACL group %s", serializedObjectId.c_str()); + + return remove_internal(SAI_OBJECT_TYPE_ACL_TABLE_GROUP, serializedObjectId); +} + +sai_status_t SwitchVpp::addRemovePortTblGrp( + _In_ sai_object_id_t port_oid, + _In_ sai_object_id_t tbl_grp_oid, + _In_ bool is_add) +{ + auto it = m_acl_tbl_grp_ports_map.find(tbl_grp_oid); + + if (it == m_acl_tbl_grp_ports_map.end()) { + if (!is_add) { + SWSS_LOG_INFO("port id %s delete failed, no table group %s", + sai_serialize_object_id(port_oid).c_str(), + sai_serialize_object_id(tbl_grp_oid).c_str()); + return SAI_STATUS_FAILURE; + } + std::list ports_list = { port_oid }; + m_acl_tbl_grp_ports_map[tbl_grp_oid] = ports_list; + } else { + std::list& ports_list = it->second; + if (is_add) { + ports_list.push_back(port_oid); + } else { + ports_list.remove(port_oid); + } + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::aclBindUnbindPort( + _In_ sai_object_id_t port_oid, + _In_ sai_object_id_t tbl_grp_oid, + _In_ bool is_input, + _In_ bool is_bind) +{ + SWSS_LOG_ENTER(); + + addRemovePortTblGrp(port_oid, tbl_grp_oid, is_bind); + + auto it = m_acl_tbl_grp_mbr_map.find(tbl_grp_oid); + + if (it == m_acl_tbl_grp_mbr_map.end()) { + auto sid = sai_serialize_object_id(tbl_grp_oid); + SWSS_LOG_INFO("ACL tbl group with id %s not found", sid.c_str()); + /* + * The tbl group is not created until a group member is added. The bind port + * will be called later when a group member is added. + */ + return SAI_STATUS_SUCCESS; + } + + std::string hwif_name; + + if (!vpp_get_hwif_name(port_oid, 0, hwif_name)) { + SWSS_LOG_WARN("VS hwif name not found for port %s", sai_serialize_object_id(port_oid).c_str()); + return SAI_STATUS_FAILURE; + } + std::list& member_list = it->second; + + for (auto member_oid: member_list) { + sai_attribute_t attr; + + attr.id = SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID; + if (get(SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER, member_oid, 1, &attr) != SAI_STATUS_SUCCESS) { + auto sid = sai_serialize_object_id(member_oid); + + SWSS_LOG_INFO("ACL table oid for acl grp member id %s not found", sid.c_str()); + continue; + } + auto tbl_oid = attr.value.oid; + auto vpp_idx_it = m_acl_swindex_map.find(tbl_oid); + if (vpp_idx_it == m_acl_swindex_map.end()) { + auto sid = sai_serialize_object_id(tbl_oid); + SWSS_LOG_INFO("VS swindex for ACL table oid %s not found", sid.c_str()); + continue; + } + auto acl_swindex = vpp_idx_it->second; + int ret; + + if (is_bind) + ret = vpp_acl_interface_bind(hwif_name.c_str(), acl_swindex, is_input); + else + ret = vpp_acl_interface_unbind(hwif_name.c_str(), acl_swindex, is_input); + + if (ret != 0) { + auto sid = sai_serialize_object_id(tbl_oid); + SWSS_LOG_ERROR("VS Acl tbl %s (swindex %u) %s failed", sid.c_str(), acl_swindex, + is_bind ? "bind": "unbind"); + return SAI_STATUS_FAILURE; + } + SWSS_LOG_NOTICE("ACL table %s %s to port %s", sai_serialize_object_id(tbl_oid).c_str(), + is_bind ? "bind": "unbind", hwif_name.c_str()); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::aclBindUnbindPorts( + _In_ sai_object_id_t tbl_grp_oid, + _In_ sai_object_id_t tbl_oid, + _In_ bool is_bind) +{ + SWSS_LOG_ENTER(); + + auto it = m_acl_tbl_grp_ports_map.find(tbl_grp_oid); + + if (it == m_acl_tbl_grp_ports_map.end()) { + auto sid = sai_serialize_object_id(tbl_grp_oid); + SWSS_LOG_INFO("ACL tbl group with id %s not found in acl ports map", sid.c_str()); + return SAI_STATUS_SUCCESS; + } + sai_attribute_t attr; + + attr.id = SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE; + if (get(SAI_OBJECT_TYPE_ACL_TABLE_GROUP, tbl_grp_oid, 1, &attr) != SAI_STATUS_SUCCESS) { + auto sid = sai_serialize_object_id(tbl_grp_oid); + + SWSS_LOG_INFO("ACL table group %s direction not found", sid.c_str()); + return SAI_STATUS_SUCCESS; + } + + int dir = attr.value.s32; + bool is_input; + + switch (dir) { + case SAI_ACL_STAGE_INGRESS: + is_input = true; + break; + + case SAI_ACL_STAGE_EGRESS: + is_input = false; + break; + + default: + { + auto sid = sai_serialize_object_id(tbl_grp_oid); + SWSS_LOG_INFO("ACL table group %s direction %d", sid.c_str(), dir); + return SAI_STATUS_SUCCESS; + } + } + + auto vpp_idx_it = m_acl_swindex_map.find(tbl_oid); + if (vpp_idx_it == m_acl_swindex_map.end()) { + auto sid = sai_serialize_object_id(tbl_oid); + SWSS_LOG_INFO("VS swindex for ACL table oid %s not found", sid.c_str()); + return SAI_STATUS_FAILURE; + } + auto acl_swindex = vpp_idx_it->second; + + std::list& member_list = it->second; + std::string hwif_name; + int ret; + + for (auto port_oid: member_list) { + + if (!vpp_get_hwif_name(port_oid, 0, hwif_name)) { + SWSS_LOG_WARN("VS hwif name not found for port %s", sai_serialize_object_id(port_oid).c_str()); + continue; + } + + if (is_bind) { + ret = vpp_acl_interface_bind(hwif_name.c_str(), acl_swindex, is_input); + if (ret == 0) { + ret = tunterm_acl_bindunbind(tbl_oid, true, hwif_name); + if(ret == 0) { + m_acl_tbl_hw_ports_map[tbl_oid].push_back(hwif_name); + } + } + } else { + ret = vpp_acl_interface_unbind(hwif_name.c_str(), acl_swindex, is_input); + if (ret == 0) { + ret = tunterm_acl_bindunbind(tbl_oid, false, hwif_name); + if(ret == 0) { + m_acl_tbl_hw_ports_map[tbl_oid].remove(hwif_name); + } + } + } + if (ret != 0) { + auto sid = sai_serialize_object_id(tbl_oid); + SWSS_LOG_ERROR("VS Acl tbl %s (swindex %u) %s failed status %d", + sid.c_str(), acl_swindex, + is_bind ? "bind": "unbind", ret); + continue; + } + SWSS_LOG_NOTICE("ACL table %s %s port %s", sai_serialize_object_id(tbl_oid).c_str(), + is_bind ? "bound to": "unbound from", hwif_name.c_str()); + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::getAclEntryStats( + _In_ sai_object_id_t ace_cntr_oid, + _In_ uint32_t attr_count, + _Out_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_status_t status = SAI_STATUS_FAILURE; + uint32_t acl_index, ace_index; + + if (aclGetVppIndices(ace_cntr_oid, &acl_index, &ace_index) == SAI_STATUS_SUCCESS) { + vpp_ace_stats_t ace_stats; + + if (vpp_acl_ace_stats_query(acl_index, ace_index, &ace_stats) == 0) { + + for (uint32_t i = 0; i < attr_count; i++) { + if (attr_list[i].id == SAI_ACL_COUNTER_ATTR_PACKETS) { + attr_list[i].value.u64 = ace_stats.packets; + } else if (attr_list[i].id == SAI_ACL_COUNTER_ATTR_BYTES) { + attr_list[i].value.u64 = ace_stats.bytes; + } + } + status = SAI_STATUS_SUCCESS; + } + } + + return status; +} diff --git a/vslib/SwitchVppAcl.h b/vslib/SwitchVppAcl.h new file mode 100644 index 000000000..4edd0297a --- /dev/null +++ b/vslib/SwitchVppAcl.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_ACL_ATTRS 12 + +typedef struct _acl_tbl_entries_ { + uint32_t priority; + + sai_attribute_t attr_range; + sai_object_id_t range_objid_list[2]; + + sai_u32_range_t range_limit[2]; + sai_acl_range_type_t range_type[2]; + uint32_t range_count; + sai_attribute_t attrs[MAX_ACL_ATTRS]; + uint32_t attrs_count; +} acl_tbl_entries_t; + +typedef struct ordered_ace_list_ { + uint32_t index; + uint32_t priority; + sai_object_id_t ace_oid; + bool is_tunterm; +} ordered_ace_list_t; + +#ifdef __cplusplus +} +#endif + diff --git a/vslib/SwitchVppBfd.cpp b/vslib/SwitchVppBfd.cpp new file mode 100644 index 000000000..e2c5d953f --- /dev/null +++ b/vslib/SwitchVppBfd.cpp @@ -0,0 +1,370 @@ +#include "SwitchVpp.h" +#include "EventPayloadNotification.h" +#include "SwitchVppUtils.h" + +#include "meta/sai_serialize.h" +#include "meta/NotificationBfdSessionStateChange.h" + +#include "swss/logger.h" +#include "swss/select.h" + +#include "vppxlate/SaiVppXlate.h" + +#include + +using namespace saivs; + +sai_status_t SwitchVpp::bfd_session_add( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + + SWSS_LOG_ENTER(); + + CHECK_STATUS(vpp_bfd_session_add(serializedObjectId, switch_id, attr_count, attr_list)); + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_BFD_SESSION, serializedObjectId, switch_id, attr_count, attr_list)); + + return SAI_STATUS_SUCCESS; + +} + +sai_status_t SwitchVpp::vpp_bfd_session_add( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + int ret = 0; + SWSS_LOG_ENTER(); + + sai_object_id_t bfd_oid; + sai_deserialize_object_id(serializedObjectId, bfd_oid); + + /* Attribute#1 */ + auto attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_MIN_RX, attr_count, attr_list); + if (!attr) + { + SWSS_LOG_ERROR("attr SAI_BFD_SESSION_ATTR_MIN_RX was not passed"); + return SAI_STATUS_FAILURE; + } + + uint32_t required_min_rx = attr->value.u32; + + /* Attribute#2 */ + attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_MIN_TX, attr_count, attr_list); + if (!attr) + { + SWSS_LOG_ERROR("attr SAI_BFD_SESSION_ATTR_MIN_TX was not passed"); + return SAI_STATUS_FAILURE; + } + + uint32_t required_min_tx = attr->value.u32; + + /* Attribute#3 */ + attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_MULTIPLIER, attr_count, attr_list); + if (!attr) + { + SWSS_LOG_ERROR("attr SAI_BFD_SESSION_ATTR_MULTIPLIER was not passed"); + return SAI_STATUS_FAILURE; + } + + uint8_t detect_mult = attr->value.u8; + + /* Attribute#4 */ + attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS, attr_count, attr_list); + if (!attr) + { + SWSS_LOG_ERROR("attr SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS was not passed"); + return SAI_STATUS_FAILURE; + } + + /*local addr */ + sai_ip_address_t local_addr = attr->value.ipaddr; + vpp_ip_addr_t vpp_local_addr; + sai_ip_address_t_to_vpp_ip_addr_t(local_addr, vpp_local_addr); + + /* Attribute#5 */ + attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS, attr_count, attr_list); + if (!attr) + { + SWSS_LOG_ERROR("attr SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS was not passed"); + return SAI_STATUS_FAILURE; + } + + /* Peer Addr*/ + sai_ip_address_t peer_addr = attr->value.ipaddr; + vpp_ip_addr_t vpp_peer_addr; + sai_ip_address_t_to_vpp_ip_addr_t(peer_addr, vpp_peer_addr); + + /* Attribute#6 - multihop is optional */ + bool multihop = false; + attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_MULTIHOP, attr_count, attr_list); + if (attr) + { + multihop = attr->value.booldata; + } + + const char *hwif_name = NULL; + if (!multihop) { + /* Attribute#7 */ + attr = sai_metadata_get_attr_by_id(SAI_BFD_SESSION_ATTR_PORT, attr_count, attr_list); + if (!attr) + { + SWSS_LOG_ERROR("attr SAI_BFD_SESSION_ATTR_PORT was not passed"); + return SAI_STATUS_FAILURE; + } + + sai_object_id_t port_id = attr->value.oid; + sai_object_type_t obj_type = objectTypeQuery(port_id); + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + std::string ifname = ""; + if (vpp_get_hwif_name(port_id, 0, ifname) == true) + { + hwif_name = ifname.c_str(); + } + else + { + SWSS_LOG_ERROR("BFD session create request FAILED due to invalid hwif name"); + + return SAI_STATUS_FAILURE; + } + } + + if (multihop || hwif_name) { + SWSS_LOG_NOTICE("BFD session create request sent to VS, hwif: %s, multihop: %d, oid: %ld", hwif_name, multihop, bfd_oid); + /*vpp call to add bfd session*/ + ret = bfd_udp_add(multihop, hwif_name, &vpp_local_addr, &vpp_peer_addr, detect_mult, required_min_tx, required_min_rx); + if (ret >= 0) + { + BFD_MUTEX; + + // store bfd attributes to sai oid mapping + vpp_bfd_info_t bfd_info; + bfd_info.multihop = multihop; + memcpy(&bfd_info.local_addr, &local_addr, sizeof(local_addr)); + memcpy(&bfd_info.peer_addr, &peer_addr, sizeof(peer_addr)); + + m_bfd_info_map[bfd_info] = bfd_oid; + } + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::bfd_session_del( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + vpp_bfd_session_del(serializedObjectId); + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_BFD_SESSION, serializedObjectId)); + + return SAI_STATUS_SUCCESS; + +} +sai_status_t SwitchVpp::vpp_bfd_session_del( + _In_ const std::string &serializedObjectId) +{ + int ret = 0; + SWSS_LOG_ENTER(); + + sai_object_id_t bfd_oid; + sai_deserialize_object_id(serializedObjectId, bfd_oid); + sai_attribute_t attr; + + /* Attribute#1 */ + attr.id = SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS; + CHECK_STATUS(get(SAI_OBJECT_TYPE_BFD_SESSION, bfd_oid, 1, &attr)); + sai_ip_address_t local_addr = attr.value.ipaddr; + vpp_ip_addr_t vpp_local_addr; + /*local addr */ + sai_ip_address_t_to_vpp_ip_addr_t(local_addr, vpp_local_addr); + + /* Attribute#2 */ + attr.id = SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS; + CHECK_STATUS(get(SAI_OBJECT_TYPE_BFD_SESSION, bfd_oid, 1, &attr)); + sai_ip_address_t peer_addr = attr.value.ipaddr; + vpp_ip_addr_t vpp_peer_addr; + /* Peer Addr*/ + sai_ip_address_t_to_vpp_ip_addr_t(peer_addr, vpp_peer_addr); + + /* Attribute#3 - multihop is optional */ + bool multihop = false; + attr.id = SAI_BFD_SESSION_ATTR_MULTIHOP; + if(get(SAI_OBJECT_TYPE_BFD_SESSION, bfd_oid, 1, &attr) == SAI_STATUS_SUCCESS) + { + multihop = attr.value.booldata; + } + + const char *hwif_name = NULL; + if (!multihop) { + /* Attribute#4 */ + attr.id = SAI_BFD_SESSION_ATTR_PORT; + CHECK_STATUS(get(SAI_OBJECT_TYPE_BFD_SESSION, bfd_oid, 1, &attr)); + sai_object_id_t port_id = attr.value.oid; + sai_object_type_t obj_type = objectTypeQuery(port_id); + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + + std::string ifname = ""; + if (vpp_get_hwif_name(port_id, 0, ifname) == true) + { + hwif_name = ifname.c_str(); + } + else + { + SWSS_LOG_ERROR("BFD session delete request FAILED due to invalid hwif name"); + + return SAI_STATUS_FAILURE; + } + } + + if (multihop || hwif_name) { + SWSS_LOG_NOTICE("BFD session delete request sent to VS, hwif: %s, multihop: %d, oid: %ld", hwif_name, multihop, bfd_oid); + /*vpp call to add bfd session*/ + ret = bfd_udp_del(multihop, hwif_name, &vpp_local_addr, &vpp_peer_addr); + if (ret >= 0) + { + BFD_MUTEX; + + // remove bfd attributes to sai oid mapping + vpp_bfd_info_t bfd_info; + bfd_info.multihop = multihop; + memcpy(&bfd_info.local_addr, &local_addr, sizeof(local_addr)); + memcpy(&bfd_info.peer_addr, &peer_addr, sizeof(peer_addr)); + + auto it = m_bfd_info_map.find(bfd_info); + + // Check if the key exists and erase by iterator + if (it != m_bfd_info_map.end()) { + m_bfd_info_map.erase(it); + } + } + } + + return SAI_STATUS_SUCCESS; +} + +void SwitchVpp::update_bfd_session_state( + _In_ sai_object_id_t bfd_oid, + _In_ sai_bfd_session_state_t state) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = SAI_BFD_SESSION_ATTR_STATE; + attr.value.s32 = state; + + sai_status_t status = set(SAI_OBJECT_TYPE_BFD_SESSION, bfd_oid, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to update bfd state %s: %d", + sai_serialize_object_id(bfd_oid).c_str(), + state); + } +} + +void SwitchVpp::send_bfd_state_change_notification( + _In_ sai_object_id_t bfd_oid, + _In_ sai_bfd_session_state_t state, + _In_ bool force) +{ + SWSS_LOG_ENTER(); + + char state_buf[64] = {0}; + sai_bfd_session_state_notification_t data; + + data.bfd_session_id = bfd_oid; + data.session_state = state; + + sai_serialize_bfd_session_state(state_buf, state); + + auto meta = getMeta(); + + if (meta) + { + meta->meta_sai_on_bfd_session_state_change(1, &data); + } + + auto objectType = objectTypeQuery(bfd_oid); + + if (objectType != SAI_OBJECT_TYPE_BFD_SESSION) + { + SWSS_LOG_ERROR("object type %s not supported for %s", + sai_serialize_object_type(objectType).c_str(), + sai_serialize_object_id(bfd_oid).c_str()); + return; + } + + sai_attribute_t attr; + + attr.id = SAI_BFD_SESSION_ATTR_STATE; + + if (get(objectType, bfd_oid, 1, &attr) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get bfd session attribute SAI_BFD_SESSION_ATTR_STATE"); + } + else + { + if (force) + { + SWSS_LOG_NOTICE("explicitly send SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY for bfd session %s: %s", + sai_serialize_object_id(data.bfd_session_id).c_str(), + state_buf); + + } + else if ((sai_bfd_session_state_t)attr.value.s32 == data.session_state) + { + SWSS_LOG_INFO("bfd session state didn't change, will not send notification"); + return; + } + } + + attr.id = SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY; + + if (get(SAI_OBJECT_TYPE_SWITCH, m_switch_id, 1, &attr) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY for switch %s", + sai_serialize_object_id(m_switch_id).c_str()); + return; + } + + if (attr.value.ptr == NULL) + { + SWSS_LOG_INFO("SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY callback is NULL"); + return; + } + + sai_switch_notifications_t sn = { }; + + sn.on_bfd_session_state_change = (sai_bfd_session_state_change_notification_fn)attr.value.ptr; + + update_bfd_session_state(bfd_oid, data.session_state); + + SWSS_LOG_NOTICE("send event SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY for bfd session %s: %s", + sai_serialize_object_id(bfd_oid).c_str(), + state_buf); + + auto str = sai_serialize_bfd_session_state_ntf(1, &data); + + auto ntf = std::make_shared(str); + + auto payload = std::make_shared(ntf, sn); + + m_switchConfig->m_eventQueue->enqueue(std::make_shared(EVENT_TYPE_NOTIFICATION, payload)); +} diff --git a/vslib/SwitchVppFdb.cpp b/vslib/SwitchVppFdb.cpp new file mode 100644 index 000000000..cf3eb917f --- /dev/null +++ b/vslib/SwitchVppFdb.cpp @@ -0,0 +1,857 @@ +#include "SwitchVpp.h" + +#include "meta/sai_serialize.h" + +#include "swss/logger.h" + +#include "vppxlate/SaiVppXlate.h" + +using namespace saivs; + +/** + * @brief FDB_ENTRY FLUSH Modes. + */ +typedef enum _fdb_flush_mode_t +{ + FLUSH_BY_INTERFACE = 1, /* Flushing DYNAMIC FDB_ENTRY on Interface */ + FLUSH_BY_BD_ID = 2, /* Flushing DYNAMIC FDB_ENTRY on Bridge */ + FLUSH_ALL = 4, /* Flushing all DYNAMIC FDB_ENTRY on all */ +} fdb_flush_mode; + +sai_status_t SwitchVpp::createVlanMember( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto sid = sai_serialize_object_id(object_id); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_VLAN_MEMBER, sid, switch_id, attr_count, attr_list)); + + return vpp_create_vlan_member(attr_count, attr_list); +} + +sai_status_t SwitchVpp::vpp_create_vlan_member( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + sai_object_id_t br_port_id; + + SWSS_LOG_ENTER(); + + //find sw_if_index for given l2 interface + auto attr_type = sai_metadata_get_attr_by_id(SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID, attr_count, attr_list); + + if (attr_type == NULL) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + br_port_id = attr_type->value.oid; + sai_object_type_t obj_type = objectTypeQuery(br_port_id); + + if (obj_type != SAI_OBJECT_TYPE_BRIDGE_PORT) + { + SWSS_LOG_ERROR("SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(br_port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + + return SAI_STATUS_FAILURE; + } + + const char *hwifname = nullptr; + auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_id)); + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_PORT_ID); + auto bp_attr = br_port_attrs[meta->attridname]; + auto port_id = bp_attr->getAttr()->value.oid; + obj_type = objectTypeQuery(port_id); + + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_NOTICE("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + + std::string if_name; + bool found = getTapNameFromPortId(port_id, if_name); + + if (found == true) + { + hwifname = tap_to_hwif_name(if_name.c_str()); + } + else + { + SWSS_LOG_NOTICE("No ports found for bridge port id :%s",sai_serialize_object_id(br_port_id).c_str()); + return SAI_STATUS_FAILURE; + } + + auto attr_vlan_member = sai_metadata_get_attr_by_id(SAI_VLAN_MEMBER_ATTR_VLAN_ID, attr_count, attr_list); + + sai_object_id_t vlan_oid; + + if (attr_vlan_member == NULL) + { + SWSS_LOG_NOTICE("attr SAI_VLAN_MEMBER_ATTR_VLAN_ID was not passed"); + return SAI_STATUS_FAILURE; + } + else + { + vlan_oid = attr_vlan_member->value.oid; + } + + auto attr_vlanid_map = m_objectHash.at(SAI_OBJECT_TYPE_VLAN).at(sai_serialize_object_id(vlan_oid)); + auto md_vlan_id = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_VLAN, SAI_VLAN_ATTR_VLAN_ID); + auto vlan_id = attr_vlanid_map.at(md_vlan_id->attridname)->getAttr()->value.u16; + + if (vlan_id == 0) + { + SWSS_LOG_NOTICE("attr VLAN object id was not passed"); + return SAI_STATUS_FAILURE; + } + + uint32_t bridge_id = (uint32_t)vlan_id; + auto attr_tag_mode = sai_metadata_get_attr_by_id(SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE, attr_count, attr_list); + uint32_t tagging_mode = 0; + const char *hw_ifname; + char host_subifname[32]; + + if (attr_tag_mode == NULL) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_VLAN_ID was not passed"); + return SAI_STATUS_FAILURE; + } + + tagging_mode = attr_tag_mode->value.u32; + + if (tagging_mode == SAI_VLAN_TAGGING_MODE_TAGGED) + { + /* + * create vpp subinterface and set it as bridge port + */ + snprintf(host_subifname, sizeof(host_subifname), "%s.%u", hwifname, vlan_id); + + /* The host(tap) subinterface is also created as part of the vpp subinterface creation */ + create_sub_interface(hwifname, vlan_id, vlan_id); + + /* Get new list of physical interfaces from VS */ + refresh_interfaces_list(); + + hw_ifname = host_subifname; + + //Create bridge and set the l2 port + set_sw_interface_l2_bridge(hw_ifname,bridge_id, true, VPP_API_PORT_TYPE_NORMAL); + + //Set interface state up + interface_set_state(hw_ifname, true); + } + else if (tagging_mode == SAI_VLAN_TAGGING_MODE_UNTAGGED) + { + hw_ifname = hwifname; + + //Create bridge and set the l2 port + set_sw_interface_l2_bridge(hw_ifname,bridge_id, true, VPP_API_PORT_TYPE_NORMAL); + + //Set the vlan member to bridge and tags rewrite + vpp_l2_vtr_op_t vtr_op = L2_VTR_PUSH_1; + vpp_vlan_type_t push_dot1q = VLAN_DOT1Q; + uint32_t tag1 = (uint32_t)vlan_id; + uint32_t tag2 = ~0; + set_l2_interface_vlan_tag_rewrite(hw_ifname, tag1, tag2, push_dot1q, vtr_op); + } + else + { + SWSS_LOG_ERROR("Tagging Mode %d not implemented", tagging_mode); + return SAI_STATUS_FAILURE; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::removeVlanMember( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + vpp_remove_vlan_member(objectId); + + auto sid = sai_serialize_object_id(objectId); + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_VLAN_MEMBER, sid)); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_remove_vlan_member( + _In_ sai_object_id_t vlan_member_oid) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = SAI_VLAN_MEMBER_ATTR_VLAN_ID; + + sai_status_t status = get(SAI_OBJECT_TYPE_VLAN_MEMBER, vlan_member_oid, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_VLAN_ID is not present"); + + return SAI_STATUS_FAILURE; + } + + sai_object_id_t vlan_oid = attr.value.oid; + + sai_object_type_t obj_type = objectTypeQuery(vlan_oid); + + if (obj_type != SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_VLAN_ID is not valid"); + return SAI_STATUS_FAILURE; + } + + attr.id = SAI_VLAN_ATTR_VLAN_ID; + status = get(SAI_OBJECT_TYPE_VLAN, vlan_oid, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_VLAN_ATTR_VLAN_ID is not present"); + + return SAI_STATUS_FAILURE; + } + + auto vlan_id = attr.value.u16; + uint32_t bridge_id = (uint32_t)vlan_id; + + attr.id = SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID; + status = get(SAI_OBJECT_TYPE_VLAN_MEMBER, vlan_member_oid, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID is not present"); + return SAI_STATUS_FAILURE; + } + + sai_object_id_t br_port_oid = attr.value.oid; + + obj_type = objectTypeQuery(br_port_oid); + + if (obj_type != SAI_OBJECT_TYPE_BRIDGE_PORT) + { + SWSS_LOG_ERROR("SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID=%s expected to be BRIDGE PORT but is: %s", + sai_serialize_object_id(br_port_oid).c_str(), + sai_serialize_object_type(obj_type).c_str()); + + return SAI_STATUS_FAILURE; + } + + const char *hw_ifname = nullptr; + auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_oid)); + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_PORT_ID); + auto bp_attr = br_port_attrs[meta->attridname]; + auto port_id = bp_attr->getAttr()->value.oid; + obj_type = objectTypeQuery(port_id); + + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_NOTICE("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + + std::string if_name; + bool found = getTapNameFromPortId(port_id, if_name); + + if (found == true) + { + hw_ifname = tap_to_hwif_name(if_name.c_str()); + } + else + { + SWSS_LOG_NOTICE("No ports found for bridge port id :%s",sai_serialize_object_id(br_port_oid).c_str()); + return SAI_STATUS_FAILURE; + } + + attr.id = SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE; + status = get(SAI_OBJECT_TYPE_VLAN_MEMBER, vlan_member_oid, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE is not present"); + + return SAI_STATUS_FAILURE; + } + + uint32_t tagging_mode = attr.value.s32; + char host_subifname[32]; + + if (tagging_mode == SAI_VLAN_TAGGING_MODE_UNTAGGED) + { + + //First disable tag-rewrite. + vpp_l2_vtr_op_t vtr_op =L2_VTR_DISABLED; + vpp_vlan_type_t push_dot1q = VLAN_DOT1Q; + uint32_t tag1 = (uint32_t)vlan_id; + uint32_t tag2 = ~0; + set_l2_interface_vlan_tag_rewrite(hw_ifname, tag1, tag2, push_dot1q, vtr_op); + + //Remove interface from bridge, interface type should be changed to others types like l3. + set_sw_interface_l2_bridge(hw_ifname, bridge_id, false, VPP_API_PORT_TYPE_NORMAL); + } + else if (tagging_mode == SAI_VLAN_TAGGING_MODE_TAGGED) + { + // set interface l2 tag-rewrite GigabitEthernet0/8/0.200 disable + snprintf(host_subifname, sizeof(host_subifname), "%s.%u", hw_ifname, vlan_id); + hw_ifname = host_subifname; + // Remove the l2 port from bridge + set_sw_interface_l2_bridge(hw_ifname, bridge_id, false, VPP_API_PORT_TYPE_NORMAL); + + // delete subinterface + delete_sub_interface(hw_ifname, vlan_id); + + // Get new list of physical interfaces from VS + refresh_interfaces_list(); + } + else + { + SWSS_LOG_ERROR("Tagging mode %d not implemented", tagging_mode); + return SAI_STATUS_FAILURE; + } + + //Check if the bridge has zero ports left, if so remove the bridge as well + uint32_t member_count = 0; + bridge_domain_get_member_count (bridge_id, &member_count); + + if (member_count == 0) + { + vpp_bridge_domain_add_del(bridge_id, false); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_create_bvi_interface( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto attr_vlan_oid = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_VLAN_ID, attr_count, attr_list); + + if (attr_vlan_oid == NULL) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_VLAN_ID was not passed"); + return SAI_STATUS_SUCCESS; + } + + sai_object_id_t vlan_oid = attr_vlan_oid->value.oid; + + sai_object_type_t obj_type = objectTypeQuery(vlan_oid); + + if (obj_type != SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_ERROR(" VLAN object type was not passed"); + return SAI_STATUS_SUCCESS; + } + + auto vlan_attrs = m_objectHash.at(SAI_OBJECT_TYPE_VLAN).at(sai_serialize_object_id(vlan_oid)); + auto md_vlan_id = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_VLAN, SAI_VLAN_ATTR_VLAN_ID); + auto vlan_id = (uint32_t) vlan_attrs.at(md_vlan_id->attridname)->getAttr()->value.u16; + + if (vlan_id == 0) + { + SWSS_LOG_NOTICE("attr VLAN object id was not passed"); + return SAI_STATUS_FAILURE; + } + + auto attr_mac_addr = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS, attr_count, attr_list); + + if (attr_mac_addr == NULL) + { + SWSS_LOG_NOTICE("attr ROUTER INTERFACE MAC Address is not found"); + return SAI_STATUS_FAILURE; + } + + sai_mac_t mac_addr; + memcpy(mac_addr, attr_mac_addr->value.mac, sizeof(sai_mac_t)); + + //Create BVI interface + create_bvi_interface(mac_addr,vlan_id); + + // Get new list of physical interfaces from VS + refresh_interfaces_list(); + + char hw_bviifname[32]; + const char *hw_ifname; + snprintf(hw_bviifname, sizeof(hw_bviifname), "bvi%u",vlan_id); + hw_ifname = hw_bviifname; + + //Create bridge and set the l2 port as BVI + set_sw_interface_l2_bridge(hw_ifname,vlan_id, true, VPP_API_PORT_TYPE_BVI); + + //Set interface state up + interface_set_state(hw_ifname, true); + + //Set the bvi as access or untagged port of the bridge + vpp_l2_vtr_op_t vtr_op = L2_VTR_PUSH_1; + vpp_vlan_type_t push_dot1q = VLAN_DOT1Q; + uint32_t tag1 = (uint32_t)vlan_id; + uint32_t tag2 = ~0; + set_l2_interface_vlan_tag_rewrite(hw_ifname, tag1, tag2, push_dot1q, vtr_op); + + //Set the arp termination for bridge + uint32_t bd_id = (uint32_t) vlan_id; + set_bridge_domain_flags(bd_id, VPP_BD_FLAG_ARP_TERM,true); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_delete_bvi_interface( + _In_ sai_object_id_t bvi_obj_id) +{ + sai_attribute_t attr; + + SWSS_LOG_ENTER(); + + attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + sai_status_t status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, bvi_obj_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE is not present"); + return SAI_STATUS_FAILURE; + } + + if (attr.value.s32 != SAI_ROUTER_INTERFACE_TYPE_VLAN) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE is not VLAN"); + return SAI_STATUS_FAILURE; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_VLAN_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, bvi_obj_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_VLAN_ID is not present"); + return SAI_STATUS_FAILURE; + } + + sai_object_id_t vlan_oid = attr.value.oid; + sai_object_type_t obj_type = objectTypeQuery(vlan_oid); + + if (obj_type != SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_ERROR("attr SAI_VLAN_MEMBER_ATTR_VLAN_ID is not valid"); + return SAI_STATUS_FAILURE; + } + + attr.id = SAI_VLAN_ATTR_VLAN_ID; + status = get(SAI_OBJECT_TYPE_VLAN, vlan_oid, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_VLAN_ATTR_VLAN_ID is not present"); + return SAI_STATUS_FAILURE; + } + + auto vlan_id = attr.value.u16; + char hw_bviifname[32]; + const char *hw_ifname; + snprintf(hw_bviifname, sizeof(hw_bviifname), "bvi%u",vlan_id); + hw_ifname = hw_bviifname; + + //Disable arp termination for bridge + uint32_t bd_id = (uint32_t) vlan_id; + set_bridge_domain_flags(bd_id, VPP_BD_FLAG_ARP_TERM, false); + + //First disable tag-rewrite. + vpp_l2_vtr_op_t vtr_op = L2_VTR_DISABLED; + vpp_vlan_type_t push_dot1q = VLAN_DOT1Q; + uint32_t tag1 = (uint32_t)vlan_id; + uint32_t tag2 = ~0; + set_l2_interface_vlan_tag_rewrite(hw_ifname, tag1, tag2, push_dot1q, vtr_op); + + //Remove interface from bridge, interface type should be changed to others types like l3. + set_sw_interface_l2_bridge(hw_ifname, bd_id, false, VPP_API_PORT_TYPE_BVI); + + //Remove the bvi interface + delete_bvi_interface(hw_ifname); + + // refresh interfaces from VS + refresh_interfaces_list(); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::FdbEntryadd( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_FDB_ENTRY, serializedObjectId, switch_id, attr_count, attr_list)); + + vpp_fdbentry_add(serializedObjectId, switch_id, attr_count, attr_list); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::FdbEntrydel( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + vpp_fdbentry_del(serializedObjectId); + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_FDB_ENTRY, serializedObjectId)); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_fdbentry_add( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_fdb_entry_t fdb_entry; + sai_deserialize_fdb_entry(serializedObjectId, fdb_entry); + + /* Attribute#1 */ + auto attr_type = sai_metadata_get_attr_by_id(SAI_FDB_ENTRY_ATTR_TYPE, attr_count, attr_list); + + if (attr_type == NULL) + { + SWSS_LOG_ERROR("attr SAI_FDB_ENTRY_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + + bool is_static = (attr_type->value.s32 == SAI_FDB_ENTRY_TYPE_STATIC ? true : false); + bool is_add = true; /* Adding the entry in FDB*/ + + /* Attribute#2 */ + sai_object_id_t br_port_id; + sai_object_id_t port_id; + + attr_type = sai_metadata_get_attr_by_id(SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID, attr_count, attr_list); + + if (attr_type == NULL) + { + SWSS_LOG_ERROR("attr SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + br_port_id = attr_type->value.oid; + sai_object_type_t obj_type = objectTypeQuery(br_port_id); + + if (obj_type != SAI_OBJECT_TYPE_BRIDGE_PORT) + { + SWSS_LOG_ERROR("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(br_port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + + return SAI_STATUS_FAILURE; + } + + auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_id)); + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_PORT_ID); + auto bp_attr = br_port_attrs[meta->attridname]; + port_id = bp_attr->getAttr()->value.oid; + obj_type = objectTypeQuery(port_id); + + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_NOTICE("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + + /* Need to extract the VLAN ID attached based on the Port_ID */ + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_PORT_VLAN_ID; + + sai_status_t get_status = get(SAI_OBJECT_TYPE_PORT, port_id, 1, &attr); + + if (get_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get port vlan id from port %s", + sai_serialize_object_id(port_id).c_str()); + return SAI_STATUS_FAILURE; + } + + uint32_t bd_id = attr.value.u16; /* bd_id is same as VLAN ID for .1Q bridge */ + + std::string ifname; + + if (vpp_get_hwif_name(port_id, 0, ifname) == true) + { + const char *hwif_name = ifname.c_str(); + auto ret = l2fib_add_del(hwif_name, fdb_entry.mac_address, bd_id, is_add, is_static); + + SWSS_LOG_NOTICE("FDB Entry Added on hwif_name %s Successful ret_val: %d", hwif_name, ret); + } + else + { + SWSS_LOG_ERROR("FDB_ENTRY failed because of INVALID PORT_ID"); + + return SAI_STATUS_FAILURE; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_fdbentry_del( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_fdb_entry_t fdb_entry; + sai_deserialize_fdb_entry(serializedObjectId, fdb_entry); + + sai_object_id_t br_port_id; + sai_object_id_t port_id; + bool is_static = false; + + sai_attribute_t attr_list[2]; + /* Attribute#1 */ + attr_list[0].id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + /* Attribute#2 */ + attr_list[1].id = SAI_FDB_ENTRY_ATTR_TYPE; + + if (get(SAI_OBJECT_TYPE_FDB_ENTRY, serializedObjectId, 1, &attr_list[0]) == SAI_STATUS_SUCCESS) + { + if (SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID == attr_list[0].id) + { + br_port_id = attr_list[0].value.oid; + } + else + { + SWSS_LOG_ERROR("DELETE FDB_ENTRY failed because of INVALID ATTR BRIDGE_PORT_ID"); + return SAI_STATUS_FAILURE; + } + + if (get(SAI_OBJECT_TYPE_FDB_ENTRY, serializedObjectId, 1, &attr_list[1]) == SAI_STATUS_SUCCESS) + { + if (SAI_FDB_ENTRY_ATTR_TYPE == attr_list[1].id ) + { + is_static = (attr_list[1].value.s32 == SAI_FDB_ENTRY_TYPE_STATIC ? true : false); + } + else + { + SWSS_LOG_ERROR("DELETE FDB_ENTRY failed because of INVALID ATTR ENTRY TYPE"); + return SAI_STATUS_FAILURE; + } + } + } + else + { + SWSS_LOG_ERROR(" Invaid Attribute IDs passed for DELETE FDB_ENTRY"); + return SAI_STATUS_FAILURE; + } + + bool is_add = false; /* Deleting the entry in FDB*/ + + sai_object_type_t obj_type = objectTypeQuery(br_port_id); + + if (obj_type != SAI_OBJECT_TYPE_BRIDGE_PORT) + { + SWSS_LOG_ERROR("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(br_port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + + return SAI_STATUS_FAILURE; + } + + auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_id)); + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_PORT_ID); + auto bp_attr = br_port_attrs[meta->attridname]; + port_id = bp_attr->getAttr()->value.oid; + obj_type = objectTypeQuery(port_id); + + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + + /* Need the VLAN ID attached based on the Port_ID */ + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_PORT_VLAN_ID; + + sai_status_t get_status = get(SAI_OBJECT_TYPE_PORT, port_id, 1, &attr); + + if (get_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get port vlan id from port %s", + sai_serialize_object_id(port_id).c_str()); + return SAI_STATUS_FAILURE; + } + + uint32_t bd_id = attr.value.u16; /* bd_id is same as VLAN ID for .1Q bridge */ + + std::string ifname; + + if (vpp_get_hwif_name(port_id, 0, ifname) == true) + { + const char *hwif_name = ifname.c_str(); + auto ret = l2fib_add_del(hwif_name, fdb_entry.mac_address, bd_id, is_add, is_static); + SWSS_LOG_NOTICE(" Delete FDB_ENTRY on hwif_name %s Successful ret_val: %d", hwif_name, ret); + } + else + { + SWSS_LOG_ERROR("FDB entry Delete: Invalid ObjectID for the hwif on this bridge"); + + return SAI_STATUS_FAILURE; + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_fdbentry_flush( + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attribute; + sai_object_id_t br_port_id = 0; + sai_object_id_t port_id; + uint32_t bd_id = 0; + uint8_t mode = 0; + bool is_static_entry = false; + + for (uint32_t i = 0; i < attr_count; i++) + { + attribute = attr_list[i]; + switch (attribute.id) + { + case SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID: + { + mode |= FLUSH_BY_INTERFACE; + br_port_id = attribute.value.oid; + sai_object_type_t obj_type = objectTypeQuery(br_port_id); + + if (obj_type != SAI_OBJECT_TYPE_BRIDGE_PORT) + { + SWSS_LOG_ERROR("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(br_port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + + return SAI_STATUS_FAILURE; + } + } + break; + + case SAI_FDB_FLUSH_ATTR_BV_ID: + { + mode |= FLUSH_BY_BD_ID; + bd_id = attribute.value.u16; + } + break; + + case SAI_FDB_FLUSH_ATTR_ENTRY_TYPE: + { + mode |= FLUSH_ALL; + is_static_entry = attribute.value.s32; + if ( is_static_entry == SAI_FDB_FLUSH_ENTRY_TYPE_STATIC) + { + SWSS_LOG_ERROR(" Cannot Flush STATIC FDB_ENTRY OBJECTS"); + return SAI_STATUS_FAILURE; + } + } + break; + + default: + + SWSS_LOG_ERROR(" Invalid Attributes for fdb entry flush OBJECT"); + return SAI_STATUS_FAILURE; + break; + } + } + + /* + Here three cases are handled, the FDB_ENTRY's are flushed based on the Attributes set, + 1. If Interface and Type(DYNAMIC is expected here), FLUSH by Interface. + 2. If Bridge_ID(VLAN_ID for .1q) and Type(DYNAMIC is expected here), FLUSH by Bridge ID. + 3. If only Type (DYNAMIC) is set then SONiC FLUSH ALL the dynamic entries. + */ + SWSS_LOG_NOTICE("VS_FDB_FLUSH mode is : %d [1,5: Interface, 2,6: Bridge, 3,4,7: Flush ALL, 0: INVALID]", mode); + + switch (mode) + { + case FLUSH_BY_INTERFACE: + case FLUSH_BY_INTERFACE | FLUSH_ALL:/*flush by interface*/ + { + auto br_port_attrs = m_objectHash.at(SAI_OBJECT_TYPE_BRIDGE_PORT).at(sai_serialize_object_id(br_port_id)); + auto meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_BRIDGE_PORT, SAI_BRIDGE_PORT_ATTR_PORT_ID); + auto bp_attr = br_port_attrs[meta->attridname]; + port_id = bp_attr->getAttr()->value.oid; + sai_object_type_t obj_type = objectTypeQuery(port_id); + + if (obj_type != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_BRIDGE_PORT_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(port_id).c_str(), + sai_serialize_object_type(obj_type).c_str()); + return SAI_STATUS_FAILURE; + } + + std::string ifname = ""; + + if (vpp_get_hwif_name(port_id, 0, ifname) == true) + { + const char *hwif_name = ifname.c_str(); + auto ret = l2fib_flush_int(hwif_name); + SWSS_LOG_NOTICE(" Flush by interface on hwif_name %s Successful ret_val: %d", hwif_name, ret); + } + else + { + SWSS_LOG_ERROR("Flush Interface FDB: Invalid ObjectID for the hwif on this bridge"); + + return SAI_STATUS_FAILURE; + } + } + break; + + case FLUSH_BY_BD_ID: + case FLUSH_BY_BD_ID | FLUSH_ALL: /*flush by bd_id/vlan id*/ + { + auto ret = l2fib_flush_bd(bd_id); + SWSS_LOG_NOTICE(" Flush on bd_id %d Successfull ret_val: %d",bd_id, ret); + } + break; + + case FLUSH_BY_INTERFACE | FLUSH_BY_BD_ID: + case FLUSH_ALL: + case FLUSH_BY_INTERFACE| FLUSH_BY_BD_ID| FLUSH_ALL: /*flush all*/ + { + auto ret = l2fib_flush_all(); + SWSS_LOG_NOTICE(" Flush ALL fdb entry ret_val: %d", ret); + } + break; + + default: + SWSS_LOG_ERROR(" Unable to find attrs for FDB_FLUSH %d", mode); + return SAI_STATUS_FAILURE; + break; + + } + + return SAI_STATUS_SUCCESS; +} diff --git a/vslib/SwitchVppHostif.cpp b/vslib/SwitchVppHostif.cpp new file mode 100644 index 000000000..8b7b97428 --- /dev/null +++ b/vslib/SwitchVppHostif.cpp @@ -0,0 +1,562 @@ +#include "SwitchVpp.h" +#include "HostInterfaceInfo.h" +#include "EventPayloadNotification.h" + +#include "meta/sai_serialize.h" +#include "meta/NotificationPortStateChange.h" + +#include "swss/logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "vppxlate/SaiVppXlate.h" + +using namespace saivs; + +// XXX set must also be supported when we change operational status up/down and +// probably also generate notification then + +#define ETH_FRAME_BUFFER_SIZE (0x4000) + +#define MAX_INTERFACE_NAME_LEN (IFNAMSIZ-1) + +#define SAI_VS_VETH_PREFIX "v" + +int SwitchVpp::vs_create_tap_device( + _In_ const char *dev, + _In_ int flags) +{ + SWSS_LOG_ENTER(); + + const char *tundev = "/dev/net/tun"; + + int fd = open(tundev, O_RDWR); + + if (fd < 0) + { + SWSS_LOG_ERROR("failed to open %s", tundev); + + return -1; + } + + return fd; +} + +int SwitchVpp::vs_set_dev_mac_address( + _In_ const char *dev, + _In_ const sai_mac_t& mac) +{ + SWSS_LOG_ENTER(); + + int s = socket(AF_INET, SOCK_DGRAM, 0); + + if (s < 0) + { + SWSS_LOG_ERROR("failed to create socket, errno: %d", errno); + + return -1; + } + + struct ifreq ifr; + + strncpy(ifr.ifr_name, dev, MAX_INTERFACE_NAME_LEN); + + memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); + + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + int err = ioctl(s, SIOCSIFHWADDR, &ifr); + + if (err < 0) + { + SWSS_LOG_ERROR("ioctl SIOCSIFHWADDR on socket %d %s failed, err %d", s, dev, err); + } + + close(s); + + return err; +} + +int SwitchVpp::promisc( + _In_ const char *dev) +{ + SWSS_LOG_ENTER(); + + return 0; +} + +bool SwitchVpp::hostif_create_tap_veth_forwarding( + _In_ const std::string &tapname, + _In_ int tapfd, + _In_ sai_object_id_t port_id) +{ + SWSS_LOG_ENTER(); + + // we assume here that veth devices were added by user before creating this + // host interface, vEthernetX will be used for packet transfer between ip + // namespaces or ethernet device name used in lane map if provided + + std::string vethname = vs_get_veth_name(tapname, port_id); + + int packet_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + + if (packet_socket < 0) + { + SWSS_LOG_ERROR("failed to open packet socket, errno: %d", errno); + + return false; + } + + int val = 1; + if (setsockopt(packet_socket, SOL_PACKET, PACKET_AUXDATA, &val, sizeof(val)) < 0) + { + SWSS_LOG_ERROR("setsockopt() set PACKET_AUXDATA failed: %s", strerror(errno)); + return false; + } + + // bind to device + + struct sockaddr_ll sock_address; + + memset(&sock_address, 0, sizeof(sock_address)); + + sock_address.sll_family = PF_PACKET; + sock_address.sll_protocol = htons(ETH_P_ALL); + sock_address.sll_ifindex = if_nametoindex(vethname.c_str()); + + if (sock_address.sll_ifindex == 0) + { + SWSS_LOG_ERROR("failed to get interface index for %s", vethname.c_str()); + + close(packet_socket); + + return false; + } + + SWSS_LOG_NOTICE("interface index = %d, %s\n", sock_address.sll_ifindex, vethname.c_str()); + + if (promisc(vethname.c_str())) + { + SWSS_LOG_ERROR("promisc failed on %s", vethname.c_str()); + + close(packet_socket); + + return false; + } + + if (bind(packet_socket, (struct sockaddr*) &sock_address, sizeof(sock_address)) < 0) + { + SWSS_LOG_ERROR("bind failed on %s", vethname.c_str()); + + close(packet_socket); + + return false; + } + + m_hostif_info_map[tapname] = + std::make_shared( + sock_address.sll_ifindex, + packet_socket, + tapfd, + tapname, + port_id, + m_switchConfig->m_eventQueue); + + // NOTE: threads are not run + + SWSS_LOG_NOTICE("setup forward rule for %s succeeded", tapname.c_str()); + + return true; +} + +sai_status_t SwitchVpp::vs_create_hostif_tap_interface( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + // validate SAI_HOSTIF_ATTR_TYPE + + auto attr_type = sai_metadata_get_attr_by_id(SAI_HOSTIF_ATTR_TYPE, attr_count, attr_list); + + if (attr_type == NULL) + { + SWSS_LOG_ERROR("attr SAI_HOSTIF_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + + /* The genetlink host interface is created to associate trap group to genetlink family and multicast group + * created by driver. It does not create any netdev interface. Hence skipping tap interface creation + */ + if (attr_type->value.s32 == SAI_HOSTIF_TYPE_GENETLINK) + { + SWSS_LOG_DEBUG("Skipping tap create for hostif type genetlink"); + + return SAI_STATUS_SUCCESS; + } + + if (attr_type->value.s32 != SAI_HOSTIF_TYPE_NETDEV) + { + SWSS_LOG_ERROR("only SAI_HOSTIF_TYPE_NETDEV is supported"); + + return SAI_STATUS_FAILURE; + } + + // validate SAI_HOSTIF_ATTR_OBJ_ID + + auto attr_obj_id = sai_metadata_get_attr_by_id(SAI_HOSTIF_ATTR_OBJ_ID, attr_count, attr_list); + + if (attr_obj_id == NULL) + { + SWSS_LOG_ERROR("attr SAI_HOSTIF_ATTR_OBJ_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + sai_object_id_t obj_id = attr_obj_id->value.oid; + + sai_object_type_t ot = objectTypeQuery(obj_id); + + if (ot == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping tap creation for hostif with object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + if (ot != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_HOSTIF_ATTR_OBJ_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(obj_id).c_str(), + sai_serialize_object_type(ot).c_str()); + + return SAI_STATUS_FAILURE; + } + + // validate SAI_HOSTIF_ATTR_NAME + + auto attr_name = sai_metadata_get_attr_by_id(SAI_HOSTIF_ATTR_NAME, attr_count, attr_list); + + if (attr_name == NULL) + { + SWSS_LOG_ERROR("attr SAI_HOSTIF_ATTR_NAME was not passed"); + + return SAI_STATUS_FAILURE; + } + + if (strnlen(attr_name->value.chardata, sizeof(attr_name->value.chardata)) >= MAX_INTERFACE_NAME_LEN) + { + SWSS_LOG_ERROR("interface name is too long: %.*s", MAX_INTERFACE_NAME_LEN, attr_name->value.chardata); + + return SAI_STATUS_FAILURE; + } + + std::string name = std::string(attr_name->value.chardata); + + // create TAP device + + SWSS_LOG_INFO("creating hostif %s", name.c_str()); + + int tapfd; + + tapfd = vs_create_tap_device(name.c_str(), IFF_TAP | IFF_MULTI_QUEUE | IFF_NO_PI | IFF_VNET_HDR); + + if (tapfd < 0) + { + SWSS_LOG_ERROR("failed to create TAP device for %s", name.c_str()); + + return SAI_STATUS_FAILURE; + } + + SWSS_LOG_INFO("created TAP device for %s, fd: %d", name.c_str(), tapfd); + + const char *dev = name.c_str(); + const char *hwif_name = tap_to_hwif_name(dev); + + configure_lcp_interface(hwif_name, dev, true); + + { + bool link_up = false; + + interface_get_state(hwif_name, &link_up); + + auto state = link_up ? SAI_PORT_OPER_STATUS_UP : SAI_PORT_OPER_STATUS_DOWN; + + send_port_oper_status_notification(obj_id, state, true); + + SWSS_LOG_NOTICE("VPP interface %s(%s) oper state %s", hwif_name, dev, + (link_up ? "UP" : "DOWN")); + } + + sai_attribute_t attr; + + memset(&attr, 0, sizeof(attr)); + + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + + sai_status_t status = get(SAI_OBJECT_TYPE_SWITCH, m_switch_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get SAI_SWITCH_ATTR_SRC_MAC_ADDRESS on switch %s: %s", + sai_serialize_object_id(m_switch_id).c_str(), + sai_serialize_status(status).c_str()); + } + + int err = vs_set_dev_mac_address(name.c_str(), attr.value.mac); + + if (err < 0) + { + SWSS_LOG_ERROR("failed to set MAC address %s for %s", + sai_serialize_mac(attr.value.mac).c_str(), + name.c_str()); + + close(tapfd); + + return SAI_STATUS_FAILURE; + } + + err = sw_interface_set_mac(hwif_name, attr.value.mac); + + if (err < 0) + { + SWSS_LOG_ERROR("failed to set MAC address %s for %s", + sai_serialize_mac(attr.value.mac).c_str(), + hwif_name); + + close(tapfd); + + return SAI_STATUS_FAILURE; + } + + SWSS_LOG_INFO("Successfully set mac to %s for %s", sai_serialize_mac(attr.value.mac).c_str(), name.c_str()); + + setIfNameToPortId(name, obj_id); + setPortIdToTapName(obj_id, name); + + SWSS_LOG_INFO("created tap interface %s", name.c_str()); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vs_remove_hostif_tap_interface( + _In_ sai_object_id_t hostif_id) +{ + SWSS_LOG_ENTER(); + + // get tap interface name + + sai_attribute_t attr; + + + attr.id = SAI_HOSTIF_ATTR_TYPE; + sai_status_t status = get(SAI_OBJECT_TYPE_HOSTIF, hostif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get attr type for hostif %s", + sai_serialize_object_id(hostif_id).c_str()); + return status; + } + + + /* The genetlink host interface is created to associate trap group to genetlink family and multicast group + * created by driver. It does not create any netdev interface. Hence skipping tap interface deletion + */ + if (attr.value.s32 == SAI_HOSTIF_TYPE_GENETLINK) + { + SWSS_LOG_DEBUG("Skipping tap delete for hostif type genetlink"); + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_HOSTIF_ATTR_OBJ_ID; + status = get(SAI_OBJECT_TYPE_HOSTIF, hostif_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get object ID for hostif %s", sai_serialize_object_id(hostif_id).c_str()); + return status; + } + if (objectTypeQuery(attr.value.oid) == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping tap deletion for hostif with object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_HOSTIF_ATTR_NAME; + + status = get(SAI_OBJECT_TYPE_HOSTIF, hostif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("failed to get attr name for hostif %s", + sai_serialize_object_id(hostif_id).c_str()); + + return status; + } + + if (strnlen(attr.value.chardata, sizeof(attr.value.chardata)) >= MAX_INTERFACE_NAME_LEN) + { + SWSS_LOG_ERROR("interface name is too long: %.*s", MAX_INTERFACE_NAME_LEN, attr.value.chardata); + + return SAI_STATUS_FAILURE; + } + + // TODO this should be hosif_id or if index ? + std::string name = std::string(attr.value.chardata); + + /* + auto it = m_hostif_info_map.find(name); + + if (it == m_hostif_info_map.end()) + { + SWSS_LOG_ERROR("failed to find host info entry for tap device: %s", name.c_str()); + + return SAI_STATUS_FAILURE; + } + + SWSS_LOG_NOTICE("attempting to remove tap device: %s", name.c_str()); + + auto info = it->second; // destructor will stop threads + */ + // remove host info entry from map + + // m_hostif_info_map.erase(it); + + // remove interface mapping + + // std::string vname = vpp_get_veth_name(name, info->m_portId); + + sai_object_id_t port_id = getPortIdFromIfName(name); + + removeIfNameToPortId(name); + removePortIdToTapName(port_id); + + SWSS_LOG_NOTICE("successfully removed hostif tap device: %s", name.c_str()); + + return SAI_STATUS_SUCCESS; +} + +bool SwitchVpp::hasIfIndex( + _In_ int ifindex) const +{ + SWSS_LOG_ENTER(); + + if (m_hostif_info_map.size() == 0) + { + return false; + } + + for (auto& kvp: m_hostif_info_map) + { + if (kvp.second->m_ifindex == ifindex) + { + return true; + } + } + + return false; +} + +// VPP + +// TODO to config +static const char *sonic_vpp_ifmap = "/usr/share/sonic/hwsku/sonic_vpp_ifmap.ini"; + +void SwitchVpp::populate_if_mapping() +{ + SWSS_LOG_ENTER(); + + if (mapping_init) + { + return; + } + + FILE *fp; + char sonic_name[64], vpp_name[64]; + + fp = fopen(sonic_vpp_ifmap, "r"); + + if (!fp) + { + return; + } + + while (fscanf(fp, "%s %s", sonic_name, vpp_name) != EOF) + { + std::string tap_name, hwif_name; + + tap_name = std::string(sonic_name); + hwif_name = std::string(vpp_name); + + m_hostif_hwif_map[tap_name] = hwif_name; + m_hwif_hostif_map[hwif_name] = tap_name; + } + + mapping_init = 1; + + fclose(fp); +} + +const char* SwitchVpp::tap_to_hwif_name( + _In_ const char *name) +{ + SWSS_LOG_ENTER(); + + populate_if_mapping(); + + std::string tap_name = std::string(name); + + auto it = m_hostif_hwif_map.find(tap_name); + + if (it == m_hostif_hwif_map.end()) + { + SWSS_LOG_ERROR("failed to find hwif info entry for hostif device: %s", tap_name.c_str()); + + return "Unknown"; + } + + SWSS_LOG_DEBUG("Found hwif %s info entry for hostif device: %s", it->second.c_str(), tap_name.c_str()); + + return it->second.c_str(); +} + +const char* SwitchVpp::hwif_to_tap_name( + _In_ const char *name) +{ + SWSS_LOG_ENTER(); + + populate_if_mapping(); + + std::string tap_name = std::string(name); + + auto it = m_hwif_hostif_map.find(tap_name); + + if (it == m_hwif_hostif_map.end()) + { + SWSS_LOG_ERROR("failed to find hostif info entry for hwif device: %s", tap_name.c_str()); + + return "Unknown"; + } + + SWSS_LOG_DEBUG("Found hostif %s info entry for hwif device: %s", it->second.c_str(), tap_name.c_str()); + + return it->second.c_str(); +} diff --git a/vslib/SwitchVppNbr.cpp b/vslib/SwitchVppNbr.cpp new file mode 100644 index 000000000..e7aa04e81 --- /dev/null +++ b/vslib/SwitchVppNbr.cpp @@ -0,0 +1,191 @@ +#include "SwitchVpp.h" + +#include "meta/sai_serialize.h" +#include "meta/NotificationPortStateChange.h" + +#include "swss/logger.h" +#include "swss/exec.h" +#include "swss/converter.h" + +#include +#include +#include +#include +#include +#include + +#include "vppxlate/SaiVppXlate.h" + +using namespace saivs; + + +sai_status_t SwitchVpp::addRemoveIpNbr( + _In_ const std::string &serializedObjectId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _In_ bool is_add) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + sai_neighbor_entry_t nbr_entry; + + sai_deserialize_neighbor_entry(serializedObjectId, nbr_entry); + + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + + CHECK_STATUS(get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, nbr_entry.rif_id, 1, &attr)); + + if (objectTypeQuery(attr.value.oid) != SAI_OBJECT_TYPE_PORT) + { + return SAI_STATUS_SUCCESS; + } + auto port_oid = attr.value.oid; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + + CHECK_STATUS(get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, nbr_entry.rif_id, 1, &attr)); + if (attr.value.s32 != SAI_ROUTER_INTERFACE_TYPE_SUB_PORT && + attr.value.s32 != SAI_ROUTER_INTERFACE_TYPE_PORT) + { + SWSS_LOG_NOTICE("Skipping neighbor add for attr type %d", attr.value.s32); + + return SAI_STATUS_SUCCESS; + } + + uint16_t vlan_id = 0; + if (attr.value.s32 == SAI_ROUTER_INTERFACE_TYPE_SUB_PORT) + { + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + + CHECK_STATUS(get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, nbr_entry.rif_id, 1, &attr)); + vlan_id = attr.value.u16; + } + + sai_mac_t nbr_mac; + bool no_mac = true; + + if (is_add) + { + for (uint32_t i = 0; i < attr_count; i++) + { + switch (attr_list[i].id) + { + case SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS: + memcpy(nbr_mac, attr_list[i].value.mac, sizeof(sai_mac_t)); + no_mac = false; + break; + + default: + break; + } + } + } else { + attr.id = SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS; + + if (get(SAI_OBJECT_TYPE_NEIGHBOR_ENTRY, serializedObjectId, 1, &attr) == SAI_STATUS_SUCCESS) { + memcpy(nbr_mac, attr.value.mac, sizeof(sai_mac_t)); + no_mac = false; + } + } + + if (no_mac == true) + { + SWSS_LOG_ERROR("No mac address passed for neighbor %s", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } + + std::string if_name; + bool found = getTapNameFromPortId(port_oid, if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } + + const char *hwif_name = tap_to_hwif_name(if_name.c_str()); + const char *vpp_ifname; + char subifname[32]; + + if (vlan_id) + { + snprintf(subifname, sizeof(subifname), "%s.%u", hwif_name, vlan_id); + + vpp_ifname = subifname; + } else { + vpp_ifname = hwif_name; + } + init_vpp_client(); + + switch (nbr_entry.ip_address.addr_family) { + case SAI_IP_ADDR_FAMILY_IPV4: + struct sockaddr_in sin; + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = nbr_entry.ip_address.addr.ip4; + + ip4_nbr_add_del(vpp_ifname, ~0, &sin, false, false, nbr_mac, is_add); + + break; + + case SAI_IP_ADDR_FAMILY_IPV6: + struct sockaddr_in6 sin6; + + sin6.sin6_family = AF_INET6; + memcpy(sin6.sin6_addr.s6_addr, nbr_entry.ip_address.addr.ip6, sizeof(sin6.sin6_addr.s6_addr)); + + ip6_nbr_add_del(vpp_ifname, ~0, &sin6, false, false, nbr_mac, is_add); + + break; + } + + return SAI_STATUS_SUCCESS; +} + +bool SwitchVpp::is_ip_nbr_active() +{ + if (nbr_env_read == false) + { + const char *val; + + val = getenv("NO_LINUX_NL"); + if (val && (*val == 'y' || *val == 'Y')) { + nbr_active = true; + } + nbr_env_read = true; + } + return nbr_active; +} + +sai_status_t SwitchVpp::addIpNbr( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + if (is_ip_nbr_active() == true) { + SWSS_LOG_NOTICE("Add neighbor in VS %s", serializedObjectId.c_str()); + addRemoveIpNbr(serializedObjectId, attr_count, attr_list, true); + } + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_NEIGHBOR_ENTRY, serializedObjectId, switch_id, attr_count, attr_list)); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::removeIpNbr( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + if (is_ip_nbr_active() == true) { + SWSS_LOG_NOTICE("Remove neighbor in VS %s", serializedObjectId.c_str()); + addRemoveIpNbr(serializedObjectId, 0, NULL, false); + } + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_NEIGHBOR_ENTRY, serializedObjectId)); + + return SAI_STATUS_SUCCESS; +} diff --git a/vslib/SwitchVppNexthop.cpp b/vslib/SwitchVppNexthop.cpp new file mode 100644 index 000000000..760a6986e --- /dev/null +++ b/vslib/SwitchVppNexthop.cpp @@ -0,0 +1,336 @@ +#include "SwitchVpp.h" +#include "SwitchVppNexthop.h" +#include "SwitchVppUtils.h" + +#include "meta/sai_serialize.h" +#include "meta/NotificationPortStateChange.h" + +#include "swss/logger.h" +#include "swss/exec.h" +#include "swss/converter.h" + +#include +#include +#include +#include +#include +#include + +#include "vppxlate/SaiVppXlate.h" + +#include + +using namespace saivs; + +#define CHECK_STATUS_QUIET(status) { \ + sai_status_t _status = (status); \ + if (_status != SAI_STATUS_SUCCESS) { return _status; } } + +sai_status_t SwitchVpp::IpRouteNexthopGroupEntry( + _In_ sai_object_id_t next_hop_grp_oid, + _Out_ nexthop_grp_config_t **nxthop_group) +{ + sai_attribute_t attr; + int32_t group_type; + auto nhg_soid = sai_serialize_object_id(next_hop_grp_oid); + + attr.id = SAI_NEXT_HOP_GROUP_ATTR_TYPE; + auto nhg_obj = get_sai_object(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, nhg_soid); + if (!nhg_obj) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP_GROUP SaiObject: %s", nhg_soid.c_str()); + return SAI_STATUS_FAILURE; + } + + CHECK_STATUS_QUIET(nhg_obj->get_mandatory_attr(attr)); + if (attr.value.s32 != SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP && + attr.value.s32 != SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_ORDERED_ECMP) { + SWSS_LOG_ERROR("Unsupported type (%d) in nexthop group %s", attr.value.s32, nhg_soid.c_str()); + return SAI_STATUS_NOT_IMPLEMENTED; + } + + group_type = attr.value.s32; + auto member_map = nhg_obj->get_child_objs(SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER); + if (member_map == nullptr || member_map->size() == 0) { + SWSS_LOG_INFO("Empty nexthop_group. OID %s", + nhg_soid.c_str()); + return SAI_STATUS_FAILURE; + } + + uint32_t next_hop_sequence = 0; + std::map nh_member_map; + for (auto pair : *member_map) { + auto member_obj = pair.second; + sai_object_id_t next_hop_oid; + uint32_t next_hop_weight = 1; + nexthop_grp_member_t mbr; + + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; + CHECK_STATUS_QUIET(member_obj->get_mandatory_attr(attr)); + next_hop_oid = attr.value.oid; + + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_WEIGHT; + member_obj->get_attr(attr); + if (member_obj->get_attr(attr) == SAI_STATUS_SUCCESS) + { + next_hop_weight = attr.value.u32; + } + + if (group_type == SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_ORDERED_ECMP) { + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_SEQUENCE_ID; + CHECK_STATUS_QUIET(member_obj->get_mandatory_attr(attr)); + next_hop_sequence = attr.value.u32; + } + else { + // sequence_id will not be set if it is not ordered_ecmp. Then just use the order of the member. + next_hop_sequence++; + } + + if(fillNHGrpMember(&mbr, next_hop_oid, next_hop_weight, next_hop_sequence) != SAI_STATUS_SUCCESS) { + return SAI_STATUS_FAILURE; + } + nh_member_map[next_hop_sequence] = mbr; + } + nexthop_grp_config_t *nxthop_grp_cfg; + + size_t grp_size = sizeof(nexthop_grp_config_t) + (nh_member_map.size() * sizeof(nexthop_grp_member_t)); + + nxthop_grp_cfg = (nexthop_grp_config_t *) calloc(1, grp_size); + if (nxthop_grp_cfg == NULL) { + SWSS_LOG_ERROR("Failed to allocate memory for nxthop_grp_cfg. member size %zu", nh_member_map.size()); + return SAI_STATUS_FAILURE; + } + nexthop_grp_member_t *nxt_grp_member; + nxt_grp_member = nxthop_grp_cfg->grp_members; + for (auto pair : nh_member_map) { + *nxt_grp_member = pair.second; + nxt_grp_member++; + } + nxthop_grp_cfg->grp_type = group_type; + nxthop_grp_cfg->nmembers = (uint32_t) nh_member_map.size(); + + *nxthop_group = nxthop_grp_cfg; + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::IpRouteNexthopEntry( + _In_ sai_object_id_t next_hop_oid, + _Out_ nexthop_grp_config_t **nxthop_group_cfg) +{ + sai_status_t status; + nexthop_grp_config_t *nxthop_group; + + nxthop_group = (nexthop_grp_config_t *) + calloc(1, sizeof(nexthop_grp_config_t) + (1 * sizeof(nexthop_grp_member_t))); + if (!nxthop_group) { + SWSS_LOG_ERROR("Failed to allocate memory for nxthop_grp_cfg. member size 1"); + return SAI_STATUS_FAILURE; + } + nxthop_group->nmembers = 1; + + nexthop_grp_member_t *nxt_grp_member = nxthop_group->grp_members; + + status = fillNHGrpMember(nxt_grp_member, next_hop_oid, 1, 0); + + if (status != SAI_STATUS_SUCCESS) { + free(nxthop_group); + return status; + } + + *nxthop_group_cfg = nxthop_group; + return SAI_STATUS_SUCCESS; +} + +// This function is responsible for filling the nexthop group member structure +// with the necessary information such as the next hop IP address, weight, sequence, +// and router interface or tunnel interface information. +// It takes the next hop object ID, next hop weight, and next hop sequence as input +// and retrieves the required attributes from the next hop object. +// The function returns SAI_STATUS_SUCCESS if the member is filled successfully, +// otherwise it returns an appropriate error status. +sai_status_t +SwitchVpp::fillNHGrpMember(nexthop_grp_member_t *nxt_grp_member, sai_object_id_t next_hop_oid, uint32_t next_hop_weight, uint32_t next_hop_sequence) +{ + sai_attribute_t attr; + auto nh_soid = sai_serialize_object_id(next_hop_oid); + + if (SAI_OBJECT_TYPE_NEXT_HOP != sai_object_type_query(next_hop_oid)) { + SWSS_LOG_ERROR("Not a SAI_OBJECT_TYPE_NEXT_HOP: %s", nh_soid.c_str()); + return SAI_STATUS_FAILURE; + } + + auto nh_obj = get_sai_object(SAI_OBJECT_TYPE_NEXT_HOP, nh_soid); + if (!nh_obj) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP SaiObject: %s", nh_soid.c_str()); + return SAI_STATUS_FAILURE; + } + + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + CHECK_STATUS_QUIET(nh_obj->get_mandatory_attr(attr)); + int32_t next_hop_type = attr.value.s32; + if (next_hop_type != SAI_NEXT_HOP_TYPE_IP && next_hop_type != SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP) { + return SAI_STATUS_NOT_IMPLEMENTED; + } + + attr.id = SAI_NEXT_HOP_ATTR_IP; + sai_ip_address_t ip_address; + CHECK_STATUS_QUIET(nh_obj->get_mandatory_attr(attr)); + ip_address = attr.value.ipaddr; + + nxt_grp_member->addr = ip_address; + nxt_grp_member->weight = next_hop_weight; + nxt_grp_member->seq_id = next_hop_sequence; + nxt_grp_member->sw_if_index = ~0; + + switch (next_hop_type) { + case SAI_NEXT_HOP_TYPE_IP: + attr.id = SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID; + if (get(SAI_OBJECT_TYPE_NEXT_HOP, next_hop_oid, 1, &attr) == SAI_STATUS_SUCCESS) { + nxt_grp_member->rif_oid = attr.value.oid; + } + break; + case SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP: { + u_int32_t sw_if_index; + if (m_tunnel_mgr.get_tunnel_if(next_hop_oid, sw_if_index) == SAI_STATUS_SUCCESS) { + nxt_grp_member->sw_if_index = sw_if_index; + SWSS_LOG_INFO("Got tunnel interface %d for nexthop %s", sw_if_index, + sai_serialize_object_id(next_hop_oid).c_str()); + } else { + SWSS_LOG_ERROR("Failed to get tunnel interface name for nexthop %s", + sai_serialize_object_id(next_hop_oid).c_str()); + return SAI_STATUS_FAILURE; + } + break; + } + default: + break; + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t +SwitchVpp::createNexthop( + _In_ const std::string& serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + const sai_attribute_value_t *next_hop_type; + uint32_t attr_index; + SWSS_LOG_ENTER(); + CHECK_STATUS(find_attrib_in_list(attr_count, attr_list, SAI_NEXT_HOP_ATTR_TYPE, + &next_hop_type, &attr_index)); + if (next_hop_type->s32 == SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP) { + //Deligate the creation of tunnel encap nexthop to tunnel manager + CHECK_STATUS(m_tunnel_mgr.create_tunnel_encap_nexthop(serializedObjectId, switch_id, attr_count, attr_list)); + } + return create_internal(SAI_OBJECT_TYPE_NEXT_HOP, serializedObjectId, switch_id, attr_count, attr_list); +} + +sai_status_t SwitchVpp::removeNexthop( + _In_ const std::string &serializedObjectId) +{ + sai_attribute_t attr; + sai_status_t status; + SWSS_LOG_ENTER(); + auto nh_obj = get_sai_object(SAI_OBJECT_TYPE_NEXT_HOP, serializedObjectId); + + if (!nh_obj) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP SaiObject: %s", serializedObjectId.c_str()); + } else { + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + status = nh_obj->get_attr(attr); + if(status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Missing SAI_NEXT_HOP_ATTR_TYPE in %s", serializedObjectId.c_str()); + } + else if (attr.value.s32 == SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP) { + CHECK_STATUS(m_tunnel_mgr.remove_tunnel_encap_nexthop(serializedObjectId)); + } + } + + return remove_internal(SAI_OBJECT_TYPE_NEXT_HOP, serializedObjectId); +} + +sai_status_t +SwitchVpp::createNexthopGroupMember( + _In_ const std::string& serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + sai_status_t status; + sai_attribute_t attr; + SWSS_LOG_ENTER(); + + SaiCachedObject nhg_mbr_obj(this, SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER, serializedObjectId, attr_count, attr_list); + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; + CHECK_STATUS_QUIET(nhg_mbr_obj.get_mandatory_attr(attr)); + SWSS_LOG_INFO("Creating NHG member %s in nhg %s", serializedObjectId.c_str(), sai_serialize_object_id(attr.value.oid).c_str()); + auto nhg_obj = nhg_mbr_obj.get_linked_object(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID); + if (nhg_obj == nullptr) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP_GROUP from %s", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } + + //call create_internal to update the mapping from NHG to NHG_MBRs, which is used to update the routes + status = create_internal(SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER, serializedObjectId, switch_id, attr_count, attr_list); + if (status != SAI_STATUS_SUCCESS) { + return status; + } + + auto routes = nhg_obj->get_child_objs(SAI_OBJECT_TYPE_ROUTE_ENTRY); + if (routes == nullptr) { + return SAI_STATUS_SUCCESS; + } + + for (auto route : *routes) { + SWSS_LOG_INFO("NHG member changed. Updating route %s", route.first.c_str()); + IpRouteAddRemove(route.second.get(), false); + IpRouteAddRemove(route.second.get(), true); + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t +SwitchVpp::removeNexthopGroupMember( + _In_ const std::string &serializedObjectId) +{ + sai_status_t status; + sai_attribute_t attr; + SWSS_LOG_ENTER(); + + auto nhg_mbr_obj = get_sai_object(SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER, serializedObjectId); + if (!nhg_mbr_obj) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER SaiObject: %s", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } + attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; + CHECK_STATUS_QUIET(nhg_mbr_obj->get_mandatory_attr(attr)); + SWSS_LOG_INFO("Deleting NHG member %s from nhg %s", serializedObjectId.c_str(), sai_serialize_object_id(attr.value.oid).c_str()); + + auto nhg_obj = nhg_mbr_obj->get_linked_object(SAI_OBJECT_TYPE_NEXT_HOP_GROUP, SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID); + if (nhg_obj == nullptr) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP_GROUP from %s", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } + + auto routes = nhg_obj->get_child_objs(SAI_OBJECT_TYPE_ROUTE_ENTRY); + + //call remove_internal to update the mapping from NHG to NHG_MBRs + status = remove_internal(SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER, serializedObjectId); + if (status != SAI_STATUS_SUCCESS) { + return status; + } + + if (routes == nullptr) { + return SAI_STATUS_SUCCESS; + } + + for (auto route : *routes) { + SWSS_LOG_INFO("NHG member changed. Updating route %s", route.first.c_str()); + IpRouteAddRemove(route.second.get(), false); + IpRouteAddRemove(route.second.get(), true); + } + return SAI_STATUS_SUCCESS; +} diff --git a/vslib/SwitchVppNexthop.h b/vslib/SwitchVppNexthop.h new file mode 100644 index 000000000..aa6cbc230 --- /dev/null +++ b/vslib/SwitchVppNexthop.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nexthop_grp_member_ { + sai_ip_address_t addr; + sai_object_id_t rif_oid; + uint32_t weight; + uint32_t seq_id; + uint32_t sw_if_index; +} nexthop_grp_member_t; + +typedef struct nexthop_grp_config_ { + int32_t grp_type; + uint32_t nmembers; + + /* Must be the last variable */ + nexthop_grp_member_t grp_members[0]; +} nexthop_grp_config_t; + +#ifdef __cplusplus +} +#endif + diff --git a/vslib/SwitchVppRif.cpp b/vslib/SwitchVppRif.cpp new file mode 100644 index 000000000..2834dfae0 --- /dev/null +++ b/vslib/SwitchVppRif.cpp @@ -0,0 +1,1883 @@ +#include "SwitchVpp.h" +#include "SwitchVppUtils.h" + +#include "meta/sai_serialize.h" +#include "meta/NotificationPortStateChange.h" + +#include "swss/logger.h" +#include "swss/exec.h" +#include "swss/converter.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "vppxlate/SaiVppXlate.h" + +#include +#include +#include +#include + +using namespace saivs; + +#define IP_CMD "/sbin/ip" + +int SwitchVpp::currentMaxInstance = 0; + +// TODO to cpp +IpVrfInfo::IpVrfInfo( + _In_ sai_object_id_t obj_id, + _In_ uint32_t vrf_id, + _In_ std::string &vrf_name, + _In_ bool is_ipv6): + m_obj_id(obj_id), + m_vrf_id(vrf_id), + m_vrf_name(vrf_name), + m_is_ipv6(is_ipv6) +{ + SWSS_LOG_ENTER(); +} + +IpVrfInfo::~IpVrfInfo() +{ +} + +bool vpp_get_intf_all_ip_prefixes ( + const std::string& linux_ifname, + std::vector& ip_prefixes) +{ + std::stringstream cmd; + std::string res; + + cmd << IP_CMD << " addr show dev " << linux_ifname << " scope global | awk '/inet/ {print $2}'"; + + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return false; + } + std::vector ipAddresses; + std::istringstream iss(res); + std::string line; + while (std::getline(iss, line)) { + swss::IpPrefix prefix(line); + ip_prefixes.push_back(prefix); + } + return true; +} + +bool vpp_get_intf_ip_address ( + const char *linux_ifname, + sai_ip_prefix_t& ip_prefix, + bool is_v6, + std::string& res) +{ + std::stringstream cmd; + + swss::IpPrefix prefix = getIpPrefixFromSaiPrefix(ip_prefix); + + if (is_v6) + { + cmd << IP_CMD << " -6 " << " addr show dev " << linux_ifname << " to " << prefix.to_string() << " scope global | awk '/inet6 / {print $2}'"; + } else { + cmd << IP_CMD << " addr show dev " << linux_ifname << " to " << prefix.to_string() << " scope global | awk '/inet / {print $2}'"; + } + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return false; + } + + if (res.length() != 0) + { + SWSS_LOG_NOTICE("%s address of %s is %s", (is_v6 ? "IPv6" : "IPv4"), linux_ifname, res.c_str()); + return true; + } else { + return false; + } +} + +bool vpp_get_intf_name_for_prefix ( + sai_ip_prefix_t& ip_prefix, + bool is_v6, + std::string& ifname) +{ + std::stringstream cmd; + + swss::IpPrefix prefix = getIpPrefixFromSaiPrefix(ip_prefix); + + if (is_v6) + { + cmd << IP_CMD << " -6 " << " addr show " << " to " << prefix.to_string(); + cmd << " scope global | awk -F':' '/[0-9]+: [a-zA-Z]+/ { printf \"%s\", $2 }' | cut -d' ' -f2 -z | sed 's/@[a-zA-Z].*//g'"; + } else { + cmd << IP_CMD << " addr show " << " to " << prefix.to_string(); + cmd << " scope global | awk -F':' '/[0-9]+: [a-zA-Z]+/ { printf \"%s\", $2 }' | cut -d' ' -f2 -z | sed 's/@[a-zA-Z].*//g'"; + } + int ret = swss::exec(cmd.str(), ifname); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return false; + } + + if (ifname.length() != 0) + { + SWSS_LOG_NOTICE("%s interface name with prefix %s is %s", (is_v6 ? "IPv6" : "IPv4"), prefix.to_string().c_str(), ifname.c_str()); + return true; + } else { + return false; + } +} + +// wrapper for vpp_get_intf_name_for_prefix +std::string get_intf_name_for_prefix ( + _In_ sai_route_entry_t& route_entry) +{ + bool is_v6 = false; + is_v6 = (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV6) ? true : false; + + std::string full_if_name = ""; + bool found = vpp_get_intf_name_for_prefix(route_entry.destination, is_v6, full_if_name); + if (found == false) + { + auto prefix_str = sai_serialize_ip_prefix(route_entry.destination); + SWSS_LOG_ERROR("host interface for prefix not found: %s", prefix_str.c_str()); + } + return full_if_name; + +} + +// Function to convert an IPv4 address from unsigned integer to string representation +std::string SwitchVpp::convertIPToString ( + _In_ const sai_ip_addr_t &ipAddress) +{ + char ipStr[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, &(ipAddress.ip4), ipStr, INET_ADDRSTRLEN) != nullptr) { + // IPv4 address + return std::string(ipStr); + } else if (inet_ntop(AF_INET6, &(ipAddress.ip6), ipStr, INET6_ADDRSTRLEN) != nullptr) { + // IPv6 address + return std::string(ipStr); + } + + // Unsupported address family or conversion failure + return ""; +} + +// Function to convert an IPv6 address from unsigned integer to string representation +std::string SwitchVpp::convertIPv6ToString ( + _In_ const sai_ip_addr_t &ipAddress, + _In_ int ipFamily) +{ + SWSS_LOG_ENTER(); + + if (ipFamily == AF_INET) { + // IPv4 address + char ipStr[INET_ADDRSTRLEN]; + struct sockaddr_in sa; + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, &(ipAddress.ip4), 4); + + if (inet_ntop(AF_INET, &(sa.sin_addr), ipStr, INET_ADDRSTRLEN) != nullptr) + { + return std::string(ipStr); + } + + } else { + // IPv6 address + char ipStr[INET6_ADDRSTRLEN]; + struct sockaddr_in6 sa6; + sa6.sin6_family = AF_INET6; + memcpy(&sa6.sin6_addr, &(ipAddress.ip6), 16); + + if (inet_ntop(AF_INET6, &(sa6.sin6_addr), ipStr, INET6_ADDRSTRLEN) != nullptr) + { + return std::string(ipStr); + } + } + + // Conversion failure + SWSS_LOG_ERROR("Failed to convert IPv6 address to string"); + return ""; +} + +std::string SwitchVpp::extractDestinationIP ( + const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_route_entry_t routeEntry; + sai_deserialize_route_entry(serializedObjectId, routeEntry); + + std::string destIPAddress = ""; + if (routeEntry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + destIPAddress = convertIPToString(routeEntry.destination.addr); + } else if (routeEntry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV6) + { + destIPAddress = convertIPv6ToString(routeEntry.destination.addr, routeEntry.destination.addr_family); + } else { + SWSS_LOG_ERROR("Could not determine IP address family! destIPStream:%s", destIPAddress.c_str()); + } + return destIPAddress; +} + +void create_route_prefix ( + sai_route_entry_t *route_entry, + vpp_ip_route_t *ip_route) +{ + const sai_ip_prefix_t *ip_address = &route_entry->destination; + + switch (ip_address->addr_family) { + case SAI_IP_ADDR_FAMILY_IPV4: + { + struct sockaddr_in *sin = &ip_route->prefix_addr.addr.ip4; + + ip_route->prefix_addr.sa_family = AF_INET; + sin->sin_addr.s_addr = ip_address->addr.ip4; + ip_route->prefix_len = getPrefixLenFromAddrMask(reinterpret_cast(&ip_address->mask.ip4), 4); + + break; + } + case SAI_IP_ADDR_FAMILY_IPV6: + { + struct sockaddr_in6 *sin6 = &ip_route->prefix_addr.addr.ip6; + + ip_route->prefix_addr.sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, ip_address->addr.ip6, sizeof(sin6->sin6_addr.s6_addr)); + ip_route->prefix_len = getPrefixLenFromAddrMask(ip_address->mask.ip6, 16); + + break; + } + } +} + +int configureLoopbackInterface ( + bool isAdd, + const std::string &hostIfname, + const std::string &destinationIp, + int prefixLen) +{ + SWSS_LOG_ENTER(); + std::stringstream cmd; + std::string res; + + // Prepare the command + std::string command = ""; + command += isAdd ? " address add " : " link delete dev "; + command += isAdd ? destinationIp + "/" + std::to_string(prefixLen) + " dev " + hostIfname : hostIfname; + cmd << IP_CMD << command; + + // Execute the command + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return -1; + } + + return 0; +} + +int SwitchVpp::getNextLoopbackInstance () +{ + SWSS_LOG_ENTER(); + + int nextInstance = 0; + + if (!availableInstances.empty()) { + nextInstance = *availableInstances.begin(); + availableInstances.erase(availableInstances.begin()); + } else { + nextInstance = currentMaxInstance; + ++currentMaxInstance; + } + + SWSS_LOG_DEBUG("Next Loopback Instance:%u", nextInstance); + + return nextInstance; +} + +void SwitchVpp::markLoopbackInstanceDeleted (int instance) +{ + availableInstances.insert(instance); +} + +bool SwitchVpp::vpp_intf_get_prefix_entry (const std::string &intf_name, std::string &ip_prefix) +{ + auto it = m_intf_prefix_map.find(intf_name); + + if (it == m_intf_prefix_map.end()) + { + SWSS_LOG_NOTICE("failed to ip prefix entry for hostif device: %s", intf_name.c_str()); + + return false; + } + SWSS_LOG_NOTICE("Found ip prefix %s for hostif device: %s", it->second.c_str(), intf_name.c_str()); + + ip_prefix = it->second; + + return true; +} + +void SwitchVpp::vpp_intf_remove_prefix_entry (const std::string& intf_name) +{ + + auto it = m_intf_prefix_map.find(intf_name); + + if (it == m_intf_prefix_map.end()) + { + SWSS_LOG_ERROR("failed to ip prefix entry for hostif device: %s", intf_name.c_str()); + + return; + } + SWSS_LOG_NOTICE("Removing ip prefix %s for hostif device: %s", it->second.c_str(), intf_name.c_str()); + + m_intf_prefix_map.erase(it); +} + +bool SwitchVpp::vpp_get_hwif_name ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _Out_ std::string& ifname) +{ + std::string if_name; + bool found = getTapNameFromPortId(object_id, if_name); + + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", sai_serialize_object_id(object_id).c_str()); + return false; + } + + const char *hwifname = tap_to_hwif_name(if_name.c_str()); + char hw_subifname[32]; + const char *hw_ifname; + + if (vlan_id) { + snprintf(hw_subifname, sizeof(hw_subifname), "%s.%u", hwifname, vlan_id); + hw_ifname = hw_subifname; + } else { + hw_ifname = hwifname; + } + ifname = std::string(hw_ifname); + + return true; +} + +void SwitchVpp::vppProcessEvents () +{ + const struct timespec req = {2, 0}; + vpp_event_info_t *evp; + int ret; + + while(m_run_vpp_events_thread) { + nanosleep(&req, NULL); + ret = vpp_sync_for_events(); + SWSS_LOG_NOTICE("Checking for any VS events status %d", ret); + while ((evp = vpp_ev_dequeue())) { + if (evp->type == VPP_INTF_LINK_STATUS) { + asyncIntfStateUpdate(evp->data.intf_status.hwif_name, + evp->data.intf_status.link_up); + SWSS_LOG_NOTICE("Received port link event for %s state %s", + evp->data.intf_status.hwif_name, + evp->data.intf_status.link_up ? "UP" : "DOWN"); + } else if (evp->type == VPP_BFD_STATE_CHANGE) { + SWSS_LOG_NOTICE("Received bfd state change event, multihop:%d, " + "sw_idx:%d, state %d", + evp->data.bfd_notif.multihop, + evp->data.bfd_notif.sw_if_index, + evp->data.bfd_notif.state); + asyncBfdStateUpdate(&evp->data.bfd_notif); + } + vpp_ev_free(evp); + } + } +} + +sai_status_t SwitchVpp::asyncBfdStateUpdate(vpp_bfd_state_notif_t *bfd_notif) +{ + sai_bfd_session_state_t sai_state; + vpp_bfd_info_t bfd_info; + memset(&bfd_info, 0, sizeof(bfd_info)); + + // Convert vpp state to sai state + switch(bfd_notif->state) { + case VPP_API_BFD_STATE_ADMIN_DOWN: + sai_state = SAI_BFD_SESSION_STATE_ADMIN_DOWN; + break; + case VPP_API_BFD_STATE_DOWN: + sai_state = SAI_BFD_SESSION_STATE_DOWN; + break; + case VPP_API_BFD_STATE_INIT: + sai_state = SAI_BFD_SESSION_STATE_INIT; + break; + case VPP_API_BFD_STATE_UP: + sai_state = SAI_BFD_SESSION_STATE_UP; + break; + default: + sai_state = SAI_BFD_SESSION_STATE_DOWN; + break; + } + + bfd_info.multihop = bfd_notif->multihop; + vpp_ip_addr_t_to_sai_ip_address_t(bfd_notif->local_addr, bfd_info.local_addr); + vpp_ip_addr_t_to_sai_ip_address_t(bfd_notif->peer_addr, bfd_info.peer_addr); + + BFD_MUTEX; + + // Find the BFD session + auto it = m_bfd_info_map.find(bfd_info); + + // Check if the key was found + if (it != m_bfd_info_map.end()) { + sai_object_id_t bfd_oid = it->second; + sai_object_type_t obj_type = objectTypeQuery(bfd_oid); + SWSS_LOG_NOTICE("Found existing bfd object %s, type %s", + sai_serialize_object_id(bfd_oid).c_str(), + sai_serialize_object_type(obj_type).c_str()); + send_bfd_state_change_notification(bfd_oid, sai_state, false); + } else { + SWSS_LOG_NOTICE("Existing bfd object not found"); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_dp_initialize() +{ + init_vpp_client(); + m_vpp_thread = std::make_shared(&SwitchVpp::vppProcessEvents, this); + + VppEventsThreadStarted = true; + + SWSS_LOG_NOTICE("VS DP initialized"); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::asyncIntfStateUpdate(const char *hwif_name, bool link_up) +{ + std::string tap_str; + const char *tap; + + tap = hwif_to_tap_name(hwif_name); + auto port_oid = getPortIdFromIfName(std::string(tap)); + + if (port_oid == SAI_NULL_OBJECT_ID) { + SWSS_LOG_NOTICE("Failed find port oid for tap interface %s", tap); + return SAI_STATUS_SUCCESS; + } + + auto state = link_up ? SAI_PORT_OPER_STATUS_UP : SAI_PORT_OPER_STATUS_DOWN; + + send_port_oper_status_notification(port_oid, state, false); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_set_interface_state ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _In_ bool is_up) +{ + if (is_ip_nbr_active() == false) { + return SAI_STATUS_SUCCESS; + } + + std::string ifname; + + if (vpp_get_hwif_name(object_id, vlan_id, ifname) == true) { + const char *hwif_name = ifname.c_str(); + + interface_set_state(hwif_name, is_up); + SWSS_LOG_NOTICE("Updating router interface admin state %s %s", hwif_name, + (is_up ? "UP" : "DOWN")); + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_set_port_mtu ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _In_ uint32_t mtu) +{ + if (is_ip_nbr_active() == false) { + return SAI_STATUS_SUCCESS; + } + + std::string ifname; + + if (vpp_get_hwif_name(object_id, vlan_id, ifname) == true) { + const char *hwif_name = ifname.c_str(); + + hw_interface_set_mtu(hwif_name, mtu); + SWSS_LOG_NOTICE("Updating router interface mtu %s to %u", hwif_name, + mtu); + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_set_interface_mtu ( + _In_ sai_object_id_t object_id, + _In_ uint32_t vlan_id, + _In_ uint32_t mtu, + int type) +{ + if (is_ip_nbr_active() == false) { + return SAI_STATUS_SUCCESS; + } + + std::string ifname; + + if (vpp_get_hwif_name(object_id, vlan_id, ifname) == true) { + const char *hwif_name = ifname.c_str(); + + sw_interface_set_mtu(hwif_name, mtu, type); + SWSS_LOG_NOTICE("Updating router interface mtu %s to %u", hwif_name, + mtu); + } + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::UpdatePort( + _In_ sai_object_id_t object_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto attr_type = sai_metadata_get_attr_by_id(SAI_PORT_ATTR_INGRESS_ACL, attr_count, attr_list); + + if (attr_type != NULL) + { + if (attr_type->value.oid == SAI_NULL_OBJECT_ID) { + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_INGRESS_ACL; + if (get(SAI_OBJECT_TYPE_PORT, object_id, 1, &attr) != SAI_STATUS_SUCCESS) { + aclBindUnbindPort(object_id, attr.value.oid, true, false); + } + } else { + aclBindUnbindPort(object_id, attr_type->value.oid, true, true); + } + } + + attr_type = sai_metadata_get_attr_by_id(SAI_PORT_ATTR_EGRESS_ACL, attr_count, attr_list); + + if (attr_type != NULL) + { + if (attr_type->value.oid == SAI_NULL_OBJECT_ID) { + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_EGRESS_ACL; + if (get(SAI_OBJECT_TYPE_PORT, object_id, 1, &attr) != SAI_STATUS_SUCCESS) { + aclBindUnbindPort(object_id, attr.value.oid, true, false); + } + } else { + aclBindUnbindPort(object_id, attr_type->value.oid, false, true); + } + } + + if (is_ip_nbr_active() == false) { + return SAI_STATUS_SUCCESS; + } + + attr_type = sai_metadata_get_attr_by_id(SAI_PORT_ATTR_ADMIN_STATE, attr_count, attr_list); + + if (attr_type != NULL) + { + vpp_set_interface_state(object_id, 0, attr_type->value.booldata); + } + + attr_type = sai_metadata_get_attr_by_id(SAI_PORT_ATTR_MTU, attr_count, attr_list); + + if (attr_type != NULL) + { + vpp_set_port_mtu(object_id, 0, attr_type->value.u32); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_add_del_intf_ip_addr ( + _In_ sai_ip_prefix_t& ip_prefix, + _In_ sai_object_id_t rif_id, + _In_ bool is_add) +{ + sai_attribute_t attr; + int32_t rif_type; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + sai_status_t status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + rif_type = attr.value.s32; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_PORT_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + sai_object_id_t obj_id = attr.value.oid; + + sai_object_type_t ot = objectTypeQuery(obj_id); + + if (ot == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + if (ot != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_ROUTER_INTERFACE_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(obj_id).c_str(), + sai_serialize_object_type(ot).c_str()); + + return SAI_STATUS_FAILURE; + } + + if (rif_type != SAI_ROUTER_INTERFACE_TYPE_SUB_PORT && + rif_type != SAI_ROUTER_INTERFACE_TYPE_PORT && + rif_type != SAI_ROUTER_INTERFACE_TYPE_LOOPBACK) + { + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + uint16_t vlan_id = 0; + if (status == SAI_STATUS_SUCCESS) + { + vlan_id = attr.value.u16; + } + + std::string if_name; + bool found = getTapNameFromPortId(obj_id, if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", sai_serialize_object_id(obj_id).c_str()); + return SAI_STATUS_FAILURE; + } + + swss::IpPrefix intf_ip_prefix; + char host_subifname[32]; + const char *linux_ifname; + bool is_v6 = false; + + is_v6 = (ip_prefix.addr_family == SAI_IP_ADDR_FAMILY_IPV6) ? true : false; + + if (vlan_id) { + snprintf(host_subifname, sizeof(host_subifname), "%s.%u", if_name.c_str(), vlan_id); + linux_ifname = host_subifname; + } else { + linux_ifname= if_name.c_str(); + } + std::string ip_prefix_key; + std::string addr_family = ((is_v6) ? "v6" : "v4"); + + ip_prefix_key = linux_ifname + addr_family + sai_serialize_ip_prefix(ip_prefix); + + if (is_add) + { + std::string ip_prefix_str; + + bool ret = vpp_get_intf_ip_address(linux_ifname, ip_prefix, is_v6, ip_prefix_str); + if (ret == false) + { + SWSS_LOG_DEBUG("No ip address to add on router interface %s", linux_ifname); + return SAI_STATUS_SUCCESS; + } + SWSS_LOG_NOTICE("Adding ip address on router interface %s", linux_ifname); + + intf_ip_prefix = swss::IpPrefix(ip_prefix_str.c_str()); + + sai_ip_prefix_t saiIpPrefix; + + copy(saiIpPrefix, intf_ip_prefix); + + m_intf_prefix_map[ip_prefix_key] = sai_serialize_ip_prefix(saiIpPrefix); + } else { + sai_ip_prefix_t saiIpPrefix; + + std::string ip_prefix_str; + + if (vpp_intf_get_prefix_entry(ip_prefix_key, ip_prefix_str) == false) + { + SWSS_LOG_DEBUG("No ip address to remove on router interface %s", linux_ifname); + return SAI_STATUS_SUCCESS; + } + SWSS_LOG_NOTICE("Removing ip address on router interface %s", linux_ifname); + + sai_deserialize_ip_prefix(ip_prefix_str, saiIpPrefix); + + intf_ip_prefix = getIpPrefixFromSaiPrefix(saiIpPrefix); + + vpp_intf_remove_prefix_entry(ip_prefix_key); + } + vpp_ip_route_t vpp_ip_prefix; + swss::IpAddress m_ip = intf_ip_prefix.getIp(); + + vpp_ip_prefix.prefix_len = intf_ip_prefix.getMaskLength(); + + switch (m_ip.getIp().family) + { + case AF_INET: + { + struct sockaddr_in *sin = &vpp_ip_prefix.prefix_addr.addr.ip4; + + vpp_ip_prefix.prefix_addr.sa_family = AF_INET; + sin->sin_addr.s_addr = m_ip.getV4Addr(); + break; + } + case AF_INET6: + { + const uint8_t *prefix = m_ip.getV6Addr(); + struct sockaddr_in6 *sin6 = &vpp_ip_prefix.prefix_addr.addr.ip6; + + vpp_ip_prefix.prefix_addr.sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, prefix, sizeof(sin6->sin6_addr.s6_addr)); + break; + } + default: + { + throw std::logic_error("Invalid family"); + } + } + + const char *hwifname = tap_to_hwif_name(if_name.c_str()); + char hw_subifname[32]; + const char *hw_ifname; + + if (vlan_id) { + snprintf(hw_subifname, sizeof(hw_subifname), "%s.%u", hwifname, vlan_id); + hw_ifname = hw_subifname; + } else { + hw_ifname = hwifname; + } + + int ret = interface_ip_address_add_del(hw_ifname, &vpp_ip_prefix, is_add); + + if (ret == 0) + { + return SAI_STATUS_SUCCESS; + } + else { + return SAI_STATUS_FAILURE; + } +} + +static void get_intf_vlanid (std::string& sub_ifname, int *vlan_id, std::string& if_name) +{ + std::size_t pos = sub_ifname.find("."); + + if (pos == std::string::npos) + { + if_name = sub_ifname; + *vlan_id = 0; + } else { + if_name = sub_ifname.substr(0, pos); + std::string vlan = sub_ifname.substr(pos+1); + *vlan_id = std::stoi(vlan); + } +} +static void get_vlan_intf_vlanid(std::string& if_name, std::string& vlan_prefix, int* vlan_id) +{ + // Check if the if_name starts with vlan_prefix + if (if_name.compare(0, vlan_prefix.length(), vlan_prefix) != 0) { + // If it doesn't start with vlan_prefix, set vlan_id to 0 + *vlan_id = 0; + return; + } + + // Find the position of the first digit in the string + size_t pos = if_name.find_first_of("0123456789"); + + // Check if a digit is found + if (pos == std::string::npos) { + // If no digit is found, set vlan_id to 0 + *vlan_id = 0; + return; + } + + // Extract the numeric part using substr + std::string numeric_part = if_name.substr(pos); + + // Convert the numeric part to an integer using stoi + *vlan_id = std::stoi(numeric_part); +} +static void vpp_serialize_intf_data (std::string& k1, std::string& k2, std::string &serializedData) +{ + serializedData.append(k1); + serializedData.append("@"); + serializedData.append(k2); +} + +static void vpp_deserialize_intf_data (std::string &serializedData, std::string& k1, std::string& k2) +{ + std::size_t pos = serializedData.find("@"); + + if (pos != std::string::npos) + { + k1 = serializedData.substr(0, pos); + k2 = serializedData.substr(pos+1); + } else { + SWSS_LOG_WARN("String %s does not contain delimiter @", serializedData.c_str()); + } +} + +sai_status_t SwitchVpp::vpp_add_del_intf_ip_addr_norif ( + _In_ const std::string& ip_prefix_key, + _In_ sai_route_entry_t& route_entry, + _In_ bool is_add) +{ + bool is_v6 = false; + + is_v6 = (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV6) ? true : false; + + std::string full_if_name; + std::string ip_prefix_str; + + if (is_add) + { + bool found = vpp_get_intf_name_for_prefix(route_entry.destination, is_v6, full_if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for prefix not found"); + return SAI_STATUS_FAILURE; + } + } else { + std::string intf_data; + + if (vpp_intf_get_prefix_entry(ip_prefix_key, intf_data) == false) + { + SWSS_LOG_DEBUG("No interface ip address found for %s", ip_prefix_key.c_str()); + return SAI_STATUS_SUCCESS; + } + vpp_deserialize_intf_data(intf_data, full_if_name, ip_prefix_str); + } + + const char *linux_ifname; + int vlan_id = 0; + std::string if_name; + + std::string vlan_prefix = "Vlan"; + if (full_if_name.compare(0, vlan_prefix.length(), vlan_prefix) == 0) + { + get_vlan_intf_vlanid(full_if_name, vlan_prefix, &vlan_id); + SWSS_LOG_NOTICE("It's Vlan interface. Vlan id: %d", vlan_id); + } else { + get_intf_vlanid(full_if_name, &vlan_id, if_name); + } + linux_ifname= full_if_name.c_str(); + + std::string addr_family = ((is_v6) ? "v6" : "v4"); + + swss::IpPrefix intf_ip_prefix; + + if (is_add) + { + bool ret = vpp_get_intf_ip_address(linux_ifname, route_entry.destination, is_v6, ip_prefix_str); + if (ret == false) + { + SWSS_LOG_DEBUG("No ip address to add on router interface %s", linux_ifname); + return SAI_STATUS_SUCCESS; + } + SWSS_LOG_NOTICE("Adding ip address on router interface %s", linux_ifname); + + intf_ip_prefix = swss::IpPrefix(ip_prefix_str.c_str()); + + sai_ip_prefix_t saiIpPrefix; + + copy(saiIpPrefix, intf_ip_prefix); + + std::string intf_data; + std::string sai_prefix; + + sai_prefix = sai_serialize_ip_prefix(saiIpPrefix); + + vpp_serialize_intf_data(full_if_name, sai_prefix, intf_data); + + m_intf_prefix_map[ip_prefix_key] = intf_data; + } else { + sai_ip_prefix_t saiIpPrefix; + + SWSS_LOG_NOTICE("Removing ip address on router interface %s", linux_ifname); + + sai_deserialize_ip_prefix(ip_prefix_str, saiIpPrefix); + + intf_ip_prefix = getIpPrefixFromSaiPrefix(saiIpPrefix); + + vpp_intf_remove_prefix_entry(ip_prefix_key); + } + + vpp_ip_route_t vpp_ip_prefix; + swss::IpAddress m_ip = intf_ip_prefix.getIp(); + + vpp_ip_prefix.prefix_len = intf_ip_prefix.getMaskLength(); + + switch (m_ip.getIp().family) + { + case AF_INET: + { + struct sockaddr_in *sin = &vpp_ip_prefix.prefix_addr.addr.ip4; + + vpp_ip_prefix.prefix_addr.sa_family = AF_INET; + sin->sin_addr.s_addr = m_ip.getV4Addr(); + break; + } + case AF_INET6: + { + const uint8_t *prefix = m_ip.getV6Addr(); + struct sockaddr_in6 *sin6 = &vpp_ip_prefix.prefix_addr.addr.ip6; + + vpp_ip_prefix.prefix_addr.sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, prefix, sizeof(sin6->sin6_addr.s6_addr)); + break; + } + default: + { + throw std::logic_error("Invalid family"); + } + } + + const char *hwifname; + char hw_subifname[32]; + char hw_bviifname[32]; + const char *hw_ifname; + if (full_if_name.compare(0, vlan_prefix.length(), vlan_prefix) == 0) + { + snprintf(hw_bviifname, sizeof(hw_bviifname), "%s%d","bvi",vlan_id); + hw_ifname = hw_bviifname; + } else { + hwifname = tap_to_hwif_name(if_name.c_str()); + if (vlan_id) { + snprintf(hw_subifname, sizeof(hw_subifname), "%s.%u", hwifname, vlan_id); + hw_ifname = hw_subifname; + } else { + hw_ifname = hwifname; + } + } + + int ret = interface_ip_address_add_del(hw_ifname, &vpp_ip_prefix, is_add); + + if (ret == 0) + { + return SAI_STATUS_SUCCESS; + } + else { + return SAI_STATUS_FAILURE; + } +} + +enum class LpbOpType { + NOP_LPB_IF = 0, + ADD_IP_LPB_IF = 1, + DEL_IP_LPB_IF = 2, + ADD_LPB_IF = 3, + DEL_LPB_IF = 4, +}; + +LpbOpType getLoopbackOperationType ( + _In_ bool is_add, + _In_ const std::string vppIfName, + _In_ sai_route_entry_t route_entr, + _In_ const std::unordered_map& lpbIpToIfMap) +{ + if (is_add) { + if ((!vppIfName.empty()) && (vppIfName.find("loop") != std::string::npos)) { + return LpbOpType::ADD_IP_LPB_IF; + } else if (vppIfName.empty()) { + return LpbOpType::ADD_LPB_IF; + } + } else { + int count = 0; + // Iterate over all elements in the unordered_map + for (const auto& pair : lpbIpToIfMap) { + if (pair.second == vppIfName) { + ++count; + } + } + if (count == 1) { + // last IP then remove the loopback interface + return LpbOpType::DEL_LPB_IF; + } else { + return LpbOpType::DEL_IP_LPB_IF; + } + } + return LpbOpType::NOP_LPB_IF; +} + +sai_status_t SwitchVpp::process_interface_loopback ( + _In_ const std::string &serializedObjectId, + _In_ bool &isLoopback, + _In_ bool is_add) +{ + SWSS_LOG_ENTER(); + + sai_route_entry_t route_entry; + sai_deserialize_route_entry(serializedObjectId, route_entry); + std::string destinationIP = extractDestinationIP(serializedObjectId); + std::string hostIfName = ""; + + if (is_add) + { + hostIfName = get_intf_name_for_prefix(route_entry); + } else + { + hostIfName = lpbIpToHostIfMap[destinationIP]; + } + + isLoopback = (hostIfName.find("Loopback") != std::string::npos); + SWSS_LOG_NOTICE("hostIfName:%s isLoopback:%u", hostIfName.c_str(), isLoopback); + + if (isLoopback) { + std::string vppIfName = lpbHostIfToVppIfMap[hostIfName]; + LpbOpType lpbOp = getLoopbackOperationType(is_add, vppIfName, route_entry, lpbIpToIfMap); + + switch (lpbOp) { + case LpbOpType::ADD_IP_LPB_IF: + + // interface exists - add ip to interface + SWSS_LOG_NOTICE("hostIfName:%s exists new-ip:%s", + hostIfName.c_str(), destinationIP.c_str()); + + // update interafce with ip add + vpp_interface_ip_address_update(vppIfName.c_str(), + serializedObjectId, true); + + lpbIpToIfMap[destinationIP] = vppIfName; + lpbIpToHostIfMap[destinationIP] = hostIfName; + break; + + case LpbOpType::DEL_IP_LPB_IF: + + // interface exists - remove ip from interface + SWSS_LOG_NOTICE("hostIfName:%s exists new-ip:%s", + hostIfName.c_str(), destinationIP.c_str()); + + // update interafce with ip remove + vpp_interface_ip_address_update(vppIfName.c_str(), + serializedObjectId, false); + + lpbIpToIfMap.erase(destinationIP); + lpbIpToHostIfMap.erase(destinationIP); + break; + + case LpbOpType::DEL_LPB_IF: + + // interface exists - delete interface + vpp_del_lpb_intf_ip_addr(serializedObjectId); + break; + + case LpbOpType::ADD_LPB_IF: + + // interface does not exist - create interface + vpp_add_lpb_intf_ip_addr(serializedObjectId); + break; + + default: + + SWSS_LOG_INFO("No matching loopback hostIfName:%s new-ip:%s", + hostIfName.c_str(), destinationIP.c_str()); + break; + } + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_interface_ip_address_update ( + _In_ const char *vppIfname, + _In_ const std::string &serializedObjectId, + _In_ bool is_add) +{ + SWSS_LOG_ENTER(); + + sai_route_entry_t route_entry; + sai_deserialize_route_entry(serializedObjectId, route_entry); + std::string destinationIP = extractDestinationIP(serializedObjectId); + + vpp_ip_route_t ip_route; + create_route_prefix(&route_entry, &ip_route); + + if (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV6) + { + char prefixIp6Str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(ip_route.prefix_addr.addr.ip6.sin6_addr), + prefixIp6Str, INET6_ADDRSTRLEN); + + } else if (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + char prefixIp4Str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(ip_route.prefix_addr.addr.ip4.sin_addr), + prefixIp4Str, INET_ADDRSTRLEN); + } else { + SWSS_LOG_ERROR("Could not determine IP address family! destinationIP:%s", + destinationIP.c_str()); + } + + int ret = interface_ip_address_add_del(vppIfname, &ip_route, is_add); + if (ret != 0) { + SWSS_LOG_ERROR("interface_ip_address_add returned error"); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_add_lpb_intf_ip_addr ( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_route_entry_t route_entry; + std::vector ip_prefixes; + sai_deserialize_route_entry(serializedObjectId, route_entry); + std::string destinationIP = extractDestinationIP(serializedObjectId); + + // Retrieve the current instance for the interface + uint32_t instance = getNextLoopbackInstance(); + + // Generate the loopback interface name + std::string vppIfName = "loop" + std::to_string(instance); + + // Store the current instance interface pair + lpbInstMap[vppIfName] = instance; + + SWSS_LOG_NOTICE("vpp_add_lpb vppIfName:%s instance:%u", + vppIfName.c_str(), instance); + + // Get new list of physical interfaces from VS + refresh_interfaces_list(); + + // Create the loopback instance in vpp + int ret = create_loopback_instance(vppIfName.c_str(), instance); + if (ret != 0) { + SWSS_LOG_ERROR("create_loopback_instance returned error: %d", ret); + } + + // Get new list of physical interfaces from VS + refresh_interfaces_list(); + + //Set state up + interface_set_state(vppIfName.c_str(), true /*is_up*/); + + const std::string hostIfname = get_intf_name_for_prefix(route_entry); + SWSS_LOG_NOTICE("get_intf_name_for_prefix:%s", hostIfname.c_str()); + lpbHostIfToVppIfMap[hostIfname] = vppIfName; + + // record the IP addresses on the host interface before destorying it + if (!vpp_get_intf_all_ip_prefixes(hostIfname, ip_prefixes)) { + return SAI_STATUS_FAILURE; + } + + // remove host looback interface before creating lcp tap + ret = configureLoopbackInterface(false/*lpb_add*/, hostIfname, "", 0); + if (ret != 0) + { + SWSS_LOG_ERROR("Failed to configure loopback interface remove"); + } + + // create lcp tap between vpp and host + { + init_vpp_client(); + SWSS_LOG_DEBUG("configure_lcp_interface vpp_name:%s sonic_name:%s", + vppIfName.c_str(), hostIfname.c_str()); + configure_lcp_interface(vppIfName.c_str(), hostIfname.c_str(), true); + } + + // restore IP addresses previously configured on looback interface after creating lcp tap + for (auto prefix: ip_prefixes) + { + std::string addr = prefix.getIp().to_string(); + ret = configureLoopbackInterface(true/*lpb_add*/, hostIfname, addr, prefix.getMaskLength()); + if (ret != 0) + { + SWSS_LOG_ERROR("Failed to configure loopback interface add"); + } + SWSS_LOG_DEBUG("configure_lcp_interface vpp_name:%s sonic_name:%s prefix:%s", + vppIfName.c_str(), hostIfname.c_str(), prefix.to_string().c_str()); + } + + // Store the ip/vppIfName pair + lpbIpToIfMap[destinationIP] = vppIfName; + lpbIpToHostIfMap[destinationIP] = hostIfname; + + // Update vpp interface ip address + vpp_interface_ip_address_update(vppIfName.c_str(), serializedObjectId, true); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_del_lpb_intf_ip_addr ( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + + sai_route_entry_t route_entry; + sai_deserialize_route_entry(serializedObjectId, route_entry); + std::string destinationIP = extractDestinationIP(serializedObjectId); + + std::string vppIfName = lpbIpToIfMap[destinationIP]; + const std::string hostIfname = lpbIpToHostIfMap[destinationIP]; + uint32_t instance = lpbInstMap[vppIfName]; + + // Update vpp interface ip address + vpp_interface_ip_address_update(vppIfName.c_str(), serializedObjectId, false); + + // Delete the loopback instance + delete_loopback(vppIfName.c_str(), instance); + + // refresh interfaces list as we have deleted the loopback interface + refresh_interfaces_list(); + + // Remove the IP/interface mappings from the maps + lpbInstMap.erase(vppIfName); + lpbIpToIfMap.erase(destinationIP); + lpbIpToHostIfMap.erase(destinationIP); + lpbHostIfToVppIfMap.erase(hostIfname); + + // Mark the loopback instance available + markLoopbackInstanceDeleted(instance); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_get_router_intf_name ( + _In_ sai_ip_prefix_t& ip_prefix, + _In_ sai_object_id_t rif_id, + std::string& nexthop_ifname) +{ + sai_attribute_t attr; + int32_t rif_type; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + sai_status_t status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + rif_type = attr.value.s32; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_PORT_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + sai_object_id_t obj_id = attr.value.oid; + + sai_object_type_t ot = objectTypeQuery(obj_id); + + if (ot == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + if (ot != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_ROUTER_INTERFACE_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(obj_id).c_str(), + sai_serialize_object_type(ot).c_str()); + + return SAI_STATUS_FAILURE; + } + + if (rif_type != SAI_ROUTER_INTERFACE_TYPE_SUB_PORT && + rif_type != SAI_ROUTER_INTERFACE_TYPE_PORT && + rif_type != SAI_ROUTER_INTERFACE_TYPE_LOOPBACK) + { + return SAI_STATUS_SUCCESS; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + uint16_t vlan_id = 0; + if (status == SAI_STATUS_SUCCESS) + { + vlan_id = attr.value.u16; + } + + std::string if_name; + bool found = getTapNameFromPortId(obj_id, if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", sai_serialize_object_id(obj_id).c_str()); + return SAI_STATUS_FAILURE; + } + + const char *hwifname = tap_to_hwif_name(if_name.c_str()); + char hw_subifname[32]; + const char *hw_ifname; + + if (vlan_id) { + snprintf(hw_subifname, sizeof(hw_subifname), "%s.%u", hwifname, vlan_id); + hw_ifname = hw_subifname; + } else { + hw_ifname = hwifname; + } + + nexthop_ifname = std::string(hw_ifname); + + SWSS_LOG_NOTICE("Configuring ip address on router interface %s", nexthop_ifname.c_str()); + + return SAI_STATUS_SUCCESS; +} + +int SwitchVpp::vpp_add_ip_vrf (_In_ sai_object_id_t objectId, uint32_t vrf_id) +{ + auto it = vrf_objMap.find(objectId); + + if (it != vrf_objMap.end()) { + auto sw = it->second; + if (sw != nullptr) { + SWSS_LOG_NOTICE("VRF(%s) with id %u already exists", sai_serialize_object_id(objectId).c_str(), sw->m_vrf_id); + } else { + SWSS_LOG_ERROR("VRF(%s) object with null data", sai_serialize_object_id(objectId).c_str()); + } + return 0; + } + + std::string vrf_name = "vrf_" + vrf_id; + + if (!vrf_id || ip_vrf_add(vrf_id, vrf_name.c_str(), false) == 0) { + SWSS_LOG_NOTICE("VRF(%s) with id %u created in VS", sai_serialize_object_id(objectId).c_str(), vrf_id); + vrf_objMap[objectId] = std::make_shared(objectId, vrf_id, vrf_name, false); + + uint32_t hash_mask = VPP_IP_API_FLOW_HASH_SRC_IP | VPP_IP_API_FLOW_HASH_DST_IP | \ + VPP_IP_API_FLOW_HASH_SRC_PORT | VPP_IP_API_FLOW_HASH_DST_PORT | \ + VPP_IP_API_FLOW_HASH_PROTO; + + int ret = vpp_ip_flow_hash_set(vrf_id, hash_mask, AF_INET); + SWSS_LOG_NOTICE("ip flow hash set for VRF %s with vrf_id %u in VS, status %d", + sai_serialize_object_id(objectId).c_str(), vrf_id, ret); + } + + return 0; +} + +int SwitchVpp::vpp_del_ip_vrf (_In_ sai_object_id_t objectId) +{ + auto it = vrf_objMap.find(objectId); + + if (it != vrf_objMap.end()) { + auto sw = it->second; + if (sw != nullptr) { + SWSS_LOG_NOTICE("Deleting VRF(%s) with id %u", sai_serialize_object_id(objectId).c_str(), sw->m_vrf_id); + ip_vrf_del(sw->m_vrf_id, sw->m_vrf_name.c_str(), sw->m_is_ipv6); + vrf_objMap.erase(it); + } + } + return 0; +} + +std::shared_ptr SwitchVpp::vpp_get_ip_vrf (_In_ sai_object_id_t objectId) +{ + auto it = vrf_objMap.find(objectId); + + if (it != vrf_objMap.end()) { + auto vrf = it->second; + if (vrf == nullptr) { + SWSS_LOG_NOTICE("No Vrf found with id %s", sai_serialize_object_id(objectId).c_str()); + } + return vrf; + } + return nullptr; +} + +/* + * VS uses linux's vrf table id when linux_nl is active + */ +int SwitchVpp::vpp_get_vrf_id (const char *linux_ifname, uint32_t *vrf_id) +{ + std::stringstream cmd; + std::string res; + + cmd << IP_CMD << " link show dev " << linux_ifname; + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + return -1; + } + + std::stringstream table_cmd; + + table_cmd << IP_CMD << " -d link show dev " << linux_ifname << " | grep -o 'vrf_slave table [0-9]\\+' | cut -d' ' -f3"; + ret = swss::exec(table_cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", table_cmd.str().c_str(), ret); + return -1; + } + + if (res.length() != 0) + { + *vrf_id = std::stoi(res); + } else { + *vrf_id = 0; + } + + return 0; +} + +sai_status_t SwitchVpp::vpp_create_router_interface( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + auto attr_type = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_TYPE, attr_count, attr_list); + + if (attr_type == NULL) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + if (attr_type->value.s32 == SAI_ROUTER_INTERFACE_TYPE_VLAN) + { + SWSS_LOG_NOTICE("Invoking BVI interface create for attr type %d", attr_type->value.s32); + return vpp_create_bvi_interface(attr_count, attr_list); + } + if (attr_type->value.s32 != SAI_ROUTER_INTERFACE_TYPE_SUB_PORT && + attr_type->value.s32 != SAI_ROUTER_INTERFACE_TYPE_PORT) + { + SWSS_LOG_NOTICE("Skipping router interface create for attr type %d", attr_type->value.s32); + + return SAI_STATUS_SUCCESS; + } + + auto attr_obj_id = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_PORT_ID, attr_count, attr_list); + + if (attr_obj_id == NULL) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_PORT_ID was not passed"); + + return SAI_STATUS_SUCCESS; + } + + sai_object_id_t obj_id = attr_obj_id->value.oid; + + sai_object_type_t ot = objectTypeQuery(obj_id); + + if (ot == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping tap creation for hostif with object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + if (ot != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_ROUTER_INTERFACE_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(obj_id).c_str(), + sai_serialize_object_type(ot).c_str()); + + return SAI_STATUS_FAILURE; + } + auto attr_vlan_id = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID, attr_count, attr_list); + + uint16_t vlan_id = 0; + if (attr_vlan_id == NULL) { + if (attr_type->value.s32 == SAI_ROUTER_INTERFACE_TYPE_SUB_PORT) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + } else { + vlan_id = attr_vlan_id->value.u16; + } + + std::string if_name; + bool found = getTapNameFromPortId(obj_id, if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", sai_serialize_object_id(obj_id).c_str()); + return SAI_STATUS_FAILURE; + } + + const char *dev = if_name.c_str(); + const char *linux_ifname; + char host_subifname[32]; + + if (attr_type->value.s32 == SAI_ROUTER_INTERFACE_TYPE_SUB_PORT) + { + snprintf(host_subifname, sizeof(host_subifname), "%s.%u", dev, vlan_id); + + /* The host(tap) subinterface is also created as part of the vpp subinterface creation */ + create_sub_interface(tap_to_hwif_name(dev), vlan_id, vlan_id); + + /* Get new list of physical interfaces from VS */ + refresh_interfaces_list(); + + linux_ifname = host_subifname; + } else { + linux_ifname = dev; + } + sai_object_id_t vrf_obj_id = 0; + + auto attr_vrf_id = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID, attr_count, attr_list); + + if (attr_vrf_id == NULL) + { + SWSS_LOG_NOTICE("attr SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID was not passed"); + } else { + vrf_obj_id = attr_vrf_id->value.oid; + SWSS_LOG_NOTICE("attr SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID %s is passed", + sai_serialize_object_id(vrf_obj_id).c_str()); + } + + uint32_t vrf_id; + int ret = vpp_get_vrf_id(linux_ifname, &vrf_id); + + vpp_add_ip_vrf(vrf_obj_id, vrf_id); + if (ret == 0 && vrf_id != 0) { + set_interface_vrf(tap_to_hwif_name(dev), vlan_id, vrf_id, false); + } + auto attr_type_mtu = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_MTU, attr_count, attr_list); + + if (attr_type_mtu != NULL) + { + vpp_set_interface_mtu(obj_id, vlan_id, attr_type_mtu->value.u32, AF_INET); + vpp_set_interface_mtu(obj_id, vlan_id, attr_type_mtu->value.u32, AF_INET6); + } + + bool v4_is_up = false, v6_is_up = false; + + auto attr_type_v4 = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE, attr_count, attr_list); + + if (attr_type_v4 != NULL) + { + v4_is_up = attr_type_v4->value.booldata; + } + auto attr_type_v6 = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE, attr_count, attr_list); + + if (attr_type_v6 != NULL) + { + v6_is_up = attr_type_v6->value.booldata; + } + + if (attr_type_v4 != NULL || attr_type_v6 != NULL) + { + return vpp_set_interface_state(obj_id, vlan_id, (v4_is_up || v6_is_up)); + } else { + return SAI_STATUS_SUCCESS; + } +} + +sai_status_t SwitchVpp::vpp_update_router_interface( + _In_ sai_object_id_t object_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + int32_t rif_type; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + sai_status_t status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, object_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + rif_type = attr.value.s32; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, object_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_PORT_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + sai_object_id_t obj_id = attr.value.oid; + + sai_object_type_t ot = objectTypeQuery(obj_id); + + if (ot == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping tap creation for hostif with object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + if (ot != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_ROUTER_INTERFACE_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(obj_id).c_str(), + sai_serialize_object_type(ot).c_str()); + + return SAI_STATUS_FAILURE; + } + + if (rif_type != SAI_ROUTER_INTERFACE_TYPE_SUB_PORT) + { + vpp_router_interface_remove_vrf(obj_id); + + return SAI_STATUS_SUCCESS; + } + + + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, object_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + uint16_t vlan_id = attr.value.u16; + + auto attr_type_mtu = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_MTU, attr_count, attr_list); + + if (attr_type_mtu != NULL) + { + vpp_set_interface_mtu(obj_id, vlan_id, attr_type_mtu->value.u32, AF_INET); + vpp_set_interface_mtu(obj_id, vlan_id, attr_type_mtu->value.u32, AF_INET6); + } + + bool v4_is_up = false, v6_is_up = false; + + auto attr_type_v4 = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE, attr_count, attr_list); + + if (attr_type_v4 != NULL) + { + v4_is_up = attr_type_v4->value.booldata; + } + auto attr_type_v6 = sai_metadata_get_attr_by_id(SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE, attr_count, attr_list); + + if (attr_type_v6 != NULL) + { + v6_is_up = attr_type_v6->value.booldata; + } + + if (attr_type_v4 != NULL || attr_type_v6 != NULL) + { + return vpp_set_interface_state(obj_id, vlan_id, (v4_is_up || v6_is_up)); + } else { + return SAI_STATUS_SUCCESS; + } +} + +sai_status_t SwitchVpp::vpp_router_interface_remove_vrf( + _In_ sai_object_id_t obj_id) +{ + SWSS_LOG_ENTER(); + + std::string if_name; + bool found = getTapNameFromPortId(obj_id, if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", sai_serialize_object_id(obj_id).c_str()); + return SAI_STATUS_FAILURE; + } + const char *linux_ifname; + + linux_ifname = if_name.c_str(); + + const char *hwif_name = tap_to_hwif_name(if_name.c_str()); + + SWSS_LOG_NOTICE("Resetting to default vrf for interface %s", linux_ifname); + + uint32_t vrf_id = 0; + /* For now support is only for ipv4 tables */ + set_interface_vrf(hwif_name, 0, vrf_id, false); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::vpp_remove_router_interface(sai_object_id_t rif_id) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + int32_t rif_type; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + sai_status_t status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_TYPE was not passed"); + + return SAI_STATUS_FAILURE; + } + if (attr.value.s32 == SAI_ROUTER_INTERFACE_TYPE_VLAN) + { + SWSS_LOG_NOTICE("Invoking BVI interface create for attr type %d", attr.value.s32); + return vpp_delete_bvi_interface(rif_id); + } + rif_type = attr.value.s32; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_PORT_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + + sai_object_id_t obj_id = attr.value.oid; + + sai_object_type_t ot = objectTypeQuery(obj_id); + + if (ot == SAI_OBJECT_TYPE_VLAN) + { + SWSS_LOG_DEBUG("Skipping tap creation for hostif with object type VLAN"); + return SAI_STATUS_SUCCESS; + } + + if (ot != SAI_OBJECT_TYPE_PORT) + { + SWSS_LOG_ERROR("SAI_ROUTER_INTERFACE_ATTR_PORT_ID=%s expected to be PORT but is: %s", + sai_serialize_object_id(obj_id).c_str(), + sai_serialize_object_type(ot).c_str()); + + return SAI_STATUS_FAILURE; + } + + if (rif_type != SAI_ROUTER_INTERFACE_TYPE_SUB_PORT) + { + vpp_router_interface_remove_vrf(obj_id); + + return SAI_STATUS_SUCCESS; + } + + + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + status = get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, rif_id, 1, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("attr SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID was not passed"); + + return SAI_STATUS_FAILURE; + } + uint16_t vlan_id = attr.value.u16; + + std::string if_name; + bool found = getTapNameFromPortId(obj_id, if_name); + if (found == false) + { + SWSS_LOG_ERROR("host interface for port id %s not found", sai_serialize_object_id(obj_id).c_str()); + return SAI_STATUS_FAILURE; + } + + const char *dev = if_name.c_str(); + + delete_sub_interface(tap_to_hwif_name(dev), vlan_id); + /* Get new list of physical interfaces from VS */ + refresh_interfaces_list(); + +/* + char host_subifname[32], hwif_name[32]; + snprintf(host_subifname, sizeof(host_subifname), "%s.%u", dev, vlan_id); + snprintf(hwif_name, sizeof(hwif_name), "%s.%u", tap_to_hwif_name(dev), vlan_id); + configure_lcp_interface(tap_to_hwif_name(dev), host_subifname); +*/ + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::createRouterif( + _In_ sai_object_id_t object_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + if (m_switchConfig->m_useTapDevice == true) + { + sai_attribute_t tattr; + + tattr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + if (get(SAI_OBJECT_TYPE_ROUTER_INTERFACE, object_id, 1, &tattr) == SAI_STATUS_ITEM_NOT_FOUND) + { + vpp_create_router_interface(attr_count, attr_list); + } else { + vpp_update_router_interface(object_id, attr_count, attr_list); + } + } + + auto sid = sai_serialize_object_id(object_id); + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_ROUTER_INTERFACE, sid, switch_id, attr_count, attr_list)); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::removeRouterif( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (m_switchConfig->m_useTapDevice == true) + { + vpp_remove_router_interface(objectId); + } + + auto sid = sai_serialize_object_id(objectId); + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_ROUTER_INTERFACE, sid)); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::removeVrf( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + + if (m_switchConfig->m_useTapDevice == true) + { + vpp_del_ip_vrf(objectId); + } + + auto sid = sai_serialize_object_id(objectId); + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_VIRTUAL_ROUTER, sid)); + + return SAI_STATUS_SUCCESS; +} diff --git a/vslib/SwitchVppRoute.cpp b/vslib/SwitchVppRoute.cpp new file mode 100644 index 000000000..cf0f736fd --- /dev/null +++ b/vslib/SwitchVppRoute.cpp @@ -0,0 +1,283 @@ +#include "SwitchVpp.h" +#include "SwitchVppNexthop.h" +#include "SwitchVppUtils.h" + +#include "meta/sai_serialize.h" +#include "meta/NotificationPortStateChange.h" + +#include "swss/logger.h" +#include "swss/exec.h" +#include "swss/converter.h" + +#include +#include +#include +#include +#include +#include + +#include "vppxlate/SaiVppXlate.h" + +using namespace saivs; + +#define CHECK_STATUS_QUIET(status) { \ + sai_status_t _status = (status); \ + if (_status != SAI_STATUS_SUCCESS) { return _status; } } + +#define CHECK_STATUS_W_MSG(status, msg, ...) { \ + sai_status_t _status = (status); \ + if (_status != SAI_STATUS_SUCCESS) { \ + char buffer[512]; \ + snprintf(buffer, 512, msg, ##__VA_ARGS__); \ + SWSS_LOG_ERROR("%s: status %d", buffer, status); \ + return _status; } } + +static void create_route_prefix_entry ( + sai_route_entry_t *route_entry, + vpp_ip_route_t *ip_route) +{ + + const sai_ip_prefix_t *ip_address = &route_entry->destination; + + switch (ip_address->addr_family) { + case SAI_IP_ADDR_FAMILY_IPV4: + { + struct sockaddr_in *sin = &ip_route->prefix_addr.addr.ip4; + + ip_route->prefix_addr.sa_family = AF_INET; + sin->sin_addr.s_addr = ip_address->addr.ip4; + ip_route->prefix_len = getPrefixLenFromAddrMask(reinterpret_cast(&ip_address->mask.ip4), 4); + + break; + } + case SAI_IP_ADDR_FAMILY_IPV6: + { + struct sockaddr_in6 *sin6 = &ip_route->prefix_addr.addr.ip6; + + ip_route->prefix_addr.sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, ip_address->addr.ip6, sizeof(sin6->sin6_addr.s6_addr)); + ip_route->prefix_len = getPrefixLenFromAddrMask(ip_address->mask.ip6, 16); + + break; + } + } +} + +void create_vpp_nexthop_entry ( + nexthop_grp_member_t *nxt_grp_member, + const char *hwif_name, + vpp_nexthop_type_e type, + vpp_ip_nexthop_t *vpp_nexthop) +{ + sai_ip_address_t *ip_address = &nxt_grp_member->addr; + + switch (ip_address->addr_family) { + case SAI_IP_ADDR_FAMILY_IPV4: + { + struct sockaddr_in *sin = &vpp_nexthop->addr.addr.ip4; + + vpp_nexthop->addr.sa_family = AF_INET; + sin->sin_addr.s_addr = ip_address->addr.ip4; + + break; + } + case SAI_IP_ADDR_FAMILY_IPV6: + { + struct sockaddr_in6 *sin6 = &vpp_nexthop->addr.addr.ip6; + + vpp_nexthop->addr.sa_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, ip_address->addr.ip6, sizeof(sin6->sin6_addr.s6_addr)); + + break; + } + } + vpp_nexthop->type = type; + vpp_nexthop->hwif_name = hwif_name; + vpp_nexthop->sw_if_index = nxt_grp_member->sw_if_index; + vpp_nexthop->weight = (uint8_t) nxt_grp_member->weight; + vpp_nexthop->preference = 0; +} + +sai_status_t SwitchVpp::IpRouteAddRemove( + _In_ const SaiObject* route_obj, + _In_ bool is_add) +{ + SWSS_LOG_ENTER(); + + int ret = SAI_STATUS_SUCCESS; + sai_status_t status; + sai_object_id_t next_hop_oid; + sai_attribute_t attr; + std::string serializedObjectId = route_obj->get_id(); + + attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + CHECK_STATUS_QUIET(route_obj->get_mandatory_attr(attr)); + next_hop_oid = attr.value.oid; + + sai_route_entry_t route_entry; + const char *hwif_name = NULL; + vpp_nexthop_type_e nexthop_type = VPP_NEXTHOP_NORMAL; + bool config_ip_route = false; + + sai_deserialize_route_entry(serializedObjectId, route_entry); + + nexthop_grp_config_t *nxthop_group = NULL; + + if (SAI_OBJECT_TYPE_ROUTER_INTERFACE == sai_object_type_query(next_hop_oid)) + { + // vpp_add_del_intf_ip_addr(route_entry.destination, next_hop_oid, is_add); + } + else if (SAI_OBJECT_TYPE_PORT == sai_object_type_query(next_hop_oid)) + { + attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + status = route_obj->get_attr(attr); + + if (status == SAI_STATUS_SUCCESS && SAI_PACKET_ACTION_FORWARD == attr.value.s32) { + vpp_add_del_intf_ip_addr_norif(serializedObjectId, route_entry, is_add); + } + } + else if (SAI_OBJECT_TYPE_NEXT_HOP == sai_object_type_query(next_hop_oid)) + { + if (IpRouteNexthopEntry(next_hop_oid, &nxthop_group) == SAI_STATUS_SUCCESS) + { + config_ip_route = true; + } + } + else if (SAI_OBJECT_TYPE_NEXT_HOP_GROUP == sai_object_type_query(next_hop_oid)) + { + if (IpRouteNexthopGroupEntry(next_hop_oid, &nxthop_group) == SAI_STATUS_SUCCESS) + { + config_ip_route = true; + } + } + + if (config_ip_route == true) + { + std::shared_ptr vrf; + uint32_t vrf_id; + + vrf = vpp_get_ip_vrf(route_entry.vr_id); + if (vrf == nullptr) { + vrf_id = 0; + } else { + vrf_id = vrf->m_vrf_id; + } + vpp_ip_route_t *ip_route = (vpp_ip_route_t *) + calloc(1, sizeof(vpp_ip_route_t) + (nxthop_group->nmembers * sizeof(vpp_ip_nexthop_t))); + if (!ip_route) { + return SAI_STATUS_FAILURE; + } + create_route_prefix_entry(&route_entry, ip_route); + ip_route->vrf_id = vrf_id; + ip_route->is_multipath = (nxthop_group->nmembers > 1) ? true : false; + + nexthop_grp_member_t *nxt_grp_member; + + nxt_grp_member = nxthop_group->grp_members; + + size_t i; + for (i = 0; i < nxthop_group->nmembers; i++) { + create_vpp_nexthop_entry(nxt_grp_member, hwif_name, nexthop_type, &ip_route->nexthop[i]); + nxt_grp_member++; + } + ip_route->nexthop_cnt = nxthop_group->nmembers; + + ret = ip_route_add_del(ip_route, is_add); + + SWSS_LOG_NOTICE("%s ip route in VS %s status %d table %u", (is_add ? "Add" : "Remove"), + serializedObjectId.c_str(), ret, vrf_id); + SWSS_LOG_NOTICE("%s route nexthop type %s count %u", (is_add ? "Add" : "Remove"), + sai_serialize_object_type(sai_object_type_query(next_hop_oid)).c_str(), + nxthop_group->nmembers); + + free(ip_route); + free(nxthop_group); + + } else { + SWSS_LOG_NOTICE("Ignoring VS ip route %s", serializedObjectId.c_str()); + } + + return ret; +} + +sai_status_t SwitchVpp::addIpRoute( + _In_ const std::string &serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + bool isLoopback = false; + bool isTunnelNh = false; + SWSS_LOG_ENTER(); + + SaiCachedObject ip_route_obj(this, SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectId, attr_count, attr_list); + auto nh_obj = ip_route_obj.get_linked_object(SAI_OBJECT_TYPE_NEXT_HOP, SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID); + if (nh_obj != nullptr) { + sai_attribute_t attr; + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + CHECK_STATUS_W_MSG(nh_obj->get_attr(attr), "Missing SAI_NEXT_HOP_ATTR_TYPE in tunnel obj"); + isTunnelNh = (attr.value.s32 == SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP); + } + + if (isTunnelNh) { + IpRouteAddRemove(&ip_route_obj, true); + } else { + process_interface_loopback(serializedObjectId, isLoopback, true); + if (isLoopback == false && is_ip_nbr_active() == true) + { + IpRouteAddRemove(&ip_route_obj, true); + } + } + + CHECK_STATUS(create_internal(SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectId, switch_id, attr_count, attr_list)); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::updateIpRoute( + _In_ const std::string &serializedObjectId, + _In_ const sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + + if (is_ip_nbr_active() == true) { + SWSS_LOG_NOTICE("ip route entry update %s", serializedObjectId.c_str()); + SaiModDBObject route_mod_obj(this, SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectId, 1, attr); + + auto route_db_obj = route_mod_obj.get_db_obj(); + if (!route_db_obj) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_ROUTE_ENTRY SaiObject: %s", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } else { + IpRouteAddRemove(route_db_obj.get(), false); + } + + IpRouteAddRemove(&route_mod_obj, true); + } + + set_internal(SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectId, attr); + + return SAI_STATUS_SUCCESS; +} + +sai_status_t SwitchVpp::removeIpRoute( + _In_ const std::string &serializedObjectId) +{ + SWSS_LOG_ENTER(); + bool isLoopback = false; + process_interface_loopback(serializedObjectId, isLoopback, false); + + if (isLoopback == false && is_ip_nbr_active() == true) + { + auto route_obj = get_sai_object(SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectId); + + if (route_obj) { + IpRouteAddRemove(route_obj.get(), false); + } + } + + CHECK_STATUS(remove_internal(SAI_OBJECT_TYPE_ROUTE_ENTRY, serializedObjectId)); + + return SAI_STATUS_SUCCESS; +} + diff --git a/vslib/SwitchVppUtils.cpp b/vslib/SwitchVppUtils.cpp new file mode 100644 index 000000000..66de7ce8b --- /dev/null +++ b/vslib/SwitchVppUtils.cpp @@ -0,0 +1,179 @@ +#include "SwitchVppUtils.h" + +#include "swss/logger.h" + +#include "vppxlate/SaiVppXlate.h" + +using namespace saivs; + +sai_status_t saivs::find_attrib_in_list( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _In_ sai_attr_id_t attrib_id, + _Out_ const sai_attribute_value_t **attr_value, + _Out_ uint32_t *index) +{ + uint32_t ii; + + SWSS_LOG_ENTER(); + + if ((attr_count) && (NULL == attr_list)) { + SWSS_LOG_ERROR("NULL value attr list\n"); + return SAI_STATUS_INVALID_PARAMETER; + } + + if (NULL == attr_value) { + SWSS_LOG_ERROR("NULL value attr value\n"); + return SAI_STATUS_INVALID_PARAMETER; + } + + if (NULL == index) { + SWSS_LOG_ERROR("NULL value index\n"); + return SAI_STATUS_INVALID_PARAMETER; + } + + for (ii = 0; ii < attr_count; ii++) { + if (attr_list[ii].id == attrib_id) { + *attr_value = &(attr_list[ii].value); + *index = ii; + return SAI_STATUS_SUCCESS; + } + } + + *attr_value = NULL; + + return SAI_STATUS_ITEM_NOT_FOUND; +} + +int saivs::getPrefixLenFromAddrMask(const uint8_t *addr, int len) +{ + // Iterate over each byte from left to right + for (int i = 0; i < len; ++i) + { + uint8_t byte = addr[i]; + + // If the byte is 0xFF, it means all bits are set, continue to the next byte + if (byte == 0xFF) + { + continue; + } + + // If the byte is not 0xFF, count the number of leading 1s in this byte + int leading_ones = 0; + for (int j = 7; j >= 0; --j) + { + if ((byte >> j) & 0x1) + { + ++leading_ones; + } + else + { + break; + } + } + + // Return the total number of leading 1s in the address mask + return i * 8 + leading_ones; + } + + // If all bytes are 0xFF, return the total length in bits + return len * 8; +} + +swss::IpPrefix saivs::getIpPrefixFromSaiPrefix(const sai_ip_prefix_t& src) +{ + swss::ip_addr_t ip; + switch(src.addr_family) + { + case SAI_IP_ADDR_FAMILY_IPV4: + ip.family = AF_INET; + ip.ip_addr.ipv4_addr = src.addr.ip4; + return swss::IpPrefix(ip, getPrefixLenFromAddrMask(reinterpret_cast(&src.mask.ip4), 4)); + case SAI_IP_ADDR_FAMILY_IPV6: + ip.family = AF_INET6; + memcpy(ip.ip_addr.ipv6_addr, src.addr.ip6, 16); + return swss::IpPrefix(ip, getPrefixLenFromAddrMask(src.mask.ip6, 16)); + default: + throw std::logic_error("Invalid family"); + } +} + +sai_ip_prefix_t& saivs::subnet(sai_ip_prefix_t& dst, const sai_ip_prefix_t& src) +{ + dst.addr_family = src.addr_family; + switch(src.addr_family) + { + case SAI_IP_ADDR_FAMILY_IPV4: + dst.addr.ip4 = src.addr.ip4 & src.mask.ip4; + dst.mask.ip4 = src.mask.ip4; + break; + case SAI_IP_ADDR_FAMILY_IPV6: + for (size_t i = 0; i < 16; i++) + { + dst.addr.ip6[i] = src.addr.ip6[i] & src.mask.ip6[i]; + dst.mask.ip6[i] = src.mask.ip6[i]; + } + break; + default: + throw std::logic_error("Invalid family"); + } + return dst; +} + +sai_ip_prefix_t& saivs::copy(sai_ip_prefix_t& dst, const swss::IpPrefix& src) +{ + auto ia = src.getIp().getIp(); + auto ma = src.getMask().getIp(); + switch(ia.family) + { + case AF_INET: + dst.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + dst.addr.ip4 = ia.ip_addr.ipv4_addr; + dst.mask.ip4 = ma.ip_addr.ipv4_addr; + break; + case AF_INET6: + dst.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(dst.addr.ip6, ia.ip_addr.ipv6_addr, 16); + memcpy(dst.mask.ip6, ma.ip_addr.ipv6_addr, 16); + break; + default: + throw std::logic_error("Invalid family"); + } + return dst; +} + +void saivs::sai_ip_address_t_to_vpp_ip_addr_t(sai_ip_address_t& src, vpp_ip_addr_t& dst) +{ + if (SAI_IP_ADDR_FAMILY_IPV4 == src.addr_family) { + struct sockaddr_in *sin = &dst.addr.ip4; + + dst.sa_family = AF_INET; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = src.addr.ip4; + } else { + struct sockaddr_in6 *sin6 = &dst.addr.ip6; + + dst.sa_family = AF_INET6; + sin6->sin6_family = AF_INET6; + memcpy(sin6->sin6_addr.s6_addr, src.addr.ip6, sizeof(sin6->sin6_addr.s6_addr)); + } +} + +/* Utility function for IP addr translation from VS to SAI */ +void saivs::vpp_ip_addr_t_to_sai_ip_address_t(vpp_ip_addr_t& src, sai_ip_address_t& dst) +{ + if (src.sa_family == AF_INET) + { + dst.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + struct sockaddr_in* sin = &src.addr.ip4; + memcpy(&dst.addr.ip4, &sin->sin_addr.s_addr, + sizeof(sin->sin_addr.s_addr)); + } + else { + dst.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + struct sockaddr_in6* sin6 = &src.addr.ip6; + memcpy(&dst.addr.ip6, sin6->sin6_addr.s6_addr, + sizeof(sin6->sin6_addr.s6_addr)); + } +} + diff --git a/vslib/SwitchVppUtils.h b/vslib/SwitchVppUtils.h new file mode 100644 index 000000000..681be72e2 --- /dev/null +++ b/vslib/SwitchVppUtils.h @@ -0,0 +1,33 @@ +#pragma once + +extern "C" { +#include "sai.h" +} + +#include "swss/ipaddress.h" +#include "swss/ipprefix.h" + +#include "vppxlate/SaiVppXlate.h" + +namespace saivs +{ + sai_status_t find_attrib_in_list( + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list, + _In_ sai_attr_id_t attrib_id, + _Out_ const sai_attribute_value_t **attr_value, + _Out_ uint32_t *index); + + int getPrefixLenFromAddrMask(const uint8_t *addr, int len); + + swss::IpPrefix getIpPrefixFromSaiPrefix(const sai_ip_prefix_t& src); + + sai_ip_prefix_t& subnet(sai_ip_prefix_t& dst, const sai_ip_prefix_t& src); + + sai_ip_prefix_t& copy(sai_ip_prefix_t& dst, const swss::IpPrefix& src); + + void sai_ip_address_t_to_vpp_ip_addr_t(sai_ip_address_t& src, vpp_ip_addr_t& dst); + + /* Utility function for IP addr translation from VS to SAI */ + void vpp_ip_addr_t_to_sai_ip_address_t(vpp_ip_addr_t& src, sai_ip_address_t& dst); +} diff --git a/vslib/TunnelManager.cpp b/vslib/TunnelManager.cpp new file mode 100644 index 000000000..c9f45d6df --- /dev/null +++ b/vslib/TunnelManager.cpp @@ -0,0 +1,396 @@ +#include "SwitchVppUtils.h" +#include "SwitchVpp.h" +#include "SaiObjectDB.h" +#include "TunnelManager.h" +#include "IpVrfInfo.h" + +#include "meta/sai_serialize.h" + +#include "swss/logger.h" + +#include "vppxlate/SaiVppXlate.h" + +using namespace saivs; + +#define CHECK_STATUS_W_MSG(status, msg, ...) { \ + sai_status_t _status = (status); \ + if (_status != SAI_STATUS_SUCCESS) { \ + char buffer[512]; \ + snprintf(buffer, 512, msg, ##__VA_ARGS__); \ + SWSS_LOG_ERROR("%s: status %d", buffer, status); \ + return _status; } } + +TunnelManager::TunnelManager(SwitchVpp* switch_db): m_switch_db(switch_db) +{ + m_router_mac = {0, 0, 0, 0, 0, 1}; + m_vxlan_port = 4789; +} + +const std::array& +TunnelManager::get_router_mac() const +{ + return m_router_mac; +} + +void +TunnelManager::set_router_mac(const sai_attribute_t* attr) +{ + for (int i = 0; i < 6; ++i) { + m_router_mac[i] = attr->value.mac[i]; + } +} + +void +TunnelManager::set_vxlan_port(const sai_attribute_t* attr) +{ + m_vxlan_port = attr->value.u16; +} +/** + * VxLAN tunnel is created in response to the creation of a tunnel encap nexthop entry. This assumes VxLAN tunnel is bidirectional and symmetric. + * The local VTEP sends packet through the tunnel to the remote VTEP. The remote VTEP sends packet back to the local VTEP through the same tunnel with the same VNI. + * Here is the VS config to be programmed in response to the creation of a tunnel encap nexthop entry: + * + * create vxlan tunnel src 1.0.0.1 dst 1.0.0.2 vni 3000 + * ip neighbor vxlan_tunnel0 1.0.0.2 00:00:00:00:00:01 no-fib-entry + * ip route add 100.1.1.0/24 via 1.0.0.2 vxlan_tunnel0 + * + * bvi create mac 00:00:00:00:00:01 + * set interface state bvi0 up + * set interface ip address bvi0 0.0.0.2/32 + * set interface l2 bridge vxlan_tunnel0 3000 1 + * set interface l2 bridge bvi0 3000 bvi + * + * corresponding to below sonic config + * In CONFIG_DB + * "VXLAN_TUNNEL": { + * "test": { + * "src_ip": "1.0.0.1" + * } + * } + * "VNET": { + * "Vnet1": { + * "peer_list": "", + * "scope": "default", + * "vni": "3000", + * "vxlan_tunnel": "test" + * }, + * } + * In APPL_DB + * "VNET_ROUTE_TUNNEL_TABLE:Vnet1:100.1.1.0/24": + * { + * "endpoint": "1.0.0.2" + * } + */ +sai_status_t +TunnelManager::tunnel_encap_nexthop_action( + _In_ const SaiObject* tunnel_nh_obj, + _In_ Action action) +{ + sai_attribute_t attr; + sai_ip_address_t src_ip; + sai_ip_address_t dst_ip; + std::unordered_map> vni_to_vrf_map; + sai_object_id_t object_id; + + + SWSS_LOG_ENTER(); + SWSS_LOG_DEBUG("tunnel_encap_nexthop_action %s %s", + action == Action::CREATE ? "CREATE" : "DELETE", tunnel_nh_obj->get_id().c_str()); + sai_deserialize_object_id(tunnel_nh_obj->get_id(), object_id); + auto tunnel_obj = tunnel_nh_obj->get_linked_object(SAI_OBJECT_TYPE_TUNNEL, SAI_NEXT_HOP_ATTR_TUNNEL_ID); + if (tunnel_obj == nullptr) { + return SAI_STATUS_FAILURE; + } + attr.id = SAI_TUNNEL_ATTR_TYPE; + CHECK_STATUS_W_MSG(tunnel_obj->get_attr(attr), "Missing SAI_TUNNEL_ATTR_TYPE in tunnel obj"); + + if (attr.value.s32 != SAI_TUNNEL_TYPE_VXLAN) { + SWSS_LOG_ERROR("Unsupported tunnel encap type %d in %s", attr.value.s32, + tunnel_obj->get_id().c_str()); + return SAI_STATUS_NOT_IMPLEMENTED; + } + + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + CHECK_STATUS_W_MSG(tunnel_obj->get_attr(attr), "Missing SAI_TUNNEL_ATTR_ENCAP_SRC_IP in tunnel obj"); + // SAI_TUNNEL_ATTR_ENCAP_TTL_MODE and SAI_TUNNEL_ATTR_ENCAP_TTL_VAL are not supported in vpp + src_ip = attr.value.ipaddr; + + attr.id = SAI_NEXT_HOP_ATTR_IP; + CHECK_STATUS_W_MSG(tunnel_nh_obj->get_attr(attr), "Missing SAI_NEXT_HOP_ATTR_IP in %s", tunnel_nh_obj->get_id().c_str()); + + dst_ip = attr.value.ipaddr; + + // Iterate tunnel encap mapper + auto tunnel_encap_mappers = tunnel_obj->get_linked_objects(SAI_OBJECT_TYPE_TUNNEL_MAP, SAI_TUNNEL_ATTR_ENCAP_MAPPERS); + + for (auto tunnel_encap_mapper : tunnel_encap_mappers) { + attr.id = SAI_TUNNEL_MAP_ATTR_TYPE; + CHECK_STATUS_W_MSG(tunnel_encap_mapper->get_attr(attr), + "Missing SAI_TUNNEL_MAP_ATTR_TYPE in %s", + tunnel_encap_mapper->get_id().c_str()); + if (attr.value.s32 != SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI) { + continue; + } + + auto tunnel_encap_mapper_entries = tunnel_encap_mapper->get_child_objs(SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY); + if (tunnel_encap_mapper_entries == nullptr) { + SWSS_LOG_DEBUG("Empty tunnel_encap_mapper table. OID %s", + tunnel_encap_mapper->get_id().c_str()); + continue; + } + for (auto pair : *tunnel_encap_mapper_entries) { + auto tunnel_encap_mapper_entry = pair.second; + vpp_vxlan_tunnel_t req; + u_int32_t tunnel_vni; + TunnelVSData tunnel_data; + + memset(&req, 0, sizeof(req)); + req.dst_port = m_vxlan_port; + req.src_port = m_vxlan_port; + req.instance = ~0; + sai_ip_address_t_to_vpp_ip_addr_t(src_ip, req.src_address); + sai_ip_address_t_to_vpp_ip_addr_t(dst_ip, req.dst_address); + req.decap_next_index = ~0; + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE; + CHECK_STATUS_W_MSG(tunnel_encap_mapper_entry->get_attr(attr), + "Missing SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY in %s", + tunnel_encap_mapper_entry->get_id().c_str()); + tunnel_vni = attr.value.u32; + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY; + CHECK_STATUS_W_MSG(tunnel_encap_mapper_entry->get_attr(attr), + "Missing SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY in %s", + tunnel_encap_mapper_entry->get_id().c_str()); + + auto ip_vrf = m_switch_db->vpp_get_ip_vrf(attr.value.oid); + if (!ip_vrf) { + SWSS_LOG_ERROR("Failed to find VR from SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY in %s", + tunnel_encap_mapper_entry->get_id().c_str()); + return SAI_STATUS_FAILURE; + } + vni_to_vrf_map[tunnel_vni] = ip_vrf; + tunnel_data.ip_vrf = ip_vrf; + req.vni = tunnel_vni; + + if (action == Action::CREATE) { + if (create_vpp_vxlan_encap(req, tunnel_data) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to create vxlan encap for %s", + tunnel_nh_obj->get_id().c_str()); + return SAI_STATUS_FAILURE; + } + + if (create_vpp_vxlan_decap(tunnel_data) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to create vxlan decap for %s", + tunnel_nh_obj->get_id().c_str()); + remove_vpp_vxlan_encap(req, tunnel_data); + return SAI_STATUS_FAILURE; + } + m_tunnel_encap_nexthop_map[object_id] = tunnel_data; + + } else if (action == Action::DELETE) { + auto encap_map_it = m_tunnel_encap_nexthop_map.find(object_id); + if (encap_map_it == m_tunnel_encap_nexthop_map.end()) { + SWSS_LOG_ERROR("Failed to find sw_if_index for %s", + tunnel_nh_obj->get_id().c_str()); + continue; + } + remove_vpp_vxlan_decap(encap_map_it->second); + remove_vpp_vxlan_encap(req, encap_map_it->second); + + m_tunnel_encap_nexthop_map.erase(encap_map_it); + } + } + } + return SAI_STATUS_SUCCESS; +} +sai_status_t +TunnelManager::create_tunnel_encap_nexthop( + _In_ const std::string& serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + + SaiCachedObject tunnel_nh_obj(m_switch_db, SAI_OBJECT_TYPE_NEXT_HOP, serializedObjectId, attr_count, attr_list); + return tunnel_encap_nexthop_action(&tunnel_nh_obj, Action::CREATE); +} + +sai_status_t +TunnelManager::remove_tunnel_encap_nexthop( + _In_ const std::string& serializedObjectId) +{ + SWSS_LOG_ENTER(); + auto tunnel_nh_obj = m_switch_db->get_sai_object(SAI_OBJECT_TYPE_NEXT_HOP, serializedObjectId); + + if (!tunnel_nh_obj) { + SWSS_LOG_ERROR("Failed to find SAI_OBJECT_TYPE_NEXT_HOP SaiObject: %s", serializedObjectId.c_str()); + return SAI_STATUS_FAILURE; + } + return tunnel_encap_nexthop_action(tunnel_nh_obj.get(), Action::DELETE); +} + +sai_status_t +TunnelManager::create_vpp_vxlan_encap( + _In_ vpp_vxlan_tunnel_t& req, + _Out_ TunnelVSData& tunnel_data) +{ + int vpp_status; + u_int32_t sw_if_index; + char src_ip_str[INET6_ADDRSTRLEN]; + char dst_ip_str[INET6_ADDRSTRLEN]; + auto router_mac = get_router_mac(); + auto bvi_mac = router_mac.data(); + + vpp_status = vpp_vxlan_tunnel_add_del(&req, 1, &sw_if_index); + vpp_ip_addr_t_to_string(&req.src_address, src_ip_str, INET6_ADDRSTRLEN); + vpp_ip_addr_t_to_string(&req.dst_address, dst_ip_str, INET6_ADDRSTRLEN); + SWSS_LOG_INFO("create vxlan tunnel src %s dst %s vni %d: sw_if_index,%d, status %d", + src_ip_str, dst_ip_str, + req.vni, sw_if_index, vpp_status); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to create vxlan tunnel"); + return SAI_STATUS_FAILURE; + } + tunnel_data.sw_if_index = sw_if_index; + /* the neighbour is to build inner ether. use no_fib_entry to avoid creating the nh in the fib, which will mess up underlay forwarding*/ + if (req.dst_address.sa_family == AF_INET6) { + ip6_nbr_add_del(NULL, sw_if_index, &req.dst_address.addr.ip6, false, true/*no_fib_entry*/, bvi_mac, 1); + } else { + ip4_nbr_add_del(NULL, sw_if_index, &req.dst_address.addr.ip4, false, true/*no_fib_entry*/, bvi_mac, 1); + } + SWSS_LOG_INFO("successfully created encap for vxlan tunnel %d", sw_if_index); + return SAI_STATUS_SUCCESS; +} + +sai_status_t +TunnelManager::remove_vpp_vxlan_encap( + _In_ vpp_vxlan_tunnel_t& req, + _In_ TunnelVSData& tunnel_data) +{ + int vpp_status; + u_int32_t sw_if_index = tunnel_data.sw_if_index; + char src_ip_str[INET6_ADDRSTRLEN]; + char dst_ip_str[INET6_ADDRSTRLEN]; + auto router_mac = get_router_mac(); + auto bvi_mac = router_mac.data(); + + if (req.dst_address.sa_family == AF_INET6) { + ip6_nbr_add_del(NULL, tunnel_data.sw_if_index, &req.dst_address.addr.ip6, false, true/*no_fib_entry*/, bvi_mac, 0); + } else { + ip4_nbr_add_del(NULL, tunnel_data.sw_if_index, &req.dst_address.addr.ip4, false, true/*no_fib_entry*/, bvi_mac, 0); + } + + vpp_status = vpp_vxlan_tunnel_add_del(&req, 0, &sw_if_index); + vpp_ip_addr_t_to_string(&req.src_address, src_ip_str, INET6_ADDRSTRLEN); + vpp_ip_addr_t_to_string(&req.dst_address, dst_ip_str, INET6_ADDRSTRLEN); + SWSS_LOG_INFO("delete vxlan tunnel src %s dst %s vni %d: sw_if_index %d, status %d", + src_ip_str, dst_ip_str, + req.vni, sw_if_index, vpp_status); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to delete vxlan tunnel"); + return SAI_STATUS_FAILURE; + } + return SAI_STATUS_SUCCESS; +} +sai_status_t +TunnelManager::create_vpp_vxlan_decap( + _Out_ TunnelVSData& tunnel_data) +{ + int vpp_status; + char hw_bvi_ifname[32]; + auto router_mac = get_router_mac(); + auto bvi_mac = router_mac.data(); + vpp_ip_route_t bvi_ip_prefix; + uint32_t tunnel_if_index = tunnel_data.sw_if_index; + SWSS_LOG_ENTER(); + //allocate bridge domain ID + int bd_id = m_switch_db->dynamic_bd_id_pool.alloc(); + if (bd_id == -1) { + SWSS_LOG_ERROR("Failed to allocate bridge domain ID"); + return SAI_STATUS_FAILURE; + } + tunnel_data.bd_id = bd_id; + //create bvi interface using instance same as bd_id + vpp_status = create_bvi_interface(bvi_mac, bd_id); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to create bvi interface"); + return SAI_STATUS_FAILURE; + } + // Get new list of physical interfaces from VS + refresh_interfaces_list(); + + //bring up bvi interface + snprintf(hw_bvi_ifname, sizeof(hw_bvi_ifname), "bvi%u", bd_id); + vpp_status = interface_set_state(hw_bvi_ifname, true); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to bring up bvi interface"); + return SAI_STATUS_FAILURE; + } + + //Create bridge and set BVI to the BD + vpp_status = set_sw_interface_l2_bridge(hw_bvi_ifname, bd_id, true, VPP_API_PORT_TYPE_BVI); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to add bvi interface to bd"); + return SAI_STATUS_FAILURE; + } + + //bind bvi to vrf + vpp_status = set_interface_vrf(hw_bvi_ifname, 0, tunnel_data.ip_vrf->m_vrf_id, tunnel_data.ip_vrf->m_is_ipv6); + + //set bvi IPv4 + uint16_t offset = (uint16_t)((uint16_t)(bd_id - SwitchVpp::dynamic_bd_id_base) + 2); + + bvi_ip_prefix.prefix_len = 32; + bvi_ip_prefix.prefix_addr.sa_family = AF_INET; + struct sockaddr_in *sin = &bvi_ip_prefix.prefix_addr.addr.ip4; + sin->sin_addr.s_addr = htonl(offset); + vpp_status = interface_ip_address_add_del(hw_bvi_ifname, &bvi_ip_prefix, true); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to config IP on bvi interface"); + return SAI_STATUS_FAILURE; + } + + //set bvi IPv6 (the same tunnel can carry ipv4 or ipv6) + bvi_ip_prefix.prefix_len = 128; + bvi_ip_prefix.prefix_addr.sa_family = AF_INET6; + struct sockaddr_in6 *sin6 = &bvi_ip_prefix.prefix_addr.addr.ip6; + memset(&sin6->sin6_addr, 0, sizeof(struct in6_addr)); + sin6->sin6_addr.s6_addr[14] = (uint8_t)(offset >> 8) & 0xFF; + sin6->sin6_addr.s6_addr[15] = (uint8_t)(offset & 0xFF); + vpp_status = interface_ip_address_add_del(hw_bvi_ifname, &bvi_ip_prefix, true); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to config IP on bvi interface"); + return SAI_STATUS_FAILURE; + } + + //set vxlan tunnel to bridge domain + vpp_status = set_sw_interface_l2_bridge_by_index(tunnel_if_index, bd_id, true, VPP_API_PORT_TYPE_NORMAL); + if (vpp_status != 0) { + SWSS_LOG_ERROR("Failed to add tunnel interface to bd"); + return SAI_STATUS_FAILURE; + } + SWSS_LOG_INFO("successfully created decap for vxlan tunnel %d with BD %d", + tunnel_if_index, bd_id); + return SAI_STATUS_SUCCESS; +} + +sai_status_t +TunnelManager::remove_vpp_vxlan_decap( + _In_ TunnelVSData& tunnel_data) +{ + char hw_bvi_ifname[32]; + + SWSS_LOG_ENTER(); + snprintf(hw_bvi_ifname, sizeof(hw_bvi_ifname), "bvi%u", tunnel_data.bd_id); + + delete_bvi_interface(hw_bvi_ifname); + + refresh_interfaces_list(); + //bd is create automatically when the fist interface is add to it but requires manual deletion + vpp_bridge_domain_add_del(tunnel_data.bd_id, false); + SWSS_LOG_INFO("successfully deleted decap of vxlan tunnel %d with BD %d", + tunnel_data.sw_if_index, tunnel_data.bd_id); + return SAI_STATUS_SUCCESS; +} diff --git a/vslib/TunnelManager.h b/vslib/TunnelManager.h new file mode 100644 index 000000000..72a9281ec --- /dev/null +++ b/vslib/TunnelManager.h @@ -0,0 +1,126 @@ +#pragma once + +#include "SwitchVpp.h" +#include "vppxlate/SaiVppXlate.h" + +namespace saivs +{ + class SwitchVpp; + enum class Action { + CREATE, + UPDATE, + DELETE + }; + /** + * @brief The TunnelVSData class represents the VS data associated with a tunnel. + */ + class TunnelVSData { + public: + TunnelVSData() : sw_if_index(0), encap_vrf_id(0) {} + u_int32_t sw_if_index; + u_int32_t encap_vrf_id; + u_int32_t bd_id; + vpp_ip_addr_t bvi_addr; + std::shared_ptr ip_vrf; + }; + + class TunnelManager { + public: + TunnelManager(SwitchVpp* switch_db); + + // sai_status_t create_tunnel_map_entry( + // _In_ const std::string &serializedObjectId, + // _In_ sai_object_id_t switch_id, + // _In_ uint32_t attr_count, + // _In_ const sai_attribute_t *attr_list); + + /** + * @brief Create a tunnel encap nexthop entry. + * + * This function creates a tunnel encap nexthop entry with the specified attributes. + * + * @param serializedObjectId The serialized object ID of the tunnel encap nexthop entry. + * @param switch_id The switch ID. + * @param attr_count The number of attributes in the attribute list. + * @param attr_list The attribute list. + * @return The status of the operation. + */ + sai_status_t create_tunnel_encap_nexthop( + _In_ const std::string& serializedObjectId, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list); + + + /** + * @brief Remove a tunnel encap nexthop entry. + * + * This function removes the tunnel encap nexthop entry with the specified serialized object ID. + * + * @param serializedObjectId The serialized object ID of the tunnel encap nexthop entry to be removed. + * @return The status of the operation. + */ + sai_status_t remove_tunnel_encap_nexthop( + _In_ const std::string& serializedObjectId); + + /** + * @brief Get the tunnel interface index based on the given nexthop OID. + * + * This method returns the tunnel interface associated with the given nexthop OID. + * + * @param nexthop_oid The nexthop OID. + * @param sw_if_index The output parameter to store the tunnel interface index. + * @return The status of the operation. + */ + sai_status_t get_tunnel_if( + _In_ sai_object_id_t nexthop_oid, + _Out_ u_int32_t &sw_if_index) { + auto it = m_tunnel_encap_nexthop_map.find(nexthop_oid); + if (it != m_tunnel_encap_nexthop_map.end()) { + sw_if_index = it->second.sw_if_index; + return SAI_STATUS_SUCCESS; + } + return SAI_STATUS_ITEM_NOT_FOUND; + } + /** + * @brief Set VxLAN router default MAC address. + */ + void set_router_mac(const sai_attribute_t* attr); + + /** + * @brief Get VxLAN router default MAC address. + */ + const std::array& get_router_mac() const; + + /** + * @brief Set VxLAN port. + */ + void set_vxlan_port(const sai_attribute_t* attr); + private: + SwitchVpp* m_switch_db; + std::array m_router_mac; + u_int16_t m_vxlan_port; + //nexthop SAI object ID to sw_if_index map + std::unordered_map m_tunnel_encap_nexthop_map; + + sai_status_t tunnel_encap_nexthop_action( + _In_ const SaiObject* tunnel_nh_obj, + _In_ Action action); + + sai_status_t create_vpp_vxlan_encap( + _In_ vpp_vxlan_tunnel_t& req, + _Out_ TunnelVSData& tunnel_data); + + sai_status_t remove_vpp_vxlan_encap( + _In_ vpp_vxlan_tunnel_t& req, + _In_ TunnelVSData& tunnel_data); + + sai_status_t create_vpp_vxlan_decap( + _Out_ TunnelVSData& tunnel_data); + + sai_status_t remove_vpp_vxlan_decap( + _In_ TunnelVSData& tunnel_data); + + }; + +} diff --git a/vslib/VirtualSwitchSaiInterface.cpp b/vslib/VirtualSwitchSaiInterface.cpp index d866f5ac1..0df3ad61f 100644 --- a/vslib/VirtualSwitchSaiInterface.cpp +++ b/vslib/VirtualSwitchSaiInterface.cpp @@ -15,6 +15,7 @@ #include "SwitchBCM56971B0.h" #include "SwitchMLNX2700.h" #include "SwitchNvdaMBF2H536C.h" +#include "SwitchVpp.h" #include @@ -95,7 +96,7 @@ std::shared_ptr VirtualSwitchSaiInterface::extractWarmBootState( return state; } -bool VirtualSwitchSaiInterface::validate_switch_warm_boot_atributes( +bool VirtualSwitchSaiInterface::validate_switch_warm_boot_attributes( _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) const { @@ -601,6 +602,11 @@ std::shared_ptr VirtualSwitchSaiInterface::init_switch( m_switchStateMap[switch_id] = std::make_shared(switch_id, m_realObjectIdManager, config, warmBootState); break; + case SAI_VS_SWITCH_TYPE_VPP: + + m_switchStateMap[switch_id] = std::make_shared(switch_id, m_realObjectIdManager, config, warmBootState); + break; + default: SWSS_LOG_WARN("unknown switch type: %d", config->m_switchType); @@ -668,7 +674,7 @@ sai_status_t VirtualSwitchSaiInterface::create( if (config->m_bootType == SAI_VS_BOOT_TYPE_WARM) { - if (!validate_switch_warm_boot_atributes(attr_count, attr_list)) + if (!validate_switch_warm_boot_attributes(attr_count, attr_list)) { SWSS_LOG_ERROR("invalid attribute passed during warm boot"); diff --git a/vslib/VirtualSwitchSaiInterface.h b/vslib/VirtualSwitchSaiInterface.h index 25855fdec..058ab7407 100644 --- a/vslib/VirtualSwitchSaiInterface.h +++ b/vslib/VirtualSwitchSaiInterface.h @@ -267,7 +267,7 @@ namespace saivs std::shared_ptr extractWarmBootState( _In_ sai_object_id_t switch_id); - bool validate_switch_warm_boot_atributes( + bool validate_switch_warm_boot_attributes( _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) const; diff --git a/vslib/VirtualSwitchSaiInterfaceFdb.cpp b/vslib/VirtualSwitchSaiInterfaceFdb.cpp index bba539ed9..61ff2e651 100644 --- a/vslib/VirtualSwitchSaiInterfaceFdb.cpp +++ b/vslib/VirtualSwitchSaiInterfaceFdb.cpp @@ -1,9 +1,11 @@ #include "VirtualSwitchSaiInterface.h" #include "SwitchStateBase.h" +#include "SwitchVpp.h" -#include "swss/logger.h" #include "meta/sai_serialize.h" +#include "swss/logger.h" + using namespace saivs; bool VirtualSwitchSaiInterface::doesFdbEntryNotMatchFlushAttr( @@ -279,6 +281,13 @@ sai_status_t VirtualSwitchSaiInterface::flushFdbEntries( data.fdb_entry.bv_id = vlanid->value.oid; } + auto vpp = std::dynamic_pointer_cast(ss); + + if (vpp) + { + vpp->vpp_fdbentry_flush(switch_id, attr_count, attr_list); + } + if (static_fdbs.size()) { SWSS_LOG_NOTICE("flushing %zu static entries", static_fdbs.size()); diff --git a/vslib/saivs.h b/vslib/saivs.h index a68da6e84..0c7a02703 100644 --- a/vslib/saivs.h +++ b/vslib/saivs.h @@ -92,6 +92,7 @@ extern "C" { #define SAI_VALUE_VS_SWITCH_TYPE_MLNX2700 "SAI_VS_SWITCH_TYPE_MLNX2700" #define SAI_VALUE_VS_SWITCH_TYPE_NVDA_MBF2H536C "SAI_VS_SWITCH_TYPE_NVDA_MBF2H536C" #define SAI_VALUE_VS_SWITCH_TYPE_DPU_SIMU_2P "SAI_VS_SWITCH_TYPE_DPU_SIMU_2P" +#define SAI_VALUE_VS_SWITCH_TYPE_VPP "SAI_VS_SWITCH_TYPE_VPP" /* * Values for SAI_KEY_BOOT_TYPE (defined in saiswitch.h) diff --git a/vslib/vppxlate/SaiAclStats.c b/vslib/vppxlate/SaiAclStats.c new file mode 100644 index 000000000..111815341 --- /dev/null +++ b/vslib/vppxlate/SaiAclStats.c @@ -0,0 +1,68 @@ +/* + *------------------------------------------------------------------ + * SaiAclStats.c + * + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include + +#include "SaiVppStats.h" +#include "SaiAclStats.h" + +static void handle_stat_two (const char *stat_name, uint32_t ace_index, + uint64_t count1, uint64_t count2, void *data) +{ + vpp_ace_stats_t *st_p = (vpp_ace_stats_t *) data; + + if (st_p->ace_index == ace_index) { + st_p->packets = count1; + st_p->bytes = count2; + } +} + +int vpp_acl_ace_stats_query (uint32_t acl_index, uint32_t ace_index, vpp_ace_stats_t *stats) +{ + char pathbuf[256]; + + snprintf(pathbuf, sizeof(pathbuf), "/acl/%u/matches", acl_index); + + memset(stats, 0, sizeof(*stats)); + stats->ace_index = ace_index; + + return vpp_stats_dump(pathbuf, NULL, handle_stat_two, stats); +} + +#ifdef MAIN +void classify_get_trace_chain(void){}; + +void +os_exit (int code) +{ + exit (code); +} + +int main (int argc, char *argv[]) +{ + vpp_interface_stats_t stats; + + if (argc > 1) + { + memset(&stats, 0, sizeof(stats)); + vpp_stats_dump(argv[1], handle_stat_one, handle_stat_two, &stats); + } + exit(0); +} +#endif diff --git a/vslib/vppxlate/SaiAclStats.h b/vslib/vppxlate/SaiAclStats.h new file mode 100644 index 000000000..cba94dd12 --- /dev/null +++ b/vslib/vppxlate/SaiAclStats.h @@ -0,0 +1,40 @@ +/* + *------------------------------------------------------------------ + * SaiAclStats.h + * + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#ifndef _SAIACLSTATS_H_ +#define _SAIACLSTATS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct vpp_ace_stats_ { + uint64_t packets; + uint64_t bytes; + uint32_t ace_index; + } vpp_ace_stats_t; + + int vpp_acl_ace_stats_query(uint32_t acl_index, uint32_t ace_index, + vpp_ace_stats_t *stats); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vslib/vppxlate/SaiIntfStats.c b/vslib/vppxlate/SaiIntfStats.c new file mode 100644 index 000000000..5809a90e3 --- /dev/null +++ b/vslib/vppxlate/SaiIntfStats.c @@ -0,0 +1,164 @@ +/* + *------------------------------------------------------------------ + * SaiIntfStats.c + * + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include + +#include "SaiVppStats.h" +#include "SaiIntfStats.h" + +#define DROPS "drops" +#define PUNT "punt" +#define IP4 "ip4" +#define IP6 "ip6" +#define RX_NO_BUF "rx-no-buf" +#define RX_MISS "rx-miss" +#define RX_ERROR "rx-error" +#define TX_ERROR "tx-error" +#define MPLS "mpls" +#define RX "rx" +#define RX_UNICAST "rx-unicast" +#define RX_MULTICAST "rx-multicast" +#define RX_BROADCAST "rx-broadcast" +#define TX "tx" +#define TX_UNICAST "tx-unicast" +#define TX_MULTICAST "tx-multicast" +#define TX_BROADCAST "tx-broadcast" + +static void handle_stat_one (const char *stat_name, uint32_t index, + uint64_t count, void *data) +{ + vpp_interface_stats_t *st_p = (vpp_interface_stats_t *) data; + + if (!strncmp(stat_name, DROPS, sizeof(DROPS))) + { + st_p->drops = count; + } + else if (!strncmp(stat_name, PUNT, sizeof(PUNT))) + { + st_p->punt = count; + } + else if (!strncmp(stat_name, IP4, sizeof(IP4))) + { + st_p->ip4 = count; + } + else if (!strncmp(stat_name, IP6, sizeof(IP6))) + { + st_p->ip6 = count; + } + else if (!strncmp(stat_name, RX_NO_BUF, sizeof(RX_NO_BUF))) + { + st_p->rx_no_buf = count; + } + else if (!strncmp(stat_name, RX_MISS, sizeof(RX_MISS))) + { + st_p->rx_miss = count; + } + else if (!strncmp(stat_name, RX_ERROR, sizeof(RX_ERROR))) + { + st_p->rx_error = count; + } + else if (!strncmp(stat_name, TX_ERROR, sizeof(TX_ERROR))) + { + st_p->tx_error = count; + } + else if (!strncmp(stat_name, MPLS, sizeof(MPLS))) + { + st_p->mpls = count; + } +} + +static void handle_stat_two (const char *stat_name, uint32_t index, + uint64_t count1, uint64_t count2, void *data) +{ + vpp_interface_stats_t *st_p = (vpp_interface_stats_t *) data; + + if (!strncmp(stat_name, RX, sizeof(RX))) + { + st_p->rx = count1; + st_p->rx_bytes = count2; + } + else if (!strncmp(stat_name, TX, sizeof(TX))) + { + st_p->tx = count1; + st_p->tx_bytes = count2; + } + else if (!strncmp(stat_name, RX_UNICAST, sizeof(RX_UNICAST))) + { + st_p->rx_unicast = count1; + st_p->rx_unicast_bytes = count2; + } + else if (!strncmp(stat_name, RX_MULTICAST, sizeof(RX_MULTICAST))) + { + st_p->rx_multicast = count1; + st_p->rx_multicast_bytes = count2; + } + else if (!strncmp(stat_name, RX_BROADCAST, sizeof(RX_BROADCAST))) + { + st_p->rx_broadcast = count1; + st_p->rx_broadcast_bytes = count2; + } + else if (!strncmp(stat_name, TX_UNICAST, sizeof(TX_UNICAST))) + { + st_p->tx_unicast = count1; + st_p->tx_unicast_bytes = count2; + } + else if (!strncmp(stat_name, TX_MULTICAST, sizeof(TX_MULTICAST))) + { + st_p->tx_multicast = count1; + st_p->tx_multicast_bytes = count2; + } + else if (!strncmp(stat_name, TX_BROADCAST, sizeof(TX_BROADCAST))) + { + st_p->tx_broadcast = count1; + st_p->tx_broadcast_bytes = count2; + } +} + +int vpp_intf_stats_query (const char *intf_name, vpp_interface_stats_t *stats) +{ + char pathbuf[256]; + + snprintf(pathbuf, sizeof(pathbuf), "/interfaces/%s/", intf_name); + + memset(stats, 0, sizeof(*stats)); + + return vpp_stats_dump(pathbuf, handle_stat_one, handle_stat_two, stats); +} + +#ifdef MAIN +void classify_get_trace_chain(void){}; + +void +os_exit (int code) +{ + exit (code); +} + +int main (int argc, char *argv[]) +{ + vpp_interface_stats_t stats; + + if (argc > 1) + { + memset(&stats, 0, sizeof(stats)); + vpp_stats_dump(argv[1], handle_stat_one, handle_stat_two, &stats); + } + exit(0); +} +#endif diff --git a/vslib/vppxlate/SaiIntfStats.h b/vslib/vppxlate/SaiIntfStats.h new file mode 100644 index 000000000..76466a24a --- /dev/null +++ b/vslib/vppxlate/SaiIntfStats.h @@ -0,0 +1,64 @@ +/* + *------------------------------------------------------------------ + * SaiIntfStats.h + * + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#ifndef _SAI_INTF_STATS_H_ +#define _SAI_INTF_STATS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct vpp_interface_stats_ { + uint64_t drops; + uint64_t punt; + uint64_t ip4; + uint64_t ip6; + uint64_t rx_no_buf; + uint64_t rx_miss; + uint64_t rx_error; + uint64_t tx_error; + uint64_t mpls; + + uint64_t rx; + uint64_t rx_bytes; + uint64_t rx_unicast; + uint64_t rx_unicast_bytes; + uint64_t rx_multicast; + uint64_t rx_multicast_bytes; + uint64_t rx_broadcast; + uint64_t rx_broadcast_bytes; + + uint64_t tx; + uint64_t tx_bytes; + uint64_t tx_unicast; + uint64_t tx_unicast_bytes; + uint64_t tx_multicast; + uint64_t tx_multicast_bytes; + uint64_t tx_broadcast; + uint64_t tx_broadcast_bytes; + +} vpp_interface_stats_t; + +int vpp_intf_stats_query(const char *intf_name, vpp_interface_stats_t *stats); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/vslib/vppxlate/SaiVppStats.c b/vslib/vppxlate/SaiVppStats.c new file mode 100644 index 000000000..c2564b8cd --- /dev/null +++ b/vslib/vppxlate/SaiVppStats.c @@ -0,0 +1,153 @@ +/* + *------------------------------------------------------------------ + * SaiVppStats.c + * + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include +#include +#include "SaiVppStats.h" + +static int is_stats_inited = 0; +static __thread stat_client_main_t vpp_stat_client_main; + +static int vpp_stats_init() +{ + if (is_stats_inited) return 0; + + is_stats_inited = 1; + return 0; +} + +int +vpp_stats_dump (const char *query_path, vpp_stat_one one, vpp_stat_two two, void *data) +{ + u8 *stat_segment_name, *pattern, **patterns = 0; + int rv; + const char *sname; + + vpp_stats_init(); + + stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE; + + pattern = (u8 *) query_path; + vec_add1 (patterns, pattern); + + rv = stat_segment_connect_r ((char *) stat_segment_name, &vpp_stat_client_main); + if (rv) + { + SAIVPP_STAT_ERR("Couldn't connect to vpp, does %s exist?\n", + stat_segment_name); + return -1; + } + + u32 *dir; + int i, j, k; + stat_segment_data_t *res; + + dir = stat_segment_ls_r (patterns, &vpp_stat_client_main); + if (!dir) + { + return -1; + } + res = stat_segment_dump_r (dir, &vpp_stat_client_main); + if (!res) + { + return -1; + } + + for (i = 0; i < vec_len (res); i++) + { + switch (res[i].type) + { + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + if (res[i].simple_counter_vec == 0) + continue; + for (k = 0; k < vec_len (res[i].simple_counter_vec); k++) + for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++) + { + SAIVPP_STAT_DBG("[%d @ %d]: %lu packets %s\n", + j, k, res[i].simple_counter_vec[k][j], + res[i].name); + sname = strrchr(res[i].name, '/'); + if (sname) + { + sname++; + if (one) + { + one(sname, j, res[i].simple_counter_vec[k][j], data); + } + } + } + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + if (res[i].combined_counter_vec == 0) + continue; + for (k = 0; k < vec_len (res[i].combined_counter_vec); k++) + for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++) + { + SAIVPP_STAT_DBG("[%d @ %d]: %lu packets, %lu bytes %s\n", + j, k, res[i].combined_counter_vec[k][j].packets, + res[i].combined_counter_vec[k][j].bytes, + res[i].name); + sname = strrchr(res[i].name, '/'); + if (sname) + { + sname++; + if (two) + { + two(sname, j, res[i].combined_counter_vec[k][j].packets, + res[i].combined_counter_vec[k][j].bytes, data); + } + } + } + break; + + case STAT_DIR_TYPE_SCALAR_INDEX: + SAIVPP_STAT_DBG("%.2f %s\n", res[i].scalar_value, res[i].name); + break; + + case STAT_DIR_TYPE_NAME_VECTOR: + if (res[i].name_vector == 0) + continue; + for (k = 0; k < vec_len (res[i].name_vector); k++) + if (res[i].name_vector[k]) + SAIVPP_STAT_DBG("[%d]: %s %s\n", k, res[i].name_vector[k], + res[i].name); + break; + + case STAT_DIR_TYPE_EMPTY: + break; + + default: + ; + } + } + stat_segment_data_free (res); + + stat_segment_disconnect_r (&vpp_stat_client_main); + + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vslib/vppxlate/SaiVppStats.h b/vslib/vppxlate/SaiVppStats.h new file mode 100644 index 000000000..f228b7ced --- /dev/null +++ b/vslib/vppxlate/SaiVppStats.h @@ -0,0 +1,32 @@ +/* + *------------------------------------------------------------------ + * SaiVppStats.h + * + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#ifndef _SAI_VPP_STATS_H_ +#define _SAI_VPP_STATS_H_ + +typedef void (*vpp_stat_one)(const char *, uint32_t index, uint64_t, void *); +typedef void (*vpp_stat_two)(const char *, uint32_t index, uint64_t, uint64_t, void *); + +int vpp_stats_dump(const char *query_path, vpp_stat_one one, vpp_stat_two two, void *data); + +#define SAIVPP_STAT_DBG(format,args...) {} +// #define SAIVPP_STAT_DBG clib_warning +#define SAIVPP_STAT_ERR clib_error + +#endif diff --git a/vslib/vppxlate/SaiVppXlate.c b/vslib/vppxlate/SaiVppXlate.c new file mode 100644 index 000000000..efa5eb1f4 --- /dev/null +++ b/vslib/vppxlate/SaiVppXlate.c @@ -0,0 +1,3199 @@ +/* + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "SaiVppXlate.h" + +#include + +#include + +/* Declare message IDs */ +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +/* l2 API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include +#undef vl_printfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 l2_api_version = v; +#include +#undef vl_api_version + +/* tunterm API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 tunterm_api_version = v; +#include +#undef vl_api_version + +/* interface API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include + +#undef vl_printfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 interface_api_version = v; +#include +#undef vl_api_version + +/* ipv4 API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include + +#undef vl_printfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 ip_api_version = v; +#include +#undef vl_api_version + +/* ip neighbor API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include + +#undef vl_printfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 ip_neighbor_api_version = v; +#include +#undef vl_api_version + +/* linux_cp API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 lcp_api_version = v; +#include +#undef vl_api_version + +/* acl API inclusion */ + +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 acl_api_version = v; +#include +#undef vl_api_version + +/* vxlan API inclusion */ +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 vxlan_api_version = v; +#include +#undef vl_api_version + +/* memclnt API inclusion */ + +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include +#undef vl_printfun + +/* instantiate all the endian swap functions we know about */ +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +/* +#define vl_api_version(n, v) static u32 memclnt_api_version = v; +#include +#undef vl_api_version +*/ + +/*include bfd */ +#define vl_typedefs +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_printfun +#include +#undef vl_printfun + +#define vl_calcsizefun +#include +#undef vl_calcsizefun + +#define vl_api_version(n, v) static u32 bfd_api_version = v; +#include +#undef vl_api_version + + +void classify_get_trace_chain(void ){} +void os_exit(int code) {} + +#define SAIVPP_DEBUG(format,args...) {} +#define SAIVPP_WARN clib_warning +#define SAIVPP_ERROR clib_error + +/** + * Wait for result and retry if necessary. The retry is necessary because there could be unsolicited + * events causing vl_socket_client_read to return before the expected result is received. If + * vam->result_ready is not set, which should be set when API callback function is called, then + * it means we get some unsolicited events and we need to retry. + */ +#define WR(ret) \ +do { \ + f64 timeout = vat_time_now (vam) + 1.0; \ + socket_client_main_t *scm = vam->socket_client_main; \ + ret = -99; \ + while (vat_time_now (vam) < timeout) { \ + if (scm && scm->socket_enable) \ + vl_socket_client_read (5); \ + if (vam->result_ready == 1) { \ + ret = vam->retval; \ + break; \ + } \ + vat_suspend (vam->vlib_main, 1e-5); \ + } \ +} while(0); + +#define VPP_MAX_CTX 2 +typedef struct _vpp_index_map_ { + uint8_t index_map; + uintptr_t ptr[VPP_MAX_CTX]; +} vpp_index_map_t; + +static vpp_index_map_t idx_map; + +static vpp_event_queue_t vpp_ev_queue, *vpp_evq_p; + +static void vpp_ev_enqueue (vpp_event_info_t *ev) +{ + *vpp_evq_p->tail = ev; + ev->next = NULL; + vpp_evq_p->tail = &ev->next; +} + +vpp_event_info_t * vpp_ev_dequeue () +{ + vpp_event_info_t *evp; + + evp = vpp_evq_p->head; + if (evp) { + vpp_evq_p->head = vpp_evq_p->head->next; + } + if (vpp_evq_p->head == NULL) { + vpp_evq_p->tail = &vpp_evq_p->head; + } + + return evp; +} + +void vpp_ev_free (vpp_event_info_t *ev) +{ + free(ev); +} + +static void vpp_evq_init () +{ + vpp_evq_p = &vpp_ev_queue; + + vpp_evq_p->head = NULL; + vpp_evq_p->tail = &vpp_evq_p->head; + vpp_evq_p->free = vpp_ev_free; +} + +static int vpp_acl_counters_enable_disable(bool enable); +static int vpp_intf_events_enable_disable(bool enable); +static int vpp_bfd_events_enable_disable(bool enable); +static int vpp_bfd_udp_enable_multihop(); + +static pthread_mutex_t vpp_mutex; + +void vpp_mutex_lock_init () +{ + pthread_mutex_init(&vpp_mutex, NULL); +} + +void vpp_mutex_lock () +{ + pthread_mutex_lock(&vpp_mutex); +} + +void vpp_mutex_unlock () +{ + pthread_mutex_unlock(&vpp_mutex); +} + +#define VPP_LOCK() vpp_mutex_lock() +#define VPP_UNLOCK() vpp_mutex_unlock() + +/* + * Right now configuration is done synchronously in a single thread. + * When the need arises for multiple requests in pipeline we can move to a pool to + * allocate index. + */ +static int alloc_index () +{ + return 1; +} + +static uint32_t store_ptr (void *ptr) +{ + int idx = alloc_index(); + + assert(idx >= 0); + idx_map.ptr[idx] = (uintptr_t) ptr; + + return idx; +} + +static void release_index (uint32_t idx) +{ +} + +static uintptr_t get_index_ptr (uint32_t idx) +{ + if (idx > VPP_MAX_CTX) { + return (uintptr_t) NULL; + } + + return idx_map.ptr[idx]; +} + +vat_main_t vat_main; +uword *interface_name_by_sw_index = NULL; + +f64 +vat_time_now (vat_main_t * vam) +{ +#if VPP_API_TEST_BUILTIN + return vlib_time_now (vam->vlib_main); +#else + return clib_time_now (&vam->clib_time); +#endif +} + +void __clib_no_tail_calls +vat_suspend (vlib_main_t *vm, f64 interval) +{ + const struct timespec req = {0, 100000000}; + nanosleep(&req, NULL); +} + +/* + * vl_msg_api_set_handlers + * preserve the old API for a while +*/ +static void +vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup, + void *endian, int size, int traced, + void *tojson, void *fromjson, void *calc_size) +{ + vl_msg_api_msg_config_t cfg; + vl_msg_api_msg_config_t *c = &cfg; + + clib_memset (c, 0, sizeof (*c)); + + c->id = id; + c->name = name; + c->handler = handler; + c->cleanup = cleanup; + c->endian = endian; + c->traced = traced; + c->replay = 1; + c->message_bounce = 0; + c->is_mp_safe = 0; + c->is_autoendian = 0; + c->tojson = tojson; + c->fromjson = fromjson; + c->calc_size = calc_size; + vl_msg_api_config (c); +} + +static bool vl_api_to_vpp_ip_addr(vl_api_address_t *vpp_addr, vpp_ip_addr_t *ipaddr) +{ + bool ret = true; + if (vpp_addr->af == ADDRESS_IP4) + { + ipaddr->sa_family = AF_INET; + struct sockaddr_in *ip4 = &(ipaddr->addr.ip4); + memcpy(&(ip4->sin_addr.s_addr), &vpp_addr->un.ip4, sizeof(ip4->sin_addr.s_addr)); + + } + else if (vpp_addr->af == ADDRESS_IP6) + { + ipaddr->sa_family = AF_INET6; + struct sockaddr_in6 *ip6 = &(ipaddr->addr.ip6); + memcpy(&(ip6->sin6_addr.s6_addr), &vpp_addr->un.ip6, sizeof(ip6->sin6_addr.s6_addr)); + } + else + { + return false; + } + return ret; +} + +static bool vpp_to_vl_api_ip_addr(vl_api_address_t *vpp_addr, vpp_ip_addr_t *ipaddr) +{ + bool ret = true; + if (ipaddr->sa_family == AF_INET) + { + struct sockaddr_in *ip4 = &(ipaddr->addr.ip4); + vpp_addr->af = ADDRESS_IP4; + memcpy(&vpp_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(vpp_addr->un.ip4)); + } + else if (ipaddr->sa_family == AF_INET6) + { + struct sockaddr_in6 *ip6 = &(ipaddr->addr.ip6); + vpp_addr->af = ADDRESS_IP6; + memcpy(&vpp_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(vpp_addr->un.ip6)); + } + else + { + return false; + } + return ret; +} + +void +vl_noop_handler (void *mp) +{ +} + +static void set_reply_status (int retval) +{ + vat_main_t *vam = &vat_main; + + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + } + else + { + vam->retval = retval; + vam->result_ready = 1; + } +} + +static void set_reply_sw_if_index (vl_api_interface_index_t sw_if_index) +{ + vat_main_t *vam = &vat_main; + vam->sw_if_index = sw_if_index; +} + +static void +vl_api_control_ping_reply_t_handler (vl_api_control_ping_reply_t *mp) +{ + vat_main_t *vam = &vat_main; + + set_reply_status(ntohl (mp->retval)); + + if (vam->socket_client_main) + vam->socket_client_main->control_pings_outstanding--; +} + +static void +vl_api_want_interface_events_reply_t_handler (vl_api_want_interface_events_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw interface events enable %s(%d)", + msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_sw_interface_event_t_handler (vl_api_sw_interface_event_t *mp) +{ + uint32_t flags, sw_if_index; + uword *ptr; + + sw_if_index = htonl(mp->sw_if_index); + ptr = hash_get(interface_name_by_sw_index, sw_if_index); + if (NULL == ptr) { + SAIVPP_WARN("vpp cannot get interface name for sw index %u", sw_if_index); + return; + } + const char *hw_ifname = (const char *) ptr[0]; + + flags = htonl(mp->flags); + if (flags & IF_STATUS_API_FLAG_ADMIN_UP && + !(flags & IF_STATUS_API_FLAG_LINK_UP)) { + return; + } + bool link_up; + if (flags & IF_STATUS_API_FLAG_LINK_UP) { + link_up = true; + } else { + link_up = false; + } + SAIVPP_WARN("Sending vpp link %s event for interface %s index %u", + link_up ? "UP" : "DOWN", hw_ifname, sw_if_index); + + vpp_event_info_t *evinfo; + evinfo = calloc(1, sizeof(*evinfo)); + + if (evinfo) { + evinfo->type = VPP_INTF_LINK_STATUS; + vpp_intf_status_t *stp = &evinfo->data.intf_status; + + stp->link_up = link_up; + strncpy(stp->hwif_name, hw_ifname, sizeof(stp->hwif_name) -1); + + vpp_ev_enqueue(evinfo); + } +} + +static void +vl_api_sw_interface_details_t_handler (vl_api_sw_interface_details_t *mp) +{ + if (mp->context) { + bool *link_up = (bool *) get_index_ptr(mp->context); + *link_up = ntohl(mp->flags) & IF_STATUS_API_FLAG_LINK_UP ? true : false; + return; + } + vat_main_t *vam = &vat_main; + u8 *s = format (0, "%s%c", mp->interface_name, 0); + + hash_set_mem (vam->sw_if_index_by_interface_name, s, + ntohl (mp->sw_if_index)); + hash_set (interface_name_by_sw_index, ntohl (mp->sw_if_index), s); + + /* In sub interface case, fill the sub interface table entry */ + if (mp->sw_if_index != mp->sup_sw_if_index) + { + sw_interface_subif_t *sub = NULL; + + vec_add2 (vam->sw_if_subif_table, sub, 1); + + vec_validate (sub->interface_name, strlen ((char *) s) + 1); + strncpy ((char *) sub->interface_name, (char *) s, + vec_len (sub->interface_name)); + sub->sw_if_index = ntohl (mp->sw_if_index); + sub->sub_id = ntohl (mp->sub_id); + + sub->raw_flags = ntohl (mp->sub_if_flags & SUB_IF_API_FLAG_MASK_VNET); + + sub->sub_number_of_tags = mp->sub_number_of_tags; + sub->sub_outer_vlan_id = ntohs (mp->sub_outer_vlan_id); + sub->sub_inner_vlan_id = ntohs (mp->sub_inner_vlan_id); + + /* vlan tag rewrite */ + sub->vtr_op = ntohl (mp->vtr_op); + sub->vtr_push_dot1q = ntohl (mp->vtr_push_dot1q); + sub->vtr_tag1 = ntohl (mp->vtr_tag1); + sub->vtr_tag2 = ntohl (mp->vtr_tag2); + } +} + +static void +vl_api_create_loopback_instance_reply_t_handler ( + vl_api_create_loopback_instance_reply_t * msg) +{ + vat_main_t *vam = &vat_main; + + /*set_reply_sw_if_index(ntohl(msg->sw_if_index));*/ + set_reply_status(ntohl(msg->retval)); +} + +static void +vl_api_delete_loopback_reply_t_handler ( + vl_api_delete_loopback_reply_t * msg) +{ + set_reply_status(ntohl(msg->retval)); +} + +static void +vl_api_create_subif_reply_t_handler (vl_api_create_subif_reply_t *msg) +{ + vat_main_t *vam = &vat_main; + + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("subinterface creation %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_delete_subif_reply_t_handler (vl_api_delete_subif_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("subinterface deletion %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_sw_interface_set_table_reply_t_handler (vl_api_sw_interface_set_table_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw interface vrf set %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_sw_interface_add_del_address_reply_t_handler (vl_api_sw_interface_add_del_address_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw interface address add/del %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_sw_interface_set_flags_reply_t_handler (vl_api_sw_interface_set_flags_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw interface state set %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_sw_interface_set_mtu_reply_t_handler (vl_api_sw_interface_set_mtu_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw interface mtu set %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} +static void +vl_api_sw_interface_set_mac_address_reply_t_handler (vl_api_sw_interface_set_mac_address_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw interface mac set %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} +static void +vl_api_hw_interface_set_mtu_reply_t_handler (vl_api_hw_interface_set_mtu_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("hw interface mtu set %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_ip_table_add_del_reply_t_handler (vl_api_ip_table_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("ip vrf add %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_ip_route_add_del_reply_t_handler (vl_api_ip_route_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("ip route add %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_set_ip_flow_hash_v2_reply_t_handler (vl_api_ip_route_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("ip flow has set %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_ip_neighbor_add_del_reply_t_handler (vl_api_ip_neighbor_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("ip neighbor add/del %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_bridge_domain_add_del_reply_t_handler (vl_api_bridge_domain_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("l2 add/del %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2 add del reply handler called %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} +static void +vl_api_sw_interface_set_l2_bridge_reply_t_handler (vl_api_sw_interface_set_l2_bridge_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("sw inteface set l2 bridge reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2 add del reply handler called %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} +static void +vl_api_l2_interface_vlan_tag_rewrite_reply_t_handler (vl_api_l2_interface_vlan_tag_rewrite_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("l2 interface vlan tag rewrite reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2 add del reply handler called %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} +static void +vl_api_bvi_create_reply_t_handler (vl_api_bvi_create_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_WARN("bvi create reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_bvi_delete_reply_t_handler (vl_api_bvi_delete_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_WARN("bvi delete reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_bridge_flags_reply_t_handler (vl_api_bridge_flags_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_WARN("bridge flags reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_l2fib_add_del_reply_t_handler (vl_api_l2fib_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("l2fib add del reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2fib add del reply handler %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} +static void +vl_api_l2fib_flush_all_reply_t_handler (vl_api_l2fib_flush_all_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("l2fib flush all reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2fib flush all reply handler %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} +static void +vl_api_l2fib_flush_int_reply_t_handler (vl_api_l2fib_flush_int_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("l2fib flush int reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2fib flush int reply handler %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} + +static void +vl_api_l2fib_flush_bd_reply_t_handler (vl_api_l2fib_flush_bd_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("l2fib flush bd reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + //SAIVPP_ERROR("l2fib flush bd reply handler %s(%d)",msg->retval ? "failed" : "successful", msg->retval); + +} + +static void +vl_api_bfd_udp_add_reply_t_handler (vl_api_bfd_udp_add_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("bfd udp add reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + +} + +static void +vl_api_bfd_udp_del_reply_t_handler (vl_api_bfd_udp_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("bfd udp del reply handler %s(%d)", msg->retval ? "failed" : "successful", msg->retval); + +} + +static void +vl_api_want_bfd_events_reply_t_handler (vl_api_want_bfd_events_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("bfd events enable %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_bfd_udp_enable_multihop_reply_t_handler (vl_api_bfd_udp_enable_multihop_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("bfd enable multihop %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_bfd_udp_session_event_t_handler (vl_api_bfd_udp_session_event_t *msg) +{ + bool multihop = (htonl(msg->sw_if_index) == ~0); + + SAIVPP_WARN("Sending bfd state change event, multihop: %d, sw_if_index: %d, " + "state: %d ", + multihop, htonl(msg->sw_if_index), htonl(msg->state)); + + vpp_event_info_t *evinfo; + evinfo = calloc(1, sizeof(*evinfo)); + + vpp_ip_addr_t vpp_local_addr, vpp_peer_addr; + memset(&vpp_local_addr, 0, sizeof(vl_api_address_t)); + memset(&vpp_peer_addr, 0, sizeof(vl_api_address_t)); + + if (evinfo) { + evinfo->type = VPP_BFD_STATE_CHANGE; + vpp_bfd_state_notif_t *bfd_notif = &evinfo->data.bfd_notif; + + bfd_notif->multihop = multihop; + bfd_notif->sw_if_index = htonl(msg->sw_if_index); + bfd_notif->state = htonl(msg->state); + + if(!((true == vl_api_to_vpp_ip_addr(&msg->local_addr, &bfd_notif->local_addr)) && \ + (true == vl_api_to_vpp_ip_addr(&msg->peer_addr, &bfd_notif->peer_addr)))) + { + SAIVPP_WARN("Invalid IP address passed from vpp for bfd event"); + return; + } + + vpp_ev_enqueue(evinfo); + } + + set_reply_status(0); + + SAIVPP_DEBUG("BFD udp session event, multihop: %d, sw_if_index: %d, " + "state: %d ", + multihop, htonl(msg->sw_if_index), htonl(msg->state)); +} + +static void +vl_api_bridge_domain_details_t_handler (vl_api_bridge_domain_details_t *mp) +{ + + if (mp->context) { + u32 *member_count = (u32 *) get_index_ptr(mp->context); + *member_count = ntohl(mp->n_sw_ifs); + SAIVPP_WARN("bridge member count: %d",ntohl(mp->n_sw_ifs)); + return; + } + return; +} + +static void +vl_api_vxlan_add_del_tunnel_v3_reply_t_handler ( + vl_api_vxlan_add_del_tunnel_v3_reply_t * msg) +{ + vat_main_t *vam = &vat_main; + + set_reply_sw_if_index(ntohl(msg->sw_if_index)); + + set_reply_status(ntohl(msg->retval)); + SAIVPP_DEBUG("vxlan_add_del handler: if_idx,%d,status,%d",vam->sw_if_index, vam->retval); +} + +static void +vl_api_tunterm_acl_add_replace_reply_t_handler(vl_api_tunterm_acl_add_replace_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + uint32_t *tunterm_index = (uint32_t *) get_index_ptr(msg->context); + *tunterm_index = ntohl(msg->tunterm_acl_index); + + SAIVPP_DEBUG("tunterm acl add_replace %s(%d) tunterm_index index %u", msg->retval ? "failed" : "successful", + msg->retval, *tunterm_index); + release_index(msg->context); +} + +static void +vl_api_tunterm_acl_del_reply_t_handler(vl_api_tunterm_acl_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("tunterm acl del %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_tunterm_acl_interface_add_del_reply_t_handler(vl_api_tunterm_acl_interface_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("tunterm acl interface set/reset %s(%d)", msg->retval ? "failed" : "successful", + msg->retval); +} + +#define vl_api_get_first_msg_id_reply_t_handler vl_noop_handler +#define vl_api_get_first_msg_id_reply_t_handler_json vl_noop_handler + +#define MEMCLNT_MSG_ID(id) VL_API_##id + +#define foreach_vpe_base_api_reply_msg \ + _(MEMCLNT_MSG_ID(GET_FIRST_MSG_ID_REPLY), get_first_msg_id_reply) \ + _(MEMCLNT_MSG_ID(CONTROL_PING_REPLY), control_ping_reply) + +static u16 interface_msg_id_base, memclnt_msg_id_base, __plugin_msg_base; +static u16 l2_msg_id_base, vxlan_msg_id_base; +static u16 tunterm_msg_id_base; +static u16 bfd_msg_id_base; + +static void vpp_base_vpe_init(void) +{ +#define _(N,n) \ + vl_msg_api_set_handlers(N+1, \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + sizeof(vl_api_##n##_t), 1, \ + vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson, \ + vl_api_##n##_t_calc_size); + + foreach_vpe_base_api_reply_msg; +#undef _ +} + +#define INTERFACE_MSG_ID(id) \ + (VL_API_##id + interface_msg_id_base) + +#define IP_MSG_ID(id) \ + (VL_API_##id + ip_msg_id_base) + +#define IP_NBR_MSG_ID(id) \ + (VL_API_##id + ip_nbr_msg_id_base) + +#define L2_MSG_ID(id) \ + (VL_API_##id + l2_msg_id_base) + +#define BFD_MSG_ID(id) \ + (VL_API_##id + bfd_msg_id_base) + +#define foreach_vpe_ext_api_reply_msg \ + _(INTERFACE_MSG_ID(SW_INTERFACE_DETAILS), sw_interface_details) \ + _(INTERFACE_MSG_ID(CREATE_LOOPBACK_INSTANCE_REPLY), create_loopback_instance_reply) \ + _(INTERFACE_MSG_ID(DELETE_LOOPBACK_REPLY), delete_loopback_reply) \ + _(INTERFACE_MSG_ID(CREATE_SUBIF_REPLY), create_subif_reply) \ + _(INTERFACE_MSG_ID(DELETE_SUBIF_REPLY), delete_subif_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_SET_TABLE_REPLY), sw_interface_set_table_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_ADD_DEL_ADDRESS_REPLY), sw_interface_add_del_address_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_SET_FLAGS_REPLY), sw_interface_set_flags_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_SET_MTU_REPLY), sw_interface_set_mtu_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_SET_MAC_ADDRESS_REPLY), sw_interface_set_mac_address_reply) \ + _(INTERFACE_MSG_ID(HW_INTERFACE_SET_MTU_REPLY), hw_interface_set_mtu_reply) \ + _(INTERFACE_MSG_ID(WANT_INTERFACE_EVENTS), want_interface_events_reply) \ + _(INTERFACE_MSG_ID(SW_INTERFACE_EVENT), sw_interface_event) \ + _(IP_MSG_ID(IP_TABLE_ADD_DEL_REPLY), ip_table_add_del_reply) \ + _(IP_MSG_ID(IP_ROUTE_ADD_DEL_REPLY), ip_route_add_del_reply) \ + _(IP_MSG_ID(SET_IP_FLOW_HASH_V2_REPLY), set_ip_flow_hash_v2_reply) \ + _(IP_NBR_MSG_ID(IP_NEIGHBOR_ADD_DEL_REPLY), ip_neighbor_add_del_reply) \ + _(L2_MSG_ID(BRIDGE_DOMAIN_ADD_DEL_REPLY), bridge_domain_add_del_reply) \ + _(L2_MSG_ID(SW_INTERFACE_SET_L2_BRIDGE_REPLY), sw_interface_set_l2_bridge_reply) \ + _(L2_MSG_ID(L2_INTERFACE_VLAN_TAG_REWRITE_REPLY), l2_interface_vlan_tag_rewrite_reply) \ + _(L2_MSG_ID(BRIDGE_DOMAIN_DETAILS), bridge_domain_details) \ + _(L2_MSG_ID(BVI_CREATE_REPLY), bvi_create_reply) \ + _(L2_MSG_ID(BVI_DELETE_REPLY), bvi_delete_reply) \ + _(L2_MSG_ID(BRIDGE_FLAGS_REPLY), bridge_flags_reply) \ + _(L2_MSG_ID(L2FIB_ADD_DEL_REPLY), l2fib_add_del_reply) \ + _(L2_MSG_ID(L2FIB_FLUSH_ALL_REPLY), l2fib_flush_all_reply) \ + _(L2_MSG_ID(L2FIB_FLUSH_INT_REPLY), l2fib_flush_int_reply) \ + _(L2_MSG_ID(L2FIB_FLUSH_BD_REPLY), l2fib_flush_bd_reply) \ + _(BFD_MSG_ID(BFD_UDP_ADD_REPLY), bfd_udp_add_reply) \ + _(BFD_MSG_ID(BFD_UDP_DEL_REPLY), bfd_udp_del_reply) \ + _(BFD_MSG_ID(BFD_UDP_SESSION_EVENT), bfd_udp_session_event) \ + _(BFD_MSG_ID(WANT_BFD_EVENTS), want_bfd_events_reply) \ + _(BFD_MSG_ID(BFD_UDP_ENABLE_MULTIHOP), bfd_udp_enable_multihop_reply) \ + + +static u16 interface_msg_id_base, ip_msg_id_base, ip_nbr_msg_id_base, lcp_msg_id_base, memclnt_msg_id_base, __plugin_msg_base; +static u16 acl_msg_id_base; + +static void vpp_ext_vpe_init(void) +{ +#define _(N,n) \ + vl_msg_api_set_handlers(N, \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + sizeof(vl_api_##n##_t), 1, \ + vl_api_##n##_t_tojson, \ + vl_api_##n##_t_fromjson, \ + vl_api_##n##_t_calc_size); + + foreach_vpe_ext_api_reply_msg; +#undef _ +} + +static void vl_api_lcp_itf_pair_add_del_reply_t_handler(vl_api_lcp_itf_pair_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("linux_cp hostif creation %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void vl_api_acl_add_replace_reply_t_handler(vl_api_acl_add_replace_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + uint32_t *acl_index = (uint32_t *) get_index_ptr(msg->context); + *acl_index = ntohl(msg->acl_index); + + SAIVPP_DEBUG("acl add_replace %s(%d) acl index %u", msg->retval ? "failed" : "successful", + msg->retval, *acl_index); + release_index(msg->context); +} + +static void vl_api_acl_del_reply_t_handler(vl_api_acl_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("acl del %s(%d)", msg->retval ? "failed" : "successful", msg->retval); +} + +static void +vl_api_acl_stats_intf_counters_enable_reply_t_handler (vl_api_acl_stats_intf_counters_enable_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("acl counters enable %s", msg->retval ? "failed" : "successful"); +} + +static void +vl_api_acl_interface_add_del_reply_t_handler(vl_api_acl_interface_add_del_reply_t *msg) +{ + set_reply_status(ntohl(msg->retval)); + + SAIVPP_DEBUG("acl interface set/reset %s(%d)", msg->retval ? "failed" : "successful", + msg->retval); +} + +#define LCP_MSG_ID(id) \ + (VL_API_##id + lcp_msg_id_base) + +#define ACL_MSG_ID(id) \ + (VL_API_##id + acl_msg_id_base) + +#define TUNTERM_MSG_ID(id) \ + (VL_API_##id + tunterm_msg_id_base) + +#define VXLAN_MSG_ID(id) \ + (VL_API_##id + vxlan_msg_id_base) + +#define foreach_vpe_plugin_api_reply_msg \ + _(LCP_MSG_ID(LCP_ITF_PAIR_ADD_DEL_REPLY), lcp_itf_pair_add_del_reply) \ + _(ACL_MSG_ID(ACL_ADD_REPLACE_REPLY), acl_add_replace_reply) \ + _(ACL_MSG_ID(ACL_DEL_REPLY), acl_del_reply) \ + _(ACL_MSG_ID(ACL_STATS_INTF_COUNTERS_ENABLE_REPLY), acl_stats_intf_counters_enable_reply) \ + _(ACL_MSG_ID(ACL_INTERFACE_ADD_DEL_REPLY), acl_interface_add_del_reply) \ + _(VXLAN_MSG_ID(VXLAN_ADD_DEL_TUNNEL_V3_REPLY), vxlan_add_del_tunnel_v3_reply) \ + _(TUNTERM_MSG_ID(TUNTERM_ACL_INTERFACE_ADD_DEL_REPLY), tunterm_acl_interface_add_del_reply) \ + _(TUNTERM_MSG_ID(TUNTERM_ACL_DEL_REPLY), tunterm_acl_del_reply) \ + _(TUNTERM_MSG_ID(TUNTERM_ACL_ADD_REPLACE_REPLY), tunterm_acl_add_replace_reply) +static void vpp_plugin_vpe_init(void) +{ +#define _(N,n) \ + vl_msg_api_set_handlers(N, \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + sizeof(vl_api_##n##_t), 1, \ + vl_noop_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_calc_size); + + foreach_vpe_plugin_api_reply_msg; +#undef _ +} + +static void get_base_msg_id() +{ + u8 *msg_base_lookup_name = format (0, "interface_%08x%c", interface_api_version, 0); + interface_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(interface_msg_id_base != (u16) ~0); + + msg_base_lookup_name = format (0, "ip_%08x%c", ip_api_version, 0); + ip_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(ip_msg_id_base != (u16) ~0); + + msg_base_lookup_name = format (0, "ip_neighbor_%08x%c", ip_neighbor_api_version, 0); + ip_nbr_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(ip_nbr_msg_id_base != (u16) ~0); + + msg_base_lookup_name = format (0, "lcp_%08x%c", lcp_api_version, 0); + lcp_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(lcp_msg_id_base != (u16) ~0); + + msg_base_lookup_name = format (0, "acl_%08x%c", acl_api_version, 0); + acl_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(acl_msg_id_base != (u16) ~0); + + msg_base_lookup_name = format (0, "l2_%08x%c", l2_api_version, 0); + l2_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(l2_msg_id_base != (u16) ~0); + //SAIVPP_ERROR("DELME: l2_msg_id_base %s msg_base_lookup_name:%s l2_api_version:%08x\n", l2_msg_id_base,msg_base_lookup_name,l2_api_version); + //printf("DELME: New change added l2_msg_id_base %s\n", l2_msg_id_base); + + msg_base_lookup_name = format (0, "bfd_%08x%c", bfd_api_version, 0); + bfd_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(bfd_msg_id_base != (u16) ~0); + + memclnt_msg_id_base = 0; + + msg_base_lookup_name = format (0, "vxlan_%08x%c", vxlan_api_version, 0); + vxlan_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(vxlan_msg_id_base != (u16) ~0); + + msg_base_lookup_name = format (0, "tunterm_acl_%08x%c", tunterm_api_version, 0); + tunterm_msg_id_base = vl_client_get_first_plugin_msg_id ((char *) msg_base_lookup_name); + assert(tunterm_msg_id_base != (u16) ~0); +} + +#define API_SOCKET_FILE "/run/vpp/api.sock" +#define VPP_SOCKET_PATH API_SOCKET_FILE + +typedef struct _vsclient_main_ { + socket_client_main_t *socket_client_main; + char *socket_name; + u32 my_client_index; +} vsclient_main_t; + +static int +vsc_socket_connect (vat_main_t * vam, char *client_name) +{ + int rv; + api_main_t *am = vlibapi_get_main (); + vam->socket_client_main = &socket_client_main; + if ((rv = vl_socket_client_connect ((char *) vam->socket_name, + client_name, + 0 /* default socket rx, tx buffer */ ))) + return rv; + + /* vpp expects the client index in network order */ + vam->my_client_index = htonl (socket_client_main.client_index); + am->my_client_index = vam->my_client_index; + return 0; +} + +typedef struct +{ + u8 *name; + u32 value; +} name_sort_t; + +int +api_sw_interface_dump (vat_main_t *vam) +{ + vl_api_sw_interface_dump_t *mp; + vl_api_control_ping_t *mp_ping; + hash_pair_t *p; + name_sort_t *nses = 0, *ns; + sw_interface_subif_t *sub = NULL; + int ret; + + VPP_LOCK(); + + /* Toss the old name table */ + hash_foreach_pair (p, vam->sw_if_index_by_interface_name, ({ + vec_add2 (nses, ns, 1); + ns->name = (u8 *) (p->key); + ns->value = (u32) p->value[0]; + })); + + hash_free (vam->sw_if_index_by_interface_name); + + vec_foreach (ns, nses) + vec_free (ns->name); + + vec_free (nses); + + /* Interface name is from the index_table which is already freed */ + hash_free (interface_name_by_sw_index); + + vec_foreach (sub, vam->sw_if_subif_table) + { + vec_free (sub->interface_name); + } + vec_free (vam->sw_if_subif_table); + + /* recreate the interface name hash table */ + vam->sw_if_index_by_interface_name = hash_create_string (0, sizeof (uword)); + __plugin_msg_base = interface_msg_id_base; + /* + * Ask for all interface names. Otherwise, the epic catalog of + * name filters becomes ridiculously long, and vat ends up needing + * to be taught about new interface types. + */ + M (SW_INTERFACE_DUMP, mp); + S (mp); + + /* Use a control ping for synchronization */ + __plugin_msg_base = memclnt_msg_id_base; + + PING (NULL, mp_ping); + S (mp_ping); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int +name_sort_cmp (void *a1, void *a2) +{ + name_sort_t *n1 = a1; + name_sort_t *n2 = a2; + + return strcmp ((char *) n1->name, (char *) n2->name); +} + +static int +dump_interface_table (vat_main_t *vam) +{ + hash_pair_t *p; + name_sort_t *nses = 0, *ns; + + if (vam->json_output) + { + clib_warning ( + "JSON output supported only for VPE API calls and dump_stats_table"); + return -99; + } + + hash_foreach_pair (p, vam->sw_if_index_by_interface_name, ({ + vec_add2 (nses, ns, 1); + ns->name = (u8 *) (p->key); + ns->value = (u32) p->value[0]; + })); + + vec_sort_with_function (nses, name_sort_cmp); + + print (vam->ofp, "%-25s%-15s", "Interface", "sw_if_index"); + vec_foreach (ns, nses) + { + print (vam->ofp, "%-25s%-15d", ns->name, ns->value); + } + vec_free (nses); + return 0; +} + +static u32 get_swif_idx (vat_main_t *vam, const char *ifname) +{ + hash_pair_t *p; + u8 *name; + u32 value; + + hash_foreach_pair (p, vam->sw_if_index_by_interface_name, ({ + name = (u8 *) (p->key); + value = (u32) p->value[0]; + if (strcmp((char *) name, ifname) == 0) return value; + })); + return ((u32) -1); +} + +static int config_lcp_hostif (vat_main_t *vam, + vl_api_interface_index_t if_idx, + const char *hostif_name, + bool is_add) +{ + vl_api_lcp_itf_pair_add_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = lcp_msg_id_base; + + M (LCP_ITF_PAIR_ADD_DEL, mp); + mp->is_add = is_add; + mp->sw_if_index = htonl(if_idx); + strncpy((char *) mp->host_if_name, hostif_name, sizeof(mp->host_if_name)); + mp->host_if_type = LCP_API_ITF_HOST_TAP; + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int delete_lcp_hostif (vat_main_t *vam, + vl_api_interface_index_t if_idx, + const char *hostif_name) +{ + vl_api_lcp_itf_pair_add_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = lcp_msg_id_base; + + M (LCP_ITF_PAIR_ADD_DEL, mp); + mp->is_add = false; + mp->sw_if_index = htonl(if_idx); + strncpy((char *) mp->host_if_name, hostif_name, sizeof(mp->host_if_name)); + mp->host_if_type = LCP_API_ITF_HOST_TAP; + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int __create_loopback_instance (vat_main_t *vam, u32 instance) +{ + vl_api_create_loopback_instance_t *mp; + int ret; + u8 mac_address[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (CREATE_LOOPBACK, mp); + mp->is_specified = true; + mp->user_instance = instance; + /* Set MAC address */ + memcpy(mp->mac_address, mac_address, sizeof(mac_address)); + + /* create loopback interfaces from vnet/interface_cli.c */ + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int __delete_loopback (vat_main_t *vam, const char *hwif_name, u32 instance) +{ + vl_api_delete_loopback_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (DELETE_LOOPBACK, mp); + u32 idx; + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int __create_sub_interface (vat_main_t *vam, vl_api_interface_index_t if_idx, u32 sub_id, u16 vlan_id) +{ + vl_api_create_subif_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (CREATE_SUBIF, mp); + mp->sw_if_index = htonl(if_idx); + mp->sub_id = htonl(sub_id); + mp->outer_vlan_id = htons(vlan_id); + + /* create_sub_interfaces() from vnet/interface_cli.c */ + mp->sub_if_flags = htonl(SUB_IF_API_FLAG_EXACT_MATCH | SUB_IF_API_FLAG_ONE_TAG); + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int __delete_sub_interface (vat_main_t *vam, vl_api_interface_index_t if_idx) +{ + vl_api_create_subif_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (DELETE_SUBIF, mp); + mp->sw_if_index = htonl(if_idx); + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static __thread int vpp_client_init; + +int init_vpp_client() +{ + if (vpp_client_init) return 0; + + vat_main_t *vam = &vat_main; + + vpp_mutex_lock_init(); + + clib_mem_init_thread_safe(0, 128 << 20); + vlib_main_init(); + clib_time_init (&vam->clib_time); + /* Set up the plugin message ID allocator right now... */ + vl_msg_api_set_first_available_msg_id (VL_MSG_MEMCLNT_LAST + 1); + + vpp_base_vpe_init(); + vam->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0); + vam->sw_if_index_by_interface_name = hash_create_string (0, sizeof (uword)); + interface_name_by_sw_index = hash_create (0, sizeof (uword)); + + if (vsc_socket_connect(vam, "sonic_vpp_api_client") == 0) { + int rc; + + SAIVPP_DEBUG("vpp socket connect successful\n"); + get_base_msg_id(); + vpp_ext_vpe_init(); + vpp_plugin_vpe_init(); + + rc = api_sw_interface_dump(vam); + if (rc == 0) { + SAIVPP_DEBUG("Interface dump available"); + } + dump_interface_table(vam); + + vpp_acl_counters_enable_disable(true); + + /* + * SONiC periodically polls the port status so currently there is no need for + * async notification. This also simplifies the synchronous design of saivpp. + * Revisit the async mechanism if there is greater reason. + */ + vpp_intf_events_enable_disable(true); + + /* Register with VPP for BFD notifications */ + vpp_bfd_events_enable_disable(true); + + /* Enable BFD multihop support in VPP */ + vpp_bfd_udp_enable_multihop(); + + vpp_evq_init(); + vpp_client_init = 1; + return 0; + } else { + SAIVPP_ERROR("vpp socket connect failed\n"); + } + return -1; +} + +int refresh_interfaces_list () +{ + vat_main_t *vam = &vat_main; + int rc; + + rc = api_sw_interface_dump(vam); + if (rc == 0) { + SAIVPP_DEBUG("Interface dump available"); + } + dump_interface_table(vam); + + return rc; +} + +int configure_lcp_interface (const char *hwif_name, const char *hostif_name, bool is_add) +{ + u32 idx; + vat_main_t *vam = &vat_main; + + idx = get_swif_idx(vam, hwif_name); + SAIVPP_DEBUG("swif index of interface %s is %u\n", hwif_name, idx); + + return config_lcp_hostif(vam, idx, hostif_name, is_add); +} + +int create_loopback_instance (const char *hwif_name, u32 instance) +{ + vat_main_t *vam = &vat_main; + return __create_loopback_instance(vam, instance); +} + +int delete_loopback (const char *hwif_name, u32 instance) +{ + vat_main_t *vam = &vat_main; + return __delete_loopback(vam, hwif_name, instance); +} + +int create_sub_interface (const char *hwif_name, u32 sub_id, u16 vlan_id) +{ + u32 idx; + vat_main_t *vam = &vat_main; + + idx = get_swif_idx(vam, hwif_name); + SAIVPP_DEBUG("swif index of interface %s is %u\n", hwif_name, idx); + + return __create_sub_interface(vam, idx, sub_id, vlan_id); +} + +int delete_sub_interface (const char *hwif_name, u32 sub_id) +{ + u32 idx; + vat_main_t *vam = &vat_main; + char tmpbuf[64]; + + snprintf(tmpbuf, sizeof(tmpbuf), "%s.%u", hwif_name, sub_id); + idx = get_swif_idx(vam, tmpbuf); + SAIVPP_DEBUG("swif index of interface %s is %u\n", tmpbuf, idx); + return __delete_sub_interface(vam, idx); +} + +static int __set_interface_vrf (vat_main_t *vam, vl_api_interface_index_t if_idx, + u32 vrf_id, bool is_ipv6) +{ + vl_api_sw_interface_set_table_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_SET_TABLE, mp); + mp->sw_if_index = htonl(if_idx); + mp->vrf_id = htonl(vrf_id); + mp->is_ipv6 = is_ipv6; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int set_interface_vrf (const char *hwif_name, u32 sub_id, u32 vrf_id, bool is_ipv6) +{ + u32 idx; + vat_main_t *vam = &vat_main; + char tmpbuf[64]; + + if (sub_id) { + snprintf(tmpbuf, sizeof(tmpbuf), "%s.%u", hwif_name, sub_id); + hwif_name = tmpbuf; + } + idx = get_swif_idx(vam, hwif_name); + SAIVPP_DEBUG("swif index of interface %s is %u\n", hwif_name, idx); + + return __set_interface_vrf(vam, idx, vrf_id, is_ipv6); +} + +static int vpp_intf_events_enable_disable (bool enable) +{ + vat_main_t *vam = &vat_main; + vl_api_want_interface_events_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (WANT_INTERFACE_EVENTS, mp); + mp->enable_disable = enable; + mp->pid = htonl(getpid()); + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int __ip_vrf_add_del (vat_main_t *vam, u32 vrf_id, + const char *vrf_name, bool is_ipv6, bool is_add) +{ + vl_api_ip_table_add_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = ip_msg_id_base; + + M (IP_TABLE_ADD_DEL, mp); + mp->is_add = is_add; + mp->table.is_ip6 = is_ipv6; + mp->table.table_id = htonl(vrf_id); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int ip_vrf_add (u32 vrf_id, const char *vrf_name, bool is_ipv6) +{ + vat_main_t *vam = &vat_main; + + return (__ip_vrf_add_del(vam, vrf_id, vrf_name, is_ipv6, true)); +} + +int ip_vrf_del (u32 vrf_id, const char *vrf_name, bool is_ipv6) +{ + vat_main_t *vam = &vat_main; + + return (__ip_vrf_add_del(vam, vrf_id, vrf_name, is_ipv6, false)); +} + +static int __ip_nbr_add_del (vat_main_t *vam, vl_api_address_t *nbr_addr, u32 if_idx, + uint8_t *mac, bool is_static, bool no_fib_entry, bool is_add) +{ + vl_api_ip_neighbor_add_del_t *mp; + int ret; + VPP_LOCK(); + + __plugin_msg_base = ip_nbr_msg_id_base; + + M (IP_NEIGHBOR_ADD_DEL, mp); + mp->is_add = is_add; + mp->neighbor.flags = IP_API_NEIGHBOR_FLAG_NONE; + if (is_static) { + mp->neighbor.flags |= IP_API_NEIGHBOR_FLAG_STATIC; + } + if (no_fib_entry) { + mp->neighbor.flags |= IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY; + } + mp->neighbor.sw_if_index = htonl(if_idx); + mp->neighbor.ip_address = *nbr_addr; + memcpy(mp->neighbor.mac_address, mac, sizeof(mp->neighbor.mac_address)); + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + return ret; +} + +static int ip_nbr_add_del (const char *hwif_name, uint32_t sw_if_index, struct sockaddr *addr, + bool is_static, bool no_fib_entry, uint8_t *mac, bool is_add) +{ + vat_main_t *vam = &vat_main; + + vl_api_address_t api_addr; + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = (struct sockaddr_in *) addr; + api_addr.af = ADDRESS_IP4; + memcpy(api_addr.un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr.un.ip4)); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *) addr; + api_addr.af = ADDRESS_IP6; + memcpy(api_addr.un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr.un.ip6)); + } else { + return -EINVAL; + } + if (sw_if_index == ~0) { + sw_if_index = get_swif_idx(vam, hwif_name); + } + + + return __ip_nbr_add_del(vam, &api_addr, sw_if_index, mac, is_static, no_fib_entry, is_add); +} + +int ip4_nbr_add_del (const char *hwif_name, uint32_t sw_if_index, struct sockaddr_in *addr, bool is_static, bool no_fib_entry, uint8_t *mac, bool is_add) +{ + return ip_nbr_add_del(hwif_name, sw_if_index, (struct sockaddr *) addr, is_static, no_fib_entry, mac, is_add); +} + +int ip6_nbr_add_del (const char *hwif_name, uint32_t sw_if_index, struct sockaddr_in6 *addr, bool is_static, bool no_fib_entry, uint8_t *mac, bool is_add) +{ + return ip_nbr_add_del(hwif_name, sw_if_index, (struct sockaddr *) addr, is_static, no_fib_entry, mac, is_add); +} + +int ip_route_add_del (vpp_ip_route_t *prefix, bool is_add) +{ + u32 idx, path_count = 1; + vat_main_t *vam = &vat_main; + vpp_ip_addr_t *addr; + vl_api_ip_route_t *ip_route; + vl_api_address_t *api_addr; + vl_api_ip_route_add_del_t *mp; + int ret; + + VPP_LOCK(); + + path_count = prefix->nexthop_cnt; + + __plugin_msg_base = ip_msg_id_base; + + M2 (IP_ROUTE_ADD_DEL, mp, sizeof (vl_api_fib_path_t) * path_count); + ip_route = &mp->route; + + api_addr = &ip_route->prefix.address; + addr = &prefix->prefix_addr; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + api_addr->af = ADDRESS_IP4; + memcpy(api_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr->un.ip4)); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + api_addr->af = ADDRESS_IP6; + memcpy(api_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr->un.ip6)); + } else { + VPP_UNLOCK(); + return -EINVAL; + } + ip_route->prefix.len = prefix->prefix_len; + ip_route->n_paths = path_count; + + for (unsigned int i = 0; i < path_count; i++) { + vpp_ip_nexthop_t *nexthop = &prefix->nexthop[i]; + vl_api_fib_path_t *fib_path = &ip_route->paths[i]; + vl_api_address_union_t *nh_addr = &fib_path->nh.address; + memset (fib_path, 0, sizeof (*fib_path)); + if (nexthop->sw_if_index != (u32) - 1) { + fib_path->sw_if_index = htonl(nexthop->sw_if_index); + } + else if (nexthop->hwif_name) { + idx = get_swif_idx(vam, nexthop->hwif_name); + if (idx != (u32) -1) { + fib_path->sw_if_index = htonl(idx); + } else { + printf("Unable to get sw_index for %s\n", nexthop->hwif_name); + } + } else { + fib_path->sw_if_index = htonl(~0); + } + + addr = &nexthop->addr; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + memcpy(nh_addr->ip4, &ip4->sin_addr.s_addr, sizeof(nh_addr->ip4)); + fib_path->proto = htonl(FIB_API_PATH_NH_PROTO_IP4); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + memcpy(nh_addr->ip6, &ip6->sin6_addr.s6_addr, sizeof(nh_addr->ip6)); + fib_path->proto = htonl(FIB_API_PATH_NH_PROTO_IP6); + } else { + VPP_UNLOCK(); + return -EINVAL; + } + if (nexthop->type == VPP_NEXTHOP_NORMAL) { + fib_path->type = htonl(FIB_API_PATH_TYPE_NORMAL); + } else if (nexthop->type == VPP_NEXTHOP_LOCAL) { + fib_path->type = htonl(FIB_API_PATH_TYPE_LOCAL); + } + fib_path->table_id = 0; + fib_path->rpf_id = htonl(~0); + fib_path->weight = nexthop->weight; + fib_path->preference = nexthop->preference; + fib_path->n_labels = 0; + } + ip_route->table_id = htonl(prefix->vrf_id); + + mp->is_add = is_add; + mp->is_multipath = prefix->is_multipath; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +static unsigned int ipv4_mask_len (uint32_t mask) +{ + u64 val = 0; + + memcpy(&val, &mask, sizeof(mask)); + return (unsigned int) count_set_bits(val); +} + +static unsigned int ipv6_mask_len (uint8_t *mask) +{ + u64 val = 0, len; + + memcpy(&val, mask, 8); + len = count_set_bits(val); + + memcpy(&val, &mask[8], 8); + len += count_set_bits(val); + + return (unsigned int) len; +} + +/* + * acl_index is set with the index returned by the VPP API reply. + */ +int vpp_acl_add_replace (vpp_acl_t *in_acl, uint32_t *acl_index, bool is_replace) +{ + u32 idx, acl_count; + vat_main_t *vam = &vat_main; + vpp_ip_addr_t *addr; + vpp_acl_rule_t *in_rule; + vl_api_address_t *api_addr; + vl_api_acl_rule_t *vpp_rule; + vl_api_acl_add_replace_t *mp; + int ret; + + init_vpp_client(); + + VPP_LOCK(); + + acl_count = in_acl->count; + + __plugin_msg_base = acl_msg_id_base; + + M2 (ACL_ADD_REPLACE, mp, sizeof (vl_api_acl_rule_t) * acl_count); + + mp->count = htonl(acl_count); + + if (is_replace) { + mp->acl_index = htonl(*acl_index); + } else { + mp->acl_index = htonl(~0); + } + strncpy(mp->tag, in_acl->acl_name, sizeof (mp->tag) - 1); + for (idx = 0; idx < acl_count; idx++) { + in_rule = &in_acl->rules[idx]; + vpp_rule = &mp->r[idx]; + + addr = &in_rule->src_prefix; + api_addr = &vpp_rule->src_prefix.address; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + api_addr->af = ADDRESS_IP4; + memcpy(api_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr->un.ip4)); + vpp_rule->src_prefix.len = ipv4_mask_len(in_rule->src_prefix_mask.addr.ip4.sin_addr.s_addr); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + api_addr->af = ADDRESS_IP6; + memcpy(api_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr->un.ip6)); + vpp_rule->src_prefix.len = ipv6_mask_len(in_rule->src_prefix_mask.addr.ip6.sin6_addr.s6_addr); + } else { + SAIVPP_WARN("Unknown protocol in source prefix"); + /* return -EINVAL; */ + } + + addr = &in_rule->dst_prefix; + api_addr = &vpp_rule->dst_prefix.address; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + api_addr->af = ADDRESS_IP4; + memcpy(api_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr->un.ip4)); + vpp_rule->dst_prefix.len = ipv4_mask_len(in_rule->dst_prefix_mask.addr.ip4.sin_addr.s_addr); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + api_addr->af = ADDRESS_IP6; + memcpy(api_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr->un.ip6)); + vpp_rule->dst_prefix.len = ipv6_mask_len(in_rule->dst_prefix_mask.addr.ip6.sin6_addr.s6_addr); + } else { + SAIVPP_WARN("Unknown protocol in destination prefix"); + /* return -EINVAL; */ + } + + vpp_rule->proto = in_rule->proto; + vpp_rule->srcport_or_icmptype_first = htons(in_rule->srcport_or_icmptype_first); + vpp_rule->srcport_or_icmptype_last = htons(in_rule->srcport_or_icmptype_last); + vpp_rule->dstport_or_icmpcode_first = htons(in_rule->dstport_or_icmpcode_first); + vpp_rule->dstport_or_icmpcode_last = htons(in_rule->dstport_or_icmpcode_last); + vpp_rule->is_permit = in_rule->action; + } + mp->context = store_ptr(acl_index); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int vpp_acl_del (uint32_t acl_index) +{ + vat_main_t *vam = &vat_main; + vl_api_acl_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = acl_msg_id_base; + + M (ACL_DEL, mp); + mp->acl_index = htonl(acl_index); + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int vpp_tunterm_acl_interface_add_del (uint32_t tunterm_index, bool is_bind, const char *hwif_name) +{ + vat_main_t *vam = &vat_main; + vl_api_tunterm_acl_interface_add_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = tunterm_msg_id_base; + M (TUNTERM_ACL_INTERFACE_ADD_DEL, mp); + + if (hwif_name) { + u32 idx; + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->is_add = is_bind; + mp->tunterm_acl_index= htonl(tunterm_index); + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + + +int vpp_tunterm_acl_del (uint32_t tunterm_index) +{ + vat_main_t *vam = &vat_main; + vl_api_tunterm_acl_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = tunterm_msg_id_base; + M (TUNTERM_ACL_DEL, mp); + + mp->tunterm_acl_index= htonl(tunterm_index); + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int vpp_tunterm_acl_add_replace (uint32_t *tunterm_index, uint32_t count, vpp_tunterm_acl_t *in_acl) +{ + u32 idx; + vat_main_t *vam = &vat_main; + vpp_ip_addr_t *addr; + vpp_tunterm_acl_rule_t *in_rule; + vl_api_address_t *api_addr; + vl_api_tunterm_acl_rule_t *vpp_rule; + vl_api_tunterm_acl_add_replace_t *mp; + int ret; + + init_vpp_client(); + + VPP_LOCK(); + + __plugin_msg_base = tunterm_msg_id_base; + + M2 (TUNTERM_ACL_ADD_REPLACE, mp, count*sizeof(vl_api_tunterm_acl_rule_t)); + + mp->count = htonl(count); + mp->tunterm_acl_index = htonl(*tunterm_index); + + for (idx = 0; idx < count; idx++) { + in_rule = &in_acl->rules[idx]; + vpp_rule = &mp->r[idx]; + + addr = &in_rule->dst_prefix; + api_addr = &vpp_rule->dst; + + if (!vpp_to_vl_api_ip_addr(api_addr, addr)) { + SAIVPP_WARN("Unknown protocol in tunterm acl destination prefix"); + VPP_UNLOCK(); + return -EINVAL; + } + + if (addr->sa_family == AF_INET6) { + mp->is_ipv6 = true; + } else { + mp->is_ipv6 = false; + } + + if (in_rule->hwif_name) { + u32 idx; + idx = get_swif_idx(vam, in_rule->hwif_name); + if (idx != (u32) -1) { + vpp_rule->path.sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", in_rule->hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + SAIVPP_ERROR("No hwif_name provided.\n"); + VPP_UNLOCK(); + return -EINVAL; + } + + if (in_rule->ip_protocol == 1) { + vpp_rule->path.proto = htonl(FIB_API_PATH_NH_PROTO_IP4); + memcpy(vpp_rule->path.nh.address.ip4, &in_rule->next_hop_ip.addr.ip4.sin_addr.s_addr, sizeof(vpp_rule->path.nh.address.ip4)); + } else if (in_rule->ip_protocol == 2) { + vpp_rule->path.proto = htonl(FIB_API_PATH_NH_PROTO_IP6); + memcpy(vpp_rule->path.nh.address.ip6, &in_rule->next_hop_ip.addr.ip6.sin6_addr.s6_addr, sizeof(vpp_rule->path.nh.address.ip6)); + } else { + SAIVPP_WARN("Unknown protocol in next hop prefix"); + VPP_UNLOCK(); + return -EINVAL; + } + } + mp->context = store_ptr(tunterm_index); + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int vpp_acl_counters_enable_disable (bool enable) +{ + vat_main_t *vam = &vat_main; + vl_api_acl_stats_intf_counters_enable_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = acl_msg_id_base; + + M (ACL_STATS_INTF_COUNTERS_ENABLE, mp); + mp->enable = enable; + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int __vpp_acl_interface_bind_unbind (const char *hwif_name, uint32_t acl_index, + bool is_input, bool is_bind) +{ + vat_main_t *vam = &vat_main; + vl_api_acl_interface_add_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = acl_msg_id_base; + M (ACL_INTERFACE_ADD_DEL, mp); + + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->is_input = is_input; + mp->is_add = is_bind; + mp->acl_index = htonl(acl_index); + + S (mp); + W (ret); + + if (ret == VNET_API_ERROR_ACL_IN_USE_INBOUND || + ret == VNET_API_ERROR_ACL_IN_USE_OUTBOUND) { + SAIVPP_WARN("ACL index %u is already bound to %s", acl_index, hwif_name); + ret = 0; + } + VPP_UNLOCK(); + + return ret; +} + +int vpp_acl_interface_bind (const char *hwif_name, uint32_t acl_index, + bool is_input) +{ + __vpp_acl_interface_bind_unbind(hwif_name, acl_index, is_input, true); +} + +int vpp_acl_interface_unbind (const char *hwif_name, uint32_t acl_index, + bool is_input) +{ + __vpp_acl_interface_bind_unbind(hwif_name, acl_index, is_input, false); +} + +int vpp_ip_flow_hash_set (uint32_t vrf_id, uint32_t hash_mask, int addr_family) +{ + vat_main_t *vam = &vat_main; + vl_api_set_ip_flow_hash_v2_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = ip_msg_id_base; + + M (SET_IP_FLOW_HASH_V2, mp); + mp->table_id = htonl(vrf_id); + mp->flow_hash_config = htonl(hash_mask); + + if (addr_family == AF_INET) { + mp->af = ADDRESS_IP4; + } else if (addr_family == AF_INET6) { + mp->af = ADDRESS_IP6; + } else { + return -1; + } + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int interface_ip_address_add_del (const char *hwif_name, vpp_ip_route_t *prefix, bool is_add) +{ + vat_main_t *vam = &vat_main; + vpp_ip_addr_t *addr; + vl_api_sw_interface_add_del_address_t *mp; + vl_api_address_t *api_addr; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_ADD_DEL_ADDRESS, mp); + + api_addr = &mp->prefix.address; + addr = &prefix->prefix_addr; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + api_addr->af = ADDRESS_IP4; + memcpy(api_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr->un.ip4)); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + api_addr->af = ADDRESS_IP6; + memcpy(api_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr->un.ip6)); + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->prefix.len = prefix->prefix_len; + + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + + mp->is_add = is_add; + mp->del_all = false; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int interface_set_state (const char *hwif_name, bool is_up) +{ + vat_main_t *vam = &vat_main; + vl_api_sw_interface_set_flags_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_SET_FLAGS, mp); + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->flags = htonl ((is_up) ? IF_STATUS_API_FLAG_ADMIN_UP : 0); + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int interface_get_state (const char *hwif_name, bool *link_is_up) +{ + vat_main_t *vam = &vat_main; + vl_api_sw_interface_dump_t *mp; + vl_api_control_ping_t *mp_ping; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_DUMP, mp); + + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->context = store_ptr(link_is_up); + + S (mp); + + /* Use a control ping for synchronization */ + __plugin_msg_base = memclnt_msg_id_base; + + PING (NULL, mp_ping); + S (mp_ping); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int vpp_sync_for_events () +{ + vat_main_t *vam = &vat_main; + vl_api_control_ping_t *mp_ping; + int ret; + + VPP_LOCK(); + + /* Use a control ping for synchronization */ + __plugin_msg_base = memclnt_msg_id_base; + + PING (NULL, mp_ping); + S (mp_ping); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int sw_interface_set_mtu (const char *hwif_name, uint32_t mtu, int type) +{ + vat_main_t *vam = &vat_main; + vl_api_sw_interface_set_mtu_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_SET_MTU, mp); + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + switch (type) { + case AF_INET: + mp->mtu[MTU_PROTO_API_IP4] = htonl(mtu); + break; + + case AF_INET6: + mp->mtu[MTU_PROTO_API_IP6] = htonl(mtu); + break; + default: + VPP_UNLOCK(); + return -EINVAL; + } + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int sw_interface_set_mac (const char *hwif_name, uint8_t *mac_address) +{ + vat_main_t *vam = &vat_main; + vl_api_sw_interface_set_mac_address_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (SW_INTERFACE_SET_MAC_ADDRESS, mp); + + if (hwif_name) { + u32 idx; + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + SAIVPP_ERROR("hwif_name cannot be NULL"); + VPP_UNLOCK(); + return -EINVAL; + } + + if (mac_address == NULL) { + SAIVPP_ERROR("mac address can't be NULL"); + VPP_UNLOCK(); + return -EINVAL; + } + memcpy(mp->mac_address, mac_address, sizeof(mp->mac_address)); + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int hw_interface_set_mtu (const char *hwif_name, uint32_t mtu) +{ + vat_main_t *vam = &vat_main; + vl_api_hw_interface_set_mtu_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = interface_msg_id_base; + + M (HW_INTERFACE_SET_MTU, mp); + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->mtu = htons(mtu); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int vpp_bridge_domain_add_del(uint32_t bridge_id, bool is_add) +{ + vat_main_t *vam = &vat_main; + vl_api_bridge_domain_add_del_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M(BRIDGE_DOMAIN_ADD_DEL, mp); + mp->is_add = is_add; + mp->bd_id = htonl(bridge_id); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} +int set_sw_interface_l2_bridge_by_index(uint32_t sw_if_index, uint32_t bridge_id, bool l2_mode, uint32_t port_type) +{ + vat_main_t *vam = &vat_main; + vl_api_sw_interface_set_l2_bridge_t *mp; + u32 shg = 0; + int ret; + + VPP_LOCK(); + + if (l2_mode && (bridge_id == 0)) + { + SAIVPP_ERROR("Invalide Bridge id\n"); + VPP_UNLOCK(); + return -EINVAL; + } + + __plugin_msg_base = l2_msg_id_base; + + M (SW_INTERFACE_SET_L2_BRIDGE, mp); + + mp->rx_sw_if_index = htonl(sw_if_index); + mp->bd_id = htonl (bridge_id); + mp->shg = (u8) shg; + mp->port_type = htonl (port_type); + mp->enable = l2_mode; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int set_sw_interface_l2_bridge(const char *hwif_name, uint32_t bridge_id, bool l2_mode, uint32_t port_type) +{ + vat_main_t *vam = &vat_main; + + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + return set_sw_interface_l2_bridge_by_index(idx, bridge_id, l2_mode, port_type); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + return -EINVAL; + } + } else { + return -EINVAL; + } + +} + +int set_l2_interface_vlan_tag_rewrite(const char *hwif_name, uint32_t tag1, uint32_t tag2, uint32_t push_dot1q, uint32_t vtr_op) +{ + vat_main_t *vam = &vat_main; + vl_api_l2_interface_vlan_tag_rewrite_t * mp; + u32 sw_if_index; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (L2_INTERFACE_VLAN_TAG_REWRITE, mp); + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + mp->vtr_op = htonl(vtr_op); + mp->push_dot1q = htonl(push_dot1q); + mp->tag1 = htonl(tag1); + mp->tag2 = htonl(tag2); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int bridge_domain_get_member_count (uint32_t bd_id, uint32_t *member_count) +{ + vat_main_t *vam = &vat_main; + vl_api_bridge_domain_dump_t *mp; + vl_api_control_ping_t *mp_ping; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (BRIDGE_DOMAIN_DUMP, mp); + + if (bd_id == 0 || bd_id == ~0) { + SAIVPP_ERROR("Invalid bridge id \n"); + VPP_UNLOCK(); + return -EINVAL; + } + + mp->bd_id = htonl(bd_id); + mp->sw_if_index = htonl(~0); + mp->context = store_ptr(member_count); + + S (mp); + + /* Use a control ping for synchronization */ + __plugin_msg_base = memclnt_msg_id_base; + + PING (NULL, mp_ping); + S (mp_ping); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} +int create_bvi_interface(uint8_t *mac_address, u32 instance) +{ + vat_main_t *vam = &vat_main; + vl_api_bvi_create_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (BVI_CREATE, mp); + + if (mac_address == NULL) { + SAIVPP_ERROR("Invalid mac address \n"); + VPP_UNLOCK(); + return -EINVAL; + } + + mp->user_instance = htonl(instance); + memcpy(mp->mac, mac_address, sizeof(mp->mac)); + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int delete_bvi_interface(const char *hwif_name) +{ + vat_main_t *vam = &vat_main; + vl_api_bvi_delete_t * mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (BVI_DELETE, mp); + + if (hwif_name) { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) { + mp->sw_if_index = htonl(idx); + } else { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } else { + VPP_UNLOCK(); + return -EINVAL; + } + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int set_bridge_domain_flags(uint32_t bd_id, vpp_bd_flags_t flag, bool enable) +{ + vat_main_t *vam = &vat_main; + vl_api_bridge_flags_t * mp; + int ret; + + SAIVPP_WARN("Setting the bd:%d flag %d\n",bd_id,flag); + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (BRIDGE_FLAGS, mp); + + mp->bd_id = htonl(bd_id); + mp->is_set = enable; + mp->flags = htonl(flag); + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int vpp_vxlan_tunnel_add_del(vpp_vxlan_tunnel_t *tunnel, bool is_add, u32 *sw_if_index) +{ + vat_main_t *vam = &vat_main; + vl_api_vxlan_add_del_tunnel_v3_t *mp; + int ret; + vpp_ip_addr_t *addr; + vl_api_address_t *api_addr; + + VPP_LOCK(); + + __plugin_msg_base = vxlan_msg_id_base; + + M (VXLAN_ADD_DEL_TUNNEL_V3, mp); + + mp->is_add = is_add; + mp->instance = htonl(tunnel->instance); + api_addr = &mp->src_address; + addr = &tunnel->src_address; + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + api_addr->af = ADDRESS_IP4; + memcpy(api_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr->un.ip4)); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + api_addr->af = ADDRESS_IP6; + memcpy(api_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr->un.ip6)); + } else { + VPP_UNLOCK(); + return -EINVAL; + } + + api_addr = &mp->dst_address; + addr = &tunnel->dst_address; + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ip4 = &addr->addr.ip4; + api_addr->af = ADDRESS_IP4; + memcpy(api_addr->un.ip4, &ip4->sin_addr.s_addr, sizeof(api_addr->un.ip4)); + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ip6 = &addr->addr.ip6; + api_addr->af = ADDRESS_IP6; + memcpy(api_addr->un.ip6, &ip6->sin6_addr.s6_addr, sizeof(api_addr->un.ip6)); + } else { + VPP_UNLOCK(); + return -EINVAL; + } + + mp->src_port = htons(tunnel->src_port); + mp->dst_port = htons(tunnel->dst_port); + mp->mcast_sw_if_index = htonl(tunnel->mcast_sw_if_index); + mp->encap_vrf_id = htonl(tunnel->encap_vrf_id); + mp->vni = htonl(tunnel->vni); + mp->is_l3 = tunnel->is_l3; + mp->decap_next_index = htonl(tunnel->decap_next_index); + + S (mp); + WR (ret); + //reply handler needs to set vam->sw_if_index from reply msg + *sw_if_index = vam->sw_if_index; + SAIVPP_DEBUG("vxlan_add_del done: if_idx,%d",vam->sw_if_index); + VPP_UNLOCK(); + return ret; +} + +int vpp_ip_addr_t_to_string(vpp_ip_addr_t *ip_addr, char *buffer, size_t maxlen) +{ + struct sockaddr_in *ip4; + struct sockaddr_in6 *ip6; + buffer[0] = 0; + if (ip_addr->sa_family == AF_INET) { + ip4 = &ip_addr->addr.ip4; + if(inet_ntop(AF_INET, &ip4->sin_addr, buffer, maxlen) == NULL){ + return -1; + } + } else if (ip_addr->sa_family == AF_INET6) { + ip6 = &ip_addr->addr.ip6; + if (inet_ntop(AF_INET6, &ip6->sin6_addr, buffer, maxlen) == NULL){ + return -1; + } + } else { + return -1; + } + return 0; +} +int l2fib_add_del(const char *hwif_name, const uint8_t *mac, uint32_t bd_id, bool is_add, bool is_static_mac) +{ + + vat_main_t *vam = &vat_main; + vl_api_l2fib_add_del_t* mp; + + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (L2FIB_ADD_DEL, mp); + + if (hwif_name) + { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) + { + mp->sw_if_index = htonl(idx); + } + else + { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } + else + { + VPP_UNLOCK(); + return -EINVAL; + } + + if (bd_id == 0 || bd_id == ~0) + { + SAIVPP_ERROR("Invalid bridge id for add/del\n"); + VPP_UNLOCK(); + return -EINVAL; + } + memcpy(mp->mac, mac, sizeof(mp->mac)); + mp->bd_id = htonl(bd_id); + mp->is_add = is_add; + mp->static_mac = is_static_mac; + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int l2fib_flush_all() +{ + + vat_main_t *vam = &vat_main; + vl_api_l2fib_flush_all_t* mp; + + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (L2FIB_FLUSH_ALL, mp); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int l2fib_flush_int(const char *hwif_name) +{ + vat_main_t *vam = &vat_main; + vl_api_l2fib_flush_int_t* mp; + + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (L2FIB_FLUSH_INT, mp); + + if (hwif_name) + { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) + { + mp->sw_if_index = htonl(idx); + } + else + { + SAIVPP_ERROR("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } + else + { + VPP_UNLOCK(); + return -EINVAL; + } + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int l2fib_flush_bd(uint32_t bd_id) +{ + vat_main_t *vam = &vat_main; + vl_api_l2fib_flush_bd_t* mp; + + int ret; + + VPP_LOCK(); + + __plugin_msg_base = l2_msg_id_base; + + M (L2FIB_FLUSH_BD, mp); + + if (bd_id == 0 || bd_id == ~0) + { + SAIVPP_ERROR("Invalid bridge id for Flush FDB Entry\n"); + VPP_UNLOCK(); + return -EINVAL; + } + + mp->bd_id = htonl(bd_id); + + S (mp); + + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +int bfd_udp_add(bool multihop, const char *hwif_name, vpp_ip_addr_t *local_addr, + vpp_ip_addr_t *peer_addr, uint8_t detect_mult, + uint32_t desired_min_tx, uint32_t required_min_rx) +{ + vat_main_t *vam = &vat_main; + vl_api_bfd_udp_add_t* mp; + + int ret; + + VPP_LOCK(); + + __plugin_msg_base = bfd_msg_id_base; + + M (BFD_UDP_ADD, mp); + + if (hwif_name) + { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) + { + mp->sw_if_index = htonl(idx); + } + else + { + SAIVPP_WARN("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } + else if (multihop) + { + /* use special sw_if_index value of ~0 to indicate multihop */ + mp->sw_if_index = ~0; + } + else + { + VPP_UNLOCK(); + return -EINVAL; + } + + vl_api_address_t vpp_local_addr, vpp_peer_addr; + memset(&vpp_local_addr, 0, sizeof(vl_api_address_t)); + memset(&vpp_peer_addr, 0, sizeof(vl_api_address_t)); + + if(!((true == vpp_to_vl_api_ip_addr(&vpp_local_addr, local_addr)) && \ + (true == vpp_to_vl_api_ip_addr(&vpp_peer_addr, peer_addr)))) + { + SAIVPP_WARN("Invalid IP address passed for vpp for bfd_add"); + VPP_UNLOCK(); + return -EINVAL; + } + + mp->desired_min_tx = htonl(desired_min_tx); + mp->required_min_rx = htonl(required_min_rx); + mp->detect_mult = detect_mult; + mp->local_addr = vpp_local_addr; + mp->peer_addr = vpp_peer_addr; + mp->is_authenticated = false; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +int bfd_udp_del(bool multihop, const char *hwif_name, vpp_ip_addr_t *local_addr, + vpp_ip_addr_t *peer_addr) +{ + vat_main_t *vam = &vat_main; + vl_api_bfd_udp_del_t* mp; + + int ret; + + VPP_LOCK(); + + __plugin_msg_base = bfd_msg_id_base; + + M (BFD_UDP_DEL, mp); + + if (hwif_name) + { + u32 idx; + + idx = get_swif_idx(vam, hwif_name); + if (idx != (u32) -1) + { + mp->sw_if_index = htonl(idx); + } + else + { + SAIVPP_WARN("Unable to get sw_index for %s\n", hwif_name); + VPP_UNLOCK(); + return -EINVAL; + } + } + else if (multihop) + { + /* use special sw_if_index value of ~0 to indicate multihop */ + mp->sw_if_index = ~0; + } + else + { + VPP_UNLOCK(); + return -EINVAL; + } + + vl_api_address_t vpp_local_addr, vpp_peer_addr; + memset(&vpp_local_addr, 0, sizeof(vl_api_address_t)); + memset(&vpp_peer_addr, 0, sizeof(vl_api_address_t)); + + if(!((true == vpp_to_vl_api_ip_addr(&vpp_local_addr, local_addr)) && \ + (true == vpp_to_vl_api_ip_addr(&vpp_peer_addr, peer_addr)))) + { + SAIVPP_WARN("Invalid IP address passed for vpp for bfd_del"); + VPP_UNLOCK(); + return -EINVAL; + } + + mp->local_addr = vpp_local_addr; + mp->peer_addr = vpp_peer_addr; + + S (mp); + + WR (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int vpp_bfd_events_enable_disable (bool enable) +{ + vat_main_t *vam = &vat_main; + vl_api_want_bfd_events_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = bfd_msg_id_base; + + M (WANT_BFD_EVENTS, mp); + mp->enable_disable = enable; + mp->pid = htonl(getpid()); + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} + +static int vpp_bfd_udp_enable_multihop () +{ + vat_main_t *vam = &vat_main; + vl_api_bfd_udp_enable_multihop_t *mp; + int ret; + + VPP_LOCK(); + + __plugin_msg_base = bfd_msg_id_base; + + M (BFD_UDP_ENABLE_MULTIHOP, mp); + + S (mp); + W (ret); + + VPP_UNLOCK(); + + return ret; +} diff --git a/vslib/vppxlate/SaiVppXlate.h b/vslib/vppxlate/SaiVppXlate.h new file mode 100644 index 000000000..41d26a585 --- /dev/null +++ b/vslib/vppxlate/SaiVppXlate.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SAI_VPP_XLATE_H_ +#define __SAI_VPP_XLATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + typedef enum { + VPP_NEXTHOP_NORMAL = 1, + VPP_NEXTHOP_LOCAL = 2 + } vpp_nexthop_type_e; + + typedef struct vpp_ip_addr_ { + int sa_family; + union { + struct sockaddr_in ip4; + struct sockaddr_in6 ip6; + } addr; + } vpp_ip_addr_t; + + typedef struct vpp_ip_nexthop_ { + vpp_ip_addr_t addr; + uint32_t sw_if_index; + const char *hwif_name; + uint8_t weight; + uint8_t preference; + vpp_nexthop_type_e type; + uint32_t flags; + } vpp_ip_nexthop_t; + + typedef struct vpp_ip_route_ { + vpp_ip_addr_t prefix_addr; + unsigned int prefix_len; + uint32_t vrf_id; + bool is_multipath; + unsigned int nexthop_cnt; + vpp_ip_nexthop_t nexthop[0]; + } vpp_ip_route_t; + + typedef enum { + VPP_ACL_ACTION_API_DENY = 0, + VPP_ACL_ACTION_API_PERMIT = 1, + VPP_ACL_ACTION_API_PERMIT_STFULL = 2, + } vpp_acl_action_e; + + typedef struct _vpp_acl_rule { + vpp_acl_action_e action; + vpp_ip_addr_t src_prefix; + vpp_ip_addr_t src_prefix_mask; + vpp_ip_addr_t dst_prefix; + vpp_ip_addr_t dst_prefix_mask; + int proto; + uint16_t srcport_or_icmptype_first; + uint16_t srcport_or_icmptype_last; + uint16_t dstport_or_icmpcode_first; + uint16_t dstport_or_icmpcode_last; + uint8_t tcp_flags_mask; + uint8_t tcp_flags_value; + } vpp_acl_rule_t; + + typedef struct _vpp_acl_ { + char *acl_name; + uint32_t count; + vpp_acl_rule_t rules[0]; + } vpp_acl_t; + + typedef struct { + vpp_ip_addr_t dst_prefix; + vpp_ip_addr_t dst_prefix_mask; + char hwif_name[64]; + uint8_t ip_protocol; + vpp_ip_addr_t next_hop_ip; + } vpp_tunterm_acl_rule_t; + + typedef struct _vpp_tunterm_acl_ { + char *acl_name; + uint32_t count; + vpp_tunterm_acl_rule_t rules[0]; + } vpp_tunterm_acl_t; + + + typedef enum { + VPP_IP_API_FLOW_HASH_SRC_IP = 1, + VPP_IP_API_FLOW_HASH_DST_IP = 2, + VPP_IP_API_FLOW_HASH_SRC_PORT = 4, + VPP_IP_API_FLOW_HASH_DST_PORT = 8, + VPP_IP_API_FLOW_HASH_PROTO = 16, + VPP_IP_API_FLOW_HASH_REVERSE = 32, + VPP_IP_API_FLOW_HASH_SYMETRIC = 64, + VPP_IP_API_FLOW_HASH_FLOW_LABEL = 128, + } vpp_ip_flow_hash_mask_e; + + typedef enum { + VPP_API_BFD_STATE_ADMIN_DOWN = 0, + VPP_API_BFD_STATE_DOWN = 1, + VPP_API_BFD_STATE_INIT = 2, + VPP_API_BFD_STATE_UP = 3, + } vpp_api_bfd_state_e; + + typedef struct vpp_intf_status_ { + char hwif_name[64]; + bool link_up; + } vpp_intf_status_t; + + typedef struct vpp_bfd_state_notif_ { + bool multihop; + uint32_t sw_if_index; + vpp_ip_addr_t local_addr; + vpp_ip_addr_t peer_addr; + vpp_api_bfd_state_e state; + } vpp_bfd_state_notif_t; + + typedef enum { + VPP_INTF_LINK_STATUS = 1, + VPP_BFD_STATE_CHANGE, + } vpp_event_type_e; + + typedef union vpp_event_data_ { + vpp_intf_status_t intf_status; + vpp_bfd_state_notif_t bfd_notif; + } vpp_event_data_t; + + typedef struct vpp_event_info_ { + struct vpp_event_info_ *next; + vpp_event_type_e type; + vpp_event_data_t data; + } vpp_event_info_t; + + typedef void (*vpp_event_free_fn)(vpp_event_info_t *); + + typedef struct vpp_event_queue_ { + vpp_event_info_t *head; + vpp_event_info_t **tail; + vpp_event_free_fn free; + } vpp_event_queue_t; + +/* VTR config options for API support */ +typedef enum +{ + L2_VTR_DISABLED, + L2_VTR_PUSH_1, + L2_VTR_PUSH_2, + L2_VTR_POP_1, + L2_VTR_POP_2, + L2_VTR_TRANSLATE_1_1, + L2_VTR_TRANSLATE_1_2, + L2_VTR_TRANSLATE_2_1, + L2_VTR_TRANSLATE_2_2 +} vpp_l2_vtr_op_t; + +/* + * VLAN tagging type + */ +typedef enum +{ + VLAN_DOT1AD, + VLAN_DOT1Q +} vpp_vlan_type_t; +typedef enum { + VPP_API_PORT_TYPE_NORMAL = 0, + VPP_API_PORT_TYPE_BVI = 1, + VPP_API_PORT_TYPE_UU_FWD = 2, +} vpp_l2_port_type_t; + +typedef enum { + VPP_BD_FLAG_NONE = 0, + VPP_BD_FLAG_LEARN = 1, + VPP_BD_FLAG_FWD = 2, + VPP_BD_FLAG_FLOOD = 4, + VPP_BD_FLAG_UU_FLOOD = 8, + VPP_BD_FLAG_ARP_TERM = 16, + VPP_BD_FLAG_ARP_UFWD = 32, +} vpp_bd_flags_t; + + typedef struct _vpp_vxlan_tunnel { + vpp_ip_addr_t src_address; + vpp_ip_addr_t dst_address; + uint16_t src_port; + uint16_t dst_port; + uint32_t vni; + uint32_t instance; /* If non-~0, specifies a custom dev instance */ + uint32_t mcast_sw_if_index; + uint32_t encap_vrf_id; + uint32_t decap_next_index; + bool is_l3; + } vpp_vxlan_tunnel_t; + + extern vpp_event_info_t * vpp_ev_dequeue(); + extern void vpp_ev_free(vpp_event_info_t *evp); + + extern int init_vpp_client(); + extern int refresh_interfaces_list(); + extern int configure_lcp_interface(const char *hwif_name, const char *hostif_name, bool is_add); + extern int create_loopback_instance(const char *hwif_name, uint32_t instance); + extern int delete_loopback(const char *hwif_name, uint32_t instance); + extern int get_sw_if_idx(const char *ifname); + extern int create_sub_interface(const char *hwif_name, uint32_t sub_id, uint16_t vlan_id); + extern int delete_sub_interface(const char *hwif_name, uint32_t sub_id); + extern int set_interface_vrf(const char *hwif_name, uint32_t sub_id, uint32_t vrf_id, bool is_ipv6); + extern int interface_ip_address_add_del(const char *hw_ifname, vpp_ip_route_t *prefix, bool is_add); + extern int interface_set_state (const char *hwif_name, bool is_up); + extern int hw_interface_set_mtu(const char *hwif_name, uint32_t mtu); + extern int sw_interface_set_mtu(const char *hwif_name, uint32_t mtu, int type); + extern int sw_interface_set_mac(const char *hwif_name, uint8_t *mac_address); + extern int ip_vrf_add(uint32_t vrf_id, const char *vrf_name, bool is_ipv6); + extern int ip_vrf_del(uint32_t vrf_id, const char *vrf_name, bool is_ipv6); + + extern int ip4_nbr_add_del(const char *hwif_name, uint32_t sw_if_index, struct sockaddr_in *addr, + bool is_static, bool no_fib_entry, uint8_t *mac, bool is_add); + extern int ip6_nbr_add_del(const char *hwif_name, uint32_t sw_if_index, struct sockaddr_in6 *addr, + bool is_static, bool no_fib_entry, uint8_t *mac, bool is_add); + extern int ip_route_add_del(vpp_ip_route_t *prefix, bool is_add); + extern int vpp_ip_flow_hash_set(uint32_t vrf_id, uint32_t mask, int addr_family); + + extern int vpp_acl_add_replace(vpp_acl_t *in_acl, uint32_t *acl_index, bool is_replace); + extern int vpp_acl_del(uint32_t acl_index); + extern int vpp_acl_interface_bind(const char *hwif_name, uint32_t acl_index, + bool is_input); + extern int vpp_acl_interface_unbind(const char *hwif_name, uint32_t acl_index, + bool is_input); + extern int vpp_tunterm_acl_add_replace (uint32_t *tunterm_index, uint32_t count, vpp_tunterm_acl_t *acl); + extern int vpp_tunterm_acl_del (uint32_t tunterm_index); + extern int vpp_tunterm_acl_interface_add_del (uint32_t tunterm_index, + bool is_bind, const char *hwif_name); + extern int interface_get_state(const char *hwif_name, bool *link_is_up); + extern int vpp_sync_for_events(); + extern int vpp_bridge_domain_add_del(uint32_t bridge_id, bool is_add); + extern int set_sw_interface_l2_bridge(const char *hwif_name, uint32_t bridge_id, bool l2_enable, uint32_t port_type); + extern int set_sw_interface_l2_bridge_by_index(uint32_t sw_if_index, uint32_t bridge_id, bool l2_enable, uint32_t port_type); + extern int set_l2_interface_vlan_tag_rewrite(const char *hwif_name, uint32_t tag1, uint32_t tag2, uint32_t push_dot1q, uint32_t vtr_op); + extern int bridge_domain_get_member_count (uint32_t bd_id, uint32_t *member_count); + extern int create_bvi_interface(uint8_t *mac_address, uint32_t instance); + extern int delete_bvi_interface(const char *hwif_name); + extern int set_bridge_domain_flags(uint32_t bd_id, vpp_bd_flags_t flag, bool enable); + extern int l2fib_add_del(const char *hwif_name, const uint8_t *mac, uint32_t bd_id, bool is_add, bool is_static_mac); + extern int l2fib_flush_all(); + extern int l2fib_flush_int(const char *hwif_name); + extern int l2fib_flush_bd(uint32_t bd_id); + extern int bfd_udp_add(bool multihop, const char *hwif_name, vpp_ip_addr_t *local_addr, + vpp_ip_addr_t *peer_addr, uint8_t detect_mult, + uint32_t desired_min_tx, uint32_t required_min_rx); + extern int bfd_udp_del(bool multihop, const char *hwif_name, vpp_ip_addr_t *local_addr, + vpp_ip_addr_t *peer_addr); + + extern int vpp_vxlan_tunnel_add_del(vpp_vxlan_tunnel_t *tunnel, bool is_add, uint32_t *sw_if_index); + extern int vpp_ip_addr_t_to_string(vpp_ip_addr_t *ip_addr, char *buffer, size_t maxlen); +#ifdef __cplusplus +} +#endif + +#endif