diff options
Diffstat (limited to 'hicn-plugin/libvapi-safe')
-rw-r--r-- | hicn-plugin/libvapi-safe/CMakeLists.txt | 128 | ||||
-rw-r--r-- | hicn-plugin/libvapi-safe/includes/vapi/vapi_safe.h | 33 | ||||
-rw-r--r-- | hicn-plugin/libvapi-safe/libsafevapi-config.cmake.in | 8 | ||||
-rw-r--r-- | hicn-plugin/libvapi-safe/src/vapi_safe.cc | 415 |
4 files changed, 584 insertions, 0 deletions
diff --git a/hicn-plugin/libvapi-safe/CMakeLists.txt b/hicn-plugin/libvapi-safe/CMakeLists.txt new file mode 100644 index 000000000..ba647aa34 --- /dev/null +++ b/hicn-plugin/libvapi-safe/CMakeLists.txt @@ -0,0 +1,128 @@ +# Copyright (c) 2021-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. + +############################################################## +# Libs and Bins names +############################################################## +set(SAFE_VAPI safevapi CACHE INTERNAL "" FORCE) +set(SAFE_VAPI_SHARED ${SAFE_VAPI}.shared CACHE INTERNAL "" FORCE) +set(SAFE_VAPI_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/includes CACHE INTERNAL "" FORCE) +set(SAFE_VAPI_LIBRARIES ${SAFE_VAPI_SHARED} CACHE INTERNAL "" FORCE) + + +############################################################## +# C/CXX Standard +############################################################## +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C_STANDARD 11) + + +############################################################## +# Dependencies and third party libs +############################################################## +find_package(Vpp ${VPP_DEFAULT_VERSION} EXACT REQUIRED) +find_package(Asio ${ASIO_DEFAULT_VERSION} REQUIRED) + + +############################################################## +# Check if building as subproject or as root project +############################################################## +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + include(CommonSetup) + find_package(HicnPlugin ${CURRENT_VERSION} EXACT REQUIRED) +else() + list(APPEND DEPENDENCIES + ${HICNPLUGIN_SHARED} + ) +endif() + + +############################################################## +# Sources +############################################################## +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/includes/vapi/vapi_safe.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/src/vapi_safe.cc +) + + +############################################################## +# Compiler Options +############################################################## +set(COMPILER_OPTIONS + ${DEFAULT_COMPILER_OPTIONS} + ${MARCH_COMPILER_OPTIONS} +) + + +############################################################## +# Libraries to link +############################################################## +set (LIBRARIES + ${VPP_LIBRARY_VAPICLIENT} +) + + +############################################################## +# Include directories +############################################################## +list (APPEND INCLUDE_DIRS + PUBLIC + $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}> + $<BUILD_INTERFACE:${HICNPLUGIN_INCLUDE_DIRS}> + $<BUILD_INTERFACE:${VPP_INCLUDE_DIR}> + $<BUILD_INTERFACE:${SAFE_VAPI_INCLUDE_DIRS}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> +) + + +############################################################## +# Compiler definitions +############################################################## +list(APPEND COMPILER_DEFINITIONS + PUBLIC "-DASIO_STANDALONE" +) + + +############################################################## +# Build library +############################################################## +build_library(${SAFE_VAPI} + SHARED + SOURCES ${SOURCE_FILES} ${HEADER_FILES} + INSTALL_HEADERS ${HEADER_FILES} + LINK_LIBRARIES PRIVATE ${LIBRARIES} + COMPONENT ${HICN_PLUGIN} + INCLUDE_DIRS ${INCLUDE_DIRS} + HEADER_ROOT_DIR "" + DEFINITIONS PRIVATE ${COMPILER_DEFINITIONS} + DEPENDS ${DEPENDENCIES} + VERSION ${CURRENT_VERSION} + EXPORT_NAME "libsafevapi" + COMPILE_OPTIONS ${COMPILER_OPTIONS} +) + + +############################################################## +# Create cmake configuration +############################################################## +create_cmake_config ( + "libsafevapi" + INCLUDE_DIRS ${SAFE_VAPI_INCLUDE_DIRS} + VERSION ${CURRENT_VERSION} + COMPONENT ${HICN_PLUGIN} + NAMESPACE hicn +) diff --git a/hicn-plugin/libvapi-safe/includes/vapi/vapi_safe.h b/hicn-plugin/libvapi-safe/includes/vapi/vapi_safe.h new file mode 100644 index 000000000..e13e73556 --- /dev/null +++ b/hicn-plugin/libvapi-safe/includes/vapi/vapi_safe.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021-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 __VAPI_SAFE__ +#define __VAPI_SAFE__ + +#include <vapi/vapi.h> +#include <pthread.h> + +#include <vapi/hicn.api.vapi.h> +#include <vapi/interface.api.vapi.h> +#include <vapi/ip.api.vapi.h> +#include <vapi/memif.api.vapi.h> +#include <vapi/udp.api.vapi.h> + +vapi_error_e vapi_connect_safe (vapi_ctx_t *vapi_ctx_ret, int async); +vapi_error_e vapi_disconnect_safe (); +void vapi_lock (); +void vapi_unlock (); + +#endif diff --git a/hicn-plugin/libvapi-safe/libsafevapi-config.cmake.in b/hicn-plugin/libvapi-safe/libsafevapi-config.cmake.in new file mode 100644 index 000000000..feb278c1f --- /dev/null +++ b/hicn-plugin/libvapi-safe/libsafevapi-config.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +set(Libsafevapi_VERSION_MAJOR "@VERSION_MAJOR@") +set(Libsafevapi_VERSION_MINOR "@VERSION_MINOR@") +set(Libsafevapi_VERSION_PATCH "@VERSION_PATCH@") + +set_and_check(Libsafe_vapi_INCLUDE_DIRS "@PACKAGE_Libsafevapi_INCLUDE_DIRS@") +include("${CMAKE_CURRENT_LIST_DIR}/libsafevapi-targets.cmake") diff --git a/hicn-plugin/libvapi-safe/src/vapi_safe.cc b/hicn-plugin/libvapi-safe/src/vapi_safe.cc new file mode 100644 index 000000000..8747ca738 --- /dev/null +++ b/hicn-plugin/libvapi-safe/src/vapi_safe.cc @@ -0,0 +1,415 @@ +/* + * 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. + */ + +extern "C" +{ +#include <stdio.h> +#include <stdlib.h> +#include <vapi/vapi_safe.h> +} + +#include <thread> +#include <random> +#include <asio.hpp> +#include <iostream> + +namespace +{ +class NonCopyable +{ +protected: + NonCopyable () = default; + ~NonCopyable () = default; + + NonCopyable (const NonCopyable &) = delete; + NonCopyable &operator= (const NonCopyable &) = delete; +}; + +template <typename T> class Singleton : NonCopyable +{ +public: + static T & + getInstance () + { + static T instance; + return instance; + } + +protected: + Singleton () {} + ~Singleton () {} +}; + +template <typename T> class ThreadLocalSingleton : NonCopyable +{ +public: + static T & + getInstance () + { + static thread_local T instance; + return instance; + } + +protected: + ThreadLocalSingleton () {} + ~ThreadLocalSingleton () {} +}; + +class EventThread +{ +public: + EventThread (asio::io_service &io_service, bool detached = false) + : internal_io_service_ (nullptr), io_service_ (std::ref (io_service)), + work_guard_ (asio::make_work_guard (io_service_.get ())), + thread_ (nullptr), detached_ (detached) + { + run (); + } + + explicit EventThread (bool detached = false) + : internal_io_service_ (std::make_unique<asio::io_service> ()), + io_service_ (std::ref (*internal_io_service_)), + work_guard_ (asio::make_work_guard (io_service_.get ())), + thread_ (nullptr), detached_ (detached) + { + run (); + } + + EventThread (const EventThread &) = delete; + EventThread &operator= (const EventThread &) = delete; + + EventThread (EventThread &&other) noexcept + : internal_io_service_ (std::move (other.internal_io_service_)), + io_service_ (std::move (other.io_service_)), + work_guard_ (std::move (other.work_guard_)), + thread_ (std::move (other.thread_)), + detached_ (other.detached_) + { + } + + ~EventThread () { stop (); } + + void + run () + { + if (stopped ()) + { + io_service_.get ().stopped (); + } + + thread_ = + std::make_unique<std::thread> ([this] () { io_service_.get ().run (); }); + + if (detached_) + { + thread_->detach (); + } + } + + std::thread::id + getThreadId () const + { + if (thread_) + { + return thread_->get_id (); + } + else + { + throw std::runtime_error ("Event thread is not running."); + } + } + + template <typename Func> + void + add (Func &&f) + { + io_service_.get ().post (std::forward<Func> (f)); + } + + template <typename Func> + void + tryRunHandlerNow (Func &&f) + { + io_service_.get ().dispatch (std::forward<Func> (f)); + } + + template <typename Func> + void + addAndWaitForExecution (Func &&f) const + { + auto promise = std::promise<void> (); + auto future = promise.get_future (); + + asio::dispatch (io_service_.get (), + [&promise, f = std::forward<Func> (f)] () { + f (); + promise.set_value (); + }); + + future.wait (); + } + + void + stop () + { + add ([this] () { work_guard_.reset (); }); + + if (thread_ && thread_->joinable ()) + { + thread_->join (); + } + + thread_.reset (); + } + + bool + stopped () const + { + return io_service_.get ().stopped (); + } + + asio::io_service & + getIoService () + { + return io_service_; + } + +private: + std::unique_ptr<asio::io_service> internal_io_service_; + std::reference_wrapper<asio::io_service> io_service_; + asio::executor_work_guard<asio::io_context::executor_type> work_guard_; + std::unique_ptr<std::thread> thread_; + bool detached_; +}; + +class UUID : public Singleton<UUID> +{ + friend class Singleton<UUID>; + static inline unsigned char hex_chars[16] = { '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' }; + +public: + static inline constexpr unsigned int UUID_LEN = 64; + + ~UUID () = default; + std::string + generate () + { + return generate_hex (UUID_LEN); + } + + std::string + generate_hex (const unsigned int len) + { + std::string ret (len, 0); + + for (auto &c : ret) + { + c = random_char (); + } + + return ret; + } + +private: + UUID () : rd_ (), gen_ (rd_ ()), dis_ (0, sizeof (hex_chars) - 1) {} + + unsigned char + random_char () + { + return hex_chars[dis_ (gen_)]; + } + +private: + std::random_device rd_; + std::mt19937 gen_; + std::uniform_int_distribution<> dis_; +}; + +} // namespace + +DEFINE_VAPI_MSG_IDS_HICN_API_JSON +DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON +DEFINE_VAPI_MSG_IDS_IP_API_JSON +DEFINE_VAPI_MSG_IDS_UDP_API_JSON +DEFINE_VAPI_MSG_IDS_MEMIF_API_JSON + +class VapiGlobalConnection : public Singleton<VapiGlobalConnection> +{ + friend class Singleton<VapiGlobalConnection>; + + static inline char kapp_name[] = "hicn_app"; + static inline char kapi_prefix[] = ""; + static inline int kresponse_queue_size = 32; + static inline int kmax_outstanding_requests = 32; + static inline uint32_t ktimeout_seconds = 1; + +public: + vapi_error_e + vapiConnectSafe (vapi_ctx_t *vapi_ctx_ret) + { + if (isConnected ()) + { + *vapi_ctx_ret = vapi_ctx_; + return VAPI_OK; + } + + std::unique_lock<std::mutex> lock (vapi_mtx_); + + auto rv = vapi_ctx_alloc (&vapi_ctx_); + if (rv != VAPI_OK) + { + return rv; + } + + rv = vapi_connect (vapi_ctx_, app_name_.c_str (), nullptr, + max_outstanding_requests_, response_queue_size_, + VAPI_MODE_BLOCKING, 1); + connected_ = true; + + vapi_set_generic_event_cb (vapi_ctx_, &VapiGlobalConnection::genericCb, + nullptr); + + if (rv == VAPI_OK) + { + // startDispatcher (); + *vapi_ctx_ret = vapi_ctx_; + } + + return rv; + } + + void + vapiLock () + { + vapi_mtx_.lock (); + } + + void + vapiUnLock () + { + vapi_mtx_.unlock (); + } + + bool + isConnected () + { + return connected_; + } + + ~VapiGlobalConnection () + { + std::cout << "\"adios1" << std::endl; + if (!isConnected ()) + { + return; + } + std::cout << "\"adios" << std::endl; + std::unique_lock<std::mutex> lock (vapi_mtx_); + vapi_disconnect (vapi_ctx_); + vapi_ctx_free (vapi_ctx_); + timer_.cancel (); + } + +private: + VapiGlobalConnection ( + const std::string &app_name = std::string (kapp_name) + "_" + + UUID::getInstance ().generate_hex (5), + const std::string &api_prefix = kapi_prefix, + int max_outstanding_requests = kmax_outstanding_requests, + int response_queue_size = kresponse_queue_size) + : app_name_ (app_name), api_prefix_ (api_prefix), + max_outstanding_requests_ (max_outstanding_requests), + response_queue_size_ (response_queue_size), vapi_mtx_ (), + vapi_ctx_ (nullptr), connected_ (false), thread_ (), + timer_ (thread_.getIoService ()) + { + } + + void + timerHandler (const std::error_code &ec) + { + if (ec) + { + // Timer was canceled + return; + } + + if (!isConnected ()) + { + return; + } + + std::unique_lock<std::mutex> lock (vapi_mtx_); + auto err = vapi_dispatch (vapi_ctx_); + if (err != VAPI_OK) + { + return; + } + + startDispatcher (); + } + + void + startDispatcher () + { + timer_.expires_after (std::chrono::seconds (ktimeout_seconds)); + timer_.async_wait (std::bind (&VapiGlobalConnection::timerHandler, this, + std::placeholders::_1)); + } + + static vapi_error_e + genericCb (vapi_ctx_t ctx, void *callback_ctx, vapi_msg_id_t id, void *msg) + { + std::cout << "Called" << std::endl; + return VAPI_OK; + } + +private: + std::string app_name_; + std::string api_prefix_; + int max_outstanding_requests_; + int response_queue_size_; + std::mutex vapi_mtx_; + vapi_ctx_t vapi_ctx_; + std::atomic_bool connected_; + EventThread thread_; + asio::steady_timer timer_; +}; + +vapi_error_e +vapi_connect_safe (vapi_ctx_t *vapi_ctx_ret, int async) +{ + return VapiGlobalConnection::getInstance ().vapiConnectSafe (vapi_ctx_ret); +} + +vapi_error_e +vapi_disconnect_safe () +{ + return VAPI_OK; +} + +void +vapi_lock () +{ + VapiGlobalConnection::getInstance ().vapiLock (); +} + +void +vapi_unlock () +{ + VapiGlobalConnection::getInstance ().vapiUnLock (); +} |