diff options
Diffstat (limited to 'libtransport')
317 files changed, 13120 insertions, 7040 deletions
diff --git a/libtransport/.clang-format b/libtransport/.clang-format index 513da4d69..ae46ce82d 100644 --- a/libtransport/.clang-format +++ b/libtransport/.clang-format @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/CMakeLists.txt b/libtransport/CMakeLists.txt index a5009f353..62c0c0788 100644 --- a/libtransport/CMakeLists.txt +++ b/libtransport/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -11,29 +11,36 @@ # See the License for the specific language governing permissions and # limitations under the License. +############################################################## +# Project and cmake version +############################################################## # CMake 3.11 required to use FetchContent cmake_minimum_required(VERSION 3.11 FATAL_ERROR) - project(libtransport) + +############################################################## +# Cmake modules +############################################################## set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules ) +include("${CMAKE_CURRENT_SOURCE_DIR}/../versions.cmake") -include(DefaultConfiguration) -include(BuildMacros) -if (NOT CMAKE_BUILD_TYPE) - message(STATUS "${PROJECT_NAME}: No build type selected, default to Release") - set(CMAKE_BUILD_TYPE "Release") -endif () +############################################################## +# C/CXX Standard +############################################################## +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C_STANDARD 11) -set(TRANSPORT_ROOT_PATH "src") +############################################################## +# Libs and Bins names +############################################################## set(LIBTRANSPORT hicntransport) -set(LIBTRANSPORT_COMPONENT libhicntransport) +set(LIBTRANSPORT_COMPONENT lib${LIBTRANSPORT}) if ((BUILD_HICNPLUGIN OR BUILD_MEMIF_CONNECTOR) AND "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") set(__vpp__ 1) @@ -42,19 +49,32 @@ endif () set(LIBTRANSPORT ${LIBTRANSPORT} CACHE INTERNAL "" FORCE) set(LIBTRANSPORT_SHARED ${LIBTRANSPORT}.shared CACHE INTERNAL "" FORCE) set(LIBTRANSPORT_STATIC ${LIBTRANSPORT}.static CACHE INTERNAL "" FORCE) -set(LIBTRANSPORT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src CACHE INTERNAL "" FORCE) -include(WindowsMacros) -include(IosMacros) -find_package_wrapper(Asio REQUIRED) -find_package(OpenSSL REQUIRED) +############################################################## +# Dependencies and third party libs +############################################################## +find_package(Asio ${ASIO_DEFAULT_VERSION} REQUIRED) +find_package(OpenSSL ${OPENSSL_DEFAULT_VERSION} EXACT REQUIRED) find_package(Threads REQUIRED) -find_package(OpenSSL REQUIRED) -find_package(Libconfig++ REQUIRED) +find_package(Libconfig++ ${LIBCONFIG_DEFAULT_VERSION} REQUIRED) +add_subdirectory(third-party) + +############################################################## +# Check if building as subproject or as root project +############################################################## if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - find_package_wrapper(Libhicn REQUIRED) + include(CommonSetup) + find_package(Libhicn ${CURRENT_VERSION} REQUIRED NO_MODULE) + + if (DISABLE_SHARED_LIBRARIES) + set(LIBTYPE static) + else() + set(LIBTYPE shared) + endif() + + list(APPEND HICN_LIBRARIES hicn::hicn.${LIBTYPE}) else() if (DISABLE_SHARED_LIBRARIES) if (WIN32) @@ -73,34 +93,16 @@ else() endif() endif() -include(Packaging) - -add_subdirectory(third-party) - -set(LIBRARIES - PRIVATE ${HICN_LIBRARIES} - PRIVATE ${CMAKE_THREAD_LIBS_INIT} - PRIVATE ${CMAKE_DL_LIBS} - PRIVATE ${OPENSSL_LIBRARIES} - PRIVATE ${LIBCONFIG_CPP_LIBRARIES} - PRIVATE ${THIRD_PARTY_LIBRARIES} -) -# Include dirs -- Order does matter! -list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS - ${HICN_INCLUDE_DIRS} - ${HICNPLUGIN_INCLUDE_DIRS} - ${CMAKE_THREADS_INCLUDE_DIRS} - ${ASIO_INCLUDE_DIRS} - ${WINDOWS_INCLUDE_DIRS} - ${OPENSSL_INCLUDE_DIR} - ${CONFIG_INCLUDE_DIRS} - ${THIRD_PARTY_INCLUDE_DIRS} -) +############################################################## +# Packaging and versioning +############################################################## +include(${CMAKE_CURRENT_SOURCE_DIR}/../versions.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/packaging.cmake) -list(APPEND DEPENDENCIES - ${THIRD_PARTY_DEPENDENCIES} -) +############################################################## +# Subdirectories +############################################################## add_subdirectory(includes/hicn/transport) -add_subdirectory(${TRANSPORT_ROOT_PATH}) +add_subdirectory(src) diff --git a/libtransport/cmake/Modules/Android.cmake b/libtransport/cmake/Modules/Android.cmake deleted file mode 100644 index 78918455a..000000000 --- a/libtransport/cmake/Modules/Android.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2017-2019 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. - -function (configure_android_environment) - set(CMAKE_CXX_FLAGS " -Wall -stdlib=libc++ -DASIO_STANDALONE -pthread -isystem -lm") - - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE) - #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ANDROID_C_FLAGS}" PARENT_SCOPE) -endfunction()
\ No newline at end of file diff --git a/libtransport/cmake/Modules/DefaultConfiguration.cmake b/libtransport/cmake/Modules/DefaultConfiguration.cmake deleted file mode 100644 index 402ad86f5..000000000 --- a/libtransport/cmake/Modules/DefaultConfiguration.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2017-2019 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. - -# C/c++ standard -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_C_STANDARD 11) - -# Compilation flags - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -if (NOT WIN32) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fpermissive") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fpermissive") - set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -fpermissive") -endif () - -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG") -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") -set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
\ No newline at end of file diff --git a/libtransport/cmake/Modules/Ios.cmake b/libtransport/cmake/Modules/Ios.cmake deleted file mode 100644 index 1b2aae2bf..000000000 --- a/libtransport/cmake/Modules/Ios.cmake +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2017-2019 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. - -function (configure_ios_environment) - find_host_package ( OpenSSL REQUIRED ) - include_directories(extras/iOS) - - find_host_package(Libhicn REQUIRED) - include_directories(${HICN_INCLUDE_DIRS}) -endfunction()
\ No newline at end of file diff --git a/libtransport/cmake/Modules/TestMacros.cmake b/libtransport/cmake/Modules/TestMacros.cmake deleted file mode 100644 index 680b5585f..000000000 --- a/libtransport/cmake/Modules/TestMacros.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2017-2019 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(CTest) - diff --git a/libtransport/cmake/Modules/Packaging.cmake b/libtransport/cmake/packaging.cmake index 9ff26aecc..f7f0c27e3 100644 --- a/libtransport/cmake/Modules/Packaging.cmake +++ b/libtransport/cmake/packaging.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -17,6 +17,11 @@ # Packages section ###################### +############################################################## +# Get VPP version +############################################################## +list(GET VPP_DEFAULT_VERSION 0 VPP_VERSION) + set(${LIBTRANSPORT_COMPONENT}_DESCRIPTION "Libhicn-transport provides transport services and \ socket API for applications willing to communicate \ @@ -28,7 +33,7 @@ set(${LIBTRANSPORT_COMPONENT}-dev_DESCRIPTION CACHE STRING "Header files for developing using libhicntransport." ) -set(${LIBTRANSPORT_COMPONENT}-devel_DESCRIPTION +set(lib${LIBTRANSPORT}-devel_DESCRIPTION CACHE STRING "Header files for developing using libhicntransport." ) @@ -37,31 +42,44 @@ set(${LIBTRANSPORT_COMPONENT}-io-modules_DESCRIPTION ) set(${LIBTRANSPORT_COMPONENT}_DEB_DEPENDENCIES - "lib${LIBHICN} (>= stable_version), libparc (>= 1.0), libconfig++9v5 (>= 1.5-0.4build1)" + "lib${LIBHICN} (= stable_version), libconfig++9v5 (>= 1.5-0.4build1)" CACHE STRING "Dependencies for deb/rpm package." ) set(${LIBTRANSPORT_COMPONENT}_RPM_DEPENDENCIES - "lib${LIBHICN} >= stable_version, libparc >= 1.0, libconfig >= 1.5-9.el8" + "lib${LIBHICN} = stable_version, libconfig >= 1.5-9.el8" CACHE STRING "Dependencies for deb/rpm package." ) set(${LIBTRANSPORT_COMPONENT}-dev_DEB_DEPENDENCIES - "${LIBTRANSPORT_COMPONENT} (>= stable_version), libasio-dev (>= 1.10), lib${LIBHICN}-dev (>= stable_version), libparc-dev (>= 1.0), libconfig++-dev (>= 1.5-0.4build1)" + "${LIBTRANSPORT_COMPONENT} (= stable_version), libasio-dev (>= 1.10), lib${LIBHICN}-dev (= stable_version), libconfig++-dev (>= 1.5-0.4build1)" CACHE STRING "Dependencies for deb/rpm package." ) set(${LIBTRANSPORT_COMPONENT}-dev_RPM_DEPENDENCIES - "${LIBTRANSPORT_COMPONENT} >= stable_version, asio-devel >= 1.10, lib${LIBHICN}-devel >= stable_version, libparc-devel >= 1.0, libconfig-devel >= 1.5-9.el8" + "${LIBTRANSPORT_COMPONENT} = stable_version, asio-devel >= 1.10, lib${LIBHICN}-devel = stable_version, libconfig-devel >= 1.5-9.el8" CACHE STRING "Dependencies for deb/rpm package." ) set(${LIBTRANSPORT_COMPONENT}-io-modules_DEB_DEPENDENCIES - "${LIBTRANSPORT_COMPONENT} (>= stable_version), libmemif (>= stable_version), vpp (>= stable_version-release), vpp (<< next_version-release), hicn-plugin (>= stable_version)" + "${LIBTRANSPORT_COMPONENT} (= stable_version), vpp (>= ${VPP_VERSION}), hicn-plugin (= stable_version)" CACHE STRING "Dependencies for deb/rpm package." ) set(${LIBTRANSPORT_COMPONENT}-io-modules_RPM_DEPENDENCIES - "${LIBTRANSPORT_COMPONENT} >= stable_version, libmemif >= stable_version, vpp >= stable_version-release, vpp < next_version-release, hicn-plugin >= stable_version" + "${LIBTRANSPORT_COMPONENT} = stable_version, vpp >= ${VPP_VERSION}, hicn-plugin = stable_version" CACHE STRING "Dependencies for deb/rpm package." ) + +if (INTERNAL_ENVIRONMENT) + include(CheckSsl) + CheckSsl() + set(${LIBTRANSPORT_COMPONENT}_DEB_DEPENDENCIES + "${${LIBTRANSPORT_COMPONENT}_DEB_DEPENDENCIES}, ${OPENSSL_DEPENDENCY}" + CACHE STRING "Dependencies for deb/rpm package." + ) + set(${LIBTRANSPORT_COMPONENT}-dev_DEB_DEPENDENCIES + "${${LIBTRANSPORT_COMPONENT}-dev_DEB_DEPENDENCIES}, ${OPENSSL_DEPENDENCY_DEV}" + CACHE STRING "Dependencies for deb/rpm package." + ) +endif () diff --git a/libtransport/includes/hicn/transport/CMakeLists.txt b/libtransport/includes/hicn/transport/CMakeLists.txt index eb339fb5a..8b9f907b9 100644 --- a/libtransport/includes/hicn/transport/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2020 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -23,14 +23,21 @@ add_subdirectory(portability) add_subdirectory(auth) add_subdirectory(utils) -set(LIBTRANSPORT_INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR}/../.. "" + +############################################################## +# Public headers directory +############################################################## +set(Libhicntransport_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/../.. CACHE INTERNAL "" FORCE ) + +############################################################## +# Header files to install +############################################################## set(LIBHICNTRANSPORT_TO_INSTALL_HEADER_FILES - ${HEADER_FILES} "" - CACHE INTERNAL - "" FORCE + ${HEADER_FILES} + PARENT_SCOPE ) diff --git a/libtransport/includes/hicn/transport/auth/CMakeLists.txt b/libtransport/includes/hicn/transport/auth/CMakeLists.txt index 1e9fe4698..0b5ae1836 100644 --- a/libtransport/includes/hicn/transport/auth/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/auth/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -15,7 +15,6 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/common.h ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.h ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.h - ${CMAKE_CURRENT_SOURCE_DIR}/identity.h ${CMAKE_CURRENT_SOURCE_DIR}/key_id.h ${CMAKE_CURRENT_SOURCE_DIR}/policies.h ${CMAKE_CURRENT_SOURCE_DIR}/signer.h diff --git a/libtransport/includes/hicn/transport/auth/common.h b/libtransport/includes/hicn/transport/auth/common.h index fb0e82eb7..d2282436e 100644 --- a/libtransport/includes/hicn/transport/auth/common.h +++ b/libtransport/includes/hicn/transport/auth/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/auth/crypto_hash.h b/libtransport/includes/hicn/transport/auth/crypto_hash.h index 90f1627e9..3c734fee3 100644 --- a/libtransport/includes/hicn/transport/auth/crypto_hash.h +++ b/libtransport/includes/hicn/transport/auth/crypto_hash.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -89,7 +89,7 @@ class CryptoHash { static std::size_t getSize(CryptoHashType hash_type); // Compare two raw buffers - static bool compareDigest(const uint8_t *h1, const uint8_t *h2, + static bool compareDigest(const uint8_t *digest1, const uint8_t *digest2, CryptoHashType hash_type); private: diff --git a/libtransport/includes/hicn/transport/auth/crypto_suite.h b/libtransport/includes/hicn/transport/auth/crypto_suite.h index d0f1de395..ed21abb91 100644 --- a/libtransport/includes/hicn/transport/auth/crypto_suite.h +++ b/libtransport/includes/hicn/transport/auth/crypto_suite.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -47,6 +47,9 @@ enum class CryptoSuite : uint8_t { // Return the suite associated to the given NID CryptoSuite getSuite(int nid); +// Return the string representation of given suite +std::string getStringSuite(CryptoSuite suite); + // Return the hash type associated to the given suite CryptoHashType getHashType(CryptoSuite suite); diff --git a/libtransport/includes/hicn/transport/auth/identity.h b/libtransport/includes/hicn/transport/auth/identity.h deleted file mode 100644 index be072f5d3..000000000 --- a/libtransport/includes/hicn/transport/auth/identity.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2017-2021 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. - */ - -#pragma once - -#include <errno.h> -#include <fcntl.h> -#include <hicn/transport/auth/signer.h> -#include <unistd.h> - -extern "C" { -#include <openssl/pkcs12.h> -#include <openssl/rand.h> -#include <openssl/x509.h> -#include <openssl/x509v3.h> -} - -namespace transport { -namespace auth { - -class Identity { - // This class holds several information about a client, including its public - // key. - public: - // Generate a new identity from the given parameters. The identity will be - // saved in 'keystore_path' and encrypted using 'keystore_pwd'. - Identity(const std::string &keystore_path, const std::string &keystore_pwd, - CryptoSuite suite, unsigned int signature_len, - unsigned int validity_days, const std::string &subject_name); - - // Create an identity from an already existing keystore path. - Identity(std::string &keystore_path, std::string &keystore_pwd, - CryptoHashType hash_type); - - Identity(const Identity &other); - Identity(Identity &&other); - ~Identity(); - - // Return the asymmetric signer object created from the public key. - std::shared_ptr<AsymmetricSigner> getSigner() const; - - // Return the key store filename. - std::string getFilename() const; - - // Return the key store password. - std::string getPassword() const; - - std::shared_ptr<X509> getCertificate() const; - - std::shared_ptr<EVP_PKEY> getPrivateKey() const; - - // Generate a new random identity. - static Identity generateIdentity(const std::string &subject_name = ""); - - private: - static void free_key(EVP_PKEY *T) { EVP_PKEY_free(T); } - - std::string pwd_; - std::string filename_; - std::shared_ptr<AsymmetricSigner> signer_; - std::shared_ptr<X509> cert_; -}; - -} // namespace auth -} // namespace transport diff --git a/libtransport/includes/hicn/transport/auth/key_id.h b/libtransport/includes/hicn/transport/auth/key_id.h index 3aa09336f..8723ae698 100644 --- a/libtransport/includes/hicn/transport/auth/key_id.h +++ b/libtransport/includes/hicn/transport/auth/key_id.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/auth/policies.h b/libtransport/includes/hicn/transport/auth/policies.h index 00464d54b..b9755595c 100644 --- a/libtransport/includes/hicn/transport/auth/policies.h +++ b/libtransport/includes/hicn/transport/auth/policies.h @@ -23,10 +23,10 @@ namespace auth { * perform after verification. */ enum class VerificationPolicy { + UNKNOWN, ABORT, - ACCEPT, DROP, - UNKNOWN, + ACCEPT, }; } // namespace auth diff --git a/libtransport/includes/hicn/transport/auth/signer.h b/libtransport/includes/hicn/transport/auth/signer.h index 405dd83cf..5a7598991 100644 --- a/libtransport/includes/hicn/transport/auth/signer.h +++ b/libtransport/includes/hicn/transport/auth/signer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -21,18 +21,19 @@ #include <hicn/transport/errors/errors.h> #include <hicn/transport/utils/membuf.h> +#include <memory> extern "C" { #include <openssl/evp.h> #include <openssl/hmac.h> +#include <openssl/pkcs12.h> +#include <openssl/x509.h> } namespace transport { namespace auth { -class Identity; class Signer { // The base class from which all signer classes derive. - friend class Identity; public: Signer(); @@ -47,6 +48,9 @@ class Signer { // Return the signature. std::vector<uint8_t> getSignature() const; + // Return the signature as a string + std::string getStringSignature() const; + // Return the signature size in bytes. virtual std::size_t getSignatureSize() const; @@ -61,6 +65,9 @@ class Signer { // Return the hash algorithm associated to the signer. CryptoHashType getHashType() const; + // Print signature to stdout + void display(); + protected: CryptoSuite suite_; std::vector<uint8_t> signature_; @@ -84,10 +91,17 @@ class AsymmetricSigner : public Signer { public: AsymmetricSigner() = default; - // Construct an AsymmetricSigner from a key store and a given crypto suite. + // Create an AsymmetricSigner from a keystore file (.p12). + AsymmetricSigner(std::string keystore_path, std::string password); + + // Construct an AsymmetricSigner from a key store and a given crypto + // suite. AsymmetricSigner(CryptoSuite suite, std::shared_ptr<EVP_PKEY> key, std::shared_ptr<EVP_PKEY> pub_key); + void setKey(CryptoSuite suite, std::shared_ptr<EVP_PKEY> key, + std::shared_ptr<EVP_PKEY> pub_key); + std::size_t getSignatureFieldSize() const override; }; diff --git a/libtransport/includes/hicn/transport/auth/verifier.h b/libtransport/includes/hicn/transport/auth/verifier.h index 6321d4ed5..677a1efe4 100644 --- a/libtransport/includes/hicn/transport/auth/verifier.h +++ b/libtransport/includes/hicn/transport/auth/verifier.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -40,9 +40,9 @@ class Verifier { // The VerificationFailedCallback will be called by the transport if a // data packet (either a manifest or a content object) was not validated. // The application decides what to do then by returning a - // VerificationPolicy object. + // new VerificationPolicy. using VerificationFailedCallback = std::function<auth::VerificationPolicy( - const core::ContentObject &content_object, std::error_code ec)>; + Suffix suffix, VerificationPolicy policy)>; // The list of VerificationPolicy that will trigger the // VerificationFailedCallback. @@ -96,13 +96,13 @@ class Verifier { void getVerificationFailedCallback( VerificationFailedCallback **verification_failed_cb); + // Call VerificationFailedCallback if it is set and update the packet policy. + void callVerificationFailedCallback(Suffix suffix, + VerificationPolicy &policy); + protected: VerificationFailedCallback verification_failed_cb_; std::vector<VerificationPolicy> failed_policies_; - - // Call VerificationFailedCallback if it is set and update the packet policy. - void callVerificationFailedCallback(PacketPtr packet, - VerificationPolicy &policy); }; class VoidVerifier : public Verifier { diff --git a/libtransport/includes/hicn/transport/core/CMakeLists.txt b/libtransport/includes/hicn/transport/core/CMakeLists.txt index 14c795a7a..34048d93a 100644 --- a/libtransport/includes/hicn/transport/core/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/core/connector.h b/libtransport/includes/hicn/transport/core/connector.h index dcf38cdc8..b671a7d89 100644 --- a/libtransport/includes/hicn/transport/core/connector.h +++ b/libtransport/includes/hicn/transport/core/connector.h @@ -15,6 +15,7 @@ #pragma once +#include <glog/logging.h> #include <hicn/transport/core/connector_stats.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/endpoint.h> @@ -50,22 +51,21 @@ class Connector : public std::enable_shared_from_this<Connector> { enum class Role : std::uint8_t { CONSUMER, PRODUCER }; - public: static constexpr std::size_t queue_size = 4096; static constexpr std::uint32_t invalid_connector = ~0; - -#ifdef LINUX + static constexpr std::uint32_t max_reconnection_reattempts = 5; static constexpr std::uint16_t max_burst = 256; -#endif using Ptr = std::shared_ptr<Connector>; - using PacketQueue = std::deque<Packet::Ptr>; - using PacketReceivedCallback = std::function<void( - Connector *, utils::MemBuf &, const std::error_code &)>; + using PacketQueue = std::deque<utils::MemBuf::Ptr>; + using PacketReceivedCallback = + std::function<void(Connector *, const std::vector<utils::MemBuf::Ptr> &, + const std::error_code &)>; using PacketSentCallback = std::function<void(Connector *, const std::error_code &)>; using OnCloseCallback = std::function<void(Connector *)>; - using OnReconnectCallback = std::function<void(Connector *)>; + using OnReconnectCallback = + std::function<void(Connector *, const std::error_code &)>; using Id = std::uint64_t; template <typename ReceiveCallback, typename SentCallback, typename OnClose, @@ -77,7 +77,8 @@ class Connector : public std::enable_shared_from_this<Connector> { on_close_callback_(std::forward<OnClose &&>(close_callback)), on_reconnect_callback_(std::forward<OnReconnect &&>(on_reconnect)), state_(State::CLOSED), - connector_id_(invalid_connector) {} + connector_id_(invalid_connector), + connection_reattempts_(0) {} virtual ~Connector(){}; @@ -115,7 +116,7 @@ class Connector : public std::enable_shared_from_this<Connector> { virtual void send(Packet &packet) = 0; - virtual void send(const uint8_t *packet, std::size_t len) = 0; + virtual void send(const utils::MemBuf::Ptr &buffer) = 0; virtual void close() = 0; @@ -206,6 +207,9 @@ class Connector : public std::enable_shared_from_this<Connector> { // Stats AtomicConnectorStats stats_; + + // Connection attempts + std::uint32_t connection_reattempts_; }; } // namespace core diff --git a/libtransport/includes/hicn/transport/core/content_object.h b/libtransport/includes/hicn/transport/core/content_object.h index 38baafc69..a8df1e8e3 100644 --- a/libtransport/includes/hicn/transport/core/content_object.h +++ b/libtransport/includes/hicn/transport/core/content_object.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -31,10 +31,9 @@ class ContentObject : public Packet { using Ptr = std::shared_ptr<ContentObject>; using HICNContentObject = hicn_header_t; - ContentObject(Packet::Format format = HF_INET6_TCP, - std::size_t additional_header_size = 0); + ContentObject(Packet::Format format, std::size_t additional_header_size = 0); - ContentObject(const Name &name, Packet::Format format = HF_INET6_TCP, + ContentObject(const Name &name, Packet::Format format, std::size_t additional_header_size = 0); ContentObject(const Name &name, hicn_format_t format, @@ -42,7 +41,7 @@ class ContentObject : public Packet { std::size_t payload_size); template <typename... Args> - ContentObject(CopyBufferOp op, Args &&... args) + ContentObject(CopyBufferOp op, Args &&...args) : Packet(op, std::forward<Args>(args)...) { if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < 0) { @@ -51,7 +50,7 @@ class ContentObject : public Packet { } template <typename... Args> - ContentObject(WrapBufferOp op, Args &&... args) + ContentObject(WrapBufferOp op, Args &&...args) : Packet(op, std::forward<Args>(args)...) { if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < 0) { @@ -60,8 +59,12 @@ class ContentObject : public Packet { } template <typename... Args> - ContentObject(CreateOp op, Args &&... args) + ContentObject(CreateOp op, Args &&...args) : Packet(op, std::forward<Args>(args)...) { + if (hicn_packet_set_data(format_, packet_start_) < 0) { + throw errors::MalformedPacketException(); + } + if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < 0) { throw errors::MalformedPacketException(); @@ -96,6 +99,10 @@ class ContentObject : public Packet { auto shared_from_this() { return utils::shared_from(this); } + bool isLast() const; + + void setLast(); + private: void resetForHash() override; }; diff --git a/libtransport/includes/hicn/transport/core/endpoint.h b/libtransport/includes/hicn/transport/core/endpoint.h index cb6b0f562..e1fa193d7 100644 --- a/libtransport/includes/hicn/transport/core/endpoint.h +++ b/libtransport/includes/hicn/transport/core/endpoint.h @@ -42,14 +42,20 @@ class Endpoint { ~Endpoint() = default; Endpoint &operator=(const Endpoint &other) { - address_ = other.address_; - port_ = other.port_; + if (this != &other) { + address_ = other.address_; + port_ = other.port_; + } + return *this; } Endpoint &operator=(Endpoint &&other) { - address_ = std::move(other.address_); - port_ = std::move(other.port_); + if (this != &other) { + address_ = std::move(other.address_); + port_ = std::move(other.port_); + } + return *this; } diff --git a/libtransport/includes/hicn/transport/core/global_object_pool.h b/libtransport/includes/hicn/transport/core/global_object_pool.h index d7f3a9e41..f98df521f 100644 --- a/libtransport/includes/hicn/transport/core/global_object_pool.h +++ b/libtransport/includes/hicn/transport/core/global_object_pool.h @@ -27,9 +27,10 @@ namespace transport { namespace core { template <std::size_t packet_pool_size = 1024, std::size_t chunk_size = 2048> -class PacketManager - : public utils::Singleton<PacketManager<packet_pool_size, chunk_size>> { - friend class utils::Singleton<PacketManager<packet_pool_size, chunk_size>>; +class PacketManager : public utils::ThreadLocalSingleton< + PacketManager<packet_pool_size, chunk_size>> { + friend class utils::ThreadLocalSingleton< + PacketManager<packet_pool_size, chunk_size>>; public: using MemoryPool = utils::FixedBlockAllocator<chunk_size, packet_pool_size>; @@ -71,7 +72,10 @@ class PacketManager template < typename PacketType, typename... Args, typename = std::enable_if_t<std::is_base_of<Packet, PacketType>::value>> - typename PacketType::Ptr getPacket(Args &&... args) { + typename PacketType::Ptr getPacket(Args &&...args) { + static_assert(sizeof(PacketType) + sizeof(std::shared_ptr<PacketType>) + + sizeof(std::max_align_t) <= + sizeof(PacketStorage::packet_and_shared_ptr)); PacketType *memory = nullptr; memory = reinterpret_cast<PacketType *>(memory_pool_.allocateBlock()); @@ -98,7 +102,7 @@ class PacketManager template <typename PacketType, typename... Args> typename PacketType::Ptr getPacketFromExistingBuffer(uint8_t *buffer, std::size_t length, - Args &&... args) { + Args &&...args) { auto offset = offsetof(PacketStorage, align); auto memory = reinterpret_cast<PacketType *>(buffer - offset); utils::STLAllocator<PacketType, MemoryPool> allocator(memory, diff --git a/libtransport/includes/hicn/transport/core/interest.h b/libtransport/includes/hicn/transport/core/interest.h index a5b9cf375..b7ce3c3a0 100644 --- a/libtransport/includes/hicn/transport/core/interest.h +++ b/libtransport/includes/hicn/transport/core/interest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -40,16 +40,15 @@ class Interest public: using Ptr = std::shared_ptr<Interest>; - Interest(Packet::Format format = HF_INET6_TCP, - std::size_t additional_header_size = 0); + Interest(Packet::Format format, std::size_t additional_header_size = 0); - Interest(const Name &interest_name, Packet::Format format = HF_INET6_TCP, + Interest(const Name &interest_name, Packet::Format format, std::size_t additional_header_size = 0); Interest(MemBuf &&buffer); template <typename... Args> - Interest(CopyBufferOp op, Args &&... args) + Interest(CopyBufferOp op, Args &&...args) : Packet(op, std::forward<Args>(args)...) { if (hicn_interest_get_name(format_, packet_start_, name_.getStructReference()) < 0) { @@ -58,7 +57,7 @@ class Interest } template <typename... Args> - Interest(WrapBufferOp op, Args &&... args) + Interest(WrapBufferOp op, Args &&...args) : Packet(op, std::forward<Args>(args)...) { if (hicn_interest_get_name(format_, packet_start_, name_.getStructReference()) < 0) { @@ -67,8 +66,12 @@ class Interest } template <typename... Args> - Interest(CreateOp op, Args &&... args) - : Packet(op, std::forward<Args>(args)...) {} + Interest(CreateOp op, Args &&...args) + : Packet(op, std::forward<Args>(args)...) { + if (hicn_packet_set_interest(format_, packet_start_) < 0) { + throw errors::MalformedPacketException(); + } + } /* Move constructor */ Interest(Interest &&other_interest); diff --git a/libtransport/includes/hicn/transport/core/io_module.h b/libtransport/includes/hicn/transport/core/io_module.h index ea3cf4e16..817d96d00 100644 --- a/libtransport/includes/hicn/transport/core/io_module.h +++ b/libtransport/includes/hicn/transport/core/io_module.h @@ -40,7 +40,7 @@ typedef struct { class Connector; -class IoModule { +class IoModule : utils::NonCopyable { protected: IoModule() : inet_address_({}), @@ -64,15 +64,20 @@ class IoModule { virtual bool isConnected() = 0; virtual void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") = 0; virtual void registerRoute(const Prefix &prefix) = 0; + virtual void sendMapme() {} + + virtual void setForwardingStrategy(const Prefix &prefix, + std::string &strategy){}; virtual std::uint32_t getMtu() = 0; - virtual bool isControlMessage(const uint8_t *message) = 0; + virtual bool isControlMessage(utils::MemBuf &packet_buffer) = 0; virtual void processControlMessageReply(utils::MemBuf &packet_buffer) = 0; @@ -89,7 +94,7 @@ class IoModule { } } - virtual void send(const uint8_t *packet, std::size_t len) = 0; + virtual void send(const utils::MemBuf::Ptr &buffer) = 0; void setContentStoreSize(uint32_t cs_size) { content_store_reserved_ = cs_size; diff --git a/libtransport/includes/hicn/transport/core/name.h b/libtransport/includes/hicn/transport/core/name.h index 033582289..5cb4efd10 100644 --- a/libtransport/includes/hicn/transport/core/name.h +++ b/libtransport/includes/hicn/transport/core/name.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -51,7 +51,7 @@ class Name { public: using NameStruct = hicn_name_t; - using Type = hicn_name_type_t; + enum class Type { UNDEFINED, V4, V6 }; Name(); @@ -69,6 +69,8 @@ class Name { Name(const Name &name); + ~Name(); + Name &operator=(const Name &name); bool operator==(const Name &name) const; @@ -91,14 +93,11 @@ class Name { uint32_t getSuffix() const; - std::shared_ptr<Sockaddr> getAddress() const; - Name &setSuffix(uint32_t seq_number); ip_prefix_t toIpAddress() const; - void copyToDestination(uint8_t *destination, - bool include_suffix = false) const; + void copyPrefixToDestination(uint8_t *destination) const; int getAddressFamily() const; diff --git a/libtransport/includes/hicn/transport/core/packet.h b/libtransport/includes/hicn/transport/core/packet.h index 269a1571a..059430f1d 100644 --- a/libtransport/includes/hicn/transport/core/packet.h +++ b/libtransport/includes/hicn/transport/core/packet.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -30,11 +30,7 @@ namespace transport { namespace auth { class Signer; -class AsymmetricSigner; -class SymmetricSigner; class Verifier; -class AsymmetricVerifier; -class SymmetricVerifier; } // namespace auth namespace core { @@ -51,16 +47,13 @@ namespace core { class Packet : public utils::MemBuf, public std::enable_shared_from_this<Packet> { friend class auth::Signer; - friend class auth::SymmetricSigner; - friend class auth::AsymmetricSigner; friend class auth::Verifier; - friend class auth::AsymmetricVerifier; - friend class auth::SymmetricVerifier; public: using Ptr = std::shared_ptr<Packet>; using MemBufPtr = std::shared_ptr<utils::MemBuf>; using Format = hicn_format_t; + static constexpr size_t default_mtu = 1500; /** @@ -68,146 +61,73 @@ class Packet : public utils::MemBuf, * the eventual payload will be added by prepending the payload buffer * to the buffer chain whose the fist buffer is the header itself. */ - Packet(Format format = HF_INET6_TCP, std::size_t additional_header_size = 0); - - /** - * Create new IP packet using raw buffer. - */ - + Packet(Format format, std::size_t additional_header_size = 0); /* Copy buffer */ Packet(CopyBufferOp, const uint8_t *buffer, std::size_t size); /* Wrap buffer */ Packet(WrapBufferOp, uint8_t *buffer, std::size_t length, std::size_t size); /* Create new using pre-allocated buffer */ Packet(CreateOp, uint8_t *buffer, std::size_t length, std::size_t size, - Format format = HF_INET6_TCP, std::size_t additional_header_size = 0); - /* Move MemBuf */ - Packet(MemBuf &&buffer); + Format format, std::size_t additional_header_size = 0); + Packet(MemBuf &&buffer); Packet(Packet &&other); - - /* - * Copy constructor and assignemnt operators. - */ Packet(const Packet &other); - Packet &operator=(const Packet &other); - - friend bool operator==(const Packet &l_packet, const Packet &r_packet); + // Destructor virtual ~Packet(); - static std::size_t getHeaderSizeFromFormat(Format format, - std::size_t signature_size = 0) { - std::size_t header_length; - hicn_packet_get_header_length_from_format(format, &header_length); - int is_ah = _is_ah(format); - return is_ah * (header_length + signature_size) + (!is_ah) * header_length; - } - - static std::size_t getHeaderSizeFromBuffer(Format format, - const uint8_t *buffer); - - static std::size_t getPayloadSizeFromBuffer(Format format, - const uint8_t *buffer); - - static bool isInterest(const uint8_t *buffer); - - bool isInterest(); - - static Format getFormatFromBuffer(const uint8_t *buffer, std::size_t length) { - Format format = HF_UNSPEC; - hicn_packet_get_format((const hicn_header_t *)buffer, &format); - return format; - } - - void reset() { - clear(); - packet_start_ = reinterpret_cast<hicn_header_t *>(writableData()); - header_offset_ = 0; - format_ = HF_UNSPEC; - payload_type_ = PayloadType::UNSPECIFIED; - name_.clear(); - - if (isChained()) { - separateChain(next(), prev()); - } - } - - void setFormat(Packet::Format format = HF_INET6_TCP, - std::size_t additional_header_size = 0); - - std::size_t payloadSize() const; - - std::size_t headerSize() const; + // Operators + Packet &operator=(const Packet &other); + friend bool operator==(const Packet &l_packet, const Packet &r_packet); + // Cast to MemBuf std::shared_ptr<utils::MemBuf> acquireMemBufReference(); - virtual const Name &getName() const = 0; + // Format + Format getFormat() const; + void setFormat(Packet::Format format, std::size_t additional_header_size = 0); + // Name + virtual const Name &getName() const = 0; virtual Name &getWritableName() = 0; - virtual void setName(const Name &name) = 0; + // Lifetime virtual void setLifetime(uint32_t lifetime) = 0; - virtual uint32_t getLifetime() const = 0; - Packet &appendPayload(const uint8_t *buffer, std::size_t length); - - Packet &appendPayload(std::unique_ptr<utils::MemBuf> &&payload); - - std::unique_ptr<utils::MemBuf> getPayload() const; - - Packet &updateLength(std::size_t length = 0); - - PayloadType getPayloadType() const; - - Packet &setPayloadType(PayloadType payload_type); - - Format getFormat() const; - - void dump() const; - - static void dump(uint8_t *buffer, std::size_t length); - + // Locator virtual void setLocator(const ip_address_t &locator) = 0; - virtual ip_address_t getLocator() const = 0; - /** - * @brief Set signature timestamp, in milliseconds. - */ - void setSignatureTimestamp(const uint64_t ×tamp_milliseconds); - - uint64_t getSignatureTimestamp() const; - - void setValidationAlgorithm(const auth::CryptoSuite &validation_algorithm); - - auth::CryptoSuite getValidationAlgorithm() const; - - void setKeyId(const auth::KeyId &key_id); + // Payload type + PayloadType getPayloadType() const; + Packet &setPayloadType(PayloadType payload_type); - auth::KeyId getKeyId() const; + // Payload + std::unique_ptr<utils::MemBuf> getPayload() const; + Packet &appendPayload(std::unique_ptr<utils::MemBuf> &&payload); + Packet &appendPayload(const uint8_t *buffer, std::size_t length); - virtual auth::CryptoHash computeDigest(auth::CryptoHashType algorithm) const; + // Sizes + std::size_t headerSize() const; + std::size_t payloadSize() const; - void setChecksum() { - uint16_t partial_csum = - csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); + // Digest + auth::CryptoHash computeDigest(auth::CryptoHashType algorithm) const; - for (utils::MemBuf *current = next(); current != this; - current = current->next()) { - partial_csum = csum(current->data(), current->length(), ~partial_csum); - } + // Reset packet + void reset(); - if (hicn_packet_compute_header_checksum(format_, packet_start_, - partial_csum) < 0) { - throw errors::MalformedPacketException(); - } - } + // Utils + bool isInterest(); + Packet &updateLength(std::size_t length = 0); + void dump() const; + // TCP methods + void setChecksum(); bool checkIntegrity() const; - Packet &setSyn(); Packet &resetSyn(); bool testSyn() const; @@ -222,53 +142,45 @@ class Packet : public utils::MemBuf, bool testFin() const; Packet &resetFlags(); std::string printFlags() const; - Packet &setSrcPort(uint16_t srcPort); Packet &setDstPort(uint16_t dstPort); uint16_t getSrcPort() const; uint16_t getDstPort() const; - Packet &setTTL(uint8_t hops); uint8_t getTTL() const; + // Authentication Header methods + bool hasAH() const; + std::vector<uint8_t> getSignature() const; + std::size_t getSignatureFieldSize() const; + std::size_t getSignatureSize() const; + uint64_t getSignatureTimestamp() const; + auth::KeyId getKeyId() const; + auth::CryptoSuite getValidationAlgorithm() const; + void setSignature(const std::vector<uint8_t> &signature); + void setSignatureFieldSize(std::size_t size); + void setSignatureSize(std::size_t size); + void setSignatureTimestamp(const uint64_t ×tamp_ms); + void setKeyId(const auth::KeyId &key_id); + void setValidationAlgorithm(const auth::CryptoSuite &algo); + + // Static methods + static Format toAHFormat(const Format &format); + static Format getFormatFromBuffer(const uint8_t *buffer, std::size_t length); + static std::size_t getHeaderSizeFromFormat(Format format, + std::size_t signature_size = 0); + static std::size_t getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer); + static std::size_t getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer); + static bool isInterest(const uint8_t *buffer, + Format format = Format::HF_UNSPEC); + static void dump(uint8_t *buffer, std::size_t length); + private: virtual void resetForHash() = 0; - void setSignatureSize(std::size_t size_bytes); - void setSignatureSizeGap(std::size_t size_bytes); void prependPayload(const uint8_t **buffer, std::size_t *size); - bool authenticationHeader() const { return _is_ah(format_); } - - std::size_t getSignatureSize() const { - size_t size_bytes; - int ret = - hicn_packet_get_signature_size(format_, packet_start_, &size_bytes); - - if (ret < 0) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - return size_bytes; - } - - std::size_t getSignatureSizeGap() const { - uint8_t size_bytes; - int ret = - hicn_packet_get_signature_gap(format_, packet_start_, &size_bytes); - - if (ret < 0) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - return (size_t)size_bytes; - } - - std::size_t getSignatureSizeReal() const { - return getSignatureSize() - getSignatureSizeGap(); - } - - uint8_t *getSignature() const; - protected: hicn_header_t *packet_start_; std::size_t header_offset_; diff --git a/libtransport/includes/hicn/transport/core/payload_type.h b/libtransport/includes/hicn/transport/core/payload_type.h index 8c918f792..3a682e177 100644 --- a/libtransport/includes/hicn/transport/core/payload_type.h +++ b/libtransport/includes/hicn/transport/core/payload_type.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/core/prefix.h b/libtransport/includes/hicn/transport/core/prefix.h index 7ef667bc8..13401e1a8 100644 --- a/libtransport/includes/hicn/transport/core/prefix.h +++ b/libtransport/includes/hicn/transport/core/prefix.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -35,6 +35,10 @@ class Prefix { Prefix(const core::Name &content_name, uint16_t prefix_length); + bool operator<(const Prefix &prefix) const; + + bool operator==(const Prefix &prefix) const; + std::unique_ptr<Sockaddr> toSockaddr() const; uint16_t getPrefixLength() const; diff --git a/libtransport/includes/hicn/transport/errors/CMakeLists.txt b/libtransport/includes/hicn/transport/errors/CMakeLists.txt index 1d35c5b34..c8642b51b 100644 --- a/libtransport/includes/hicn/transport/errors/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/errors/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/errors.h b/libtransport/includes/hicn/transport/errors/errors.h index b659820fa..09ce97221 100644 --- a/libtransport/includes/hicn/transport/errors/errors.h +++ b/libtransport/includes/hicn/transport/errors/errors.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/indexing_exception.h b/libtransport/includes/hicn/transport/errors/indexing_exception.h index 731314f0e..bdcc39aef 100644 --- a/libtransport/includes/hicn/transport/errors/indexing_exception.h +++ b/libtransport/includes/hicn/transport/errors/indexing_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/invalid_ip_address_exception.h b/libtransport/includes/hicn/transport/errors/invalid_ip_address_exception.h index 60226f576..26e0f98af 100644 --- a/libtransport/includes/hicn/transport/errors/invalid_ip_address_exception.h +++ b/libtransport/includes/hicn/transport/errors/invalid_ip_address_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/malformed_ahpacket_exception.h b/libtransport/includes/hicn/transport/errors/malformed_ahpacket_exception.h index f0cfe0b82..9b3ec7466 100644 --- a/libtransport/includes/hicn/transport/errors/malformed_ahpacket_exception.h +++ b/libtransport/includes/hicn/transport/errors/malformed_ahpacket_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/malformed_name_exception.h b/libtransport/includes/hicn/transport/errors/malformed_name_exception.h index 4ef45d2e8..57412cf7f 100644 --- a/libtransport/includes/hicn/transport/errors/malformed_name_exception.h +++ b/libtransport/includes/hicn/transport/errors/malformed_name_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/malformed_packet_exception.h b/libtransport/includes/hicn/transport/errors/malformed_packet_exception.h index ec5c97e6e..996d6a4ab 100644 --- a/libtransport/includes/hicn/transport/errors/malformed_packet_exception.h +++ b/libtransport/includes/hicn/transport/errors/malformed_packet_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/not_implemented_exception.h b/libtransport/includes/hicn/transport/errors/not_implemented_exception.h index e9869163d..3291fa425 100644 --- a/libtransport/includes/hicn/transport/errors/not_implemented_exception.h +++ b/libtransport/includes/hicn/transport/errors/not_implemented_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/null_pointer_exception.h b/libtransport/includes/hicn/transport/errors/null_pointer_exception.h index bd06485ed..7edb619b8 100644 --- a/libtransport/includes/hicn/transport/errors/null_pointer_exception.h +++ b/libtransport/includes/hicn/transport/errors/null_pointer_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/runtime_exception.h b/libtransport/includes/hicn/transport/errors/runtime_exception.h index ba5128a7e..498c41a7c 100644 --- a/libtransport/includes/hicn/transport/errors/runtime_exception.h +++ b/libtransport/includes/hicn/transport/errors/runtime_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/tokenizer_exception.h b/libtransport/includes/hicn/transport/errors/tokenizer_exception.h index 76eda838e..31f79644a 100644 --- a/libtransport/includes/hicn/transport/errors/tokenizer_exception.h +++ b/libtransport/includes/hicn/transport/errors/tokenizer_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/errors/unexpected_manifest_exception.h b/libtransport/includes/hicn/transport/errors/unexpected_manifest_exception.h index 6f71471e4..c8e4eadd4 100644 --- a/libtransport/includes/hicn/transport/errors/unexpected_manifest_exception.h +++ b/libtransport/includes/hicn/transport/errors/unexpected_manifest_exception.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/http/CMakeLists.txt b/libtransport/includes/hicn/transport/http/CMakeLists.txt index 9f4cdaf39..36a2ca5ce 100644 --- a/libtransport/includes/hicn/transport/http/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/http/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2020 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/http/client_connection.h b/libtransport/includes/hicn/transport/http/client_connection.h index 7e78e9c59..d5edf6cd0 100644 --- a/libtransport/includes/hicn/transport/http/client_connection.h +++ b/libtransport/includes/hicn/transport/http/client_connection.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -31,7 +31,7 @@ namespace http { using namespace interface; using namespace core; -class HTTPClientConnection { +class HTTPClientConnection : private utils::NonCopyable { static constexpr uint32_t max_buffer_capacity = 64 * 1024; public: @@ -39,7 +39,7 @@ class HTTPClientConnection { public: virtual void onBytesReceived(std::unique_ptr<utils::MemBuf> &&buffer) = 0; virtual void onSuccess(std::size_t bytes) = 0; - virtual void onError(const std::error_code ec) = 0; + virtual void onError(const std::error_code &ec) = 0; }; enum class RC : uint32_t { DOWNLOAD_FAILED, DOWNLOAD_SUCCESS }; diff --git a/libtransport/includes/hicn/transport/http/default_values.h b/libtransport/includes/hicn/transport/http/default_values.h index 2d5a6b821..8cf247e19 100644 --- a/libtransport/includes/hicn/transport/http/default_values.h +++ b/libtransport/includes/hicn/transport/http/default_values.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/http/facade.h b/libtransport/includes/hicn/transport/http/facade.h index 8a465ce94..c886a7588 100644 --- a/libtransport/includes/hicn/transport/http/facade.h +++ b/libtransport/includes/hicn/transport/http/facade.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/http/message.h b/libtransport/includes/hicn/transport/http/message.h index b8756224f..a07941413 100644 --- a/libtransport/includes/hicn/transport/http/message.h +++ b/libtransport/includes/hicn/transport/http/message.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/http/request.h b/libtransport/includes/hicn/transport/http/request.h index b62f5b061..9939fbcaf 100644 --- a/libtransport/includes/hicn/transport/http/request.h +++ b/libtransport/includes/hicn/transport/http/request.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/http/response.h b/libtransport/includes/hicn/transport/http/response.h index bab41acb8..79def27cd 100644 --- a/libtransport/includes/hicn/transport/http/response.h +++ b/libtransport/includes/hicn/transport/http/response.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt b/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt index 40623dfe1..1acaadcf0 100644 --- a/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/interfaces/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -21,6 +21,7 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/global_conf_interface.h ${CMAKE_CURRENT_SOURCE_DIR}/statistics.h ${CMAKE_CURRENT_SOURCE_DIR}/portal.h + ${CMAKE_CURRENT_SOURCE_DIR}/notification.h ) if (${OPENSSL_VERSION} VERSION_EQUAL "1.1.1a" OR ${OPENSSL_VERSION} VERSION_GREATER "1.1.1a") diff --git a/libtransport/includes/hicn/transport/interfaces/callbacks.h b/libtransport/includes/hicn/transport/interfaces/callbacks.h index 22e111799..41e4fba3b 100644 --- a/libtransport/includes/hicn/transport/interfaces/callbacks.h +++ b/libtransport/includes/hicn/transport/interfaces/callbacks.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/auth/policies.h> +#include <hicn/transport/interfaces/notification.h> #include <hicn/transport/interfaces/statistics.h> #include <functional> @@ -60,6 +61,12 @@ using ConsumerTimerCallback = std::function<void(ConsumerSocket &, const TransportStatistics &stats)>; /** + * The ConsumerTimerCallback is called when the forwarding/recovery stategy is + * changes. + */ +using StrategyCallback = std::function<void(notification::Strategy strategy)>; + +/** * The ProducerContentCallback will be called by the producer socket right after * a content has been segmented and published. */ diff --git a/libtransport/src/protocols/fec_base.cc b/libtransport/includes/hicn/transport/interfaces/notification.h index 9252bc473..a0945c3f0 100644 --- a/libtransport/src/protocols/fec_base.cc +++ b/libtransport/includes/hicn/transport/interfaces/notification.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright (c) 2022 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: @@ -13,12 +13,19 @@ * limitations under the License. */ -#include <protocols/fec/rs.h> -#include <protocols/fec_base.h> +#pragma once + +#include <variant> namespace transport { -namespace protocol { +namespace interface { +namespace notification { + +enum class ForwardingStrategy { BEST_PATH, REPLICATION, BOTH, NONE }; +enum class RecoveryStrategy { RECOVERY_OFF, RTX_ONLY, FEC_ONLY, RTX_AND_FEC }; + +using Strategy = std::variant<ForwardingStrategy, RecoveryStrategy>; -namespace fec {} // namespace fec -} // namespace protocol +} // namespace notification +} // namespace interface } // namespace transport
\ No newline at end of file diff --git a/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_consumer.h b/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_consumer.h index 224493f00..a67634c1e 100644 --- a/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_consumer.h +++ b/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h b/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h index d86744514..e197a3658 100644 --- a/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h +++ b/libtransport/includes/hicn/transport/interfaces/p2psecure_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,7 +15,6 @@ #pragma once -#include <hicn/transport/auth/identity.h> #include <hicn/transport/interfaces/socket_producer.h> namespace transport { @@ -25,8 +24,8 @@ namespace interface { class P2PSecureProducerSocket : public ProducerSocket { public: P2PSecureProducerSocket(); - P2PSecureProducerSocket(bool rtc, - const std::shared_ptr<auth::Identity> &identity); + P2PSecureProducerSocket(bool rtc, std::string &keystore_path, + std::string &keystore_pwd); ~P2PSecureProducerSocket() = default; }; diff --git a/libtransport/includes/hicn/transport/interfaces/portal.h b/libtransport/includes/hicn/transport/interfaces/portal.h index 66fc84256..1a22b1f1d 100644 --- a/libtransport/includes/hicn/transport/interfaces/portal.h +++ b/libtransport/includes/hicn/transport/interfaces/portal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -19,6 +19,8 @@ #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <hicn/transport/core/prefix.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/noncopyable.h> #include <functional> @@ -28,58 +30,18 @@ namespace transport { namespace interface { -template <typename PrefixType> -class BasicBindConfig { - static_assert(std::is_same<core::Prefix, PrefixType>::value, - "Prefix must be a Prefix type."); - - const uint32_t standard_cs_reserved = 5000; - - public: - template <typename T> - BasicBindConfig(T &&prefix) - : prefix_(std::forward<T &&>(prefix)), - content_store_reserved_(standard_cs_reserved) {} - - template <typename T> - BasicBindConfig(T &&prefix, uint32_t cs_reserved) - : prefix_(std::forward<T &&>(prefix)), - content_store_reserved_(cs_reserved) {} - - TRANSPORT_ALWAYS_INLINE const PrefixType &prefix() const { return prefix_; } - - TRANSPORT_ALWAYS_INLINE uint32_t csReserved() const { - return content_store_reserved_; - } - - private: - PrefixType prefix_; - uint32_t content_store_reserved_; -}; - -using BindConfig = BasicBindConfig<core::Prefix>; - -class Portal { +class Portal : private utils::NonCopyable { public: /** - * Consumer callback is an abstract class containing two methods to be - * implemented by a consumer application. + * Transport callback is an abstract class containing two methods to be + * implemented by a consumer/producer application. */ - class ConsumerCallback { + class TransportCallback { public: + virtual void onInterest(core::Interest &i) = 0; virtual void onContentObject(core::Interest &i, core::ContentObject &c) = 0; virtual void onTimeout(core::Interest::Ptr &i, const core::Name &n) = 0; - virtual void onError(std::error_code ec) = 0; - }; - - /** - * Producer callback is an abstract class containing two methods to be - * implemented by a producer application. - */ - class ProducerCallback { - public: - virtual void onInterest(core::Interest &i) = 0; - virtual void onError(std::error_code ec) = 0; + virtual void onError(const std::error_code &ec) = 0; }; using OnContentObjectCallback = @@ -89,21 +51,14 @@ class Portal { Portal(); - Portal(asio::io_service &io_service); + Portal(::utils::EventThread &worker); /** - * Set the consumer callback. - * - * @param consumer_callback - The pointer to the ConsumerCallback object. - */ - void setConsumerCallback(ConsumerCallback *consumer_callback); - - /** - * Set the producer callback. + * Set the transport protocl callback. * * @param producer_callback - The pointer to the ProducerCallback object. */ - void setProducerCallback(ProducerCallback *producer_callback); + void registerTransportCallback(TransportCallback *transport_callback); /** * Connect the transport to the local hicn forwarder. @@ -146,34 +101,12 @@ class Portal { UNSET_CALLBACK); /** - * Register a producer name to the local forwarder and optionally set the - * content store size in a per-face manner. - * - * @param config - The configuration for the local forwarder binding. - */ - void bind(const BindConfig &config); - - void runEventsLoop(); - - /** - * Run one event and return. - */ - void runOneEvent(); - - /** * Send a data packet to the local forwarder. As opposite to sendInterest, the * ownership of the content object is not transferred to the portal. * * @param content_object - The data packet. */ void sendContentObject(core::ContentObject &content_object); - /** - * Stop the event loop, canceling all the pending events in the event queue. - * - * Beware that stopping the event loop DOES NOT disconnect the transport from - * the local forwarder, the connector underneath will stay connected. - */ - void stopEventsLoop(); /** * Disconnect the transport from the local forwarder. @@ -188,15 +121,26 @@ class Portal { /** * Get a reference to the io_service object. */ - asio::io_service &getIoService(); + utils::EventThread &getThread(); /** * Register a route to the local forwarder. */ void registerRoute(core::Prefix &prefix); + /** + * Send a MAP-Me command to traverse NATs. + */ + void sendMapme(); + + /** + * Set forwarding strategy + */ + void setForwardingStrategy(core::Prefix &prefix, std::string &strategy); + private: - void *implementation_; + class Impl; + Impl *implementation_; }; } // namespace interface diff --git a/libtransport/includes/hicn/transport/interfaces/publication_options.h b/libtransport/includes/hicn/transport/interfaces/publication_options.h index 6910e5371..03a0763f5 100644 --- a/libtransport/includes/hicn/transport/interfaces/publication_options.h +++ b/libtransport/includes/hicn/transport/interfaces/publication_options.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/interfaces/socket_consumer.h b/libtransport/includes/hicn/transport/interfaces/socket_consumer.h index 5e0e81b9f..e26fe5a85 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_consumer.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -23,6 +23,8 @@ #include <hicn/transport/interfaces/callbacks.h> #include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/noncopyable.h> #define CONSUMER_FINISHED 0 #define CONSUMER_BUSY 1 @@ -45,7 +47,7 @@ using namespace core; * It allows to retrieve an application data from one/many producers, by * hiding all the complexity of the transport protocol used underneath. */ -class ConsumerSocket { +class ConsumerSocket : private utils::NonCopyable { public: /** * The ReadCallback is a class which can be used by the transport for both @@ -124,7 +126,7 @@ class ConsumerSocket { * * @param ec - An error code describing the error. */ - virtual void readError(const std::error_code ec) noexcept = 0; + virtual void readError(const std::error_code &ec) noexcept = 0; /** * This callback will be invoked when the whole content is retrieved. The @@ -166,7 +168,12 @@ class ConsumerSocket { * 110, 104-117 * - RTC: Real time communication */ - explicit ConsumerSocket(int protocol, asio::io_service &io_service); + explicit ConsumerSocket(int protocol, ::utils::EventThread &worker); + + /** + * @brief Move contructor + */ + ConsumerSocket(ConsumerSocket &&other) noexcept; /** * @brief Destroy the consumer socket. @@ -200,12 +207,10 @@ class ConsumerSocket { * content retrieval succeeded. This information can be obtained from the * error code in CONTENT_RETRIEVED callback. */ - int consume(const Name &name); - int asyncConsume(const Name &name); + int consume(const Name &name, bool blocking = false); /** - * Stops the consumer socket. If several downloads are queued (using - * asyncConsume), this call stops just the current one. + * Stops the consumer socket. */ void stop(); @@ -255,6 +260,12 @@ class ConsumerSocket { int setSocketOption(int socket_option_key, ConsumerTimerCallback socket_option_value); + int setSocketOption(int socket_option_key, + StrategyCallback socket_option_value); + + int setSocketOption(int socket_option_key, + Packet::Format socket_option_value); + int getSocketOption(int socket_option_key, double &socket_option_value); int getSocketOption(int socket_option_key, uint32_t &socket_option_value); @@ -280,8 +291,14 @@ class ConsumerSocket { ConsumerTimerCallback **socket_option_value); int getSocketOption(int socket_option_key, + StrategyCallback **socket_option_value); + + int getSocketOption(int socket_option_key, interface::TransportStatistics **socket_option_value); + int getSocketOption(int socket_option_key, + Packet::Format &socket_option_value); + protected: ConsumerSocket(); std::unique_ptr<implementation::ConsumerSocket> socket_; diff --git a/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h b/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h index f4945ac8a..04454852b 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_options_default_values.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,7 +15,10 @@ #pragma once +extern "C" { #include <hicn/base.h> +#include <hicn/compat.h> +} #include <chrono> #include <cstdint> @@ -26,6 +29,16 @@ namespace interface { namespace default_values { +// Packet format +// #define NEW_PACKET_FORMAT +static constexpr hicn_format_t packet_format = +#ifdef NEW_PACKET_FORMAT + HF_INET6_UDP; +#else + HF_INET6_TCP; +#endif + +// Parameters static constexpr uint32_t interest_lifetime = 1001; // milliseconds static constexpr uint32_t never_expire_time = HICN_MAX_LIFETIME; static constexpr uint32_t content_object_expiry_time = @@ -40,6 +53,8 @@ static constexpr uint32_t key_locator_size = 60; // bytes static constexpr uint32_t limit_guard = 80; // bytes static constexpr uint32_t digest_size = 34; // bytes static constexpr uint32_t max_out_of_order_segments = 3; // content object +static constexpr uint32_t max_unverified_delay = 2001; // milliseconds +static constexpr uint32_t manifest_capacity = 30; // RAAQM static constexpr int sample_number = 30; diff --git a/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h b/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h index 00cd44075..90f218770 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_options_keys.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -26,15 +26,27 @@ namespace transport { namespace interface { typedef enum { - RAAQM = 0, - CBR = 1, - RTC = 2, + UNKNOWN = 0, + BYTE_STREAM = 1, + RTC_PROD = 2, +} ProductionProtocolAlgorithms; + +typedef enum { + RAAQM = 10, + CBR = 11, + RTC = 12, } TransportProtocolAlgorithms; typedef enum { - BYTE_STREAM = 10, - RTC_PROD = 11, -} ProductionProtocolAlgorithms; + RECOVERY_OFF = 20, + RTX_ONLY = 21, + FEC_ONLY = 22, + DELAY_BASED = 23, + LOW_RATE = 24, + LOW_RATE_AND_BESTPATH = 25, + LOW_RATE_AND_REPLICATION = 26, + LOW_RATE_AND_ALL_FWD_STRATEGIES = 27, +} RtcTransportRecoveryStrategies; typedef enum { INPUT_BUFFER_SIZE = 101, @@ -45,6 +57,8 @@ typedef enum { DATA_PACKET_SIZE = 106, INTEREST_LIFETIME = 107, CONTENT_OBJECT_EXPIRY_TIME = 108, + MAX_SEGMENT_SIZE = 109, + MAX_UNVERIFIED_TIME = 110, MIN_WINDOW_SIZE = 111, MAX_WINDOW_SIZE = 112, CURRENT_WINDOW_SIZE = 113, @@ -57,7 +71,9 @@ typedef enum { SIGNER = 121, VERIFIER = 122, STATS_INTERVAL = 125, - SUFFIX_STRATEGY = 126 + SUFFIX_STRATEGY = 126, + PACKET_FORMAT = 127, + FEC_TYPE = 128, } GeneralTransportOptions; typedef enum { @@ -87,7 +103,9 @@ typedef enum { CONTENT_OBJECT_TO_VERIFY = 413, VERIFICATION_FAILED = 414, READ_CALLBACK = 415, - STATS_SUMMARY = 416 + STATS_SUMMARY = 416, + FWD_STRATEGY_CHANGE = 417, + REC_STRATEGY_CHANGE = 418, } ConsumerCallbacksOptions; typedef enum { @@ -100,7 +118,8 @@ typedef enum { CONTENT_OBJECT_READY = 510, CONTENT_OBJECT_OUTPUT = 511, CONTENT_PRODUCED = 512, - CONTENT_OBJECT_TO_SIGN = 513 + CONTENT_OBJECT_TO_SIGN = 513, + PRODUCER_CALLBACK = 514, } ProducerCallbacksOptions; typedef enum { OUTPUT_INTERFACE = 601 } DataLinkOptions; @@ -116,6 +135,11 @@ typedef enum { RSA_256 = 802, } SignatureType; +typedef enum { + RECOVERY_STRATEGY = 901, + AGGREGATED_DATA = 902, +} RtcTransportOptions; + } // namespace interface } // end namespace transport diff --git a/libtransport/includes/hicn/transport/interfaces/socket_producer.h b/libtransport/includes/hicn/transport/interfaces/socket_producer.h index 27b603dfe..77b89742a 100644 --- a/libtransport/includes/hicn/transport/interfaces/socket_producer.h +++ b/libtransport/includes/hicn/transport/interfaces/socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -23,6 +23,8 @@ #include <hicn/transport/interfaces/callbacks.h> #include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/noncopyable.h> namespace transport { @@ -34,12 +36,28 @@ namespace interface { using namespace core; -class ProducerSocket { +class ProducerSocket : private utils::NonCopyable { public: + /** + * @brief This class is used by the transport to notify events to the + * application. + */ + class Callback { + public: + /** + * @brief This will invoked in an error occurred in the production protocol. + * + * @param ec - An error code describing the error. + */ + virtual void produceError(const std::error_code &ec) noexcept = 0; + }; + explicit ProducerSocket( int protocol = ProductionProtocolAlgorithms::BYTE_STREAM); - explicit ProducerSocket(int protocol, asio::io_service &io_service); + explicit ProducerSocket(int protocol, ::utils::EventThread &worker); + + ProducerSocket(ProducerSocket &&other) noexcept; virtual ~ProducerSocket(); @@ -63,21 +81,18 @@ class ProducerSocket { uint32_t produceDatagram(const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer); - void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size, - bool is_last = true, uint32_t *start_offset = nullptr); - - void asyncProduce(Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr); - void produce(ContentObject &content_object); - void serveForever(); + void sendMapme(); void stop(); + void start(); + asio::io_service &getIoService(); + int setSocketOption(int socket_option_key, Callback *socket_option_value); + int setSocketOption(int socket_option_key, uint32_t socket_option_value); int setSocketOption(int socket_option_key, @@ -111,6 +126,9 @@ class ProducerSocket { int setSocketOption(int socket_option_key, const std::string &socket_option_value); + int setSocketOption(int socket_option_key, + Packet::Format socket_option_value); + int getSocketOption(int socket_option_key, uint32_t &socket_option_value); int getSocketOption(int socket_option_key, bool &socket_option_value); @@ -138,6 +156,9 @@ class ProducerSocket { int getSocketOption(int socket_option_key, std::string &socket_option_value); + int getSocketOption(int socket_option_key, + Packet::Format &socket_option_value); + protected: ProducerSocket(bool); std::unique_ptr<implementation::ProducerSocket> socket_; diff --git a/libtransport/includes/hicn/transport/interfaces/statistics.h b/libtransport/includes/hicn/transport/interfaces/statistics.h index 1ff6f3edd..4d9e1bfe2 100644 --- a/libtransport/includes/hicn/transport/interfaces/statistics.h +++ b/libtransport/includes/hicn/transport/interfaces/statistics.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/portability/c_portability.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <cstdint> @@ -37,6 +38,8 @@ class TransportStatistics { static constexpr double default_alpha = 0.7; public: + enum class statsAlerts : uint8_t { CONGESTION, LATENCY, LOSSES }; + TransportStatistics(double alpha = default_alpha) : retx_count_(0), bytes_received_(0), @@ -51,11 +54,15 @@ class TransportStatistics { lost_data_(0), definitely_lost_data_(0), recovered_data_(0), - status_(-1), + status_(0), // avg_data_rtt_(0), avg_pending_pkt_(0.0), received_nacks_(0), - received_fec_(0) {} + received_fec_(0), + in_congestion_(false), + residual_loss_rate_(0.0), + quality_score_(5), + alerts_(0) {} TRANSPORT_ALWAYS_INLINE void updateRetxCount(uint64_t retx) { retx_count_ += retx; @@ -65,8 +72,11 @@ class TransportStatistics { bytes_received_ += bytes; } - TRANSPORT_ALWAYS_INLINE void updateAverageRtt(uint64_t rtt) { - average_rtt_ = (alpha_ * average_rtt_) + ((1. - alpha_) * double(rtt)); + TRANSPORT_ALWAYS_INLINE void updateAverageRtt( + const utils::SteadyTime::Milliseconds &rtt) { + auto rtt_milliseconds = rtt.count(); + average_rtt_ = + (alpha_ * average_rtt_) + ((1. - alpha_) * double(rtt_milliseconds)); } TRANSPORT_ALWAYS_INLINE void updateAverageWindowSize(double current_window) { @@ -120,6 +130,26 @@ class TransportStatistics { received_fec_ += pkt; } + TRANSPORT_ALWAYS_INLINE void updateResidualLossRate(double val) { + residual_loss_rate_ = val; + } + + TRANSPORT_ALWAYS_INLINE void updateQualityScore(uint8_t val) { + quality_score_ = val; + } + + TRANSPORT_ALWAYS_INLINE void updateCongestionState(bool state) { + in_congestion_ = state; + } + + TRANSPORT_ALWAYS_INLINE void setAlert(statsAlerts x) { + alerts_ |= 1UL << (uint32_t)x; + } + + TRANSPORT_ALWAYS_INLINE void clearAlert(statsAlerts x) { + alerts_ &= ~(1UL << (uint32_t)x); + } + TRANSPORT_ALWAYS_INLINE uint64_t getRetxCount() const { return retx_count_; } TRANSPORT_ALWAYS_INLINE uint64_t getBytesRecv() const { @@ -174,6 +204,18 @@ class TransportStatistics { return received_fec_; } + TRANSPORT_ALWAYS_INLINE double getResidualLossRate() const { + return residual_loss_rate_; + } + + TRANSPORT_ALWAYS_INLINE uint8_t getQualityScore() const { + return quality_score_; + } + + TRANSPORT_ALWAYS_INLINE bool isCongested() const { return in_congestion_; } + + TRANSPORT_ALWAYS_INLINE uint32_t getAlerts() const { return alerts_; } + TRANSPORT_ALWAYS_INLINE void setAlpha(double val) { alpha_ = val; } TRANSPORT_ALWAYS_INLINE void reset() { @@ -193,6 +235,8 @@ class TransportStatistics { avg_pending_pkt_ = 0; received_nacks_ = 0; received_fec_ = 0; + in_congestion_ = false; + quality_score_ = 5; } private: @@ -213,6 +257,14 @@ class TransportStatistics { double avg_pending_pkt_; uint32_t received_nacks_; uint32_t received_fec_; + bool in_congestion_; + double residual_loss_rate_; + uint8_t quality_score_; + + // alerts is a bit vector used to signal to the upper layer that + // something bad is appening in the network, the encode is done accoding to + // the enum alerts; + uint32_t alerts_; }; } // namespace interface diff --git a/libtransport/includes/hicn/transport/portability/CMakeLists.txt b/libtransport/includes/hicn/transport/portability/CMakeLists.txt index 8094c0661..7a688b1f1 100644 --- a/libtransport/includes/hicn/transport/portability/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/portability/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -14,6 +14,7 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/c_portability.h ${CMAKE_CURRENT_SOURCE_DIR}/portability.h + ${CMAKE_CURRENT_SOURCE_DIR}/cpu.h ) list(APPEND SOURCE_FILES diff --git a/libtransport/includes/hicn/transport/portability/c_portability.h b/libtransport/includes/hicn/transport/portability/c_portability.h index 2675de000..bc697d8b1 100644 --- a/libtransport/includes/hicn/transport/portability/c_portability.h +++ b/libtransport/includes/hicn/transport/portability/c_portability.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/libtransport/includes/hicn/transport/portability/cpu.h b/libtransport/includes/hicn/transport/portability/cpu.h new file mode 100644 index 000000000..036bf9cd9 --- /dev/null +++ b/libtransport/includes/hicn/transport/portability/cpu.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 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. + */ + +#pragma once + +#if defined(__aarch64__) && defined(__ARM_NEON) || defined(__i686__) +#define TRANSPORT_HAVE_VEC128 +#endif + +#if defined(__SSE4_2__) && __GNUC__ >= 4 +#define TRANSPORT_HAVE_VEC128 +#endif + +#if defined(__ALTIVEC__) +#define TRANSPORT_HAVE_VEC128 +#endif + +#if defined(__AVX2__) +#define TRANSPORT_HAVE_VEC256 +#if defined(__clang__) && __clang_major__ < 4 +#undef TRANSPORT_HAVE_VEC256 +#endif +#endif + +#if defined(__AVX512BITALG__) +#define TRANSPORT_HAVE_VEC512 +#endif diff --git a/libtransport/includes/hicn/transport/portability/portability.h b/libtransport/includes/hicn/transport/portability/portability.h index 24ef012f7..b093f8892 100644 --- a/libtransport/includes/hicn/transport/portability/portability.h +++ b/libtransport/includes/hicn/transport/portability/portability.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,9 +28,6 @@ namespace portability { -constexpr bool little_endian_arch = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; -constexpr bool big_endian_arch = !little_endian_arch; - // Generalize warning push/pop. #if defined(__GNUC__) || defined(__clang__) // Clang & GCC diff --git a/libtransport/includes/hicn/transport/portability/win_portability.h b/libtransport/includes/hicn/transport/portability/win_portability.h index 24c7a932a..81aa828cd 100644 --- a/libtransport/includes/hicn/transport/portability/win_portability.h +++ b/libtransport/includes/hicn/transport/portability/win_portability.h @@ -1,44 +1,44 @@ -/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Copyright 2017 Facebook, Inc.
- *
- * 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.
- */
-
-#pragma once
-#define WIN32_LEAN_AND_MEAN
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <fcntl.h>
-#include <io.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2ipdef.h>
-#include <ws2tcpip.h>
-
-#include <algorithm>
-
-#define __ORDER_LITTLE_ENDIAN__ 0x41424344UL
-#define __ORDER_BIG_ENDIAN__ 0x44434241UL
-#define __BYTE_ORDER__ ('ABCD')
-#undef DELETE
-
-#define HAVE_STRUCT_TIMESPEC
+/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * 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. + */ + +#pragma once +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <fcntl.h> +#include <io.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <windows.h> +#include <winsock2.h> +#include <ws2ipdef.h> +#include <ws2tcpip.h> + +#include <algorithm> + +#define __ORDER_LITTLE_ENDIAN__ 0x41424344UL +#define __ORDER_BIG_ENDIAN__ 0x44434241UL +#define __BYTE_ORDER__ ('ABCD') +#undef DELETE + +#define HAVE_STRUCT_TIMESPEC #include <pthread.h>
\ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/CMakeLists.txt b/libtransport/includes/hicn/transport/utils/CMakeLists.txt index 75f727f03..bbd149e09 100644 --- a/libtransport/includes/hicn/transport/utils/CMakeLists.txt +++ b/libtransport/includes/hicn/transport/utils/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -19,6 +19,7 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/chrono_typedefs.h ${CMAKE_CURRENT_SOURCE_DIR}/branch_prediction.h ${CMAKE_CURRENT_SOURCE_DIR}/ring_buffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_quality_score.h ${CMAKE_CURRENT_SOURCE_DIR}/literals.h ${CMAKE_CURRENT_SOURCE_DIR}/conversions.h ${CMAKE_CURRENT_SOURCE_DIR}/linux.h diff --git a/libtransport/includes/hicn/transport/utils/array.h b/libtransport/includes/hicn/transport/utils/array.h index 7c0ed65d8..d57bfabaf 100644 --- a/libtransport/includes/hicn/transport/utils/array.h +++ b/libtransport/includes/hicn/transport/utils/array.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/branch_prediction.h b/libtransport/includes/hicn/transport/utils/branch_prediction.h index 8cbfaca76..626cb1573 100644 --- a/libtransport/includes/hicn/transport/utils/branch_prediction.h +++ b/libtransport/includes/hicn/transport/utils/branch_prediction.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/chrono_typedefs.h b/libtransport/includes/hicn/transport/utils/chrono_typedefs.h index 8f28e763c..ddfbd00cd 100644 --- a/libtransport/includes/hicn/transport/utils/chrono_typedefs.h +++ b/libtransport/includes/hicn/transport/utils/chrono_typedefs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -17,11 +17,102 @@ #include <chrono> +namespace std { +namespace chrono { +namespace detail { + +template <typename From, typename To> +struct posix_duration_cast; + +// chrono -> timespec caster +template <typename Rep, typename Period> +struct posix_duration_cast<std::chrono::duration<Rep, Period>, + struct timespec> { + static struct timespec cast(std::chrono::duration<Rep, Period> const &d) { + struct timespec tv; + + std::chrono::seconds const sec = + std::chrono::duration_cast<std::chrono::seconds>(d); + + tv.tv_sec = sec.count(); + tv.tv_nsec = + std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count(); + + return tv; + } +}; + +// timespec -> chrono caster +template <typename Rep, typename Period> +struct posix_duration_cast<struct timespec, + std::chrono::duration<Rep, Period>> { + static std::chrono::duration<Rep, Period> cast(struct timespec const &tv) { + return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>( + std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec)); + } +}; + +} // namespace detail + +// chrono -> timespec +template <typename T, typename Rep, typename Period> +auto duration_cast(std::chrono::duration<Rep, Period> const &d) -> + typename std::enable_if<std::is_same<T, struct timespec>::value, + struct timespec>::type { + return detail::posix_duration_cast<std::chrono::duration<Rep, Period>, + timespec>::cast(d); +} + +// timespec -> chrono +template <typename Duration> +Duration duration_cast(struct timespec const &tv) { + return detail::posix_duration_cast<struct timespec, Duration>::cast(tv); +} + +} // namespace chrono +} // namespace std + namespace utils { -using SteadyClock = std::chrono::steady_clock; -using TimePoint = SteadyClock::time_point; -using Milliseconds = std::chrono::milliseconds; -using Microseconds = std::chrono::microseconds; +template <typename T> +class Time { + public: + using Clock = T; + using TimePoint = typename Clock::time_point; + using Rep = double; + using Seconds = std::chrono::duration<Rep>; + using Milliseconds = std::chrono::duration<Rep, std::milli>; + using Microseconds = std::chrono::duration<Rep, std::micro>; + + static auto now() { return Clock::now(); } + + // From epochs + static auto nowMs() { + return std::chrono::duration_cast<Milliseconds>(now().time_since_epoch()); + } + + // From epoch + static auto nowUs() { + return std::chrono::duration_cast<Microseconds>(now().time_since_epoch()); + } + + template <typename D> + static auto getDuration(const TimePoint &start, const TimePoint &end) { + return std::chrono::duration_cast<D>(end - start); + } + + static auto getDurationS(const TimePoint &start, const TimePoint &end) { + return getDuration<Seconds>(start, end); + } + static auto getDurationMs(const TimePoint &start, const TimePoint &end) { + return getDuration<Milliseconds>(start, end); + } + static auto getDurationUs(const TimePoint &start, const TimePoint &end) { + return getDuration<Microseconds>(start, end); + } +}; + +using SteadyTime = Time<std::chrono::steady_clock>; +using SystemTime = Time<std::chrono::system_clock>; } // namespace utils diff --git a/libtransport/includes/hicn/transport/utils/conversions.h b/libtransport/includes/hicn/transport/utils/conversions.h index 52d3e3168..812803175 100644 --- a/libtransport/includes/hicn/transport/utils/conversions.h +++ b/libtransport/includes/hicn/transport/utils/conversions.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/daemonizator.h b/libtransport/includes/hicn/transport/utils/daemonizator.h index 028d74865..1d0a0b309 100644 --- a/libtransport/includes/hicn/transport/utils/daemonizator.h +++ b/libtransport/includes/hicn/transport/utils/daemonizator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/event_thread.h b/libtransport/includes/hicn/transport/utils/event_thread.h index 15ec1d62c..2cd2f3aca 100644 --- a/libtransport/includes/hicn/transport/utils/event_thread.h +++ b/libtransport/includes/hicn/transport/utils/event_thread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -97,6 +97,19 @@ class EventThread { io_service_.get().dispatch(std::forward<Func&&>(f)); } + template <typename Func> + void addAndWaitForExecution(Func&& f) { + 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() { work_.reset(); diff --git a/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h b/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h index 298b6f9d1..19b52b37e 100644 --- a/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h +++ b/libtransport/includes/hicn/transport/utils/fixed_block_allocator.h @@ -1,23 +1,28 @@ /* - * Copyright (c) 2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #pragma once #include <hicn/transport/portability/c_portability.h> +#include <hicn/transport/utils/branch_prediction.h> #include <hicn/transport/utils/singleton.h> #include <hicn/transport/utils/spinlock.h> #include <stdint.h> #include <cassert> #include <cstdlib> +#include <list> #include <memory> namespace utils { template <std::size_t SIZE = 512, std::size_t OBJECTS = 4096> class FixedBlockAllocator - : public utils::Singleton<FixedBlockAllocator<SIZE, OBJECTS>> { - friend class utils::Singleton<FixedBlockAllocator<SIZE, OBJECTS>>; + : public utils::ThreadLocalSingleton<FixedBlockAllocator<SIZE, OBJECTS>> { + friend class utils::ThreadLocalSingleton<FixedBlockAllocator<SIZE, OBJECTS>>; + + static inline const std::size_t BLOCK_SIZE = SIZE; + static inline const std::size_t BLOCKS_PER_POOL = OBJECTS; public: ~FixedBlockAllocator() { @@ -26,19 +31,19 @@ class FixedBlockAllocator } } - void* allocateBlock(size_t size = SIZE) { - assert(size <= SIZE); + void* allocateBlock() { uint32_t index; - SpinLock::Acquire locked(lock_); void* p_block = pop(); if (!p_block) { - if (TRANSPORT_EXPECT_FALSE(current_pool_index_ >= max_objects_)) { + if (TRANSPORT_EXPECT_FALSE(current_pool_index_ >= BLOCKS_PER_POOL)) { // Allocate new memory block p_pools_.emplace_front( - new typename std::aligned_storage<SIZE>::type[max_objects_]); + new typename std::aligned_storage<SIZE>::type[BLOCKS_PER_POOL]); // reset current_pool_index_ current_pool_index_ = 0; + // Increase total block count + block_count_ += BLOCKS_PER_POOL; } auto& latest = p_pools_.front(); @@ -59,7 +64,7 @@ class FixedBlockAllocator } public: - std::size_t blockSize() { return block_size_; } + std::size_t blockSize() { return BLOCK_SIZE; } uint32_t blockCount() { return block_count_; } @@ -69,20 +74,32 @@ class FixedBlockAllocator uint32_t deallocations() { return deallocations_; } + void reset() { + p_head_ = nullptr; + blocks_in_use_ = 0; + allocations_ = 0; + deallocations_ = 0; + current_pool_index_ = 0; + block_count_ = BLOCKS_PER_POOL; + + // Delete all memory pools but the first one + for (auto it = std::next(p_pools_.begin()); it != p_pools_.end();) { + delete[] * it; + it = p_pools_.erase(it); + } + } + private: FixedBlockAllocator() - : block_size_(SIZE), - object_size_(SIZE), - max_objects_(OBJECTS), - p_head_(NULL), + : p_head_(NULL), current_pool_index_(0), - block_count_(0), + block_count_(BLOCKS_PER_POOL), blocks_in_use_(0), allocations_(0), deallocations_(0) { static_assert(SIZE >= sizeof(long*), "SIZE must be at least 8 bytes"); p_pools_.emplace_front( - new typename std::aligned_storage<SIZE>::type[max_objects_]); + new typename std::aligned_storage<SIZE>::type[BLOCKS_PER_POOL]); } void push(void* p_memory) { @@ -106,12 +123,6 @@ class FixedBlockAllocator Block* p_next; }; - static std::unique_ptr<FixedBlockAllocator> instance_; - - const std::size_t block_size_; - const std::size_t object_size_; - const std::size_t max_objects_; - Block* p_head_; uint32_t current_pool_index_; std::list<typename std::aligned_storage<SIZE>::type*> p_pools_; @@ -123,10 +134,6 @@ class FixedBlockAllocator SpinLock lock_; }; -template <std::size_t A, std::size_t B> -std::unique_ptr<FixedBlockAllocator<A, B>> - FixedBlockAllocator<A, B>::instance_ = nullptr; - /** * STL Allocator trait to be used with allocate_shared. */ diff --git a/libtransport/includes/hicn/transport/utils/hash.h b/libtransport/includes/hicn/transport/utils/hash.h index 6815ca4bf..7943596e6 100644 --- a/libtransport/includes/hicn/transport/utils/hash.h +++ b/libtransport/includes/hicn/transport/utils/hash.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/libtransport/includes/hicn/transport/utils/linux.h b/libtransport/includes/hicn/transport/utils/linux.h index 4fbf5f01e..03d29c1db 100644 --- a/libtransport/includes/hicn/transport/utils/linux.h +++ b/libtransport/includes/hicn/transport/utils/linux.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/literals.h b/libtransport/includes/hicn/transport/utils/literals.h index bd00e0a58..531f67362 100644 --- a/libtransport/includes/hicn/transport/utils/literals.h +++ b/libtransport/includes/hicn/transport/utils/literals.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/log.h b/libtransport/includes/hicn/transport/utils/log.h index 0947b755e..f4d39b6b1 100644 --- a/libtransport/includes/hicn/transport/utils/log.h +++ b/libtransport/includes/hicn/transport/utils/log.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/move_wrapper.h b/libtransport/includes/hicn/transport/utils/move_wrapper.h index 5dc3b461d..73389948d 100644 --- a/libtransport/includes/hicn/transport/utils/move_wrapper.h +++ b/libtransport/includes/hicn/transport/utils/move_wrapper.h @@ -23,6 +23,7 @@ namespace utils { template <typename F> struct MoveWrapper : F { MoveWrapper(F&& f) : F(std::move(f)) {} + ~MoveWrapper() = default; MoveWrapper(MoveWrapper&&) = default; MoveWrapper& operator=(MoveWrapper&&) = default; diff --git a/libtransport/includes/hicn/transport/utils/noncopyable.h b/libtransport/includes/hicn/transport/utils/noncopyable.h index 83923e647..0c54d24a3 100644 --- a/libtransport/includes/hicn/transport/utils/noncopyable.h +++ b/libtransport/includes/hicn/transport/utils/noncopyable.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/object_pool.h b/libtransport/includes/hicn/transport/utils/object_pool.h index d9b28e663..63288c655 100644 --- a/libtransport/includes/hicn/transport/utils/object_pool.h +++ b/libtransport/includes/hicn/transport/utils/object_pool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/utils/branch_prediction.h> +#include <hicn/transport/utils/noncopyable.h> #include <hicn/transport/utils/spinlock.h> #include <deque> @@ -25,7 +26,7 @@ namespace utils { template <typename T> -class ObjectPool { +class ObjectPool : private utils::NonCopyable { class ObjectDeleter { public: ObjectDeleter(ObjectPool<T> *pool = nullptr) : pool_(pool) {} @@ -47,8 +48,20 @@ class ObjectPool { ObjectPool() : destructor_(false) {} - // No copies - ObjectPool(const ObjectPool &other) = delete; + ObjectPool(ObjectPool &&other) + : object_pool_lock_(std::move(other.object_pool_lock_)), + object_pool_(std::move(other.object_pool_)), + destructor_(other.destructor_) {} + + ObjectPool &operator=(ObjectPool &&other) { + if (this != &other) { + object_pool_lock_ = std::move(other.object_pool_lock_); + object_pool_ = std::move(other.object_pool_); + destructor_ = other.destructor_; + } + + return *this; + } ~ObjectPool() { destructor_ = true; diff --git a/libtransport/includes/hicn/transport/utils/ring_buffer.h b/libtransport/includes/hicn/transport/utils/ring_buffer.h index 52629b82b..3032032c9 100644 --- a/libtransport/includes/hicn/transport/utils/ring_buffer.h +++ b/libtransport/includes/hicn/transport/utils/ring_buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/rtc_quality_score.h b/libtransport/includes/hicn/transport/utils/rtc_quality_score.h new file mode 100644 index 000000000..2e8ca97d3 --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/rtc_quality_score.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCQualityScore { + public: + RTCQualityScore(){}; + ~RTCQualityScore(){}; + + uint8_t getQualityScore(uint64_t RTT, uint32_t loss_rate) { + uint32_t delay_index = getDelay(RTT); + uint32_t loss_index = getLossRate(loss_rate); + return quality_score_[loss_index][delay_index]; + } + + private: + // see quality score map + uint8_t quality_score_[7][6]{{5, 5, 5, 4, 3, 1}, {5, 4, 3, 2, 1, 1}, + {5, 3, 2, 1, 1, 1}, {5, 2, 1, 1, 1, 1}, + {4, 1, 1, 1, 1, 1}, {3, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1}}; + + uint8_t getDelay(uint64_t RTT) { + uint64_t OWD = RTT / 2; + // we should never get a OWD of 0. so we take the first col if OWD is < 5ms + if (OWD < 5) return 0; + if (OWD < 50) return 1; + if (OWD < 100) return 2; + if (OWD < 200) return 3; + if (OWD < 300) return 4; + return 5; + } + + uint8_t getLossRate(uint32_t loss_rate) { + // we use 3% as mean loss rate + if (loss_rate < 3) return 0; + if (loss_rate < 10) return 1; + if (loss_rate < 20) return 2; + if (loss_rate < 30) return 3; + if (loss_rate < 40) return 4; + if (loss_rate < 50) return 5; + return 6; + } +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/includes/hicn/transport/utils/singleton.h b/libtransport/includes/hicn/transport/utils/singleton.h index 4b7b19c0a..cdd8b03bf 100644 --- a/libtransport/includes/hicn/transport/utils/singleton.h +++ b/libtransport/includes/hicn/transport/utils/singleton.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -32,4 +32,17 @@ class Singleton : NonCopyable { ~Singleton() {} }; +template <typename T> +class ThreadLocalSingleton : NonCopyable { + public: + static T& getInstance() { + static thread_local T instance; + return instance; + } + + protected: + ThreadLocalSingleton() {} + ~ThreadLocalSingleton() {} +}; + } // namespace utils
\ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/spinlock.h b/libtransport/includes/hicn/transport/utils/spinlock.h index 009a94454..40fc48de3 100644 --- a/libtransport/includes/hicn/transport/utils/spinlock.h +++ b/libtransport/includes/hicn/transport/utils/spinlock.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/string_tokenizer.h b/libtransport/includes/hicn/transport/utils/string_tokenizer.h index 36630eb58..2b2b893a0 100644 --- a/libtransport/includes/hicn/transport/utils/string_tokenizer.h +++ b/libtransport/includes/hicn/transport/utils/string_tokenizer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/string_utils.h b/libtransport/includes/hicn/transport/utils/string_utils.h index 313c28cc6..5f9cca13b 100644 --- a/libtransport/includes/hicn/transport/utils/string_utils.h +++ b/libtransport/includes/hicn/transport/utils/string_utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/includes/hicn/transport/utils/thread_pool.h b/libtransport/includes/hicn/transport/utils/thread_pool.h new file mode 100644 index 000000000..e4e47209c --- /dev/null +++ b/libtransport/includes/hicn/transport/utils/thread_pool.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/noncopyable.h> + +#include <thread> +#include <vector> + +namespace utils { + +class ThreadPool : public NonCopyable { + public: + explicit ThreadPool( + std::size_t n_threads = std::thread::hardware_concurrency()) + : workers_(n_threads > 0 ? n_threads : 1) {} + + std::size_t getNThreads() const { return workers_.size(); } + EventThread &getWorker(std::size_t i) { return workers_.at(i); } + + private: + std::vector<EventThread> workers_; +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/includes/hicn/transport/utils/uri.h b/libtransport/includes/hicn/transport/utils/uri.h index 7c28e8552..30675e880 100644 --- a/libtransport/includes/hicn/transport/utils/uri.h +++ b/libtransport/includes/hicn/transport/utils/uri.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/CMakeLists.txt b/libtransport/src/CMakeLists.txt index 5e0cd38e7..477a21cb1 100644 --- a/libtransport/src/CMakeLists.txt +++ b/libtransport/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -11,10 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(GNUInstallDirs) - -set(ASIO_STANDALONE 1) - +############################################################## +# Source files +############################################################## add_subdirectory(core) add_subdirectory(interfaces) add_subdirectory(protocols) @@ -23,49 +22,131 @@ add_subdirectory(implementation) add_subdirectory(utils) add_subdirectory(http) -include(Packager) -extract_version() -configure_file("config.h.in" "hicn/transport/config.h" @ONLY) -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/hicn/transport/config.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hicn/transport - COMPONENT ${LIBTRANSPORT_COMPONENT}-dev + +############################################################## +# Libraries to link +############################################################## +set(LIBRARIES + PRIVATE ${HICN_LIBRARIES} + PRIVATE ${CMAKE_THREAD_LIBS_INIT} + PRIVATE ${CMAKE_DL_LIBS} + PRIVATE ${OPENSSL_LIBRARIES} + PRIVATE ${LIBCONFIG_CPP_LIBRARIES} + PRIVATE ${THIRD_PARTY_LIBRARIES} ) -install( - FILES "transport.config" - DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn - COMPONENT ${LIBTRANSPORT_COMPONENT} + +############################################################## +# Include dirs +############################################################## +list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS + PRIVATE + ${CMAKE_THREADS_INCLUDE_DIRS} + ${ASIO_INCLUDE_DIRS} + ${WINDOWS_INCLUDE_DIRS} + ${LIBCONFIG_CPP_INCLUDE_DIRS} + ${THIRD_PARTY_INCLUDE_DIRS} + PUBLIC + ${ASIO_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIR} ) -list(APPEND COMPILER_DEFINITIONS - "-DASIO_STANDALONE" +list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/ + PUBLIC + $<BUILD_INTERFACE:${Libhicntransport_INCLUDE_DIRS}> + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) -list(INSERT LIBTRANSPORT_INTERNAL_INCLUDE_DIRS 0 - ${CMAKE_CURRENT_SOURCE_DIR}/ - ${CMAKE_CURRENT_BINARY_DIR}/ + +############################################################## +# Dependencies +############################################################## +list(APPEND DEPENDENCIES + ${THIRD_PARTY_DEPENDENCIES} ) -set(LIBTRANSPORT_INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - "" CACHE INTERNAL - "" FORCE +############################################################## +# Compiler definitions +############################################################## +list(APPEND COMPILER_DEFINITIONS + PUBLIC "-DASIO_STANDALONE" +) + +if (ENABLE_RELY) + list(APPEND COMPILER_DEFINITIONS + PRIVATE "-DENABLE_RELY=1" + ) +endif() + + +############################################################## +# Compiler options +############################################################## +list(APPEND COMPILER_OPTIONS + ${DEFAULT_COMPILER_OPTIONS} ) if (NOT WIN32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + list(APPEND COMPILER_OPTIONS + PRIVATE "-pthread" + ) else () - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4200 -D_WIN32_WINDOWS=0x0400") + list(APPEND COMPILER_OPTIONS + PRIVATE "-/wd4200 -D_WIN32_WINDOWS=0x0400" + ) if (CMAKE_BUILD_TYPE EQUAL "RELEASE") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:\"MSVCRTD\"" ) + list(APPEND COMPILER_OPTIONS + PRIVATE "/NODEFAULTLIB:\"MSVCRTD\"" + ) endif () endif () if (${CMAKE_SYSTEM_NAME} MATCHES "Android") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -isystem -lm") + list(APPEND COMPILER_OPTIONS + PRIVATE "-stdlib=libc++" + PRIVATE "-isystem" + PRIVATE "-lm" + ) endif() + +############################################################## +# Configuration file +############################################################## +configure_file("config.h.in" "hicn/transport/config.h" @ONLY) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/hicn/transport/config.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hicn/transport + COMPONENT ${LIBTRANSPORT_COMPONENT}-dev +) + +if (${CMAKE_SYSTEM_NAME} MATCHES Darwin OR ${CMAKE_SYSTEM_NAME} MATCHES Linux) + install( + FILES "transport.config" + DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + COMPONENT ${LIBTRANSPORT_COMPONENT} + ) +else () + install( + FILES "transport.config" + DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/hicn + COMPONENT ${LIBTRANSPORT_COMPONENT} + ) +endif() + + +############################################################## +# IO Modules +############################################################## +add_subdirectory(io_modules) + + +############################################################## +# Build type +############################################################## set (BUILD_TYPES "STATIC") if (NOT DISABLE_SHARED_LIBRARIES) @@ -74,8 +155,10 @@ if (NOT DISABLE_SHARED_LIBRARIES) ) endif() -add_subdirectory(io_modules) +############################################################## +# Build library +############################################################## build_library(${LIBTRANSPORT} ${BUILD_TYPES} SOURCES ${SOURCE_FILES} ${HEADER_FILES} @@ -84,12 +167,29 @@ build_library(${LIBTRANSPORT} OBJECT_LIBRARIES ${THIRD_PARTY_OBJECT_LIBRARIES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - HEADER_ROOT_DIR hicn/transport + INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION} + VERSION ${CURRENT_VERSION} + EXPORT_NAME ${LIBTRANSPORT_COMPONENT} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ) + +############################################################## +# Unit tests +############################################################## if (${BUILD_TESTS}) add_subdirectory(test) endif() + + +############################################################## +# Cmake config files +############################################################## +create_cmake_config ( + ${LIBTRANSPORT_COMPONENT} + INCLUDE_DIRS ${Libhicntransport_INCLUDE_DIRS} + VERSION ${CURRENT_VERSION} + COMPONENT ${LIBTRANSPORT_COMPONENT} + NAMESPACE hicn +) diff --git a/libtransport/src/auth/CMakeLists.txt b/libtransport/src/auth/CMakeLists.txt index 699bc1050..8d3286338 100644 --- a/libtransport/src/auth/CMakeLists.txt +++ b/libtransport/src/auth/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -14,7 +14,6 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc - ${CMAKE_CURRENT_SOURCE_DIR}/identity.cc ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.cc ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.cc ) diff --git a/libtransport/src/auth/crypto_hash.cc b/libtransport/src/auth/crypto_hash.cc index b4b0a8b81..f60f46051 100644 --- a/libtransport/src/auth/crypto_hash.cc +++ b/libtransport/src/auth/crypto_hash.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,8 +15,6 @@ #include <hicn/transport/auth/crypto_hash.h> -using namespace std; - namespace transport { namespace auth { @@ -28,7 +26,7 @@ CryptoHash::CryptoHash(const CryptoHash &other) digest_size_(other.digest_size_) {} CryptoHash::CryptoHash(CryptoHash &&other) - : digest_type_(move(other.digest_type_)), + : digest_type_(std::move(other.digest_type_)), digest_(other.digest_), digest_size_(other.digest_size_) { other.reset(); @@ -43,13 +41,16 @@ CryptoHash::CryptoHash(const uint8_t *hash, size_t size, memcpy(digest_.data(), hash, size); } -CryptoHash::CryptoHash(const vector<uint8_t> &hash, CryptoHashType hash_type) +CryptoHash::CryptoHash(const std::vector<uint8_t> &hash, + CryptoHashType hash_type) : CryptoHash(hash.data(), hash.size(), hash_type) {} CryptoHash &CryptoHash::operator=(const CryptoHash &other) { - digest_type_ = other.digest_type_; - digest_ = other.digest_; - digest_size_ = other.digest_size_; + if (this != &other) { + digest_type_ = other.digest_type_; + digest_ = other.digest_; + digest_size_ = other.digest_size_; + } return *this; } @@ -68,7 +69,7 @@ void CryptoHash::computeDigest(const uint8_t *buffer, size_t len) { (*hash_evp)(), nullptr); } -void CryptoHash::computeDigest(const vector<uint8_t> &buffer) { +void CryptoHash::computeDigest(const std::vector<uint8_t> &buffer) { computeDigest(buffer.data(), buffer.size()); } @@ -102,15 +103,15 @@ void CryptoHash::computeDigest(const utils::MemBuf *buffer) { EVP_MD_CTX_free(mcdtx); } -vector<uint8_t> CryptoHash::getDigest() const { return digest_; } +std::vector<uint8_t> CryptoHash::getDigest() const { return digest_; } -string CryptoHash::getStringDigest() const { - stringstream string_digest; +std::string CryptoHash::getStringDigest() const { + std::stringstream string_digest; - string_digest << hex << setfill('0'); + string_digest << std::hex << std::setfill('0'); for (auto byte : digest_) { - string_digest << hex << setw(2) << static_cast<int>(byte); + string_digest << std::hex << std::setw(2) << static_cast<int>(byte); } return string_digest.str(); @@ -130,23 +131,23 @@ void CryptoHash::setType(CryptoHashType hash_type) { void CryptoHash::display() { switch (digest_type_) { case CryptoHashType::SHA256: - cout << "SHA256"; + std::cout << "SHA256"; break; case CryptoHashType::SHA512: - cout << "SHA512"; + std::cout << "SHA512"; break; case CryptoHashType::BLAKE2S256: - cout << "BLAKE2s256"; + std::cout << "BLAKE2s256"; break; case CryptoHashType::BLAKE2B512: - cout << "BLAKE2b512"; + std::cout << "BLAKE2b512"; break; default: - cout << "UNKNOWN"; + std::cout << "UNKNOWN"; break; } - cout << ": " << getStringDigest() << endl; + std::cout << ": " << getStringDigest() << std::endl; } void CryptoHash::reset() { @@ -159,19 +160,14 @@ CryptoHashEVP CryptoHash::getEVP(CryptoHashType hash_type) { switch (hash_type) { case CryptoHashType::SHA256: return &EVP_sha256; - break; case CryptoHashType::SHA512: return &EVP_sha512; - break; case CryptoHashType::BLAKE2S256: return &EVP_blake2s256; - break; case CryptoHashType::BLAKE2B512: return &EVP_blake2b512; - break; default: return nullptr; - break; } } diff --git a/libtransport/src/auth/crypto_suite.cc b/libtransport/src/auth/crypto_suite.cc index 7e898ef09..44de85212 100644 --- a/libtransport/src/auth/crypto_suite.cc +++ b/libtransport/src/auth/crypto_suite.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -22,31 +22,61 @@ CryptoSuite getSuite(int nid) { switch (nid) { case NID_ecdsa_with_SHA256: return CryptoSuite::ECDSA_SHA256; - break; case NID_ecdsa_with_SHA512: return CryptoSuite::ECDSA_SHA512; - break; case NID_sha256WithRSAEncryption: return CryptoSuite::RSA_SHA256; - break; case NID_sha512WithRSAEncryption: return CryptoSuite::RSA_SHA512; - break; case NID_hmacWithSHA256: return CryptoSuite::HMAC_SHA256; - break; case NID_hmacWithSHA512: return CryptoSuite::HMAC_SHA512; - break; case NID_dsa_with_SHA256: return CryptoSuite::DSA_SHA256; - break; case NID_dsa_with_SHA512: return CryptoSuite::DSA_SHA512; - break; default: return CryptoSuite::UNKNOWN; - break; + } +} + +std::string getStringSuite(CryptoSuite suite) { + switch (suite) { + case CryptoSuite::ECDSA_BLAKE2B512: + return "ECDSA_BLAKE2B512"; + case CryptoSuite::ECDSA_BLAKE2S256: + return "ECDSA_BLAKE2S256"; + case CryptoSuite::ECDSA_SHA256: + return "ECDSA_SHA256"; + case CryptoSuite::ECDSA_SHA512: + return "ECDSA_SHA512"; + case CryptoSuite::RSA_BLAKE2B512: + return "RSA_BLAKE2B512"; + case CryptoSuite::RSA_BLAKE2S256: + return "RSA_BLAKE2S256"; + case CryptoSuite::RSA_SHA256: + return "RSA_SHA256"; + case CryptoSuite::RSA_SHA512: + return "RSA_SHA512"; + case CryptoSuite::HMAC_BLAKE2B512: + return "HMAC_BLAKE2B512"; + case CryptoSuite::HMAC_BLAKE2S256: + return "HMAC_BLAKE2S256"; + case CryptoSuite::HMAC_SHA256: + return "HMAC_SHA256"; + case CryptoSuite::HMAC_SHA512: + return "HMAC_SHA512"; + case CryptoSuite::DSA_BLAKE2B512: + return "DSA_BLAKE2B512"; + case CryptoSuite::DSA_BLAKE2S256: + return "DSA_BLAKE2S256"; + case CryptoSuite::DSA_SHA256: + return "DSA_SHA256"; + case CryptoSuite::DSA_SHA512: + return "DSA_SHA512"; + default: + return "UNKNOWN"; } } diff --git a/libtransport/src/auth/identity.cc b/libtransport/src/auth/identity.cc deleted file mode 100644 index f56532033..000000000 --- a/libtransport/src/auth/identity.cc +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2017-2019 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 <hicn/transport/auth/identity.h> - -using namespace std; - -// function needed to create the a certificate -static bool _addRandomSerial(X509 *cert) { - unsigned long serial = 0; - unsigned char serial_bytes[sizeof(serial)]; - - // Construct random positive serial number. - RAND_bytes(serial_bytes, sizeof(serial_bytes)); - serial_bytes[0] &= 0x7F; - serial = 0; - for (size_t i = 0; i < sizeof(serial_bytes); i++) { - serial = (256 * serial) + serial_bytes[i]; - } - ASN1_INTEGER_set(X509_get_serialNumber(cert), serial); - return true; -} - -static bool _addValidityPeriod(X509 *cert, size_t validityDays) { - // Set the validity from now for the specified number of days. - X509_gmtime_adj(X509_get_notBefore(cert), (long)0); - X509_gmtime_adj(X509_get_notAfter(cert), (long)(60 * 60 * 24 * validityDays)); - return true; -} - -static bool _addSubjectName(X509 *cert, const char *subjectname) { - // Set up the simple subject name and issuer name for the certificate. - X509_NAME *name = X509_get_subject_name(cert); - - if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, - (unsigned char *)subjectname, -1, -1, 0)) { - if (X509_set_issuer_name(cert, name)) { - return true; - } - } - return false; -} -static bool _addCertificateExtensionWithContext(X509 *cert, int nid, - const char *value) { - X509_EXTENSION *extension; - X509V3_CTX context; - - X509V3_set_ctx_nodb(&context); - X509V3_set_ctx(&context, cert, cert, NULL, NULL, 0); - extension = X509V3_EXT_conf_nid(NULL, &context, nid, value); - if (extension == NULL) { - return false; - } - X509_add_ext(cert, extension, -1); - X509_EXTENSION_free(extension); - return true; -} -static bool _addCertificateExtension(X509 *cert, int nid, const char *value) { - X509_EXTENSION *extension = X509V3_EXT_conf_nid(NULL, NULL, nid, value); - if (extension == NULL) { - return false; - } - X509_add_ext(cert, extension, -1); - X509_EXTENSION_free(extension); - return true; -} - -static bool _addExtensions(X509 *cert) { - // Add the necessary extensions. - if (_addCertificateExtension(cert, NID_basic_constraints, - "critical,CA:FALSE") == true) { - if (_addCertificateExtension( - cert, NID_key_usage, - "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment," - "keyAgreement") == true) { - if (_addCertificateExtension(cert, NID_ext_key_usage, "clientAuth") == - true) { - return true; - } - } - } - return false; -} -static bool _addKeyIdentifier(X509 *cert) { - unsigned char spkid[SHA256_DIGEST_LENGTH]; - char spkid_hex[1 + 2 * SHA256_DIGEST_LENGTH]; - if (ASN1_item_digest(ASN1_ITEM_rptr(X509_PUBKEY), EVP_sha256(), - X509_get_X509_PUBKEY(cert), spkid, NULL)) { - for (int i = 0; i < 32; i++) { - snprintf(&spkid_hex[2 * i], 3, "%02X", (unsigned)spkid[i]); - } - if (_addCertificateExtension(cert, NID_subject_key_identifier, spkid_hex) == - true) { - if (_addCertificateExtensionWithContext( - cert, NID_authority_key_identifier, "keyid:always") == true) { - return true; - } - } - } - return false; -} - -namespace transport { -namespace auth { - -Identity::Identity(const string &keystore_path, const string &keystore_pwd, - CryptoSuite suite, unsigned int signature_len, - unsigned int validity_days, const string &subject_name) - : cert_(X509_new(), ::X509_free) { - // create the file and complete it. - // first we create the certificate - - // to create the cert we will need a private key - - std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); - - if (suite == CryptoSuite::RSA_SHA512 || suite == CryptoSuite::RSA_SHA256) { - RSA *rsa = RSA_new(); - BIGNUM *pub_exp; - - pub_exp = BN_new(); - - BN_set_word(pub_exp, RSA_F4); - if (1 != RSA_generate_key_ex(rsa, signature_len, pub_exp, NULL)) - throw errors::RuntimeException("can't generate the key"); - if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa)) - throw errors::RuntimeException("can't generate the key"); - } else if (suite == CryptoSuite::ECDSA_SHA256) { - int curve_params; - switch (signature_len) { - case 160u: - curve_params = NID_secp160k1; - break; - case 192u: - curve_params = NID_secp192k1; - break; - case 224u: - curve_params = NID_secp224k1; - break; - case 256u: - curve_params = NID_secp256k1; - break; - default: - curve_params = -1; - break; - } - if (curve_params == -1) - throw errors::RuntimeException("can't generate the key"); - EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve_params); - if (ec_key == NULL) - throw errors::RuntimeException("can't create ecdsa key from curve"); - EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE); - if (EC_KEY_generate_key(ec_key) == 0) - throw errors::RuntimeException("can't generate the ecdsa key"); - if (EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_key) == 0) - throw errors::RuntimeException("can't generate the ecdsa key"); - } else if (suite == CryptoSuite::DSA_SHA256) { - DSA *dsa = DSA_new(); - unsigned char buffer[32]; - if (RAND_bytes(buffer, sizeof(buffer)) != 1) { - throw errors::RuntimeException("can't generate the key"); - } - if (DSA_generate_parameters_ex(dsa, signature_len, buffer, sizeof(buffer), - NULL, NULL, NULL) != 1) - throw errors::RuntimeException("can't generate the key"); - if (DSA_generate_key(dsa) != 1) - throw errors::RuntimeException("can't generate the key"); - if (EVP_PKEY_set1_DSA(privateKey.get(), dsa) != 1) - throw errors::RuntimeException("can't generate the key"); - } - bool success = true; - success = success && (X509_set_version(cert_.get(), 2) == 1); // 2 => X509v3 - success = success && _addRandomSerial(cert_.get()); - success = success && _addValidityPeriod(cert_.get(), validity_days); - success = success && (X509_set_pubkey(cert_.get(), privateKey.get()) == 1); - success = success && _addSubjectName(cert_.get(), subject_name.c_str()); - success = success && _addExtensions(cert_.get()); - success = - success && (X509_sign(cert_.get(), privateKey.get(), EVP_sha256()) != 0); - success = success && _addKeyIdentifier(cert_.get()); - if (!success) { - throw errors::RuntimeException("error while creating the certificate"); - } - // the certificate is created. We create the pkcs12 object to write the p12 - // file - PKCS12 *p12 = PKCS12_create( - keystore_pwd.c_str(), "ccnxuser", privateKey.get(), cert_.get(), NULL, 0, - 0, 0 /*default iter*/, PKCS12_DEFAULT_ITER /*mac_iter*/, 0); - filename_ = keystore_path; - int fp = open(filename_.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0600); - if (fp == -1) throw errors::RuntimeException("impossible to create the file"); - FILE *fp_f = fdopen(fp, "wb"); - if (fp_f == NULL) - throw errors::RuntimeException("impossible to create the file"); - i2d_PKCS12_fp(fp_f, p12); - fclose(fp_f); - close(fp); - std::shared_ptr<EVP_PKEY> publickey(X509_get_pubkey(cert_.get()), - EVP_PKEY_free); - signer_ = std::shared_ptr<AsymmetricSigner>( - new AsymmetricSigner(suite, privateKey, publickey)); - signer_->signature_len_ = signature_len; -} - -Identity::Identity(string &keystore_path, string &keystore_pwd, - CryptoHashType hash_type) - : cert_(X509_new(), ::X509_free) { - filename_ = keystore_path; - pwd_ = keystore_path; - // get the key and certificate by first opening the keystore file - FILE *p12file = fopen(keystore_path.c_str(), "r"); - if (p12file == NULL) - throw errors::RuntimeException("impossible open keystore"); - PKCS12 *p12 = d2i_PKCS12_fp(p12file, NULL); - EVP_PKEY *privatekey; - EVP_PKEY *publickey; - X509 *cert = cert_.get(); - // now we parse the file to get the first key and certificate - if (1 != PKCS12_parse(p12, keystore_pwd.c_str(), &privatekey, &cert, NULL)) - throw errors::RuntimeException("impossible to get the private key"); - publickey = X509_get_pubkey(cert); - // to have the cryptosuite we use the nid number that is used to identify the - // suite. - CryptoSuite suite = getSuite(X509_get_signature_nid(cert)); - signer_ = std::shared_ptr<AsymmetricSigner>(new AsymmetricSigner( - suite, std::shared_ptr<EVP_PKEY>(privatekey, EVP_PKEY_free), - std::shared_ptr<EVP_PKEY>(publickey, EVP_PKEY_free))); - PKCS12_free(p12); -} - -Identity::Identity(const Identity &other) { - pwd_ = other.pwd_; - filename_ = other.filename_; - signer_ = other.signer_; - cert_ = other.cert_; -} - -Identity::Identity(Identity &&other) { - signer_ = std::move(other.signer_); - other.signer_.reset(); - cert_ = std::move(other.cert_); - other.cert_.reset(); - pwd_ = other.pwd_; - other.pwd_ = ""; - filename_ = other.filename_; - other.filename_ = ""; - signer_ = other.signer_; - other.signer_ = nullptr; -} - -Identity::~Identity() {} - -shared_ptr<AsymmetricSigner> Identity::getSigner() const { return signer_; } - -string Identity::getFilename() const { return filename_; } - -std::shared_ptr<X509> Identity::getCertificate() const { return cert_; } -std::shared_ptr<EVP_PKEY> Identity::getPrivateKey() const { - return signer_->key_; -} - -string Identity::getPassword() const { return pwd_; } - -Identity Identity::generateIdentity(const string &subject_name) { - string keystore_name = "keystore"; - string keystore_password = "password"; - size_t key_length = 1024; - unsigned int validity_days = 30; - CryptoSuite suite = CryptoSuite::RSA_SHA256; - - return Identity(keystore_name, keystore_password, suite, - (unsigned int)key_length, validity_days, subject_name); -} - -} // namespace auth -} // namespace transport diff --git a/libtransport/src/auth/signer.cc b/libtransport/src/auth/signer.cc index 884e850ca..e74e2f1b8 100644 --- a/libtransport/src/auth/signer.cc +++ b/libtransport/src/auth/signer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -13,9 +13,9 @@ * limitations under the License. */ +#include <glog/logging.h> #include <hicn/transport/auth/signer.h> - -using namespace std; +#include <hicn/transport/utils/chrono_typedefs.h> namespace transport { namespace auth { @@ -29,31 +29,29 @@ Signer::Signer() Signer::~Signer() {} void Signer::signPacket(PacketPtr packet) { - assert(key_ != nullptr); + DCHECK(key_ != nullptr); core::Packet::Format format = packet->getFormat(); - if (!packet->authenticationHeader()) { + if (!packet->hasAH()) { throw errors::MalformedAHPacketException(); } // Set signature size size_t signature_field_len = getSignatureFieldSize(); - packet->setSignatureSize(signature_field_len); - packet->setSignatureSizeGap(0u); + packet->setSignatureFieldSize(signature_field_len); + packet->updateLength(); // update IP payload length // Copy IP+TCP / ICMP header before zeroing them hicn_header_t header_copy; hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); // Fill in the hICN AH header - auto now = chrono::duration_cast<chrono::milliseconds>( - chrono::system_clock::now().time_since_epoch()) - .count(); + auto now = utils::SteadyTime::nowMs().count(); packet->setSignatureTimestamp(now); packet->setValidationAlgorithm(suite_); // Set key ID - vector<uint8_t> key_id = key_id_.getDigest(); + std::vector<uint8_t> key_id = key_id_.getDigest(); packet->setKeyId({key_id.data(), key_id.size()}); // Reset fields to compute the packet hash @@ -61,22 +59,22 @@ void Signer::signPacket(PacketPtr packet) { // Compute the signature and put it in the packet signBuffer(packet); - hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); + packet->setSignature(signature_); + packet->setSignatureSize(signature_len_); - // Set the gap between the signature field size and the signature real size. - packet->setSignatureSizeGap(signature_field_len - signature_len_); - memcpy(packet->getSignature(), signature_.data(), signature_len_); + // Restore header + hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); } void Signer::signBuffer(const std::vector<uint8_t> &buffer) { - assert(key_ != nullptr); + DCHECK(key_ != nullptr); CryptoHashEVP hash_evp = CryptoHash::getEVP(getHashType()); if (hash_evp == nullptr) { throw errors::RuntimeException("Unknown hash type"); } - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -106,7 +104,7 @@ void Signer::signBuffer(const std::vector<uint8_t> &buffer) { } void Signer::signBuffer(const utils::MemBuf *buffer) { - assert(key_ != nullptr); + DCHECK(key_ != nullptr); CryptoHashEVP hash_evp = CryptoHash::getEVP(getHashType()); if (hash_evp == nullptr) { @@ -114,7 +112,7 @@ void Signer::signBuffer(const utils::MemBuf *buffer) { } const utils::MemBuf *p = buffer; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -147,7 +145,18 @@ void Signer::signBuffer(const utils::MemBuf *buffer) { signature_.resize(signature_len_); } -vector<uint8_t> Signer::getSignature() const { return signature_; } +std::vector<uint8_t> Signer::getSignature() const { return signature_; } + +std::string Signer::getStringSignature() const { + std::stringstream string_sig; + string_sig << std::hex << std::setfill('0'); + + for (auto byte : signature_) { + string_sig << std::hex << std::setw(2) << static_cast<int>(byte); + } + + return string_sig.str(); +} size_t Signer::getSignatureSize() const { return signature_len_; } @@ -159,36 +168,75 @@ size_t Signer::getSignatureFieldSize() const { return (signature_len_ + 4) - (signature_len_ % 4); } +CryptoSuite Signer::getSuite() const { return suite_; } + CryptoHashType Signer::getHashType() const { return ::transport::auth::getHashType(suite_); } -CryptoSuite Signer::getSuite() const { return suite_; } +void Signer::display() { + std::cout << getStringSuite(suite_) << ": " << getStringSignature() + << std::endl; +} // --------------------------------------------------------- // Void Signer // --------------------------------------------------------- -void VoidSigner::signPacket(PacketPtr packet){}; +void VoidSigner::signPacket(PacketPtr packet) {} -void VoidSigner::signBuffer(const std::vector<uint8_t> &buffer){}; +void VoidSigner::signBuffer(const std::vector<uint8_t> &buffer) {} -void VoidSigner::signBuffer(const utils::MemBuf *buffer){}; +void VoidSigner::signBuffer(const utils::MemBuf *buffer) {} // --------------------------------------------------------- // Asymmetric Signer // --------------------------------------------------------- -AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, shared_ptr<EVP_PKEY> key, - shared_ptr<EVP_PKEY> pub_key) { +AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, + std::shared_ptr<EVP_PKEY> key, + std::shared_ptr<EVP_PKEY> pub_key) { + setKey(suite, key, pub_key); +} + +AsymmetricSigner::AsymmetricSigner(std::string keystore_path, + std::string password) { + FILE *p12file = fopen(keystore_path.c_str(), "r"); + + if (p12file == nullptr) { + throw errors::RuntimeException("failed to read keystore"); + } + + std::unique_ptr<PKCS12, decltype(&PKCS12_free)> p12( + d2i_PKCS12_fp(p12file, nullptr), &PKCS12_free); + X509 *cert_raw; + EVP_PKEY *key_raw; + + if (PKCS12_parse(p12.get(), password.c_str(), &key_raw, &cert_raw, nullptr) != + 1) { + fclose(p12file); + throw errors::RuntimeException("failed to parse keystore"); + } + + std::shared_ptr<EVP_PKEY> key(key_raw, EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pub_key(X509_get_pubkey(cert_raw), EVP_PKEY_free); + + setKey(transport::auth::getSuite(X509_get_signature_nid(cert_raw)), key, + pub_key); + + fclose(p12file); +} + +void AsymmetricSigner::setKey(CryptoSuite suite, std::shared_ptr<EVP_PKEY> key, + std::shared_ptr<EVP_PKEY> pub_key) { suite_ = suite; key_ = key; - key_id_ = CryptoHash(getHashType()); + signature_len_ = EVP_PKEY_size(key.get()); + signature_.resize(signature_len_); - vector<uint8_t> pbk(i2d_PublicKey(pub_key.get(), nullptr)); + std::vector<uint8_t> pbk(i2d_PublicKey(pub_key.get(), nullptr)); uint8_t *pbk_ptr = pbk.data(); int len = i2d_PublicKey(pub_key.get(), &pbk_ptr); - signature_len_ = EVP_PKEY_size(key.get()); - signature_.resize(signature_len_); + key_id_ = CryptoHash(getHashType()); key_id_.computeDigest(pbk_ptr, len); } @@ -205,9 +253,10 @@ size_t AsymmetricSigner::getSignatureFieldSize() const { // --------------------------------------------------------- // Symmetric Signer // --------------------------------------------------------- -SymmetricSigner::SymmetricSigner(CryptoSuite suite, const string &passphrase) { +SymmetricSigner::SymmetricSigner(CryptoSuite suite, + const std::string &passphrase) { suite_ = suite; - key_ = shared_ptr<EVP_PKEY>( + key_ = std::shared_ptr<EVP_PKEY>( EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, nullptr, (const unsigned char *)passphrase.c_str(), passphrase.size()), diff --git a/libtransport/src/auth/verifier.cc b/libtransport/src/auth/verifier.cc index c9ac1650f..0c35437f3 100644 --- a/libtransport/src/auth/verifier.cc +++ b/libtransport/src/auth/verifier.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,12 +16,10 @@ #include <hicn/transport/auth/verifier.h> #include <protocols/errors.h> -using namespace std; - namespace transport { namespace auth { -const vector<VerificationPolicy> Verifier::DEFAULT_FAILED_POLICIES = { +const std::vector<VerificationPolicy> Verifier::DEFAULT_FAILED_POLICIES = { VerificationPolicy::DROP, VerificationPolicy::ABORT, }; @@ -38,24 +36,21 @@ Verifier::~Verifier() {} bool Verifier::verifyPacket(PacketPtr packet) { core::Packet::Format format = packet->getFormat(); - if (!packet->authenticationHeader()) { + if (!packet->hasAH()) { throw errors::MalformedAHPacketException(); } // Get crypto suite, hash type, signature length CryptoSuite suite = packet->getValidationAlgorithm(); CryptoHashType hash_type = getHashType(suite); - size_t signature_len = packet->getSignatureSizeReal(); // Copy IP+TCP / ICMP header before zeroing them hicn_header_t header_copy; hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); - packet->setSignatureSizeGap(0u); // Retrieve packet signature - uint8_t *packet_signature = packet->getSignature(); - vector<uint8_t> signature_raw(packet_signature, - packet_signature + signature_len); + std::vector<uint8_t> signature_raw = packet->getSignature(); + signature_raw.resize(packet->getSignatureSize()); // Reset fields that are not used to compute signature packet->resetForHash(); @@ -66,12 +61,14 @@ bool Verifier::verifyPacket(PacketPtr packet) { // Restore header hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); - packet->setSignatureSizeGap(packet->getSignatureSize() - signature_len); + packet->setSignature(signature_raw); + packet->setSignatureSize(signature_raw.size()); return valid_packet; } -Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets) { +Verifier::PolicyMap Verifier::verifyPackets( + const std::vector<PacketPtr> &packets) { PolicyMap policies; for (const auto &packet : packets) { @@ -82,8 +79,8 @@ Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets) { policy = VerificationPolicy::ACCEPT; } + callVerificationFailedCallback(suffix, policy); policies[suffix] = policy; - callVerificationFailedCallback(packet, policy); } return policies; @@ -105,14 +102,15 @@ Verifier::PolicyMap Verifier::verifyHashes(const SuffixMap &packet_map, } } + callVerificationFailedCallback(packet_hash.first, policy); policies[packet_hash.first] = policy; } return policies; } -Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets, - const SuffixMap &suffix_map) { +Verifier::PolicyMap Verifier::verifyPackets( + const std::vector<PacketPtr> &packets, const SuffixMap &suffix_map) { PolicyMap policies; for (const auto &packet : packets) { @@ -130,8 +128,8 @@ Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets, } } + callVerificationFailedCallback(suffix, policy); policies[suffix] = policy; - callVerificationFailedCallback(packet, policy); } return policies; @@ -139,7 +137,7 @@ Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets, void Verifier::setVerificationFailedCallback( VerificationFailedCallback verfication_failed_cb, - const vector<VerificationPolicy> &failed_policies) { + const std::vector<VerificationPolicy> &failed_policies) { verification_failed_cb_ = verfication_failed_cb; failed_policies_ = failed_policies; } @@ -149,7 +147,7 @@ void Verifier::getVerificationFailedCallback( *verfication_failed_cb = &verification_failed_cb_; } -void Verifier::callVerificationFailedCallback(PacketPtr packet, +void Verifier::callVerificationFailedCallback(Suffix suffix, VerificationPolicy &policy) { if (verification_failed_cb_ == interface::VOID_HANDLER) { return; @@ -157,10 +155,7 @@ void Verifier::callVerificationFailedCallback(PacketPtr packet, if (find(failed_policies_.begin(), failed_policies_.end(), policy) != failed_policies_.end()) { - policy = verification_failed_cb_( - static_cast<const core::ContentObject &>(*packet), - make_error_code( - protocol::protocol_error::signature_verification_failed)); + policy = verification_failed_cb_(suffix, policy); } } @@ -169,71 +164,75 @@ void Verifier::callVerificationFailedCallback(PacketPtr packet, // --------------------------------------------------------- bool VoidVerifier::verifyPacket(PacketPtr packet) { return true; } -bool VoidVerifier::verifyBuffer(const vector<uint8_t> &buffer, - const vector<uint8_t> &signature, +bool VoidVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { return true; } bool VoidVerifier::verifyBuffer(const utils::MemBuf *buffer, - const vector<uint8_t> &signature, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { return true; } Verifier::PolicyMap VoidVerifier::verifyPackets( - const vector<PacketPtr> &packets) { + const std::vector<PacketPtr> &packets) { PolicyMap policies; for (const auto &packet : packets) { - policies[packet->getName().getSuffix()] = VerificationPolicy::ACCEPT; + auth::Suffix suffix = packet->getName().getSuffix(); + VerificationPolicy policy = VerificationPolicy::ACCEPT; + callVerificationFailedCallback(suffix, policy); + policies[suffix] = policy; } return policies; } Verifier::PolicyMap VoidVerifier::verifyPackets( - const vector<PacketPtr> &packets, const SuffixMap &suffix_map) { + const std::vector<PacketPtr> &packets, const SuffixMap &suffix_map) { return verifyPackets(packets); } // --------------------------------------------------------- // Asymmetric Verifier // --------------------------------------------------------- -AsymmetricVerifier::AsymmetricVerifier(shared_ptr<EVP_PKEY> key) { +AsymmetricVerifier::AsymmetricVerifier(std::shared_ptr<EVP_PKEY> key) { setKey(key); } -AsymmetricVerifier::AsymmetricVerifier(const string &cert_path) { +AsymmetricVerifier::AsymmetricVerifier(const std::string &cert_path) { useCertificate(cert_path); } -AsymmetricVerifier::AsymmetricVerifier(shared_ptr<X509> cert) { +AsymmetricVerifier::AsymmetricVerifier(std::shared_ptr<X509> cert) { useCertificate(cert); } -void AsymmetricVerifier::setKey(shared_ptr<EVP_PKEY> key) { key_ = key; }; +void AsymmetricVerifier::setKey(std::shared_ptr<EVP_PKEY> key) { key_ = key; }; -void AsymmetricVerifier::useCertificate(const string &cert_path) { +void AsymmetricVerifier::useCertificate(const std::string &cert_path) { FILE *certf = fopen(cert_path.c_str(), "rb"); if (certf == nullptr) { throw errors::RuntimeException("Certificate not found"); } - shared_ptr<X509> cert = shared_ptr<X509>( + std::shared_ptr<X509> cert = std::shared_ptr<X509>( PEM_read_X509(certf, nullptr, nullptr, nullptr), ::X509_free); useCertificate(cert); fclose(certf); } -void AsymmetricVerifier::useCertificate(shared_ptr<X509> cert) { - key_ = shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), ::EVP_PKEY_free); +void AsymmetricVerifier::useCertificate(std::shared_ptr<X509> cert) { + key_ = + std::shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), ::EVP_PKEY_free); } -bool AsymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, - const vector<uint8_t> &signature, +bool AsymmetricVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -241,7 +240,7 @@ bool AsymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, throw errors::RuntimeException("Unknown hash type"); } - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -261,7 +260,7 @@ bool AsymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, } bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, - const vector<uint8_t> &signature, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -270,7 +269,7 @@ bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, } const utils::MemBuf *p = buffer; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -296,21 +295,21 @@ bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, // --------------------------------------------------------- // Symmetric Verifier // --------------------------------------------------------- -SymmetricVerifier::SymmetricVerifier(const string &passphrase) { +SymmetricVerifier::SymmetricVerifier(const std::string &passphrase) { setPassphrase(passphrase); } // Create and set a symmetric key from a passphrase. -void SymmetricVerifier::setPassphrase(const string &passphrase) { - key_ = shared_ptr<EVP_PKEY>( +void SymmetricVerifier::setPassphrase(const std::string &passphrase) { + key_ = std::shared_ptr<EVP_PKEY>( EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, nullptr, (const unsigned char *)passphrase.c_str(), passphrase.size()), EVP_PKEY_free); } -bool SymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, - const vector<uint8_t> &signature, +bool SymmetricVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -318,9 +317,9 @@ bool SymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, throw errors::RuntimeException("Unknown hash type"); } - vector<uint8_t> signature_bis(signature.size()); + std::vector<uint8_t> signature_bis(signature.size()); size_t signature_bis_len; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -344,7 +343,7 @@ bool SymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, } bool SymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, - const vector<uint8_t> &signature, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -353,9 +352,9 @@ bool SymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, } const utils::MemBuf *p = buffer; - vector<uint8_t> signature_bis(signature.size()); + std::vector<uint8_t> signature_bis(signature.size()); size_t signature_bis_len; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); diff --git a/libtransport/src/config.h.in b/libtransport/src/config.h.in index 73a326a84..231a370aa 100644 --- a/libtransport/src/config.h.in +++ b/libtransport/src/config.h.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -19,13 +19,12 @@ #define HICNTRANSPORT_VERSION_MAJOR "@VERSION_MAJOR@" #define HICNTRANSPORT_VERSION_MINOR "@VERSION_MINOR@" -#define HICNTRANSPORT_VERSION_REVISION "@VERSION_REVISION@" +#define HICNTRANSPORT_VERSION_PATCH "@VERSION_PATCH@" #ifndef ASIO_STANDALONE #cmakedefine ASIO_STANDALONE #endif #define RAAQM_CONFIG_PATH "@raaqm_config_path@" -#define ENABLE_RELY @ENABLE_RELY@ #cmakedefine __vpp__ diff --git a/libtransport/src/core/CMakeLists.txt b/libtransport/src/core/CMakeLists.txt index e442bb863..b9b024d60 100644 --- a/libtransport/src/core/CMakeLists.txt +++ b/libtransport/src/core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -22,6 +22,9 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.h ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.h ${CMAKE_CURRENT_SOURCE_DIR}/local_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_workers.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_listener.h ) list(APPEND SOURCE_FILES @@ -36,7 +39,21 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.cc ${CMAKE_CURRENT_SOURCE_DIR}/io_module.cc ${CMAKE_CURRENT_SOURCE_DIR}/local_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_listener.cc ) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES Android) + if (UNIX AND NOT APPLE) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc + ) + + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ) + endif() +endif() + set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/core/content_object.cc b/libtransport/src/core/content_object.cc index 411494fdf..643e0388e 100644 --- a/libtransport/src/core/content_object.cc +++ b/libtransport/src/core/content_object.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -35,6 +35,11 @@ namespace core { ContentObject::ContentObject(const Name &name, Packet::Format format, std::size_t additional_header_size) : Packet(format, additional_header_size) { + if (TRANSPORT_EXPECT_FALSE(hicn_packet_set_data(format_, packet_start_) < + 0)) { + throw errors::MalformedPacketException(); + } + if (TRANSPORT_EXPECT_FALSE( hicn_data_set_name(format, packet_start_, &name.name_) < 0)) { throw errors::RuntimeException("Error filling the packet name."); @@ -47,15 +52,20 @@ ContentObject::ContentObject(const Name &name, Packet::Format format, } } -#ifdef __ANDROID__ ContentObject::ContentObject(hicn_format_t format, std::size_t additional_header_size) - : ContentObject(Name("0::0|0"), format, additional_header_size) {} + : ContentObject( +#ifdef __ANDROID__ + Name("0::0|0"), #else -ContentObject::ContentObject(hicn_format_t format, - std::size_t additional_header_size) - : ContentObject(Packet::base_name, format, additional_header_size) {} + Packet::base_name, #endif + format, additional_header_size) { + if (TRANSPORT_EXPECT_FALSE(hicn_packet_set_data(format_, packet_start_) < + 0)) { + throw errors::MalformedPacketException(); + } +} ContentObject::ContentObject(const Name &name, hicn_format_t format, std::size_t additional_header_size, @@ -167,6 +177,23 @@ void ContentObject::resetForHash() { } } +bool ContentObject::isLast() const { + int is_last = 0; + if (hicn_data_is_last(format_, packet_start_, &is_last) < 0) { + throw errors::RuntimeException( + "Impossible to get last data flag from packet header."); + } + + return is_last; +} + +void ContentObject::setLast() { + if (hicn_data_set_last(format_, packet_start_) < 0) { + throw errors::RuntimeException( + "Impossible to set last data flag to packet header."); + } +} + } // end namespace core } // end namespace transport diff --git a/libtransport/src/core/errors.cc b/libtransport/src/core/errors.cc index 82647a60b..68fd7bf38 100644 --- a/libtransport/src/core/errors.cc +++ b/libtransport/src/core/errors.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -39,6 +39,15 @@ std::string core_category_impl::message(int ev) const { case core_error::configuration_not_applied: { return "Configuration was not applied due to wrong parameters."; } + case core_error::send_failed: { + return "Error sending data to socket."; + } + case core_error::send_buffer_allocation_failed: { + return "Error allocating buffers to send data."; + } + case core_error::receive_failed: { + return "Error receiving data from socket."; + } default: { return "Unknown core error"; } diff --git a/libtransport/src/core/errors.h b/libtransport/src/core/errors.h index a46f1dbcd..4532e6dc5 100644 --- a/libtransport/src/core/errors.h +++ b/libtransport/src/core/errors.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -36,7 +36,10 @@ const std::error_category& core_category(); enum class core_error { success = 0, configuration_parse_failed, - configuration_not_applied + configuration_not_applied, + send_failed, + send_buffer_allocation_failed, + receive_failed }; /** diff --git a/libtransport/src/core/facade.h b/libtransport/src/core/facade.h index 199081271..1ad4437e2 100644 --- a/libtransport/src/core/facade.h +++ b/libtransport/src/core/facade.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/core/global_workers.h b/libtransport/src/core/global_workers.h new file mode 100644 index 000000000..1ac254188 --- /dev/null +++ b/libtransport/src/core/global_workers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +#include <hicn/transport/utils/singleton.h> +#include <hicn/transport/utils/thread_pool.h> + +#include <atomic> +#include <mutex> + +namespace transport { +namespace core { + +class GlobalWorkers : public utils::Singleton<GlobalWorkers> { + public: + friend class utils::Singleton<GlobalWorkers>; + + ::utils::EventThread& getWorker() { + return thread_pool_.getWorker(counter_++ % thread_pool_.getNThreads()); + } + + private: + GlobalWorkers() : counter_(0), thread_pool_() {} + + std::atomic_uint16_t counter_; + ::utils::ThreadPool thread_pool_; +}; + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/interest.cc b/libtransport/src/core/interest.cc index 9d868ced0..b7719b3ed 100644 --- a/libtransport/src/core/interest.cc +++ b/libtransport/src/core/interest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -34,6 +34,10 @@ namespace core { Interest::Interest(const Name &interest_name, Packet::Format format, std::size_t additional_header_size) : Packet(format, additional_header_size) { + if (hicn_packet_set_interest(format_, packet_start_) < 0) { + throw errors::MalformedPacketException(); + } + if (hicn_interest_set_name(format_, packet_start_, interest_name.getConstStructReference()) < 0) { throw errors::MalformedPacketException(); @@ -45,13 +49,18 @@ Interest::Interest(const Name &interest_name, Packet::Format format, } } -#ifdef __ANDROID__ Interest::Interest(hicn_format_t format, std::size_t additional_header_size) - : Interest(Name("0::0|0"), format, additional_header_size) {} + : Interest( +#ifdef __ANDROID__ + Name("0::0|0"), #else -Interest::Interest(hicn_format_t format, std::size_t additional_header_size) - : Interest(base_name, format, additional_header_size) {} + base_name, #endif + format, additional_header_size) { + if (hicn_packet_set_interest(format_, packet_start_) < 0) { + throw errors::MalformedPacketException(); + } +} Interest::Interest(MemBuf &&buffer) : Packet(std::move(buffer)) { if (hicn_interest_get_name(format_, packet_start_, diff --git a/libtransport/src/core/io_module.cc b/libtransport/src/core/io_module.cc index a751eabf5..69e4e8bcf 100644 --- a/libtransport/src/core/io_module.cc +++ b/libtransport/src/core/io_module.cc @@ -19,8 +19,10 @@ #include <glog/logging.h> #include <hicn/transport/core/io_module.h> +#include <iostream> + #ifdef ANDROID -#include <io_modules/udp/hicn_forwarder_module.h> +#include <io_modules/hicn-light-ng/hicn_forwarder_module.h> #elif _WIN32 #include <hicn/util/windows/windows_utils.h> #endif @@ -55,8 +57,9 @@ IoModule *IoModule::load(const char *module_name) { if (!creator) { if ((error = dlerror()) != 0) { LOG(ERROR) << error; - return 0; } + + return 0; } // create object and return it @@ -85,4 +88,4 @@ bool IoModule::unload(IoModule *module) { } } // namespace core -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/core/local_connector.cc b/libtransport/src/core/local_connector.cc index 50dadc677..f27be2e5c 100644 --- a/libtransport/src/core/local_connector.cc +++ b/libtransport/src/core/local_connector.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -32,13 +32,17 @@ void LocalConnector::send(Packet &packet) { return; } + auto buffer = + std::static_pointer_cast<utils::MemBuf>(packet.shared_from_this()); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending packet to local socket."; - io_service_.get().post([this, p{packet.shared_from_this()}]() mutable { - receive_callback_(this, *p, std::make_error_code(std::errc(0))); + io_service_.get().post([this, buffer]() mutable { + std::vector<utils::MemBuf::Ptr> v{std::move(buffer)}; + receive_callback_(this, v, std::make_error_code(std::errc(0))); }); } -void LocalConnector::send(const uint8_t *packet, std::size_t len) { +void LocalConnector::send(const utils::MemBuf::Ptr &buffer) { throw errors::NotImplementedException(); } diff --git a/libtransport/src/core/local_connector.h b/libtransport/src/core/local_connector.h index 0e2d8f676..eede89e74 100644 --- a/libtransport/src/core/local_connector.h +++ b/libtransport/src/core/local_connector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -42,7 +42,7 @@ class LocalConnector : public Connector { void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; void close() override; diff --git a/libtransport/src/core/manifest.cc b/libtransport/src/core/manifest.cc index 3f890f3d0..da2689426 100644 --- a/libtransport/src/core/manifest.cc +++ b/libtransport/src/core/manifest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/core/manifest.h b/libtransport/src/core/manifest.h index 9b25ebd67..5bdbfc6ff 100644 --- a/libtransport/src/core/manifest.h +++ b/libtransport/src/core/manifest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #pragma once #include <core/manifest_format.h> +#include <glog/logging.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/name.h> @@ -40,17 +41,20 @@ class Manifest : public Base { using Encoder = typename FormatTraits::Encoder; using Decoder = typename FormatTraits::Decoder; - Manifest(std::size_t signature_size = 0) - : Base(HF_INET6_TCP_AH, signature_size), + Manifest(Packet::Format format, std::size_t signature_size = 0) + : Base(format, signature_size), encoder_(*this, signature_size), decoder_(*this) { + DCHECK(_is_ah(format)); Base::setPayloadType(PayloadType::MANIFEST); } - Manifest(const core::Name &name, std::size_t signature_size = 0) - : Base(name, HF_INET6_TCP_AH, signature_size), + Manifest(Packet::Format format, const core::Name &name, + std::size_t signature_size = 0) + : Base(name, format, signature_size), encoder_(*this, signature_size), decoder_(*this) { + DCHECK(_is_ah(format)); Base::setPayloadType(PayloadType::MANIFEST); } @@ -62,6 +66,11 @@ class Manifest : public Base { Base::setPayloadType(PayloadType::MANIFEST); } + // Useful for decoding manifests while avoiding packet copy + template <typename T> + Manifest(T &base) + : Base(base.getFormat()), encoder_(base, 0, false), decoder_(base) {} + virtual ~Manifest() = default; std::size_t estimateManifestSize(std::size_t additional_entries = 0) { @@ -78,24 +87,27 @@ class Manifest : public Base { Manifest &decode() { Manifest::decoder_.decode(); - manifest_type_ = decoder_.getManifestType(); + manifest_type_ = decoder_.getType(); + manifest_transport_type_ = decoder_.getTransportType(); hash_algorithm_ = decoder_.getHashAlgorithm(); - is_last_ = decoder_.getIsFinalManifest(); + is_last_ = decoder_.getIsLast(); return static_cast<ManifestImpl &>(*this).decodeImpl(); } - static std::size_t getManifestHeaderSize() { - return Encoder::getManifestHeaderSize(); + static std::size_t manifestHeaderSize( + interface::ProductionProtocolAlgorithms transport_type = + interface::ProductionProtocolAlgorithms::UNKNOWN) { + return Encoder::manifestHeaderSize(transport_type); } - static std::size_t getManifestEntrySize() { - return Encoder::getManifestEntrySize(); + static std::size_t manifestEntrySize() { + return Encoder::manifestEntrySize(); } - Manifest &setManifestType(ManifestType type) { + Manifest &setType(ManifestType type) { manifest_type_ = type; - encoder_.setManifestType(manifest_type_); + encoder_.setType(manifest_type_); return *this; } @@ -105,31 +117,46 @@ class Manifest : public Base { return *this; } - auth::CryptoHashType getHashAlgorithm() { return hash_algorithm_; } + auth::CryptoHashType getHashAlgorithm() const { return hash_algorithm_; } - ManifestType getManifestType() const { return manifest_type_; } + ManifestType getType() const { return manifest_type_; } + + interface::ProductionProtocolAlgorithms getTransportType() const { + return manifest_transport_type_; + } - bool isFinalManifest() const { return is_last_; } + bool getIsLast() const { return is_last_; } Manifest &setVersion(ManifestVersion version) { encoder_.setVersion(version); return *this; } - Manifest &setFinalBlockNumber(std::uint32_t final_block_number) { - encoder_.setFinalBlockNumber(final_block_number); + Manifest &setParamsBytestream(const ParamsBytestream ¶ms) { + manifest_transport_type_ = + interface::ProductionProtocolAlgorithms::BYTE_STREAM; + encoder_.setParamsBytestream(params); return *this; } - uint32_t getFinalBlockNumber() const { - return decoder_.getFinalBlockNumber(); + Manifest &setParamsRTC(const ParamsRTC ¶ms) { + manifest_transport_type_ = + interface::ProductionProtocolAlgorithms::RTC_PROD; + encoder_.setParamsRTC(params); + return *this; + } + + ParamsBytestream getParamsBytestream() const { + return decoder_.getParamsBytestream(); } + ParamsRTC getParamsRTC() const { return decoder_.getParamsRTC(); } + ManifestVersion getVersion() const { return decoder_.getVersion(); } - Manifest &setFinalManifest(bool is_final_manifest) { - encoder_.setIsFinalManifest(is_final_manifest); - is_last_ = is_final_manifest; + Manifest &setIsLast(bool is_last) { + encoder_.setIsLast(is_last); + is_last_ = is_last; return *this; } @@ -141,6 +168,7 @@ class Manifest : public Base { protected: ManifestType manifest_type_; + interface::ProductionProtocolAlgorithms manifest_transport_type_; auth::CryptoHashType hash_algorithm_; bool is_last_; diff --git a/libtransport/src/core/manifest_format.h b/libtransport/src/core/manifest_format.h index 90d221f5e..38f26067e 100644 --- a/libtransport/src/core/manifest_format.h +++ b/libtransport/src/core/manifest_format.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -17,6 +17,7 @@ #include <hicn/transport/auth/crypto_hash.h> #include <hicn/transport/core/name.h> +#include <hicn/transport/interfaces/socket_options_keys.h> #include <cinttypes> #include <type_traits> @@ -26,15 +27,6 @@ namespace transport { namespace core { -enum class ManifestFields : uint8_t { - VERSION, - HASH_ALGORITHM, - SEGMENT_CALCULATION_STRATEGY, - FINAL_MANIFEST, - NAME_HASH_LIST, - BASE_NAME -}; - enum class ManifestVersion : uint8_t { VERSION_1 = 1, }; @@ -45,18 +37,24 @@ enum class ManifestType : uint8_t { FLIC_MANIFEST = 3, }; -/** - * INCREMENTAL: Manifests will be received inline with the data with no specific - * assumption regarding the manifest capacity. Consumers can send interests - * using a +1 heuristic. - * - * MANIFEST_CAPACITY_BASED: manifests with capacity N have a suffix multiple of - * N+1: 0, N+1, 2(N+1) etc. Contents have a suffix incremented by 1 except when - * it conflicts with a manifest: 1, 2, ..., N, N+2, N+3, ..., 2N+1, 2N+3 - */ -enum class NextSegmentCalculationStrategy : uint8_t { - INCREMENTAL = 1, - MANIFEST_CAPACITY_BASED = 2, +struct ParamsRTC { + std::uint64_t timestamp; + std::uint32_t prod_rate; + std::uint32_t prod_seg; + std::uint32_t support_fec; + + bool operator==(const ParamsRTC &other) const { + return (timestamp == other.timestamp && prod_rate == other.prod_rate && + prod_seg == other.prod_seg && support_fec == other.support_fec); + } +}; + +struct ParamsBytestream { + std::uint32_t final_segment; + + bool operator==(const ParamsBytestream &other) const { + return (final_segment == other.final_segment); + } }; template <typename T> @@ -84,24 +82,14 @@ class ManifestEncoder { return static_cast<Implementation &>(*this).clearImpl(); } - ManifestEncoder &setManifestType(ManifestType type) { - return static_cast<Implementation &>(*this).setManifestTypeImpl(type); + ManifestEncoder &setType(ManifestType type) { + return static_cast<Implementation &>(*this).setTypeImpl(type); } ManifestEncoder &setHashAlgorithm(auth::CryptoHashType hash) { return static_cast<Implementation &>(*this).setHashAlgorithmImpl(hash); } - ManifestEncoder &setFinalChunkNumber(uint32_t final_chunk) { - return static_cast<Implementation &>(*this).setFinalChunkImpl(final_chunk); - } - - ManifestEncoder &setNextSegmentCalculationStrategy( - NextSegmentCalculationStrategy strategy) { - return static_cast<Implementation &>(*this) - .setNextSegmentCalculationStrategyImpl(strategy); - } - template < typename T, typename = std::enable_if_t<std::is_same< @@ -116,8 +104,8 @@ class ManifestEncoder { suffix, std::forward<Hash &&>(hash)); } - ManifestEncoder &setIsFinalManifest(bool is_last) { - return static_cast<Implementation &>(*this).setIsFinalManifestImpl(is_last); + ManifestEncoder &setIsLast(bool is_last) { + return static_cast<Implementation &>(*this).setIsLastImpl(is_last); } ManifestEncoder &setVersion(ManifestVersion version) { @@ -133,17 +121,22 @@ class ManifestEncoder { return static_cast<Implementation &>(*this).updateImpl(); } - ManifestEncoder &setFinalBlockNumber(std::uint32_t final_block_number) { - return static_cast<Implementation &>(*this).setFinalBlockNumberImpl( - final_block_number); + ManifestEncoder &setParamsBytestream(const ParamsBytestream ¶ms) { + return static_cast<Implementation &>(*this).setParamsBytestreamImpl(params); + } + + ManifestEncoder &setParamsRTC(const ParamsRTC ¶ms) { + return static_cast<Implementation &>(*this).setParamsRTCImpl(params); } - static std::size_t getManifestHeaderSize() { - return Implementation::getManifestHeaderSizeImpl(); + static std::size_t manifestHeaderSize( + interface::ProductionProtocolAlgorithms transport_type = + interface::ProductionProtocolAlgorithms::UNKNOWN) { + return Implementation::manifestHeaderSizeImpl(transport_type); } - static std::size_t getManifestEntrySize() { - return Implementation::getManifestEntrySizeImpl(); + static std::size_t manifestEntrySize() { + return Implementation::manifestEntrySizeImpl(); } }; @@ -158,21 +151,16 @@ class ManifestDecoder { void decode() { static_cast<Implementation &>(*this).decodeImpl(); } - ManifestType getManifestType() const { - return static_cast<const Implementation &>(*this).getManifestTypeImpl(); - } - - auth::CryptoHashType getHashAlgorithm() const { - return static_cast<const Implementation &>(*this).getHashAlgorithmImpl(); + ManifestType getType() const { + return static_cast<const Implementation &>(*this).getTypeImpl(); } - uint32_t getFinalChunkNumber() const { - return static_cast<const Implementation &>(*this).getFinalChunkImpl(); + interface::ProductionProtocolAlgorithms getTransportType() const { + return static_cast<const Implementation &>(*this).getTransportTypeImpl(); } - NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() const { - return static_cast<const Implementation &>(*this) - .getNextSegmentCalculationStrategyImpl(); + auth::CryptoHashType getHashAlgorithm() const { + return static_cast<const Implementation &>(*this).getHashAlgorithmImpl(); } core::Name getBaseName() const { @@ -183,8 +171,8 @@ class ManifestDecoder { return static_cast<Implementation &>(*this).getSuffixHashListImpl(); } - bool getIsFinalManifest() const { - return static_cast<const Implementation &>(*this).getIsFinalManifestImpl(); + bool getIsLast() const { + return static_cast<const Implementation &>(*this).getIsLastImpl(); } ManifestVersion getVersion() const { @@ -196,8 +184,12 @@ class ManifestDecoder { .estimateSerializedLengthImpl(number_of_entries); } - uint32_t getFinalBlockNumber() const { - return static_cast<const Implementation &>(*this).getFinalBlockNumberImpl(); + ParamsBytestream getParamsBytestream() const { + return static_cast<const Implementation &>(*this).getParamsBytestreamImpl(); + } + + ParamsRTC getParamsRTC() const { + return static_cast<const Implementation &>(*this).getParamsRTCImpl(); } }; diff --git a/libtransport/src/core/manifest_format_fixed.cc b/libtransport/src/core/manifest_format_fixed.cc index 11d4a56cb..4c8a5e031 100644 --- a/libtransport/src/core/manifest_format_fixed.cc +++ b/libtransport/src/core/manifest_format_fixed.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -27,164 +27,288 @@ FixedManifestEncoder::FixedManifestEncoder(Packet &packet, bool clear) : packet_(packet), max_size_(Packet::default_mtu - packet_.headerSize()), - manifest_header_(reinterpret_cast<ManifestHeader *>( - packet_.writableData() + packet_.headerSize())), - manifest_entries_( - reinterpret_cast<ManifestEntry *>(manifest_header_ + 1)), - current_entry_(0), - signature_size_(signature_size) { + signature_size_(signature_size), + transport_type_(interface::ProductionProtocolAlgorithms::UNKNOWN), + encoded_(false), + params_bytestream_({0}), + params_rtc_({0}) { + manifest_meta_ = reinterpret_cast<ManifestMeta *>(packet_.writableData() + + packet_.headerSize()); + manifest_entry_meta_ = + reinterpret_cast<ManifestEntryMeta *>(manifest_meta_ + 1); + if (clear) { - *manifest_header_ = {0}; + *manifest_meta_ = {0}; + *manifest_entry_meta_ = {0}; } } FixedManifestEncoder::~FixedManifestEncoder() {} FixedManifestEncoder &FixedManifestEncoder::encodeImpl() { - packet_.append(sizeof(ManifestHeader) + - manifest_header_->number_of_entries * sizeof(ManifestEntry)); + if (encoded_) { + return *this; + } + + manifest_meta_->transport_type = static_cast<uint8_t>(transport_type_); + manifest_entry_meta_->nb_entries = manifest_entries_.size(); + + packet_.append(FixedManifestEncoder::manifestHeaderSizeImpl()); packet_.updateLength(); + + switch (transport_type_) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: + packet_.appendPayload( + reinterpret_cast<const uint8_t *>(¶ms_bytestream_), + MANIFEST_PARAMS_BYTESTREAM_SIZE); + break; + case interface::ProductionProtocolAlgorithms::RTC_PROD: + packet_.appendPayload(reinterpret_cast<const uint8_t *>(¶ms_rtc_), + MANIFEST_PARAMS_RTC_SIZE); + break; + default: + break; + } + + packet_.appendPayload( + reinterpret_cast<const uint8_t *>(manifest_entries_.data()), + manifest_entries_.size() * FixedManifestEncoder::manifestEntrySizeImpl()); + + if (TRANSPORT_EXPECT_FALSE(packet_.payloadSize() < + estimateSerializedLengthImpl())) { + throw errors::RuntimeException("Error encoding the manifest"); + } + + encoded_ = true; return *this; } FixedManifestEncoder &FixedManifestEncoder::clearImpl() { - packet_.trimEnd(sizeof(ManifestHeader) + - manifest_header_->number_of_entries * sizeof(ManifestEntry)); - current_entry_ = 0; - *manifest_header_ = {0}; + if (encoded_) { + packet_.trimEnd(FixedManifestEncoder::manifestHeaderSizeImpl() + + manifest_entries_.size() * + FixedManifestEncoder::manifestEntrySizeImpl()); + } + + transport_type_ = interface::ProductionProtocolAlgorithms::UNKNOWN; + encoded_ = false; + params_bytestream_ = {0}; + params_rtc_ = {0}; + *manifest_meta_ = {0}; + *manifest_entry_meta_ = {0}; + manifest_entries_.clear(); + return *this; } -FixedManifestEncoder &FixedManifestEncoder::setHashAlgorithmImpl( - auth::CryptoHashType algorithm) { - manifest_header_->hash_algorithm = static_cast<uint8_t>(algorithm); +FixedManifestEncoder &FixedManifestEncoder::updateImpl() { + max_size_ = Packet::default_mtu - packet_.headerSize() - signature_size_; + return *this; +} + +FixedManifestEncoder &FixedManifestEncoder::setVersionImpl( + ManifestVersion version) { + manifest_meta_->version = static_cast<uint8_t>(version); return *this; } -FixedManifestEncoder &FixedManifestEncoder::setManifestTypeImpl( +FixedManifestEncoder &FixedManifestEncoder::setTypeImpl( ManifestType manifest_type) { - manifest_header_->manifest_type = static_cast<uint8_t>(manifest_type); + manifest_meta_->type = static_cast<uint8_t>(manifest_type); + return *this; +} + +FixedManifestEncoder &FixedManifestEncoder::setHashAlgorithmImpl( + auth::CryptoHashType algorithm) { + manifest_meta_->hash_algorithm = static_cast<uint8_t>(algorithm); return *this; } -FixedManifestEncoder & -FixedManifestEncoder::setNextSegmentCalculationStrategyImpl( - NextSegmentCalculationStrategy strategy) { - manifest_header_->next_segment_strategy = static_cast<uint8_t>(strategy); +FixedManifestEncoder &FixedManifestEncoder::setIsLastImpl(bool is_last) { + manifest_meta_->is_last = static_cast<uint8_t>(is_last); return *this; } FixedManifestEncoder &FixedManifestEncoder::setBaseNameImpl( const core::Name &base_name) { - base_name.copyToDestination( - reinterpret_cast<uint8_t *>(&manifest_header_->prefix[0]), false); - manifest_header_->flags.ipv6 = + manifest_entry_meta_->is_ipv6 = base_name.getAddressFamily() == AF_INET6 ? 1_U8 : 0_U8; + base_name.copyPrefixToDestination( + reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix[0])); return *this; } -FixedManifestEncoder &FixedManifestEncoder::addSuffixAndHashImpl( - uint32_t suffix, const auth::CryptoHash &hash) { - auto _hash = hash.getDigest(); - addSuffixHashBytes(suffix, _hash.data(), _hash.size()); +FixedManifestEncoder &FixedManifestEncoder::setParamsBytestreamImpl( + const ParamsBytestream ¶ms) { + transport_type_ = interface::ProductionProtocolAlgorithms::BYTE_STREAM; + params_bytestream_ = TransportParamsBytestream{ + .final_segment = params.final_segment, + }; return *this; } -void FixedManifestEncoder::addSuffixHashBytes(uint32_t suffix, - const uint8_t *hash, - std::size_t length) { - manifest_entries_[current_entry_].suffix = htonl(suffix); - // std::copy(hash, hash + length, - // manifest_entries_[current_entry_].hash); - std::memcpy( - reinterpret_cast<uint8_t *>(manifest_entries_[current_entry_].hash), hash, - length); +FixedManifestEncoder &FixedManifestEncoder::setParamsRTCImpl( + const ParamsRTC ¶ms) { + transport_type_ = interface::ProductionProtocolAlgorithms::RTC_PROD; + params_rtc_ = TransportParamsRTC{ + .timestamp = params.timestamp, + .prod_rate = params.prod_rate, + .prod_seg = params.prod_seg, + .support_fec = params.support_fec, + }; + return *this; +} - manifest_header_->number_of_entries++; - current_entry_++; +FixedManifestEncoder &FixedManifestEncoder::addSuffixAndHashImpl( + uint32_t suffix, const auth::CryptoHash &hash) { + std::vector<uint8_t> _hash = hash.getDigest(); + + manifest_entries_.push_back(ManifestEntry{ + .suffix = htonl(suffix), + .hash = {0}, + }); + + std::memcpy(reinterpret_cast<uint8_t *>(manifest_entries_.back().hash), + _hash.data(), _hash.size()); if (TRANSPORT_EXPECT_FALSE(estimateSerializedLengthImpl() > max_size_)) { throw errors::RuntimeException("Manifest size exceeded the packet MTU!"); } -} - -FixedManifestEncoder &FixedManifestEncoder::setIsFinalManifestImpl( - bool is_last) { - manifest_header_->flags.is_last = static_cast<uint8_t>(is_last); - return *this; -} -FixedManifestEncoder &FixedManifestEncoder::setVersionImpl( - ManifestVersion version) { - manifest_header_->version = static_cast<uint8_t>(version); return *this; } std::size_t FixedManifestEncoder::estimateSerializedLengthImpl( std::size_t additional_entries) { - return sizeof(ManifestHeader) + - (manifest_header_->number_of_entries + additional_entries) * - sizeof(ManifestEntry); + return FixedManifestEncoder::manifestHeaderSizeImpl(transport_type_) + + (manifest_entries_.size() + additional_entries) * + FixedManifestEncoder::manifestEntrySizeImpl(); } -FixedManifestEncoder &FixedManifestEncoder::updateImpl() { - max_size_ = Packet::default_mtu - packet_.headerSize() - signature_size_; - return *this; -} - -FixedManifestEncoder &FixedManifestEncoder::setFinalBlockNumberImpl( - std::uint32_t final_block_number) { - manifest_header_->final_block_number = htonl(final_block_number); - return *this; -} +std::size_t FixedManifestEncoder::manifestHeaderSizeImpl( + interface::ProductionProtocolAlgorithms transport_type) { + uint32_t params_size = 0; + + switch (transport_type) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: + params_size = MANIFEST_PARAMS_BYTESTREAM_SIZE; + break; + case interface::ProductionProtocolAlgorithms::RTC_PROD: + params_size = MANIFEST_PARAMS_RTC_SIZE; + break; + default: + break; + } -std::size_t FixedManifestEncoder::getManifestHeaderSizeImpl() { - return sizeof(ManifestHeader); + return MANIFEST_META_SIZE + MANIFEST_ENTRY_META_SIZE + params_size; } -std::size_t FixedManifestEncoder::getManifestEntrySizeImpl() { - return sizeof(ManifestEntry); +std::size_t FixedManifestEncoder::manifestEntrySizeImpl() { + return MANIFEST_ENTRY_SIZE; } FixedManifestDecoder::FixedManifestDecoder(Packet &packet) - : packet_(packet), - manifest_header_(reinterpret_cast<ManifestHeader *>( - packet_.getPayload()->writableData())), - manifest_entries_(reinterpret_cast<ManifestEntry *>( - packet_.getPayload()->writableData() + sizeof(ManifestHeader))) {} + : packet_(packet), decoded_(false) { + manifest_meta_ = + reinterpret_cast<ManifestMeta *>(packet_.getPayload()->writableData()); + manifest_entry_meta_ = + reinterpret_cast<ManifestEntryMeta *>(manifest_meta_ + 1); + transport_type_ = getTransportTypeImpl(); + + switch (transport_type_) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: + params_bytestream_ = reinterpret_cast<TransportParamsBytestream *>( + manifest_entry_meta_ + 1); + manifest_entries_ = + reinterpret_cast<ManifestEntry *>(params_bytestream_ + 1); + break; + case interface::ProductionProtocolAlgorithms::RTC_PROD: + params_rtc_ = + reinterpret_cast<TransportParamsRTC *>(manifest_entry_meta_ + 1); + manifest_entries_ = reinterpret_cast<ManifestEntry *>(params_rtc_ + 1); + break; + default: + manifest_entries_ = + reinterpret_cast<ManifestEntry *>(manifest_entry_meta_ + 1); + break; + } +} FixedManifestDecoder::~FixedManifestDecoder() {} void FixedManifestDecoder::decodeImpl() { + if (decoded_) { + return; + } + std::size_t packet_size = packet_.payloadSize(); - if (packet_size < sizeof(ManifestHeader) || + if (packet_size < + FixedManifestEncoder::manifestHeaderSizeImpl(transport_type_) || packet_size < estimateSerializedLengthImpl()) { throw errors::RuntimeException( "The packet does not match expected manifest size."); } + + decoded_ = true; +} + +FixedManifestDecoder &FixedManifestDecoder::clearImpl() { + decoded_ = false; + return *this; } -FixedManifestDecoder &FixedManifestDecoder::clearImpl() { return *this; } +ManifestType FixedManifestDecoder::getTypeImpl() const { + return static_cast<ManifestType>(manifest_meta_->type); +} -ManifestType FixedManifestDecoder::getManifestTypeImpl() const { - return static_cast<ManifestType>(manifest_header_->manifest_type); +ManifestVersion FixedManifestDecoder::getVersionImpl() const { + return static_cast<ManifestVersion>(manifest_meta_->version); +} + +interface::ProductionProtocolAlgorithms +FixedManifestDecoder::getTransportTypeImpl() const { + return static_cast<interface::ProductionProtocolAlgorithms>( + manifest_meta_->transport_type); } auth::CryptoHashType FixedManifestDecoder::getHashAlgorithmImpl() const { - return static_cast<auth::CryptoHashType>(manifest_header_->hash_algorithm); + return static_cast<auth::CryptoHashType>(manifest_meta_->hash_algorithm); } -NextSegmentCalculationStrategy -FixedManifestDecoder::getNextSegmentCalculationStrategyImpl() const { - return static_cast<NextSegmentCalculationStrategy>( - manifest_header_->next_segment_strategy); +bool FixedManifestDecoder::getIsLastImpl() const { + return static_cast<bool>(manifest_meta_->is_last); +} + +core::Name FixedManifestDecoder::getBaseNameImpl() const { + if (static_cast<bool>(manifest_entry_meta_->is_ipv6)) { + return core::Name( + AF_INET6, reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix)); + } else { + return core::Name( + AF_INET, reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix)); + } +} + +ParamsBytestream FixedManifestDecoder::getParamsBytestreamImpl() const { + return ParamsBytestream{ + .final_segment = params_bytestream_->final_segment, + }; +} + +ParamsRTC FixedManifestDecoder::getParamsRTCImpl() const { + return ParamsRTC{ + .timestamp = params_rtc_->timestamp, + .prod_rate = params_rtc_->prod_rate, + .prod_seg = params_rtc_->prod_seg, + .support_fec = params_rtc_->support_fec, + }; } typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { typename Fixed::SuffixList hash_list; - for (int i = 0; i < manifest_header_->number_of_entries; i++) { + for (int i = 0; i < manifest_entry_meta_->nb_entries; i++) { hash_list.insert(hash_list.end(), std::make_pair(ntohl(manifest_entries_[i].suffix), reinterpret_cast<uint8_t *>( @@ -194,33 +318,11 @@ typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { return hash_list; } -core::Name FixedManifestDecoder::getBaseNameImpl() const { - if (static_cast<bool>(manifest_header_->flags.ipv6)) { - return core::Name(AF_INET6, - reinterpret_cast<uint8_t *>(&manifest_header_->prefix)); - } else { - return core::Name(AF_INET, - reinterpret_cast<uint8_t *>(&manifest_header_->prefix)); - } -} - -bool FixedManifestDecoder::getIsFinalManifestImpl() const { - return static_cast<bool>(manifest_header_->flags.is_last); -} - -ManifestVersion FixedManifestDecoder::getVersionImpl() const { - return static_cast<ManifestVersion>(manifest_header_->version); -} - std::size_t FixedManifestDecoder::estimateSerializedLengthImpl( std::size_t additional_entries) const { - return sizeof(ManifestHeader) + - (additional_entries + manifest_header_->number_of_entries) * - sizeof(ManifestEntry); -} - -uint32_t FixedManifestDecoder::getFinalBlockNumberImpl() const { - return ntohl(manifest_header_->final_block_number); + return FixedManifestEncoder::manifestHeaderSizeImpl(transport_type_) + + (manifest_entry_meta_->nb_entries + additional_entries) * + FixedManifestEncoder::manifestEntrySizeImpl(); } } // end namespace core diff --git a/libtransport/src/core/manifest_format_fixed.h b/libtransport/src/core/manifest_format_fixed.h index 56ad4ef6d..ade4bf02c 100644 --- a/libtransport/src/core/manifest_format_fixed.h +++ b/libtransport/src/core/manifest_format_fixed.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -24,26 +24,74 @@ namespace transport { namespace core { -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |Version| MType |HashAlg|NextStr| Flags |NumberOfEntries| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Final Block Number | -// +---------------------------------------------------------------| -// | | -// + + -// | | -// + Prefix + -// | | -// + + -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Suffix | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Hash Value | -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// Manifest Metadata: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Version| Type | Transport Type| Hash Algorithm|L| Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Entry Metadata: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Nb entries |I| Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + + +// | | +// + Prefix + +// | | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Transport Parameters - Bytestream: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Final Segment | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Transport Parameters - RTC: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Timestamp + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Production Rate | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Current Segment | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |F| | +// + Reserved for future parameters + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Entry: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Packet Suffix | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + + +// | | +// + + +// | | +// + + +// | | +// + Packet Digest + +// | | +// + + +// | | +// + + +// | | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ class FixedManifestEncoder; class FixedManifestDecoder; @@ -58,28 +106,47 @@ struct Fixed { using SuffixList = std::list<std::pair<uint32_t, uint8_t *>>; }; -struct Flags { - std::uint8_t ipv6 : 1; - std::uint8_t is_last : 1; - std::uint8_t unused : 6; +const size_t MANIFEST_META_SIZE = 4; +struct __attribute__((__packed__)) ManifestMeta { + std::uint8_t version : 4; + std::uint8_t type : 4; + std::uint8_t transport_type; + std::uint8_t hash_algorithm; + std::uint8_t is_last; }; +static_assert(sizeof(ManifestMeta) == MANIFEST_META_SIZE); -struct ManifestEntry { - std::uint32_t suffix; - std::uint32_t hash[8]; +const size_t MANIFEST_ENTRY_META_SIZE = 20; +struct __attribute__((__packed__)) ManifestEntryMeta { + std::uint8_t nb_entries; + std::uint8_t is_ipv6; + std::uint16_t unused; + std::uint32_t prefix[4]; }; +static_assert(sizeof(ManifestEntryMeta) == MANIFEST_ENTRY_META_SIZE); -struct ManifestHeader { - std::uint8_t version : 4; - std::uint8_t manifest_type : 4; - std::uint8_t hash_algorithm : 4; - std::uint8_t next_segment_strategy : 4; - Flags flags; - std::uint8_t number_of_entries; - std::uint32_t final_block_number; - std::uint32_t prefix[4]; - ManifestEntry entries[0]; +const size_t MANIFEST_PARAMS_BYTESTREAM_SIZE = 4; +struct __attribute__((__packed__)) TransportParamsBytestream { + std::uint32_t final_segment; +}; +static_assert(sizeof(TransportParamsBytestream) == + MANIFEST_PARAMS_BYTESTREAM_SIZE); + +const size_t MANIFEST_PARAMS_RTC_SIZE = 20; +struct __attribute__((__packed__)) TransportParamsRTC { + std::uint64_t timestamp; + std::uint32_t prod_rate; + std::uint32_t prod_seg; + std::uint32_t support_fec; +}; +static_assert(sizeof(TransportParamsRTC) == MANIFEST_PARAMS_RTC_SIZE); + +const size_t MANIFEST_ENTRY_SIZE = 36; +struct __attribute__((__packed__)) ManifestEntry { + std::uint32_t suffix; + std::uint32_t hash[8]; }; +static_assert(sizeof(ManifestEntry) == MANIFEST_ENTRY_SIZE); static const constexpr std::uint8_t manifest_version = 1; @@ -91,46 +158,48 @@ class FixedManifestEncoder : public ManifestEncoder<FixedManifestEncoder> { ~FixedManifestEncoder(); FixedManifestEncoder &encodeImpl(); - FixedManifestEncoder &clearImpl(); + FixedManifestEncoder &updateImpl(); - FixedManifestEncoder &setManifestTypeImpl(ManifestType manifest_type); - + // ManifestMeta + FixedManifestEncoder &setVersionImpl(ManifestVersion version); + FixedManifestEncoder &setTypeImpl(ManifestType manifest_type); FixedManifestEncoder &setHashAlgorithmImpl(Fixed::HashType algorithm); + FixedManifestEncoder &setIsLastImpl(bool is_last); - FixedManifestEncoder &setNextSegmentCalculationStrategyImpl( - NextSegmentCalculationStrategy strategy); - + // ManifestEntryMeta FixedManifestEncoder &setBaseNameImpl(const core::Name &base_name); + // TransportParams + FixedManifestEncoder &setParamsBytestreamImpl(const ParamsBytestream ¶ms); + FixedManifestEncoder &setParamsRTCImpl(const ParamsRTC ¶ms); + + // ManifestEntry FixedManifestEncoder &addSuffixAndHashImpl(uint32_t suffix, const Fixed::Hash &hash); - FixedManifestEncoder &setIsFinalManifestImpl(bool is_last); - - FixedManifestEncoder &setVersionImpl(ManifestVersion version); - std::size_t estimateSerializedLengthImpl(std::size_t additional_entries = 0); - FixedManifestEncoder &updateImpl(); - - FixedManifestEncoder &setFinalBlockNumberImpl( - std::uint32_t final_block_number); - - static std::size_t getManifestHeaderSizeImpl(); - - static std::size_t getManifestEntrySizeImpl(); + static std::size_t manifestHeaderSizeImpl( + interface::ProductionProtocolAlgorithms transport_type = + interface::ProductionProtocolAlgorithms::UNKNOWN); + static std::size_t manifestEntrySizeImpl(); private: - void addSuffixHashBytes(uint32_t suffix, const uint8_t *hash, - std::size_t length); - Packet &packet_; std::size_t max_size_; - ManifestHeader *manifest_header_; - ManifestEntry *manifest_entries_; - std::size_t current_entry_; std::size_t signature_size_; + interface::ProductionProtocolAlgorithms transport_type_; + bool encoded_; + + // Manifest Header + ManifestMeta *manifest_meta_; + ManifestEntryMeta *manifest_entry_meta_; + TransportParamsBytestream params_bytestream_; + TransportParamsRTC params_rtc_; + + // Manifest Entries + std::vector<ManifestEntry> manifest_entries_; }; class FixedManifestDecoder : public ManifestDecoder<FixedManifestDecoder> { @@ -140,31 +209,40 @@ class FixedManifestDecoder : public ManifestDecoder<FixedManifestDecoder> { ~FixedManifestDecoder(); void decodeImpl(); - FixedManifestDecoder &clearImpl(); - ManifestType getManifestTypeImpl() const; - + // ManifestMeta + ManifestVersion getVersionImpl() const; + ManifestType getTypeImpl() const; + interface::ProductionProtocolAlgorithms getTransportTypeImpl() const; Fixed::HashType getHashAlgorithmImpl() const; + bool getIsLastImpl() const; - NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; - - typename Fixed::SuffixList getSuffixHashListImpl(); - + // ManifestEntryMeta core::Name getBaseNameImpl() const; - bool getIsFinalManifestImpl() const; + // TransportParams + ParamsBytestream getParamsBytestreamImpl() const; + ParamsRTC getParamsRTCImpl() const; + + // ManifestEntry + typename Fixed::SuffixList getSuffixHashListImpl(); std::size_t estimateSerializedLengthImpl( std::size_t additional_entries = 0) const; - ManifestVersion getVersionImpl() const; - - uint32_t getFinalBlockNumberImpl() const; - private: Packet &packet_; - ManifestHeader *manifest_header_; + interface::ProductionProtocolAlgorithms transport_type_; + bool decoded_; + + // Manifest Header + ManifestMeta *manifest_meta_; + ManifestEntryMeta *manifest_entry_meta_; + TransportParamsBytestream *params_bytestream_; + TransportParamsRTC *params_rtc_; + + // Manifest Entries ManifestEntry *manifest_entries_; }; diff --git a/libtransport/src/core/manifest_inline.h b/libtransport/src/core/manifest_inline.h index a487ccfe3..ca48a4a79 100644 --- a/libtransport/src/core/manifest_inline.h +++ b/libtransport/src/core/manifest_inline.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -40,25 +40,26 @@ class ManifestInline public: ManifestInline() : ManifestBase() {} - ManifestInline(const core::Name &name, std::size_t signature_size = 0) - : ManifestBase(name, signature_size) {} + ManifestInline(Packet::Format format, const core::Name &name, + std::size_t signature_size = 0) + : ManifestBase(format, name, signature_size) {} template <typename T> ManifestInline(T &&base) : ManifestBase(std::forward<T &&>(base)) {} + template <typename T> + ManifestInline(T &base) : ManifestBase(base) {} + static TRANSPORT_ALWAYS_INLINE ManifestInline *createManifest( - const core::Name &manifest_name, ManifestVersion version, - ManifestType type, HashType algorithm, bool is_last, - const Name &base_name, NextSegmentCalculationStrategy strategy, - std::size_t signature_size) { - auto manifest = new ManifestInline(manifest_name, signature_size); + Packet::Format format, const core::Name &manifest_name, + ManifestVersion version, ManifestType type, bool is_last, + const Name &base_name, HashType hash_algo, std::size_t signature_size) { + auto manifest = new ManifestInline(format, manifest_name, signature_size); manifest->setVersion(version); - manifest->setManifestType(type); - manifest->setHashAlgorithm(algorithm); - manifest->setFinalManifest(is_last); + manifest->setType(type); + manifest->setHashAlgorithm(hash_algo); + manifest->setIsLast(is_last); manifest->setBaseName(base_name); - manifest->setNextSegmentCalculationStrategy(strategy); - return manifest; } @@ -69,8 +70,6 @@ class ManifestInline ManifestInline &decodeImpl() { base_name_ = ManifestBase::decoder_.getBaseName(); - next_segment_strategy_ = - ManifestBase::decoder_.getNextSegmentCalculationStrategy(); suffix_hash_map_ = ManifestBase::decoder_.getSuffixHashList(); return *this; @@ -96,18 +95,6 @@ class ManifestInline // Call this function only after the decode function! const SuffixList &getSuffixList() { return suffix_hash_map_; } - ManifestInline &setNextSegmentCalculationStrategy( - NextSegmentCalculationStrategy strategy) { - next_segment_strategy_ = strategy; - ManifestBase::encoder_.setNextSegmentCalculationStrategy( - next_segment_strategy_); - return *this; - } - - NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() { - return next_segment_strategy_; - } - // Convert several manifests into a single map from suffixes to packet hashes. // All manifests must have been decoded beforehand. static std::unordered_map<Suffix, Hash> getSuffixMap( @@ -134,7 +121,6 @@ class ManifestInline private: core::Name base_name_; - NextSegmentCalculationStrategy next_segment_strategy_; SuffixList suffix_hash_map_; }; diff --git a/libtransport/src/core/memif_connector.cc b/libtransport/src/core/memif_connector.cc new file mode 100644 index 000000000..fb38a6e23 --- /dev/null +++ b/libtransport/src/core/memif_connector.cc @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2021 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 <core/errors.h> +#include <core/memif_connector.h> +#include <glog/logging.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <sys/epoll.h> + +#include <cstdlib> + +/* sstrncpy */ +#include <hicn/util/sstrncpy.h> + +#define CANCEL_TIMER 1 + +namespace transport { + +namespace core { + +MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, + PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, + OnReconnectCallback &&on_reconnect, + asio::io_service &io_service, + std::string app_name) + : Connector(std::move(receive_callback), std::move(packet_sent), + std::move(close_callback), std::move(on_reconnect)), + event_reactor_(), + memif_worker_(std::bind(&MemifConnector::threadMain, this)), + timer_set_(false), + send_timer_(event_reactor_), + disconnect_timer_(event_reactor_), + io_service_(io_service), + work_(asio::make_work_guard(io_service_)), + memif_connection_({0}), + tx_buf_counter_(0), + is_reconnection_(false), + data_available_(false), + app_name_(app_name), + socket_filename_(""), + buffer_size_(kbuf_size), + log2_ring_size_(klog2_ring_size), + max_memif_bufs_(1 << klog2_ring_size) {} + +MemifConnector::~MemifConnector() { close(); } + +void MemifConnector::connect(uint32_t memif_id, long memif_mode, + const std::string &socket_filename, + std::size_t buffer_size, + std::size_t log2_ring_size) { + state_ = State::CONNECTING; + + memif_id_ = memif_id; + socket_filename_ = socket_filename; + buffer_size_ = buffer_size; + log2_ring_size_ = log2_ring_size; + max_memif_bufs_ = 1 << log2_ring_size; + createMemif(memif_id, memif_mode); +} + +int MemifConnector::createMemif(uint32_t index, uint8_t is_master) { + int err = MEMIF_ERR_SUCCESS; + + memif_socket_args_t socket_args; + memif_conn_args_t args; + memset(&socket_args, 0, sizeof(memif_socket_args_t)); + memset(&args, 0, sizeof(memif_conn_args_t)); + + // Setup memif socket first + + int rc = strcpy_s(socket_args.path, sizeof(socket_args.path) - 1, + socket_filename_.c_str()); + if (rc != EOK) { + std::string error = "Provided socket path is larger than " + + std::to_string(sizeof(socket_args.path)) + " bytes."; + throw errors::RuntimeException(error); + } + + rc = strcpy_s(socket_args.app_name, sizeof(socket_args.app_name) - 1, + app_name_.c_str()); + if (rc != EOK) { + std::string error = "Provided app_name is larger than " + + std::to_string(sizeof(socket_args.app_name)) + + " bytes."; + throw errors::RuntimeException(error); + } + + socket_args.on_control_fd_update = controlFdUpdate; + socket_args.alloc = nullptr; + socket_args.realloc = nullptr; + socket_args.free = nullptr; + + err = memif_create_socket(&args.socket, &socket_args, this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + + // Setup memif connection using provided memif_socket_handle_t + args.is_master = is_master; + args.log2_ring_size = log2_ring_size_; + args.buffer_size = buffer_size_; + args.num_s2m_rings = 1; + args.num_m2s_rings = 1; + strcpy_s((char *)args.interface_name, sizeof(args.interface_name), IF_NAME); + args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + args.interface_id = index; + err = memif_create(&memif_connection_.conn, &args, onConnect, onDisconnect, + onInterrupt, this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + + memif_connection_.index = (uint16_t)index; + memif_connection_.tx_qid = 0; + /* alloc memif buffers */ + memif_connection_.rx_buf_num = 0; + memif_connection_.rx_bufs = static_cast<memif_buffer_t *>( + malloc(sizeof(memif_buffer_t) * max_memif_bufs_)); + memif_connection_.tx_buf_num = 0; + memif_connection_.tx_bufs = static_cast<memif_buffer_t *>( + malloc(sizeof(memif_buffer_t) * max_memif_bufs_)); + + return 0; +} + +int MemifConnector::deleteMemif() { + if (memif_connection_.rx_bufs) { + free(memif_connection_.rx_bufs); + } + + memif_connection_.rx_bufs = nullptr; + memif_connection_.rx_buf_num = 0; + + if (memif_connection_.tx_bufs) { + free(memif_connection_.tx_bufs); + } + + memif_connection_.tx_bufs = nullptr; + memif_connection_.tx_buf_num = 0; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete(&memif_connection_.conn); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_delete: " << memif_strerror(err); + } + + if (TRANSPORT_EXPECT_FALSE(memif_connection_.conn != nullptr)) { + LOG(ERROR) << "memif delete fail"; + } + + state_ = State::CLOSED; + + return 0; +} + +int MemifConnector::controlFdUpdate(memif_fd_event_t fde, void *private_ctx) { + auto self = reinterpret_cast<MemifConnector *>(private_ctx); + uint32_t evt = 0; + + /* convert memif event definitions to epoll events */ + auto events = fde.type; + auto fd = fde.fd; + + if (events & MEMIF_FD_EVENT_ERROR) { + LOG(ERROR) << "memif fd event: Error"; + return -1; + } + + if (events & MEMIF_FD_EVENT_DEL) { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: DEL fd " << fd; + return self->event_reactor_.delFileDescriptor(fd); + } + + if (events & MEMIF_FD_EVENT_MOD) { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: MOD fd " << fd; + return self->event_reactor_.modFileDescriptor(fd, evt); + } + + if (events & MEMIF_FD_EVENT_READ) { + evt |= EPOLLIN; + } + + if (events & MEMIF_FD_EVENT_WRITE) { + evt |= EPOLLOUT; + } + + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: ADD fd " << fd; + return self->event_reactor_.addFileDescriptor( + fd, evt, [fde](const utils::Event &evt) -> int { + int event = 0; + int memif_err = 0; + + if (evt.events & EPOLLIN) { + event |= MEMIF_FD_EVENT_READ; + } + + if (evt.events & EPOLLOUT) { + event |= MEMIF_FD_EVENT_WRITE; + } + + if (evt.events & EPOLLERR) { + event |= MEMIF_FD_EVENT_ERROR; + } + + memif_err = memif_control_fd_handler(fde.private_ctx, + memif_fd_event_type_t(event)); + + if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_control_fd_handler: " + << memif_strerror(memif_err); + } + + return 0; + }); +} + +uint16_t MemifConnector::bufferAlloc(long n, uint16_t qid, + std::error_code &ec) { + int err; + uint16_t r = 0; + /* set data pointer to shared memory and set buffer_len to shared mmeory + * buffer len */ + err = memif_buffer_alloc(memif_connection_.conn, qid, + memif_connection_.tx_bufs, n, &r, buffer_size_); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + ec = make_error_code(core_error::send_buffer_allocation_failed); + } + + memif_connection_.tx_buf_num += r; + return r; +} + +uint16_t MemifConnector::txBurst(uint16_t qid, std::error_code &ec) { + int err = MEMIF_ERR_SUCCESS; + ec = make_error_code(core_error::success); + uint16_t tx = 0; + + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst(memif_connection_.conn, qid, memif_connection_.tx_bufs, + memif_connection_.tx_buf_num, &tx); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + ec = make_error_code(core_error::send_failed); + } + + memif_connection_.tx_buf_num -= tx; + return tx; +} + +void MemifConnector::scheduleSend(std::uint64_t delay) { + if (!timer_set_) { + timer_set_ = true; + send_timer_.expiresFromNow(std::chrono::microseconds(delay)); + send_timer_.asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } +} + +void MemifConnector::sendCallback(const std::error_code &ec) { + timer_set_ = false; + + if (TRANSPORT_EXPECT_TRUE(!ec && state_ == State::CONNECTED)) { + doSend(); + } +} + +/* informs user about connected status. private_ctx is used by user to identify + connection (multiple connections WIP) */ +int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { + auto self = reinterpret_cast<MemifConnector *>(private_ctx); + self->state_ = State::CONNECTED; + memif_refill_queue(conn, 0, -1, 0); + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Memif " << self->app_name_ << " connected"; + + // We are connected. Notify higher layers. + self->io_service_.post([self]() { + self->on_reconnect_callback_(self, make_error_code(core_error::success)); + }); + + self->doSend(); + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + identify connection (multiple connections WIP) */ +int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { + MemifConnector *connector = (MemifConnector *)private_ctx; + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Memif " << connector->app_name_ << " disconnected"; + return 0; +} + +void MemifConnector::threadMain() { event_reactor_.runEventLoop(200); } + +int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid) { + MemifConnector *connector = (MemifConnector *)private_ctx; + + Details &c = connector->memif_connection_; + std::weak_ptr<MemifConnector> self = connector->shared_from_this(); + std::vector<::utils::MemBuf::Ptr> v; + std::error_code ec = make_error_code(core_error::success); + + int err = MEMIF_ERR_SUCCESS, ret_val; + uint16_t rx = 0; + + do { + err = memif_rx_burst(conn, qid, c.rx_bufs, max_burst, &rx); + ret_val = err; + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && + err != MEMIF_ERR_NOBUF)) { + ec = make_error_code(core_error::receive_failed); + LOG(ERROR) << "memif_rx_burst: " << memif_strerror(err); + goto error; + } + + c.rx_buf_num += rx; + + if (TRANSPORT_EXPECT_FALSE(connector->io_service_.stopped())) { + LOG(ERROR) << "socket stopped: ignoring " << rx << " packets"; + goto error; + } + + std::size_t packet_length; + v.reserve(rx); + for (int i = 0; i < rx; i++) { + auto buffer = connector->getRawBuffer(); + packet_length = (c.rx_bufs + i)->len; + std::memcpy(buffer.first, (c.rx_bufs + i)->data, packet_length); + auto packet = connector->getPacketFromBuffer(buffer.first, packet_length); + v.emplace_back(std::move(packet)); + } + + /* mark memif buffers and shared memory buffers as free */ + /* free processed buffers */ + + err = memif_refill_queue(conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); + } + + c.rx_buf_num -= rx; + + } while (ret_val == MEMIF_ERR_NOBUF); + + connector->io_service_.post([self, buffers = std::move(v)]() { + if (auto c = self.lock()) { + c->receive_callback_(c.get(), buffers, + std::make_error_code(std::errc(0))); + } + }); + + return 0; + +error: + err = memif_refill_queue(c.conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); + } + c.rx_buf_num -= rx; + + connector->io_service_.post([self, ec]() { + if (auto c = self.lock()) { + c->receive_callback_(c.get(), {}, ec); + } + }); + + return 0; +} + +void MemifConnector::close() { + if (state_ != State::CLOSED) { + disconnect_timer_.expiresFromNow(std::chrono::microseconds(50)); + disconnect_timer_.asyncWait([this](const std::error_code &ec) { + deleteMemif(); + event_reactor_.stop(); + }); + } + + if (memif_worker_.joinable()) { + memif_worker_.join(); + } +} + +void MemifConnector::send(Packet &packet) { send(packet.shared_from_this()); } + +void MemifConnector::send(const utils::MemBuf::Ptr &buffer) { + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + output_buffer_.push_back(buffer); + } +#if CANCEL_TIMER + scheduleSend(50); +#endif +} + +int MemifConnector::doSend() { + std::size_t max = 0; + std::size_t size = 0; + std::error_code ec = make_error_code(core_error::success); + int ret = 0; + uint64_t delay = 50; // microseconds + + utils::SpinLock::Acquire locked(write_msgs_lock_); + + // Check if there are pending buffers to send + if (memif_connection_.tx_buf_num > 0) { + ret = txBurst(memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + delay = 200; + goto done; + } + } + + // Continue trying to send buffers in output_buffer_ + size = output_buffer_.size(); + max = size < max_burst ? size : max_burst; + + ret = bufferAlloc(max, memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool() && ret == 0)) { + delay = 200; + goto done; + } + + // Fill allocated buffers and remove them from output_buffer_ + for (uint16_t i = 0; i < ret; i++) { + auto packet = output_buffer_.front().get(); + const utils::MemBuf *current = packet; + std::size_t offset = 0; + uint8_t *shared_buffer = + reinterpret_cast<uint8_t *>(memif_connection_.tx_bufs[i].data); + do { + std::memcpy(shared_buffer + offset, current->data(), current->length()); + offset += current->length(); + current = current->next(); + } while (current != packet); + + memif_connection_.tx_bufs[i].len = uint32_t(offset); + output_buffer_.pop_front(); + } + + // Try to send them + ret = txBurst(memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + LOG(ERROR) << "Tx burst failed " << ec.message(); + delay = 200; + goto done; + } + +done: + memif_refill_queue(memif_connection_.conn, memif_connection_.tx_qid, ret, 0); + + // If there are still packets to send, schedule another send + if (memif_connection_.tx_buf_num > 0 || !output_buffer_.empty()) { + scheduleSend(delay); + } + + // If error, signal to upper layers + if (ec.operator bool()) { + std::weak_ptr<MemifConnector> self = shared_from_this(); + io_service_.post([self, ec]() { + if (auto c = self.lock()) { + c->sent_callback_(c.get(), ec); + } + }); + } + + return 0; +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/memif/memif_connector.h b/libtransport/src/core/memif_connector.h index 0a189f893..d36be4616 100644 --- a/libtransport/src/io_modules/memif/memif_connector.h +++ b/libtransport/src/core/memif_connector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -25,27 +25,48 @@ #include <utils/fd_deadline_timer.h> #include <deque> +#include <future> #include <mutex> #include <thread> +extern "C" { +#include <libmemif.h> +}; + #define _Static_assert static_assert namespace transport { namespace core { -typedef struct memif_connection memif_connection_t; - #define APP_NAME "libtransport" #define IF_NAME "vpp_connection" -#define MEMIF_BUF_SIZE 2048 -#define MEMIF_LOG2_RING_SIZE 13 -#define MAX_MEMIF_BUFS (1 << MEMIF_LOG2_RING_SIZE) - class MemifConnector : public Connector { - using memif_conn_handle_t = void *; + static inline std::size_t kbuf_size = 2048; + static inline std::size_t klog2_ring_size = 13; + using PacketRing = utils::CircularFifo<utils::MemBuf::Ptr, queue_size>; + struct Details { + // index + uint16_t index; + // memif conenction handle + memif_conn_handle_t conn; + // transmit queue id + uint16_t tx_qid; + // tx buffers + memif_buffer_t *tx_bufs; + // allocated tx buffers counter + // number of tx buffers pointing to shared memory + uint16_t tx_buf_num; + // rx buffers + memif_buffer_t *rx_bufs; + // allocated rx buffers counter + // number of rx buffers pointing to shared memory + uint16_t rx_buf_num; + // interface ip address + uint8_t ip_addr[4]; + }; public: MemifConnector(PacketReceivedCallback &&receive_callback, @@ -59,11 +80,14 @@ class MemifConnector : public Connector { void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; void close() override; - void connect(uint32_t memif_id, long memif_mode); + void connect(uint32_t memif_id, long memif_mode, + const std::string &socket_filename, + std::size_t buffer_size = kbuf_size, + std::size_t log2_ring_size = klog2_ring_size); TRANSPORT_ALWAYS_INLINE uint32_t getMemifId() { return memif_id_; }; @@ -72,13 +96,13 @@ class MemifConnector : public Connector { int doSend(); - int createMemif(uint32_t index, uint8_t mode, char *s); + int createMemif(uint32_t index, uint8_t is_master); uint32_t getMemifConfiguration(); int deleteMemif(); - static int controlFdUpdate(int fd, uint8_t events, void *private_ctx); + static int controlFdUpdate(memif_fd_event_t fde, void *private_ctx); static int onConnect(memif_conn_handle_t conn, void *private_ctx); @@ -89,27 +113,26 @@ class MemifConnector : public Connector { void threadMain(); - int txBurst(uint16_t qid); + uint16_t txBurst(uint16_t qid, std::error_code &ec); + + uint16_t bufferAlloc(long n, uint16_t qid, std::error_code &ec); - int bufferAlloc(long n, uint16_t qid); + void scheduleSend(std::uint64_t delay); void sendCallback(const std::error_code &ec); - void processInputBuffer(std::uint16_t total_packets); + auto shared_from_this() { return utils::shared_from(this); } private: - static utils::EpollEventReactor main_event_reactor_; - static std::unique_ptr<std::thread> main_worker_; - int epfd; - std::unique_ptr<std::thread> memif_worker_; utils::EpollEventReactor event_reactor_; + std::thread memif_worker_; std::atomic_bool timer_set_; - std::unique_ptr<utils::FdDeadlineTimer> send_timer_; - std::unique_ptr<utils::FdDeadlineTimer> disconnect_timer_; + utils::FdDeadlineTimer send_timer_; + utils::FdDeadlineTimer disconnect_timer_; asio::io_service &io_service_; asio::executor_work_guard<asio::io_context::executor_type> work_; - std::unique_ptr<memif_connection_t> memif_connection_; + Details memif_connection_; uint16_t tx_buf_counter_; PacketRing input_buffer_; @@ -121,8 +144,9 @@ class MemifConnector : public Connector { uint16_t transmission_index_; utils::SpinLock write_msgs_lock_; std::string socket_filename_; - - static std::once_flag flag_; + std::size_t buffer_size_; + std::size_t log2_ring_size_; + std::size_t max_memif_bufs_; }; } // end namespace core diff --git a/libtransport/src/core/name.cc b/libtransport/src/core/name.cc index 795c8a697..98091eea5 100644 --- a/libtransport/src/core/name.cc +++ b/libtransport/src/core/name.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -26,30 +26,28 @@ namespace core { Name::Name() { name_ = {}; } +/** + * XXX This function does not use the name API provided by libhicn + */ Name::Name(int family, const uint8_t *ip_address, std::uint32_t suffix) : name_({}) { - name_.type = HNT_UNSPEC; std::size_t length; uint8_t *dst = NULL; if (family == AF_INET) { - dst = name_.ip4.prefix_as_u8; + dst = name_.prefix.v4.as_u8; length = IPV4_ADDR_LEN; - name_.type = HNT_CONTIGUOUS_V4; } else if (family == AF_INET6) { - dst = name_.ip6.prefix_as_u8; + dst = name_.prefix.v6.as_u8; length = IPV6_ADDR_LEN; - name_.type = HNT_CONTIGUOUS_V6; } else { throw errors::RuntimeException("Specified name family does not exist."); } std::memcpy(dst, ip_address, length); - *reinterpret_cast<std::uint32_t *>(dst + length) = suffix; + name_.suffix = suffix; } - Name::Name(const char *name, uint32_t segment) { - name_.type = HNT_UNSPEC; if (hicn_name_create(name, segment, &name_) < 0) { throw errors::InvalidIpAddressException(); } @@ -59,7 +57,6 @@ Name::Name(const std::string &uri, uint32_t segment) : Name(uri.c_str(), segment) {} Name::Name(const std::string &uri) { - name_.type = HNT_UNSPEC; utils::StringTokenizer tokenizer(uri, "|"); std::string ip_address; std::string seq_number; @@ -80,9 +77,13 @@ Name::Name(const std::string &uri) { Name::Name(const Name &name) { this->name_ = name.name_; } +Name::~Name() {} + Name &Name::operator=(const Name &name) { - if (hicn_name_copy(&this->name_, &name.name_) < 0) { - throw errors::MalformedNameException(); + if (this != &name) { + if (hicn_name_copy(&this->name_, &name.name_) < 0) { + throw errors::MalformedNameException(); + } } return *this; @@ -129,9 +130,13 @@ uint32_t Name::getHash32(bool consider_suffix) const { return hash; } -void Name::clear() { name_.type = HNT_UNSPEC; }; +void Name::clear() { std::memset(&name_, 0, sizeof(name_)); }; -Name::Type Name::getType() const { return name_.type; } +Name::Type Name::getType() const { + int family; + hicn_name_get_family(&name_, &family); + return family == AF_INET ? Name::Type::V4 : Name::Type::V6; +} uint32_t Name::getSuffix() const { uint32_t ret = 0; @@ -151,29 +156,6 @@ Name &Name::setSuffix(uint32_t seq_number) { return *this; } -std::shared_ptr<Sockaddr> Name::getAddress() const { - Sockaddr *ret = nullptr; - - switch (name_.type) { - case HNT_CONTIGUOUS_V4: - case HNT_IOV_V4: - ret = (Sockaddr *)new Sockaddr4; - break; - case HNT_CONTIGUOUS_V6: - case HNT_IOV_V6: - ret = (Sockaddr *)new Sockaddr6; - break; - default: - throw errors::MalformedNameException(); - } - - if (hicn_name_to_sockaddr_address((hicn_name_t *)&name_, ret) < 0) { - throw errors::MalformedNameException(); - } - - return std::shared_ptr<Sockaddr>(ret); -} - ip_prefix_t Name::toIpAddress() const { ip_prefix_t ret; std::memset(&ret, 0, sizeof(ret)); @@ -195,8 +177,8 @@ int Name::getAddressFamily() const { return ret; } -void Name::copyToDestination(uint8_t *destination, bool include_suffix) const { - if (hicn_name_copy_to_destination(destination, &name_, include_suffix) < 0) { +void Name::copyPrefixToDestination(uint8_t *destination) const { + if (hicn_name_copy_prefix_to_destination(destination, &name_) < 0) { throw errors::RuntimeException( "Impossibe to copy the name into the " "provided destination"); diff --git a/libtransport/src/core/packet.cc b/libtransport/src/core/packet.cc index 51337201f..df27444af 100644 --- a/libtransport/src/core/packet.cc +++ b/libtransport/src/core/packet.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -41,13 +41,6 @@ Packet::Packet(Format format, std::size_t additional_header_size) setFormat(format_, additional_header_size); } -Packet::Packet(MemBuf &&buffer) - : utils::MemBuf(std::move(buffer)), - packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), - header_offset_(0), - format_(getFormatFromBuffer(data(), length())), - payload_type_(PayloadType::UNSPECIFIED) {} - Packet::Packet(CopyBufferOp, const uint8_t *buffer, std::size_t size) : utils::MemBuf(COPY_BUFFER, buffer, size), packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), @@ -74,11 +67,11 @@ Packet::Packet(CreateOp, uint8_t *buffer, std::size_t length, std::size_t size, setFormat(format_, additional_header_size); } -Packet::Packet(const Packet &other) - : utils::MemBuf(other), +Packet::Packet(MemBuf &&buffer) + : utils::MemBuf(std::move(buffer)), packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), - header_offset_(other.header_offset_), - format_(other.format_), + header_offset_(0), + format_(getFormatFromBuffer(data(), length())), payload_type_(PayloadType::UNSPECIFIED) {} Packet::Packet(Packet &&other) @@ -92,6 +85,13 @@ Packet::Packet(Packet &&other) other.header_offset_ = 0; } +Packet::Packet(const Packet &other) + : utils::MemBuf(other), + packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), + header_offset_(other.header_offset_), + format_(other.format_), + payload_type_(PayloadType::UNSPECIFIED) {} + Packet::~Packet() {} Packet &Packet::operator=(const Packet &other) { @@ -103,29 +103,19 @@ Packet &Packet::operator=(const Packet &other) { return *this; } -std::size_t Packet::getHeaderSizeFromBuffer(Format format, - const uint8_t *buffer) { - size_t header_length; - - if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, - &header_length) < 0) { - throw errors::MalformedPacketException(); - } - - return header_length; +std::shared_ptr<utils::MemBuf> Packet::acquireMemBufReference() { + return std::static_pointer_cast<utils::MemBuf>(shared_from_this()); } -bool Packet::isInterest(const uint8_t *buffer) { - bool is_interest = false; - - if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece(HF_INET6_TCP, - (const hicn_header_t *)buffer, - &is_interest) < 0)) { - throw errors::RuntimeException( - "Impossible to retrieve ece flag from packet"); +Packet::Format Packet::getFormat() const { + // We check packet start because after a movement it will result in a nullptr + if (format_ == HF_UNSPEC && length()) { + if (hicn_packet_get_format(packet_start_, &format_) < 0) { + LOG(ERROR) << "Unexpected packet format HF_UNSPEC."; + } } - return !is_interest; + return format_; } void Packet::setFormat(Packet::Format format, @@ -136,47 +126,43 @@ void Packet::setFormat(Packet::Format format, } auto header_size = getHeaderSizeFromFormat(format_); - assert(header_size <= tailroom()); + DCHECK(header_size <= tailroom()); append(header_size); - assert(additional_header_size <= tailroom()); + DCHECK(additional_header_size <= tailroom()); append(additional_header_size); header_offset_ = length(); } -bool Packet::isInterest() { return Packet::isInterest(data()); } +PayloadType Packet::getPayloadType() const { + if (payload_type_ == PayloadType::UNSPECIFIED) { + hicn_payload_type_t ret; -std::size_t Packet::getPayloadSizeFromBuffer(Format format, - const uint8_t *buffer) { - std::size_t payload_length; - if (TRANSPORT_EXPECT_FALSE( - hicn_packet_get_payload_length(format, (hicn_header_t *)buffer, - &payload_length) < 0)) { - throw errors::MalformedPacketException(); + if (hicn_packet_get_payload_type(format_, packet_start_, &ret) < 0) { + throw errors::RuntimeException("Impossible to retrieve payload type."); + } + + payload_type_ = (PayloadType)ret; } - return payload_length; + return payload_type_; } -std::size_t Packet::payloadSize() const { - std::size_t ret = 0; - - if (length()) { - ret = getPayloadSizeFromBuffer(format_, - reinterpret_cast<uint8_t *>(packet_start_)); +Packet &Packet::setPayloadType(PayloadType payload_type) { + if (hicn_packet_set_payload_type(format_, packet_start_, + hicn_payload_type_t(payload_type)) < 0) { + throw errors::RuntimeException("Error setting payload type of the packet."); } - return ret; + payload_type_ = payload_type; + return *this; } -std::size_t Packet::headerSize() const { - if (header_offset_ == 0 && length()) { - const_cast<Packet *>(this)->header_offset_ = getHeaderSizeFromBuffer( - format_, reinterpret_cast<uint8_t *>(packet_start_)); - } - - return header_offset_; +std::unique_ptr<utils::MemBuf> Packet::getPayload() const { + auto ret = clone(); + ret->trimStart(headerSize()); + return ret; } Packet &Packet::appendPayload(std::unique_ptr<utils::MemBuf> &&payload) { @@ -196,12 +182,56 @@ Packet &Packet::appendPayload(const uint8_t *buffer, std::size_t length) { return *this; } -std::unique_ptr<utils::MemBuf> Packet::getPayload() const { - auto ret = clone(); - ret->trimStart(headerSize()); +std::size_t Packet::headerSize() const { + if (header_offset_ == 0 && length()) { + const_cast<Packet *>(this)->header_offset_ = getHeaderSizeFromBuffer( + format_, reinterpret_cast<uint8_t *>(packet_start_)); + } + + return header_offset_; +} + +std::size_t Packet::payloadSize() const { + std::size_t ret = 0; + + if (length()) { + ret = getPayloadSizeFromBuffer(format_, + reinterpret_cast<uint8_t *>(packet_start_)); + } + return ret; } +auth::CryptoHash Packet::computeDigest(auth::CryptoHashType algorithm) const { + auth::CryptoHash hash; + hash.setType(algorithm); + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format_, packet_start_, &header_copy, false); + const_cast<Packet *>(this)->resetForHash(); + + hash.computeDigest(this); + hicn_packet_copy_header(format_, &header_copy, packet_start_, false); + + return hash; +} + +void Packet::reset() { + clear(); + packet_start_ = reinterpret_cast<hicn_header_t *>(writableData()); + header_offset_ = 0; + format_ = HF_UNSPEC; + payload_type_ = PayloadType::UNSPECIFIED; + name_.clear(); + + if (isChained()) { + separateChain(next(), prev()); + } +} + +bool Packet::isInterest() { return Packet::isInterest(data(), format_); } + Packet &Packet::updateLength(std::size_t length) { std::size_t total_length = length; @@ -221,47 +251,6 @@ Packet &Packet::updateLength(std::size_t length) { return *this; } -PayloadType Packet::getPayloadType() const { - if (payload_type_ == PayloadType::UNSPECIFIED) { - hicn_payload_type_t ret; - if (hicn_packet_get_payload_type(packet_start_, &ret) < 0) { - throw errors::RuntimeException("Impossible to retrieve payload type."); - } - - payload_type_ = (PayloadType)ret; - } - - return payload_type_; -} - -Packet &Packet::setPayloadType(PayloadType payload_type) { - if (hicn_packet_set_payload_type(packet_start_, - hicn_payload_type_t(payload_type)) < 0) { - throw errors::RuntimeException("Error setting payload type of the packet."); - } - - payload_type_ = payload_type; - - return *this; -} - -Packet::Format Packet::getFormat() const { - /** - * We check packet start because after a movement it will result in a nullptr - */ - if (format_ == HF_UNSPEC && length()) { - if (hicn_packet_get_format(packet_start_, &format_) < 0) { - LOG(ERROR) << "Unexpected packet format HF_UNSPEC."; - } - } - - return format_; -} - -std::shared_ptr<utils::MemBuf> Packet::acquireMemBufReference() { - return std::static_pointer_cast<utils::MemBuf>(shared_from_this()); -} - void Packet::dump() const { LOG(INFO) << "HEADER -- Length: " << headerSize(); LOG(INFO) << "PAYLOAD -- Length: " << payloadSize(); @@ -274,180 +263,42 @@ void Packet::dump() const { } while (current != this); } -void Packet::dump(uint8_t *buffer, std::size_t length) { - hicn_packet_dump(buffer, length); -} - -void Packet::setSignatureSize(std::size_t size_bytes) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_signature_size(format_, packet_start_, size_bytes); - - if (ret < 0) { - throw errors::RuntimeException("Error setting signature size."); - } -} - -void Packet::setSignatureSizeGap(std::size_t size_bytes) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_signature_gap(format_, packet_start_, - (uint8_t)size_bytes); - - if (ret < 0) { - throw errors::RuntimeException("Error setting signature size."); - } -} - -uint8_t *Packet::getSignature() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - uint8_t *signature; - int ret = hicn_packet_get_signature(format_, packet_start_, &signature); - - if (ret < 0) { - throw errors::RuntimeException("Error getting signature."); - } - - return signature; -} - -void Packet::setSignatureTimestamp(const uint64_t ×tamp) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = - hicn_packet_set_signature_timestamp(format_, packet_start_, timestamp); - - if (ret < 0) { - throw errors::RuntimeException("Error setting the signature timestamp."); - } -} - -uint64_t Packet::getSignatureTimestamp() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - uint64_t return_value; - int ret = hicn_packet_get_signature_timestamp(format_, packet_start_, - &return_value); - - if (ret < 0) { - throw errors::RuntimeException("Error getting the signature timestamp."); - } - - return return_value; -} - -void Packet::setValidationAlgorithm( - const auth::CryptoSuite &validation_algorithm) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_validation_algorithm(format_, packet_start_, - uint8_t(validation_algorithm)); - - if (ret < 0) { - throw errors::RuntimeException("Error setting the validation algorithm."); - } -} - -auth::CryptoSuite Packet::getValidationAlgorithm() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - uint8_t return_value; - int ret = hicn_packet_get_validation_algorithm(format_, packet_start_, - &return_value); - - if (ret < 0) { - throw errors::RuntimeException("Error getting the validation algorithm."); - } - - return auth::CryptoSuite(return_value); -} - -void Packet::setKeyId(const auth::KeyId &key_id) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_key_id(format_, packet_start_, key_id.first); - - if (ret < 0) { - throw errors::RuntimeException("Error setting the key id."); - } -} - -auth::KeyId Packet::getKeyId() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } +void Packet::setChecksum() { + if (_is_tcp(format_)) { + uint16_t partial_csum = + csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); - auth::KeyId return_value; - int ret = hicn_packet_get_key_id(format_, packet_start_, &return_value.first, - &return_value.second); + for (utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); + } - if (ret < 0) { - throw errors::RuntimeException("Error getting the validation algorithm."); + if (hicn_packet_compute_header_checksum(format_, packet_start_, + partial_csum) < 0) { + throw errors::MalformedPacketException(); + } } - - return return_value; -} - -auth::CryptoHash Packet::computeDigest(auth::CryptoHashType algorithm) const { - auth::CryptoHash hash; - hash.setType(algorithm); - - // Copy IP+TCP/ICMP header before zeroing them - hicn_header_t header_copy; - - hicn_packet_copy_header(format_, packet_start_, &header_copy, false); - - const_cast<Packet *>(this)->resetForHash(); - - hash.computeDigest(this); - hicn_packet_copy_header(format_, &header_copy, packet_start_, false); - - return hash; } bool Packet::checkIntegrity() const { - uint16_t partial_csum = - csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); + if (_is_tcp(format_)) { + uint16_t partial_csum = + csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); - for (const utils::MemBuf *current = next(); current != this; - current = current->next()) { - partial_csum = csum(current->data(), current->length(), ~partial_csum); - } + for (const utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); + } - if (hicn_packet_check_integrity_no_payload(format_, packet_start_, - partial_csum) < 0) { - return false; + if (hicn_packet_check_integrity_no_payload(format_, packet_start_, + partial_csum) < 0) { + return false; + } } return true; } -void Packet::prependPayload(const uint8_t **buffer, std::size_t *size) { - auto last = prev(); - auto to_copy = std::min(*size, last->tailroom()); - std::memcpy(last->writableTail(), *buffer, to_copy); - last->append(to_copy); - *size -= to_copy; - *buffer += to_copy; -} - Packet &Packet::setSyn() { if (hicn_packet_set_syn(format_, packet_start_) < 0) { throw errors::RuntimeException("Error setting syn bit in the packet."); @@ -553,29 +404,23 @@ Packet &Packet::resetFlags() { resetAck(); resetRst(); resetFin(); - return *this; } std::string Packet::printFlags() const { std::string flags; - if (testSyn()) { flags += "S"; } - if (testAck()) { flags += "A"; } - if (testRst()) { flags += "R"; } - if (testFin()) { flags += "F"; } - return flags; } @@ -634,6 +479,245 @@ uint8_t Packet::getTTL() const { return hops; } +bool Packet::hasAH() const { return _is_ah(format_); } + +std::vector<uint8_t> Packet::getSignature() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint8_t *signature; + int ret = hicn_packet_get_signature(format_, packet_start_, &signature); + + if (ret < 0) { + throw errors::RuntimeException("Error getting signature."); + } + + return std::vector<uint8_t>(signature, signature + getSignatureFieldSize()); +} + +std::size_t Packet::getSignatureFieldSize() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + size_t field_size; + int ret = hicn_packet_get_signature_size(format_, packet_start_, &field_size); + if (ret < 0) { + throw errors::RuntimeException("Error reading signature field size"); + } + return field_size; +} + +std::size_t Packet::getSignatureSize() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + size_t padding; + int ret = hicn_packet_get_signature_padding(format_, packet_start_, &padding); + if (ret < 0) { + throw errors::RuntimeException("Error reading signature padding"); + } + + size_t size = getSignatureFieldSize() - padding; + if (size < 0) { + throw errors::RuntimeException("Error reading signature size"); + } + + return size; +} + +uint64_t Packet::getSignatureTimestamp() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint64_t timestamp; + int ret = + hicn_packet_get_signature_timestamp(format_, packet_start_, ×tamp); + if (ret < 0) { + throw errors::RuntimeException("Error getting the signature timestamp."); + } + return timestamp; +} + +auth::KeyId Packet::getKeyId() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + auth::KeyId key_id; + int ret = hicn_packet_get_key_id(format_, packet_start_, &key_id.first, + &key_id.second); + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + return key_id; +} + +auth::CryptoSuite Packet::getValidationAlgorithm() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint8_t return_value; + int ret = hicn_packet_get_validation_algorithm(format_, packet_start_, + &return_value); + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + return auth::CryptoSuite(return_value); +} + +void Packet::setSignature(const std::vector<uint8_t> &signature) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint8_t *signature_field; + int ret = hicn_packet_get_signature(format_, packet_start_, &signature_field); + if (ret < 0) { + throw errors::RuntimeException("Error getting signature."); + } + memcpy(signature_field, signature.data(), signature.size()); +} + +void Packet::setSignatureFieldSize(std::size_t size) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = hicn_packet_set_signature_size(format_, packet_start_, size); + if (ret < 0) { + throw errors::RuntimeException("Error setting signature size."); + } +} + +void Packet::setSignatureSize(std::size_t size) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + size_t padding = getSignatureFieldSize() - size; + if (padding < 0) { + throw errors::RuntimeException("Error setting signature padding."); + } + + int ret = hicn_packet_set_signature_padding(format_, packet_start_, padding); + if (ret < 0) { + throw errors::RuntimeException("Error setting signature padding."); + } +} + +void Packet::setSignatureTimestamp(const uint64_t ×tamp) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = + hicn_packet_set_signature_timestamp(format_, packet_start_, timestamp); + if (ret < 0) { + throw errors::RuntimeException("Error setting the signature timestamp."); + } +} + +void Packet::setKeyId(const auth::KeyId &key_id) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = hicn_packet_set_key_id(format_, packet_start_, key_id.first); + if (ret < 0) { + throw errors::RuntimeException("Error setting the key id."); + } +} + +void Packet::setValidationAlgorithm( + const auth::CryptoSuite &validation_algorithm) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = hicn_packet_set_validation_algorithm(format_, packet_start_, + uint8_t(validation_algorithm)); + if (ret < 0) { + throw errors::RuntimeException("Error setting the validation algorithm."); + } +} + +Packet::Format Packet::toAHFormat(const Format &format) { + return hicn_get_ah_format(format); +} + +Packet::Format Packet::getFormatFromBuffer(const uint8_t *buffer, + std::size_t /* length */) { + Packet::Format format = HF_UNSPEC; + hicn_packet_get_format((const hicn_header_t *)buffer, &format); + return format; +} + +std::size_t Packet::getHeaderSizeFromFormat(Format format, + std::size_t signature_size) { + std::size_t header_length; + hicn_packet_get_header_length_from_format(format, &header_length); + int is_ah = _is_ah(format); + return is_ah * (header_length + signature_size) + (!is_ah) * header_length; +} + +std::size_t Packet::getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer) { + size_t header_length; + + if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, + &header_length) < 0) { + throw errors::MalformedPacketException(); + } + + return header_length; +} + +std::size_t Packet::getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer) { + std::size_t payload_length; + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_get_payload_length(format, (hicn_header_t *)buffer, + &payload_length) < 0)) { + throw errors::MalformedPacketException(); + } + + return payload_length; +} + +bool Packet::isInterest(const uint8_t *buffer, Format format) { + int is_interest = 0; + + if (TRANSPORT_EXPECT_FALSE(format == Format::HF_UNSPEC)) { + format = getFormatFromBuffer(buffer, /* Unused length */ 128); + } + + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_is_interest(format, (const hicn_header_t *)buffer, + &is_interest) < 0)) { + throw errors::RuntimeException("Error reading ece flag from packet"); + } + + return is_interest; +} + +void Packet::dump(uint8_t *buffer, std::size_t length) { + hicn_packet_dump(buffer, length); +} + +void Packet::prependPayload(const uint8_t **buffer, std::size_t *size) { + auto last = prev(); + auto to_copy = std::min(*size, last->tailroom()); + std::memcpy(last->writableTail(), *buffer, to_copy); + last->append(to_copy); + *size -= to_copy; + *buffer += to_copy; +} + } // end namespace core } // end namespace transport diff --git a/libtransport/src/core/pending_interest.cc b/libtransport/src/core/pending_interest.cc deleted file mode 100644 index fbe98cab5..000000000 --- a/libtransport/src/core/pending_interest.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2017-2019 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 <core/pending_interest.h> - -namespace transport { - -namespace core { - -PendingInterest::PendingInterest() - : interest_(nullptr, nullptr), - timer_(), - on_content_object_callback_(), - on_interest_timeout_callback_() {} - -PendingInterest::PendingInterest(Interest::Ptr &&interest, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), - on_content_object_callback_(), - on_interest_timeout_callback_() {} - -PendingInterest::PendingInterest( - Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object, - OnInterestTimeoutCallback &&on_interest_timeout, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), - on_content_object_callback_(std::move(on_content_object)), - on_interest_timeout_callback_(std::move(on_interest_timeout)) {} - -PendingInterest::~PendingInterest() {} - -void PendingInterest::cancelTimer() { timer_->cancel(); } - -void PendingInterest::setInterest(Interest::Ptr &&interest) { - interest_ = std::move(interest); -} - -Interest::Ptr &&PendingInterest::getInterest() { return std::move(interest_); } - -const OnContentObjectCallback &PendingInterest::getOnDataCallback() const { - return on_content_object_callback_; -} - -void PendingInterest::setOnContentObjectCallback( - OnContentObjectCallback &&on_content_object) { - PendingInterest::on_content_object_callback_ = on_content_object; -} - -const OnInterestTimeoutCallback &PendingInterest::getOnTimeoutCallback() const { - return on_interest_timeout_callback_; -} - -void PendingInterest::setOnTimeoutCallback( - OnInterestTimeoutCallback &&on_interest_timeout) { - PendingInterest::on_interest_timeout_callback_ = on_interest_timeout; -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/core/pending_interest.h b/libtransport/src/core/pending_interest.h index 99a8bd327..f8a4ba10e 100644 --- a/libtransport/src/core/pending_interest.h +++ b/libtransport/src/core/pending_interest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -48,19 +48,17 @@ class PendingInterest { // on_content_object_callback_(), // on_interest_timeout_callback_() {} - PendingInterest(Interest::Ptr &&interest, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), + PendingInterest(asio::io_service &io_service, const Interest::Ptr &interest) + : interest_(interest), + timer_(io_service), on_content_object_callback_(), on_interest_timeout_callback_() {} - PendingInterest(Interest::Ptr &&interest, + PendingInterest(asio::io_service &io_service, const Interest::Ptr &interest, OnContentObjectCallback &&on_content_object, - OnInterestTimeoutCallback &&on_interest_timeout, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), + OnInterestTimeoutCallback &&on_interest_timeout) + : interest_(interest), + timer_(io_service), on_content_object_callback_(std::move(on_content_object)), on_interest_timeout_callback_(std::move(on_interest_timeout)) {} @@ -68,12 +66,12 @@ class PendingInterest { template <typename Handler> TRANSPORT_ALWAYS_INLINE void startCountdown(Handler &&cb) { - timer_->expires_from_now( + timer_.expires_from_now( std::chrono::milliseconds(interest_->getLifetime())); - timer_->async_wait(std::forward<Handler &&>(cb)); + timer_.async_wait(std::forward<Handler &&>(cb)); } - TRANSPORT_ALWAYS_INLINE void cancelTimer() { timer_->cancel(); } + TRANSPORT_ALWAYS_INLINE void cancelTimer() { timer_.cancel(); } TRANSPORT_ALWAYS_INLINE Interest::Ptr &&getInterest() { return std::move(interest_); @@ -105,7 +103,7 @@ class PendingInterest { private: Interest::Ptr interest_; - std::unique_ptr<asio::steady_timer> timer_; + asio::steady_timer timer_; OnContentObjectCallback on_content_object_callback_; OnInterestTimeoutCallback on_interest_timeout_callback_; }; diff --git a/libtransport/src/core/portal.cc b/libtransport/src/core/portal.cc index c4c0cf8ba..d8e8d78ea 100644 --- a/libtransport/src/core/portal.cc +++ b/libtransport/src/core/portal.cc @@ -30,11 +30,11 @@ namespace core { #ifdef ANDROID static const constexpr char default_module[] = ""; #elif defined(MACINTOSH) -static const constexpr char default_module[] = "hicnlight_module.dylib"; +static const constexpr char default_module[] = "hicnlightng_module.dylib"; #elif defined(LINUX) -static const constexpr char default_module[] = "hicnlight_module.so"; +static const constexpr char default_module[] = "hicnlightng_module.so"; #elif defined(WINDOWS) -static const constexpr char default_module[] = "hicnlight_module.lib"; +static const constexpr char default_module[] = "hicnlightng_module.lib"; #endif IoModuleConfiguration Portal::conf_; @@ -57,7 +57,7 @@ std::string Portal::defaultIoModule() { void Portal::getModuleConfiguration(ConfigurationObject& object, std::error_code& ec) { - assert(object.getKey() == io_module_section); + DCHECK(object.getKey() == io_module_section); auto conf = dynamic_cast<const IoModuleConfiguration&>(object); conf = conf_; @@ -103,7 +103,7 @@ std::string getIoModulePath(const std::string& name, void Portal::setModuleConfiguration(const ConfigurationObject& object, std::error_code& ec) { - assert(object.getKey() == io_module_section); + DCHECK(object.getKey() == io_module_section); const IoModuleConfiguration& conf = dynamic_cast<const IoModuleConfiguration&>(object); diff --git a/libtransport/src/core/portal.h b/libtransport/src/core/portal.h index f6a9ce85b..aae4c573e 100644 --- a/libtransport/src/core/portal.h +++ b/libtransport/src/core/portal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,6 +15,7 @@ #pragma once +#include <core/global_workers.h> #include <core/pending_interest.h> #include <glog/logging.h> #include <hicn/transport/config.h> @@ -28,6 +29,7 @@ #include <hicn/transport/interfaces/global_conf_interface.h> #include <hicn/transport/interfaces/portal.h> #include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/event_thread.h> #include <hicn/transport/utils/fixed_block_allocator.h> #include <future> @@ -54,11 +56,11 @@ class HandlerMemory { HandlerMemory(const HandlerMemory &) = delete; HandlerMemory &operator=(const HandlerMemory &) = delete; - TRANSPORT_ALWAYS_INLINE void *allocate(std::size_t size) { + void *allocate(std::size_t size) { return utils::FixedBlockAllocator<128, 8192>::getInstance().allocateBlock(); } - TRANSPORT_ALWAYS_INLINE void deallocate(void *pointer) { + void deallocate(void *pointer) { utils::FixedBlockAllocator<128, 8192>::getInstance().deallocateBlock( pointer); } @@ -69,13 +71,9 @@ class HandlerMemory { HandlerMemory(const HandlerMemory &) = delete; HandlerMemory &operator=(const HandlerMemory &) = delete; - TRANSPORT_ALWAYS_INLINE void *allocate(std::size_t size) { - return ::operator new(size); - } + void *allocate(std::size_t size) { return ::operator new(size); } - TRANSPORT_ALWAYS_INLINE void deallocate(void *pointer) { - ::operator delete(pointer); - } + void deallocate(void *pointer) { ::operator delete(pointer); } #endif }; @@ -92,21 +90,19 @@ class HandlerAllocator { HandlerAllocator(const HandlerAllocator<U> &other) noexcept : memory_(other.memory_) {} - TRANSPORT_ALWAYS_INLINE bool operator==( - const HandlerAllocator &other) const noexcept { + bool operator==(const HandlerAllocator &other) const noexcept { return &memory_ == &other.memory_; } - TRANSPORT_ALWAYS_INLINE bool operator!=( - const HandlerAllocator &other) const noexcept { + bool operator!=(const HandlerAllocator &other) const noexcept { return &memory_ != &other.memory_; } - TRANSPORT_ALWAYS_INLINE T *allocate(std::size_t n) const { + T *allocate(std::size_t n) const { return static_cast<T *>(memory_.allocate(sizeof(T) * n)); } - TRANSPORT_ALWAYS_INLINE void deallocate(T *p, std::size_t /*n*/) const { + void deallocate(T *p, std::size_t /*n*/) const { return memory_.deallocate(p); } @@ -135,7 +131,7 @@ class CustomAllocatorHandler { } template <typename... Args> - void operator()(Args &&... args) { + void operator()(Args &&...args) { handler_(std::forward<Args>(args)...); } @@ -151,49 +147,16 @@ inline CustomAllocatorHandler<Handler> makeCustomAllocatorHandler( return CustomAllocatorHandler<Handler>(m, h); } -class Pool { - public: - Pool(asio::io_service &io_service) : io_service_(io_service) { - increasePendingInterestPool(); - } - - TRANSPORT_ALWAYS_INLINE void increasePendingInterestPool() { - // Create pool of pending interests to reuse - for (uint32_t i = 0; i < pit_size; i++) { - pending_interests_pool_.add(new PendingInterest( - Interest::Ptr(nullptr), - std::make_unique<asio::steady_timer>(io_service_))); - } - } - PendingInterest::Ptr getPendingInterest() { - auto res = pending_interests_pool_.get(); - while (TRANSPORT_EXPECT_FALSE(!res.first)) { - increasePendingInterestPool(); - res = pending_interests_pool_.get(); - } - - return std::move(res.second); - } - - private: - utils::ObjectPool<PendingInterest> pending_interests_pool_; - asio::io_service &io_service_; -}; - } // namespace portal_details class PortalConfiguration; -using PendingInterestHashTable = - std::unordered_map<uint32_t, PendingInterest::Ptr>; - -using interface::BindConfig; +using PendingInterestHashTable = std::unordered_map<uint32_t, PendingInterest>; /** * Portal is a opaque class which is used for sending/receiving interest/data - * packets over multiple kind of connector. The connector itself is defined by - * the template ForwarderInt, which is resolved at compile time. It is then not - * possible to decide at runtime what the connector will be. + * packets over multiple kind of io_modules. The io_module itself is an external + * module loaded at runtime. * * The tasks performed by portal are the following: * - Sending/Receiving Interest packets @@ -210,22 +173,16 @@ using interface::BindConfig; * users of this class. */ -class Portal { - public: - using ConsumerCallback = interface::Portal::ConsumerCallback; - using ProducerCallback = interface::Portal::ProducerCallback; - - friend class PortalConfiguration; - - Portal() : Portal(internal_io_service_) {} +class Portal : public ::utils::NonCopyable, + public std::enable_shared_from_this<Portal> { + private: + Portal() : Portal(GlobalWorkers::getInstance().getWorker()) {} - Portal(asio::io_service &io_service) + Portal(::utils::EventThread &worker) : io_module_(nullptr, [](IoModule *module) { IoModule::unload(module); }), - io_service_(io_service), - packet_pool_(io_service), + worker_(worker), app_name_("libtransport_application"), - consumer_callback_(nullptr), - producer_callback_(nullptr), + transport_callback_(nullptr), is_consumer_(false) { /** * This workaroung allows to initialize memory for packet buffers *before* @@ -235,58 +192,105 @@ class Portal { */ PacketManager<>::getInstance(); } + + public: + using TransportCallback = interface::Portal::TransportCallback; + friend class PortalConfiguration; + + static std::shared_ptr<Portal> createShared() { + return std::shared_ptr<Portal>(new Portal()); + } + + static std::shared_ptr<Portal> createShared(::utils::EventThread &worker) { + return std::shared_ptr<Portal>(new Portal(worker)); + } + + bool isConnected() const { return io_module_.get() != nullptr; } + /** - * Set the consumer callback. + * Set the transport callback. Must be called from the same worker thread. * - * @param consumer_callback - The pointer to the ConsumerCallback object. + * @param consumer_callback - The pointer to the TransportCallback object. */ - void setConsumerCallback(ConsumerCallback *consumer_callback) { - consumer_callback_ = consumer_callback; + void registerTransportCallback(TransportCallback *transport_callback) { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + transport_callback_ = transport_callback; } /** - * Set the producer callback. - * - * @param producer_callback - The pointer to the ProducerCallback object. + * Unset the consumer callback. Must be called from the same worker thread. */ - void setProducerCallback(ProducerCallback *producer_callback) { - producer_callback_ = producer_callback; + void unregisterTransportCallback() { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + transport_callback_ = nullptr; } /** - * Specify the output interface to use. This method will be useful in a future - * scenario where the library will be able to forward packets without + * Specify the output interface to use. This method will be useful in a + * future scenario where the library will be able to forward packets without * connecting to a local forwarder. Now it is not used. * * @param output_interface - The output interface to use for * forwarding/receiving packets. */ - TRANSPORT_ALWAYS_INLINE void setOutputInterface( - const std::string &output_interface) { - io_module_->setOutputInterface(output_interface); + void setOutputInterface(const std::string &output_interface) { + if (io_module_) { + io_module_->setOutputInterface(output_interface); + } } /** * Connect the transport to the local hicn forwarder. * - * @param is_consumer - Boolean specifying if the application on top of portal - * is a consumer or a producer. + * @param is_consumer - Boolean specifying if the application on top of + * portal is a consumer or a producer. */ - TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { - if (!io_module_) { - pending_interest_hash_table_.reserve(portal_details::pit_size); - io_module_.reset(IoModule::load(io_module_path_.c_str())); - - CHECK(io_module_); - - io_module_->init(std::bind(&Portal::processIncomingMessages, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - std::bind(&Portal::setLocalRoutes, this), io_service_, - app_name_); - io_module_->connect(is_consumer); - is_consumer_ = is_consumer; + void connect(bool is_consumer = true) { + if (isConnected()) { + return; } + + worker_.addAndWaitForExecution([this, is_consumer]() { + if (!io_module_) { + pending_interest_hash_table_.reserve(portal_details::pit_size); + io_module_.reset(IoModule::load(io_module_path_.c_str())); + + CHECK(io_module_); + + std::weak_ptr<Portal> self(shared_from_this()); + + io_module_->init( + [self](Connector *c, const std::vector<utils::MemBuf::Ptr> &buffers, + const std::error_code &ec) { + if (auto ptr = self.lock()) { + ptr->processIncomingMessages(c, buffers, ec); + } + }, + [self](Connector *c, const std::error_code &ec) { + if (!ec) { + return; + } + auto ptr = self.lock(); + if (ptr && ptr->transport_callback_) { + ptr->transport_callback_->onError(ec); + } + }, + [self](Connector *c, const std::error_code &ec) { + auto ptr = self.lock(); + if (ptr) { + if (ec && ptr->transport_callback_) { + ptr->transport_callback_->onError(ec); + return; + } + ptr->setLocalRoutes(); + } + }, + worker_.getIoService(), app_name_); + + io_module_->connect(is_consumer); + is_consumer_ = is_consumer; + } + }); } /** @@ -295,18 +299,13 @@ class Portal { ~Portal() { killConnection(); } /** - * Compute name hash - */ - TRANSPORT_ALWAYS_INLINE uint32_t getHash(const Name &name) { - return name.getHash32(false) + name.getSuffix(); - } - - /** * Check if there is already a pending interest for a given name. * * @param name - The interest name. */ - TRANSPORT_ALWAYS_INLINE bool interestIsPending(const Name &name) { + bool interestIsPending(const Name &name) { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + auto it = pending_interest_hash_table_.find(getHash(name)); if (it != pending_interest_hash_table_.end()) { return true; @@ -321,19 +320,21 @@ class Portal { * @param interest - The pointer to the interest. The ownership of the * interest is transferred by the caller to portal. * - * @param on_content_object_callback - If the caller wishes to use a different - * callback to be called for this interest, it can set this parameter. - * Otherwise ConsumerCallback::onContentObject will be used. + * @param on_content_object_callback - If the caller wishes to use a + * different callback to be called for this interest, it can set this + * parameter. Otherwise ConsumerCallback::onContentObject will be used. * * @param on_interest_timeout_callback - If the caller wishes to use a * different callback to be called for this interest, it can set this * parameter. Otherwise ConsumerCallback::onTimeout will be used. */ - TRANSPORT_ALWAYS_INLINE void sendInterest( + void sendInterest( Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object_callback = UNSET_CALLBACK, OnInterestTimeoutCallback &&on_interest_timeout_callback = UNSET_CALLBACK) { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + // Send it interest->encodeSuffixes(); io_module_->send(*interest); @@ -346,35 +347,42 @@ class Portal { uint32_t counter = 0; // Set timers do { - auto pending_interest = packet_pool_.getPendingInterest(); - pending_interest->setInterest(interest); + if (suffix) { + hash = initial_hash + *suffix; + seq = *suffix; + suffix++; + } + + auto it = pending_interest_hash_table_.find(hash); + PendingInterest *pending_interest = nullptr; + if (it != pending_interest_hash_table_.end()) { + it->second.cancelTimer(); + pending_interest = &it->second; + pending_interest->setInterest(interest); + } else { + auto pend_int = pending_interest_hash_table_.try_emplace( + hash, worker_.getIoService(), interest); + pending_interest = &pend_int.first->second; + } + pending_interest->setOnContentObjectCallback( std::move(on_content_object_callback)); pending_interest->setOnTimeoutCallback( std::move(on_interest_timeout_callback)); + auto self = weak_from_this(); pending_interest->startCountdown( portal_details::makeCustomAllocatorHandler( async_callback_memory_, - std::bind(&Portal::timerHandler, this, std::placeholders::_1, - hash, seq))); + [self, hash, seq](const std::error_code &ec) { + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + return; + } - auto it = pending_interest_hash_table_.find(hash); - if (it != pending_interest_hash_table_.end()) { - it->second->cancelTimer(); - - // Get reference to interest packet in order to have it destroyed. - auto _int = it->second->getInterest(); - it->second = std::move(pending_interest); - } else { - pending_interest_hash_table_[hash] = std::move(pending_interest); - } - - if (suffix) { - hash = initial_hash + *suffix; - seq = *suffix; - suffix++; - } + if (auto ptr = self.lock()) { + ptr->timerHandler(hash, seq); + } + })); } while (counter++ < n_suffixes); } @@ -385,214 +393,174 @@ class Portal { * @param ec - Error code which says whether the timer expired or has been * canceled upon data packet reception. * - * @param hash - The index of the interest in the pending interest hash table. + * @param hash - The index of the interest in the pending interest hash + * table. */ - TRANSPORT_ALWAYS_INLINE void timerHandler(const std::error_code &ec, - uint32_t hash, uint32_t seq) { - bool is_stopped = io_service_.stopped(); - if (TRANSPORT_EXPECT_FALSE(is_stopped)) { - return; - } + void timerHandler(uint32_t hash, uint32_t seq) { + PendingInterestHashTable::iterator it = + pending_interest_hash_table_.find(hash); + if (it != pending_interest_hash_table_.end()) { + PendingInterest &pend_interest = it->second; + auto _int = pend_interest.getInterest(); + auto callback = pend_interest.getOnTimeoutCallback(); + pending_interest_hash_table_.erase(it); + Name &name = const_cast<Name &>(_int->getName()); + name.setSuffix(seq); - if (TRANSPORT_EXPECT_TRUE(!ec)) { - PendingInterestHashTable::iterator it = - pending_interest_hash_table_.find(hash); - if (it != pending_interest_hash_table_.end()) { - PendingInterest::Ptr ptr = std::move(it->second); - pending_interest_hash_table_.erase(it); - auto _int = ptr->getInterest(); - Name &name = const_cast<Name &>(_int->getName()); - name.setSuffix(seq); - - if (ptr->getOnTimeoutCallback() != UNSET_CALLBACK) { - ptr->on_interest_timeout_callback_(_int, name); - } else if (consumer_callback_) { - consumer_callback_->onTimeout(_int, name); - } + if (callback != UNSET_CALLBACK) { + callback(_int, name); + } else if (transport_callback_) { + transport_callback_->onTimeout(_int, name); } } } /** - * Register a producer name to the local forwarder and optionally set the - * content store size in a per-face manner. - * - * @param config - The configuration for the local forwarder binding. - */ - TRANSPORT_ALWAYS_INLINE void bind(const BindConfig &config) { - assert(io_module_); - io_module_->setContentStoreSize(config.csReserved()); - served_namespaces_.push_back(config.prefix()); - setLocalRoutes(); - } - - /** - * Start the event loop. This function blocks here and calls the callback set - * by the application upon interest/data received or timeout. - */ - TRANSPORT_ALWAYS_INLINE void runEventsLoop() { - if (io_service_.stopped()) { - io_service_.reset(); // ensure that run()/poll() will do some work - } - - io_service_.run(); - } - - /** - * Run one event and return. - */ - TRANSPORT_ALWAYS_INLINE void runOneEvent() { - if (io_service_.stopped()) { - io_service_.reset(); // ensure that run()/poll() will do some work - } - - io_service_.run_one(); - } - - /** - * Send a data packet to the local forwarder. As opposite to sendInterest, the - * ownership of the content object is not transferred to the portal. + * Send a data packet to the local forwarder. * * @param content_object - The data packet. */ - TRANSPORT_ALWAYS_INLINE void sendContentObject( - ContentObject &content_object) { + void sendContentObject(ContentObject &content_object) { + DCHECK(io_module_); + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + io_module_->send(content_object); } /** - * Stop the event loop, canceling all the pending events in the event queue. - * - * Beware that stopping the event loop DOES NOT disconnect the transport from - * the local forwarder, the connector underneath will stay connected. + * Disconnect the transport from the local forwarder. */ - TRANSPORT_ALWAYS_INLINE void stopEventsLoop() { - if (!io_service_.stopped()) { - io_service_.dispatch([this]() { - clear(); - io_service_.stop(); - }); + void killConnection() { + if (TRANSPORT_EXPECT_TRUE(io_module_ != nullptr)) { + io_module_->closeConnection(); } } /** - * Disconnect the transport from the local forwarder. + * Clear the pending interest hash table. */ - TRANSPORT_ALWAYS_INLINE void killConnection() { - io_module_->closeConnection(); + void clear() { + worker_.tryRunHandlerNow([self{shared_from_this()}]() { self->doClear(); }); } /** - * Clear the pending interest hash table. + * Get a reference to the io_service object. */ - TRANSPORT_ALWAYS_INLINE void clear() { - if (!io_service_.stopped()) { - io_service_.dispatch(std::bind(&Portal::doClear, this)); - } else { - doClear(); - } - } + utils::EventThread &getThread() { return worker_; } /** - * Remove one pending interest. + * Register a route to the local forwarder. */ - TRANSPORT_ALWAYS_INLINE void clearOne(const Name &name) { - if (!io_service_.stopped()) { - io_service_.dispatch(std::bind(&Portal::doClearOne, this, name)); - } else { - doClearOne(name); - } + void registerRoute(const Prefix &prefix) { + std::weak_ptr<Portal> self = shared_from_this(); + worker_.tryRunHandlerNow([self, prefix]() { + if (auto ptr = self.lock()) { + auto ret = ptr->served_namespaces_.insert(prefix); + if (ret.second && ptr->io_module_ && ptr->io_module_->isConnected()) { + ptr->io_module_->registerRoute(prefix); + } + } + }); } /** - * Get a reference to the io_service object. + * Send a MAP-Me update to traverse NATs. */ - TRANSPORT_ALWAYS_INLINE asio::io_service &getIoService() { - return io_service_; + TRANSPORT_ALWAYS_INLINE void sendMapme() { + if (io_module_->isConnected()) { + io_module_->sendMapme(); + } } /** - * Register a route to the local forwarder. + * set forwarding strategy */ - TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { - served_namespaces_.push_back(prefix); + TRANSPORT_ALWAYS_INLINE void setForwardingStrategy(Prefix &prefix, + std::string &strategy) { if (io_module_->isConnected()) { - io_module_->registerRoute(prefix); + io_module_->setForwardingStrategy(prefix, strategy); } } /** * Check if the transport is connected to a forwarder or not */ - TRANSPORT_ALWAYS_INLINE bool isConnectedToFwd() { + bool isConnectedToFwd() { std::string mod = io_module_path_.substr(0, io_module_path_.find(".")); if (mod == "forwarder_module") return false; return true; } + auto &getServedNamespaces() { return served_namespaces_; } + private: /** - * Clear the pending interest hash table. + * Compute name hash */ - TRANSPORT_ALWAYS_INLINE void doClear() { - for (auto &pend_interest : pending_interest_hash_table_) { - pend_interest.second->cancelTimer(); - - // Get interest packet from pending interest and do nothing with it. It - // will get destroyed as it goes out of scope. - auto _int = pend_interest.second->getInterest(); - } - - pending_interest_hash_table_.clear(); + uint32_t getHash(const Name &name) { + return name.getHash32(false) + name.getSuffix(); } /** - * Remove one pending interest. + * Clear the pending interest hash table. */ - TRANSPORT_ALWAYS_INLINE void doClearOne(const Name &name) { - auto it = pending_interest_hash_table_.find(getHash(name)); - - if (it != pending_interest_hash_table_.end()) { - it->second->cancelTimer(); - - // Get interest packet from pending interest and do nothing with it. It - // will get destroyed as it goes out of scope. - auto _int = it->second->getInterest(); - - pending_interest_hash_table_.erase(it); + void doClear() { + for (auto &pend_interest : pending_interest_hash_table_) { + pend_interest.second.cancelTimer(); } + + pending_interest_hash_table_.clear(); } /** - * Callback called by the underlying connector upon reception of a packet from - * the local forwarder. + * Callback called by the underlying connector upon reception of a packet + * from the local forwarder. * * @param packet_buffer - The bytes of the packet. */ - TRANSPORT_ALWAYS_INLINE void processIncomingMessages( - Connector *c, utils::MemBuf &buffer, const std::error_code &ec) { - bool is_stopped = io_service_.stopped(); - if (TRANSPORT_EXPECT_FALSE(is_stopped)) { + void processIncomingMessages(Connector *c, + const std::vector<utils::MemBuf::Ptr> &buffers, + const std::error_code &ec) { + if (!transport_callback_) { return; } - if (TRANSPORT_EXPECT_FALSE(io_module_->isControlMessage(buffer.data()))) { - processControlMessage(buffer); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + // Error receiving from underlying infra. + if (transport_callback_) { + transport_callback_->onError(ec); + } + return; } - // The buffer is a base class for an interest or a content object - Packet &packet_buffer = static_cast<Packet &>(buffer); + for (auto &buffer_ptr : buffers) { + auto &buffer = *buffer_ptr; + + if (TRANSPORT_EXPECT_FALSE(io_module_->isControlMessage(buffer))) { + processControlMessage(buffer); + return; + } - auto format = packet_buffer.getFormat(); - if (TRANSPORT_EXPECT_TRUE(_is_tcp(format))) { - if (is_consumer_) { - processContentObject(static_cast<ContentObject &>(packet_buffer)); + auto format = Packet::getFormatFromBuffer(buffer.data(), buffer.length()); + if (TRANSPORT_EXPECT_TRUE(_is_cmpr(format) || _is_tcp(format))) { + // The buffer is a base class for an interest or a content object + Packet &packet_buffer = static_cast<Packet &>(buffer); + if (is_consumer_ && !packet_buffer.isInterest()) { + processContentObject(static_cast<ContentObject &>(packet_buffer)); + } else if (!is_consumer_ && packet_buffer.isInterest()) { + processInterest(static_cast<Interest &>(packet_buffer)); + } else { + auto packet_type = + packet_buffer.isInterest() ? "Interest" : "ContentObject"; + auto socket_type = is_consumer_ ? "consumer " : "producer "; + LOG(ERROR) << "Received a " << packet_type << " packet with name " + << packet_buffer.getName() << " in a " << socket_type + << " transport. Ignoring it."; + } } else { - processInterest(static_cast<Interest &>(packet_buffer)); + LOG(ERROR) << "Received not supported packet. Ignoring it."; } - } else { - LOG(ERROR) << "Received not supported packet. Ignoring it."; } } @@ -601,19 +569,21 @@ class Portal { * It register the prefixes in the served_namespaces_ list to the local * forwarder. */ - TRANSPORT_ALWAYS_INLINE void setLocalRoutes() { + void setLocalRoutes() { + DCHECK(io_module_); + DCHECK(io_module_->isConnected()); + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + for (auto &prefix : served_namespaces_) { - if (io_module_->isConnected()) { - io_module_->registerRoute(prefix); - } + io_module_->registerRoute(prefix); } } - TRANSPORT_ALWAYS_INLINE void processInterest(Interest &interest) { + void processInterest(Interest &interest) { // Interest for a producer DLOG_IF(INFO, VLOG_IS_ON(3)) << "processInterest " << interest.getName(); - if (TRANSPORT_EXPECT_TRUE(producer_callback_ != nullptr)) { - producer_callback_->onInterest(interest); + if (TRANSPORT_EXPECT_TRUE(transport_callback_ != nullptr)) { + transport_callback_->onInterest(interest); } } @@ -625,8 +595,7 @@ class Portal { * * @param content_object - The data packet */ - TRANSPORT_ALWAYS_INLINE void processContentObject( - ContentObject &content_object) { + void processContentObject(ContentObject &content_object) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "processContentObject " << content_object.getName(); uint32_t hash = getHash(content_object.getName()); @@ -635,15 +604,16 @@ class Portal { if (it != pending_interest_hash_table_.end()) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Found pending interest."; - PendingInterest::Ptr interest_ptr = std::move(it->second); + PendingInterest &pend_interest = it->second; + pend_interest.cancelTimer(); + auto _int = pend_interest.getInterest(); + auto callback = pend_interest.getOnDataCallback(); pending_interest_hash_table_.erase(it); - interest_ptr->cancelTimer(); - auto _int = interest_ptr->getInterest(); - if (interest_ptr->getOnDataCallback() != UNSET_CALLBACK) { - interest_ptr->on_content_object_callback_(*_int, content_object); - } else if (consumer_callback_) { - consumer_callback_->onContentObject(*_int, content_object); + if (callback != UNSET_CALLBACK) { + callback(*_int, content_object); + } else if (transport_callback_) { + transport_callback_->onContentObject(*_int, content_object); } } else { DLOG_IF(INFO, VLOG_IS_ON(3)) @@ -652,12 +622,11 @@ class Portal { } /** - * Process a control message. Control messages are different depending on the - * connector, then the forwarder_interface will do the job of understanding - * them. + * Process a control message. Control messages are different depending on + * the connector, then the forwarder_interface will do the job of + * understanding them. */ - TRANSPORT_ALWAYS_INLINE void processControlMessage( - utils::MemBuf &packet_buffer) { + void processControlMessage(utils::MemBuf &packet_buffer) { io_module_->processControlMessageReply(packet_buffer); } @@ -665,17 +634,14 @@ class Portal { portal_details::HandlerMemory async_callback_memory_; std::unique_ptr<IoModule, void (*)(IoModule *)> io_module_; - asio::io_service &io_service_; - asio::io_service internal_io_service_; - portal_details::Pool packet_pool_; + ::utils::EventThread &worker_; std::string app_name_; PendingInterestHashTable pending_interest_hash_table_; - std::list<Prefix> served_namespaces_; + std::set<Prefix> served_namespaces_; - ConsumerCallback *consumer_callback_; - ProducerCallback *producer_callback_; + TransportCallback *transport_callback_; bool is_consumer_; diff --git a/libtransport/src/core/prefix.cc b/libtransport/src/core/prefix.cc index d598cff75..4c1e191e9 100644 --- a/libtransport/src/core/prefix.cc +++ b/libtransport/src/core/prefix.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -99,6 +99,14 @@ void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, ip_prefix_.family = family; } +bool Prefix::operator<(const Prefix &other) const { + return ip_prefix_cmp(&ip_prefix_, &other.ip_prefix_) < 0; +} + +bool Prefix::operator==(const Prefix &other) const { + return ip_prefix_cmp(&ip_prefix_, &other.ip_prefix_) == 0; +} + std::unique_ptr<Sockaddr> Prefix::toSockaddr() const { Sockaddr *ret = nullptr; @@ -211,8 +219,6 @@ Name Prefix::getName(const core::Name &mask, const core::Name &components, } } - // if (this->contains(name_ip)) - // throw errors::RuntimeException("Mask overrides the prefix"); return Name(ip_prefix_.family, (uint8_t *)&name_ip); } @@ -279,8 +285,6 @@ Prefix &Prefix::setNetwork(std::string &network) { } Name Prefix::makeRandomName() const { - srand((unsigned int)time(nullptr)); - if (ip_prefix_.family == AF_INET6) { std::default_random_engine eng((std::random_device())()); std::uniform_int_distribution<uint32_t> idis( diff --git a/libtransport/src/core/tcp_socket_connector.cc b/libtransport/src/core/tcp_socket_connector.cc index a30264271..7758e2cf2 100644 --- a/libtransport/src/core/tcp_socket_connector.cc +++ b/libtransport/src/core/tcp_socket_connector.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -85,8 +85,10 @@ void TcpSocketConnector::send(const uint8_t *packet, std::size_t len, const PacketSentCallback &packet_sent) { if (packet_sent != 0) { asio::async_write(socket_, asio::buffer(packet, len), - [packet_sent](std::error_code ec, - std::size_t /*length*/) { packet_sent(); }); + [packet_sent](const std::error_code &ec) + std::size_t /*length*/) { + packet_sent(); + }); } else { if (state_ == ConnectorState::CONNECTED) { asio::write(socket_, asio::buffer(packet, len)); @@ -151,7 +153,7 @@ void TcpSocketConnector::doWrite() { asio::async_write( socket_, std::move(array), - [this, packet_store = std::move(packet_store)](std::error_code ec, + [this, packet_store = std::move(packet_store)](const std::error_code &ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { if (!output_buffer_.empty()) { @@ -172,7 +174,7 @@ void TcpSocketConnector::doReadBody(std::size_t body_length) { asio::async_read( socket_, asio::buffer(read_msg_->writableTail(), body_length), asio::transfer_exactly(body_length), - [this](std::error_code ec, std::size_t length) { + [this](const std::error_code &ec, std::size_t length) { read_msg_->append(length); if (TRANSPORT_EXPECT_TRUE(!ec)) { receive_callback_(std::move(read_msg_)); @@ -195,7 +197,7 @@ void TcpSocketConnector::doReadHeader() { asio::buffer(read_msg_->writableData(), NetworkMessage::fixed_header_length), asio::transfer_exactly(NetworkMessage::fixed_header_length), - [this](std::error_code ec, std::size_t length) { + [this](const std::error_code &ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { read_msg_->append(NetworkMessage::fixed_header_length); std::size_t body_length = 0; @@ -235,7 +237,7 @@ void TcpSocketConnector::tryReconnect() { void TcpSocketConnector::doConnect() { asio::async_connect( socket_, endpoint_iterator_, - [this](std::error_code ec, tcp::resolver::iterator) { + [this](const std::error_code &ec, tcp::resolver::iterator) { if (!ec) { timer_.cancel(); state_ = ConnectorState::CONNECTED; diff --git a/libtransport/src/core/tcp_socket_connector.h b/libtransport/src/core/tcp_socket_connector.h index 21db8301e..2901a5c9d 100644 --- a/libtransport/src/core/tcp_socket_connector.h +++ b/libtransport/src/core/tcp_socket_connector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/core/udp_connector.cc b/libtransport/src/core/udp_connector.cc new file mode 100644 index 000000000..ee0c7ea9c --- /dev/null +++ b/libtransport/src/core/udp_connector.cc @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#include <core/errors.h> +#include <core/udp_connector.h> +#include <glog/logging.h> +#include <hicn/transport/utils/branch_prediction.h> + +#include <iostream> +#include <thread> +#include <vector> + +namespace transport { +namespace core { + +UdpTunnelConnector::~UdpTunnelConnector() {} + +void UdpTunnelConnector::connect(const std::string &hostname, uint16_t port, + const std::string &bind_address, + uint16_t bind_port) { + if (state_ == State::CLOSED) { + state_ = State::CONNECTING; + + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), hostname, + std::to_string(port)); + + endpoint_iterator_ = resolver_.resolve(query); + remote_endpoint_send_ = *endpoint_iterator_; + socket_->open(remote_endpoint_send_.protocol()); + + if (!bind_address.empty() && bind_port != 0) { + using namespace asio::ip; + + auto address = address::from_string(bind_address); + if (address.is_v6()) { + std::error_code ec; + socket_->set_option(asio::ip::v6_only(false), ec); + // Call succeeds only on dual stack systems. + } + + socket_->bind(udp::endpoint(address, bind_port)); + } + + remote_endpoint_ = Endpoint(remote_endpoint_send_); + local_endpoint_ = Endpoint(socket_->local_endpoint()); + + auto self = shared_from_this(); + doConnect(self); + } +} + +void UdpTunnelConnector::send(Packet &packet) { + send(packet.shared_from_this()); +} + +void UdpTunnelConnector::send(const utils::MemBuf::Ptr &buffer) { + auto self = shared_from_this(); + io_service_.post([self, pkt{buffer}]() { + bool write_in_progress = !self->output_buffer_.empty(); + self->output_buffer_.push_back(std::move(pkt)); + if (TRANSPORT_EXPECT_TRUE(self->state_ == State::CONNECTED)) { + if (!write_in_progress) { + self->doSendPacket(self); + } + } else { + self->data_available_ = true; + } + }); +} + +void UdpTunnelConnector::close() { + DLOG_IF(INFO, VLOG_IS_ON(2)) << "UDPTunnelConnector::close"; + state_ = State::CLOSED; + bool is_socket_owned = socket_.use_count() == 1; + if (is_socket_owned) { + // Here we use a shared ptr to keep the object alive until we call the close + // function + auto self = shared_from_this(); + io_service_.dispatch([this, self]() { + socket_->close(); + // on_close_callback_(shared_from_this()); + }); + } +} + +void UdpTunnelConnector::doSendPacket( + const std::shared_ptr<UdpTunnelConnector> &self) { +#ifdef LINUX + send_timer_.expires_from_now(std::chrono::microseconds(50)); + send_timer_.async_wait([self](const std::error_code &ec) { + if (ec) { + return; + } + + self->writeHandler(); + }); +#else + auto packet = output_buffer_.front().get(); + auto array = std::vector<asio::const_buffer>(); + + const ::utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_->async_send_to( + std::move(array), remote_endpoint_send_, + [this, self](const std::error_code &ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + sent_callback_(this, make_error_code(core_error::success)); + } else if (ec.value() == + static_cast<int>(std::errc::operation_canceled)) { + // The connection has been closed by the application. + return; + } else { + sendFailed(); + sent_callback_(this, ec); + } + + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doSendPacket(self); + } + }); +#endif +} + +void UdpTunnelConnector::retryConnection() { + // The connection was refused. In this case let's retry to reconnect. + connection_reattempts_++; + LOG(ERROR) << "Error in UDP: Connection refused. Retrying..."; + state_ = State::CONNECTING; + timer_.expires_from_now(std::chrono::milliseconds(500)); + std::weak_ptr<UdpTunnelConnector> self = shared_from_this(); + timer_.async_wait([self, this](const std::error_code &ec) { + if (ec) { + } + if (auto ptr = self.lock()) { + doConnect(ptr); + } + }); + return; +} + +#ifdef LINUX +void UdpTunnelConnector::writeHandler() { + if (TRANSPORT_EXPECT_FALSE(state_ != State::CONNECTED)) { + return; + } + + auto len = std::min(output_buffer_.size(), std::size_t(Connector::max_burst)); + + if (len) { + int m = 0; + for (auto &p : output_buffer_) { + auto packet = p.get(); + ::utils::MemBuf *current = packet; + int b = 0; + do { + // array.push_back(asio::const_buffer(current->data(), + // current->length())); + tx_iovecs_[m][b].iov_base = current->writableData(); + tx_iovecs_[m][b].iov_len = current->length(); + current = current->next(); + b++; + } while (current != packet); + + tx_msgs_[m].msg_hdr.msg_iov = tx_iovecs_[m]; + tx_msgs_[m].msg_hdr.msg_iovlen = b; + tx_msgs_[m].msg_hdr.msg_name = remote_endpoint_send_.data(); + tx_msgs_[m].msg_hdr.msg_namelen = remote_endpoint_send_.size(); + m++; + + if (--len == 0) { + break; + } + } + + int retval = sendmmsg(socket_->native_handle(), tx_msgs_, m, MSG_DONTWAIT); + if (retval > 0) { + while (retval--) { + output_buffer_.pop_front(); + } + } else if (errno != EWOULDBLOCK && errno != EAGAIN) { + LOG(ERROR) << "Error sending messages: " << strerror(errno); + sent_callback_(this, make_error_code(core_error::send_failed)); + return; + } + } + + if (!output_buffer_.empty()) { + send_timer_.expires_from_now(std::chrono::microseconds(50)); + std::weak_ptr<UdpTunnelConnector> self = shared_from_this(); + send_timer_.async_wait([self](const std::error_code &ec) { + if (ec) { + return; + } + if (auto ptr = self.lock()) { + ptr->writeHandler(); + } + }); + } +} + +void UdpTunnelConnector::readHandler(const std::error_code &ec) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; + + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + if (current_position_ == 0) { + for (int i = 0; i < max_burst; i++) { + auto read_buffer = getRawBuffer(); + rx_iovecs_[i][0].iov_base = read_buffer.first; + rx_iovecs_[i][0].iov_len = read_buffer.second; + rx_msgs_[i].msg_hdr.msg_iov = rx_iovecs_[i]; + rx_msgs_[i].msg_hdr.msg_iovlen = 1; + } + } + + int res = recvmmsg(socket_->native_handle(), rx_msgs_ + current_position_, + max_burst - current_position_, MSG_DONTWAIT, nullptr); + if (res < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + // Try again later + return; + } + + if (errno == ECONNREFUSED && + connection_reattempts_ < max_reconnection_reattempts) { + retryConnection(); + return; + } + + LOG(ERROR) << "Error receiving messages! " << strerror(errno) << " " + << res; + std::vector<utils::MemBuf::Ptr> v; + auto ec = make_error_code(core_error::receive_failed); + + receive_callback_(this, v, ec); + return; + } + + std::vector<utils::MemBuf::Ptr> v; + v.reserve(res); + for (int i = 0; i < res; i++) { + auto packet = getPacketFromBuffer( + reinterpret_cast<uint8_t *>( + rx_msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), + rx_msgs_[current_position_].msg_len); + receiveSuccess(*packet); + v.push_back(std::move(packet)); + ++current_position_; + } + + receive_callback_(this, v, make_error_code(core_error::success)); + + doRecvPacket(); + } else { + LOG(ERROR) << "Error in UDP: Receiving packets from a not " + "connected socket."; + } + } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { + LOG(ERROR) << "The connection has been closed by the application."; + return; + } else { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + // receive_callback_(this, *read_msg_, ec); + LOG(ERROR) << "Error in UDP connector: " << ec.value() << " " + << ec.message(); + } else { + LOG(ERROR) << "Error in connector while not connected. " << ec.value() + << " " << ec.message(); + } + } +} +#endif + +void UdpTunnelConnector::doRecvPacket() { + std::weak_ptr<UdpTunnelConnector> self = shared_from_this(); +#ifdef LINUX + if (state_ == State::CONNECTED) { +#if ((ASIO_VERSION / 100 % 1000) < 11) + socket_->async_receive(asio::null_buffers(), +#else + socket_->async_wait(asio::ip::tcp::socket::wait_read, +#endif + [self](const std::error_code &ec) { + if (ec) { + LOG(ERROR) + << "Error in UDP connector: " << ec.value() + << " " << ec.message(); + return; + } + if (auto ptr = self.lock()) { + ptr->readHandler(ec); + } + }); + } +#else + DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; + read_msg_ = getRawBuffer(); + socket_->async_receive_from( + asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_recv_, + [this, self](const std::error_code &ec, std::size_t length) { + if (auto ptr = self.lock()) { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "UdpTunnelConnector received packet length=" << length; + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + auto packet = getPacketFromBuffer(read_msg_.first, length); + receiveSuccess(*packet); + std::vector<utils::MemBuf::Ptr> v{std::move(packet)}; + receive_callback_(this, v, make_error_code(core_error::success)); + doRecvPacket(); + } else { + LOG(ERROR) << "Error in UDP: Receiving packets from a not " + "connected socket."; + } + } else if (ec.value() == + static_cast<int>(std::errc::operation_canceled)) { + LOG(ERROR) << "The connection has been closed by the application."; + return; + } else if (ec.value() == + static_cast<int>(std::errc::connection_refused)) { + if (connection_reattempts_ < max_reconnection_reattempts) { + retryConnection(); + } + } else { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + LOG(ERROR) << "Error in UDP connector: " << ec.value() + << ec.message(); + } else { + LOG(ERROR) << "Error while not connected"; + } + } + } + }); +#endif +} + +void UdpTunnelConnector::doConnect( + std::shared_ptr<UdpTunnelConnector> &self_shared) { + std::weak_ptr<UdpTunnelConnector> self = self_shared; + asio::async_connect(*socket_, endpoint_iterator_, + [this, self](const std::error_code &ec, + asio::ip::udp::resolver::iterator) { + if (auto ptr = self.lock()) { + if (!ec) { + state_ = State::CONNECTED; + doRecvPacket(); + + if (data_available_) { + data_available_ = false; + doSendPacket(ptr); + } + + on_reconnect_callback_( + this, make_error_code(core_error::success)); + } else { + LOG(ERROR) << "UDP Connection failed!!!"; + retryConnection(); + } + } + }); +} + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel.h b/libtransport/src/core/udp_connector.h index 4f044f93f..65821852d 100644 --- a/libtransport/src/io_modules/forwarder/udp_tunnel.h +++ b/libtransport/src/core/udp_connector.h @@ -1,13 +1,13 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #pragma once +#include <core/errors.h> #include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/connector.h> #include <hicn/transport/portability/platform.h> -#include <io_modules/forwarder/errors.h> #include <iostream> #include <memory> @@ -30,16 +30,15 @@ class UdpTunnelConnector : public Connector { : Connector(receive_callback, packet_sent, on_close_callback, on_reconnect), io_service_(io_service), - strand_(std::make_shared<asio::io_service::strand>(io_service_)), socket_(std::make_shared<asio::ip::udp::socket>(io_service_)), resolver_(io_service_), timer_(io_service_), #ifdef LINUX send_timer_(io_service_), - tx_iovecs_{0}, - tx_msgs_{0}, - rx_iovecs_{0}, - rx_msgs_{0}, + tx_iovecs_{}, + tx_msgs_{}, + rx_iovecs_{}, + rx_msgs_{}, current_position_(0), #else read_msg_(nullptr, 0), @@ -61,17 +60,16 @@ class UdpTunnelConnector : public Connector { #else io_service_((asio::io_context &)(socket->get_executor().context())), #endif - strand_(strand), socket_(socket), resolver_(io_service_), remote_endpoint_send_(std::forward<EndpointType &&>(remote_endpoint)), timer_(io_service_), #ifdef LINUX send_timer_(io_service_), - tx_iovecs_{0}, - tx_msgs_{0}, - rx_iovecs_{0}, - rx_msgs_{0}, + tx_iovecs_{}, + tx_msgs_{}, + rx_iovecs_{}, + rx_msgs_{}, current_position_(0), #else read_msg_(nullptr, 0), @@ -88,7 +86,7 @@ class UdpTunnelConnector : public Connector { void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; void close() override; @@ -99,26 +97,27 @@ class UdpTunnelConnector : public Connector { auto shared_from_this() { return utils::shared_from(this); } private: - void doConnect(); + void retryConnection(); + void doConnect(std::shared_ptr<UdpTunnelConnector> &self); void doRecvPacket(); - void doRecvPacket(utils::MemBuf &buffer) { - receive_callback_(this, buffer, make_error_code(forwarder_error::success)); + void doRecvPacket(utils::MemBuf::Ptr &buffer) { + std::vector<utils::MemBuf::Ptr> v{std::move(buffer)}; + receive_callback_(this, v, make_error_code(core_error::success)); } #ifdef LINUX - void readHandler(std::error_code ec); - void writeHandler(std::error_code ec); + void readHandler(const std::error_code &ec); + void writeHandler(); #endif void setConnected() { state_ = State::CONNECTED; } - void doSendPacket(); + void doSendPacket(const std::shared_ptr<UdpTunnelConnector> &self); void doClose(); private: asio::io_service &io_service_; - std::shared_ptr<asio::io_service::strand> strand_; std::shared_ptr<asio::ip::udp::socket> socket_; asio::ip::udp::resolver resolver_; asio::ip::udp::resolver::iterator endpoint_iterator_; diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc b/libtransport/src/core/udp_listener.cc index d047cc568..c67673392 100644 --- a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc +++ b/libtransport/src/core/udp_listener.cc @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ +#include <core/udp_connector.h> +#include <core/udp_listener.h> #include <glog/logging.h> #include <hicn/transport/utils/hash.h> -#include <io_modules/forwarder/udp_tunnel.h> -#include <io_modules/forwarder/udp_tunnel_listener.h> #ifndef LINUX namespace std { @@ -35,7 +35,7 @@ void UdpTunnelListener::close() { } #ifdef LINUX -void UdpTunnelListener::readHandler(std::error_code ec) { +void UdpTunnelListener::readHandler(const std::error_code &ec) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; if (TRANSPORT_EXPECT_TRUE(!ec)) { @@ -102,7 +102,8 @@ void UdpTunnelListener::readHandler(std::error_code ec) { std::make_shared<UdpTunnelConnector>( socket_, strand_, receive_callback_, [](Connector *, const std::error_code &) {}, [](Connector *) {}, - [](Connector *) {}, std::move(remote_endpoint_))); + [](Connector *, const std::error_code &) {}, + std::move(remote_endpoint_))); connector = ret.first; connector->second->setConnectorId(connector_id); } @@ -112,7 +113,7 @@ void UdpTunnelListener::readHandler(std::error_code ec) { */ UdpTunnelConnector *c = dynamic_cast<UdpTunnelConnector *>(connector->second.get()); - c->doRecvPacket(*packet); + c->doRecvPacket(packet); ++current_position_; } @@ -141,7 +142,7 @@ void UdpTunnelListener::doRecvPacket() { read_msg_ = Connector::getRawBuffer(); socket_->async_receive_from( asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_, - [this](std::error_code ec, std::size_t length) { + [this](const std::error_code &ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { auto packet = Connector::getPacketFromBuffer(read_msg_.first, length); auto connector_id = @@ -153,7 +154,8 @@ void UdpTunnelListener::doRecvPacket() { connector_id, std::make_shared<UdpTunnelConnector>( socket_, strand_, receive_callback_, [](Connector *, const std::error_code &) {}, - [](Connector *) {}, [](Connector *) {}, + [](Connector *) {}, + [](Connector *, const std::error_code &) {}, std::move(remote_endpoint_))); connector = ret.first; connector->second->setConnectorId(connector_id); @@ -161,7 +163,7 @@ void UdpTunnelListener::doRecvPacket() { UdpTunnelConnector *c = dynamic_cast<UdpTunnelConnector *>(connector->second.get()); - c->doRecvPacket(*packet); + c->doRecvPacket(packet); doRecvPacket(); } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h b/libtransport/src/core/udp_listener.h index 5d197dcb0..813520309 100644 --- a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h +++ b/libtransport/src/core/udp_listener.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #pragma once @@ -44,8 +44,8 @@ class UdpTunnelListener #ifndef LINUX read_msg_(nullptr, 0) #else - iovecs_{0}, - msgs_{0}, + iovecs_{}, + msgs_{}, current_position_(0) #endif { @@ -83,7 +83,7 @@ class UdpTunnelListener private: void doRecvPacket(); - void readHandler(std::error_code ec); + void readHandler(const std::error_code &ec); asio::io_service &io_service_; std::shared_ptr<asio::io_service::strand> strand_; diff --git a/libtransport/src/http/CMakeLists.txt b/libtransport/src/http/CMakeLists.txt index 2407faea3..0060f8714 100644 --- a/libtransport/src/http/CMakeLists.txt +++ b/libtransport/src/http/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/src/http/client_connection.cc b/libtransport/src/http/client_connection.cc index b4ab7cbc3..05fa3e335 100644 --- a/libtransport/src/http/client_connection.cc +++ b/libtransport/src/http/client_connection.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -77,9 +77,7 @@ class HTTPClientConnection::Implementation auto end = std::chrono::steady_clock::now(); LOG(INFO) << method_map[method].c_str() << " " << url.c_str() << " [" << name_.str() << "] duration: " - << std::chrono::duration_cast<std::chrono::microseconds>(end - - start) - .count() + << utils::SteadyTime::getDurationUs(start, end).count() << " [usec] " << size << " [bytes]"; }; @@ -106,7 +104,7 @@ class HTTPClientConnection::Implementation HTTPClientConnection &setTimeout(const std::chrono::seconds &timeout) { timer_->cancel(); timer_->expires_from_now(timeout); - timer_->async_wait([this](std::error_code ec) { + timer_->async_wait([this](const std::error_code &ec) { if (!ec) { consumer_.stop(); } @@ -147,18 +145,14 @@ class HTTPClientConnection::Implementation name_ << ipv6_first_word << ":"; - for (uint16_t *word = (uint16_t *)&locator_hash; - std::size_t(word) < - (std::size_t(&locator_hash) + sizeof(locator_hash)); - word++) { - name_ << ":" << std::hex << *word; + uint16_t *word = (uint16_t *)(&locator_hash); + for (std::size_t i = 0; i < sizeof(locator_hash) / 2; i++) { + name_ << ":" << std::hex << word[i]; } - for (uint16_t *word = (uint16_t *)&request_hash; - std::size_t(word) < - (std::size_t(&request_hash) + sizeof(request_hash)); - word++) { - name_ << ":" << std::hex << *word; + word = (uint16_t *)(&request_hash); + for (std::size_t i = 0; i < sizeof(request_hash) / 2; i++) { + name_ << ":" << std::hex << word[i]; } name_ << "|0"; @@ -199,7 +193,7 @@ class HTTPClientConnection::Implementation } } - void readError(const std::error_code ec) noexcept override { + void readError(const std::error_code &ec) noexcept override { LOG(ERROR) << "Error " << ec.message() << " during download of " << current_url_.c_str(); if (read_bytes_callback_) { diff --git a/libtransport/src/http/request.cc b/libtransport/src/http/request.cc index 29118fd88..ad726741a 100644 --- a/libtransport/src/http/request.cc +++ b/libtransport/src/http/request.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/http/response.cc b/libtransport/src/http/response.cc index c665fbc5f..727e2c755 100644 --- a/libtransport/src/http/response.cc +++ b/libtransport/src/http/response.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/implementation/CMakeLists.txt b/libtransport/src/implementation/CMakeLists.txt index daf899d06..1f2a33a4c 100644 --- a/libtransport/src/implementation/CMakeLists.txt +++ b/libtransport/src/implementation/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.cc b/libtransport/src/implementation/p2psecure_socket_consumer.cc index 4b14da5d2..6b67a5487 100644 --- a/libtransport/src/implementation/p2psecure_socket_consumer.cc +++ b/libtransport/src/implementation/p2psecure_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -44,7 +44,7 @@ int readOld(BIO *b, char *buf, int size) { if (!socket->something_to_read_) { if (!socket->transport_protocol_->isRunning()) { socket->network_name_.setSuffix(socket->random_suffix_); - socket->ConsumerSocket::asyncConsume(socket->network_name_); + socket->ConsumerSocket::consume(socket->network_name_); } if (!socket->something_to_read_) socket->cv_.wait(lck); @@ -312,36 +312,6 @@ int P2PSecureConsumerSocket::consume(const Name &name) { return tls_consumer_->consume((prefix->mapName(name))); } -int P2PSecureConsumerSocket::asyncConsume(const Name &name) { - if (transport_protocol_->isRunning()) { - return CONSUMER_BUSY; - } - - if (handshake() != 1) { - throw errors::RuntimeException("Unable to perform client handshake"); - } else { - DLOG_IF(INFO, VLOG_IS_ON(2)) << "Handshake performed!"; - } - - initSessionSocket(); - - if (tls_consumer_ == nullptr) { - throw errors::RuntimeException("TLS socket does not exist"); - } - - std::shared_ptr<Name> prefix_name = std::make_shared<Name>( - secure_prefix_.family, - ip_address_get_buffer(&(secure_prefix_.address), secure_prefix_.family)); - std::shared_ptr<Prefix> prefix = - std::make_shared<Prefix>(*prefix_name, secure_prefix_.len); - - if (payload_ != NULL) - return tls_consumer_->asyncConsume((prefix->mapName(name)), - std::move(payload_)); - else - return tls_consumer_->asyncConsume((prefix->mapName(name))); -} - void P2PSecureConsumerSocket::registerPrefix(const Prefix &producer_namespace) { producer_namespace_ = producer_namespace; } @@ -385,7 +355,7 @@ void P2PSecureConsumerSocket::readBufferAvailable( cv_.notify_one(); } -void P2PSecureConsumerSocket::readError(const std::error_code ec) noexcept {}; +void P2PSecureConsumerSocket::readError(const std::error_code &ec) noexcept {}; void P2PSecureConsumerSocket::readSuccess(std::size_t total_size) noexcept { std::unique_lock<std::mutex> lck(this->mtx_); diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.h b/libtransport/src/implementation/p2psecure_socket_consumer.h index a35a50352..a5e69f611 100644 --- a/libtransport/src/implementation/p2psecure_socket_consumer.h +++ b/libtransport/src/implementation/p2psecure_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -48,8 +48,6 @@ class P2PSecureConsumerSocket : public ConsumerSocket, int consume(const Name &name) override; - int asyncConsume(const Name &name) override; - void registerPrefix(const Prefix &producer_namespace); int setSocketOption( @@ -120,7 +118,7 @@ class P2PSecureConsumerSocket : public ConsumerSocket, virtual void readBufferAvailable( std::unique_ptr<utils::MemBuf> &&buffer) noexcept override; - virtual void readError(const std::error_code ec) noexcept override; + virtual void readError(const std::error_code &ec) noexcept override; virtual void readSuccess(std::size_t total_size) noexcept override; diff --git a/libtransport/src/implementation/p2psecure_socket_producer.cc b/libtransport/src/implementation/p2psecure_socket_producer.cc index 3748001fc..ee78ea53b 100644 --- a/libtransport/src/implementation/p2psecure_socket_producer.cc +++ b/libtransport/src/implementation/p2psecure_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -20,6 +20,7 @@ #include <interfaces/tls_rtc_socket_producer.h> #include <interfaces/tls_socket_producer.h> #include <openssl/bio.h> +#include <openssl/pkcs12.h> #include <openssl/rand.h> #include <openssl/ssl.h> @@ -41,7 +42,7 @@ P2PSecureProducerSocket::P2PSecureProducerSocket( P2PSecureProducerSocket::P2PSecureProducerSocket( interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr<auth::Identity> &identity) + std::string &keystore_path, std::string &keystore_pwd) : ProducerSocket(producer_socket, ProductionProtocolAlgorithms::BYTE_STREAM), rtc_(rtc), @@ -50,8 +51,16 @@ P2PSecureProducerSocket::P2PSecureProducerSocket( map_producers(), list_producers() { /* Setup SSL context (identity and parameter to use TLS 1.3) */ - cert_509_ = identity->getCertificate().get(); - pkey_rsa_ = identity->getPrivateKey().get(); + FILE *p12file = fopen(keystore_path.c_str(), "r"); + if (p12file == NULL) + throw errors::RuntimeException("impossible open keystore"); + std::unique_ptr<PKCS12, decltype(&::PKCS12_free)> p12( + d2i_PKCS12_fp(p12file, NULL), ::PKCS12_free); + // now we parse the file to get the first key and certificate + if (1 != PKCS12_parse(p12.get(), keystore_pwd.c_str(), &pkey_rsa_, &cert_509_, + NULL)) + throw errors::RuntimeException("impossible to get the private key"); + fclose(p12file); /* Set the callback so that when an interest is received we catch it and we * decrypt the payload before passing it to the application. */ @@ -133,15 +142,15 @@ uint32_t P2PSecureProducerSocket::produceDatagram( // TODO throw errors::NotImplementedException(); - if (!rtc_) { - throw errors::RuntimeException( - "RTC must be the transport protocol to start the production of current " - "data. Aborting."); - } + // if (!rtc_) { + // throw errors::RuntimeException( + // "RTC must be the transport protocol to start the production of + // current " "data. Aborting."); + // } - std::unique_lock<std::mutex> lck(mtx_); + // std::unique_lock<std::mutex> lck(mtx_); - if (list_producers.empty()) cv_.wait(lck); + // if (list_producers.empty()) cv_.wait(lck); // TODO // for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) @@ -151,7 +160,7 @@ uint32_t P2PSecureProducerSocket::produceDatagram( // rtc_producer->produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); // } - return 0; + // return 0; } uint32_t P2PSecureProducerSocket::produceStream( @@ -197,44 +206,6 @@ uint32_t P2PSecureProducerSocket::produceStream(const Name &content_name, return segments; } -// void P2PSecureProducerSocket::asyncProduce(const Name &content_name, -// const uint8_t *buf, -// size_t buffer_size, bool is_last, -// uint32_t *start_offset) { -// if (rtc_) { -// throw errors::RuntimeException( -// "RTC transport protocol is not compatible with the production of " -// "current data. Aborting."); -// } - -// std::unique_lock<std::mutex> lck(mtx_); -// if (list_producers.empty()) cv_.wait(lck); - -// for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) -// { -// (*it)->asyncProduce(content_name, buf, buffer_size, is_last, -// start_offset); -// } -// } - -void P2PSecureProducerSocket::asyncProduce( - Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, bool is_last, - uint32_t offset, uint32_t **last_segment) { - if (rtc_) { - throw errors::RuntimeException( - "RTC transport protocol is not compatible with the production of " - "current data. Aborting."); - } - - std::unique_lock<std::mutex> lck(mtx_); - if (list_producers.empty()) cv_.wait(lck); - - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) { - (*it)->asyncProduce(content_name, buffer->clone(), is_last, offset, - last_segment); - } -} - /* Redefinition of socket options to avoid name hiding */ int P2PSecureProducerSocket::setSocketOption( int socket_option_key, ProducerInterestCallback socket_option_value) { diff --git a/libtransport/src/implementation/p2psecure_socket_producer.h b/libtransport/src/implementation/p2psecure_socket_producer.h index f94347258..00f407a75 100644 --- a/libtransport/src/implementation/p2psecure_socket_producer.h +++ b/libtransport/src/implementation/p2psecure_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,7 +15,6 @@ #pragma once -#include <hicn/transport/auth/identity.h> #include <hicn/transport/auth/signer.h> #include <implementation/socket_producer.h> // #include <implementation/tls_rtc_socket_producer.h> @@ -38,9 +37,9 @@ class P2PSecureProducerSocket : public ProducerSocket { public: explicit P2PSecureProducerSocket(interface::ProducerSocket *producer_socket); - explicit P2PSecureProducerSocket( - interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr<auth::Identity> &identity); + explicit P2PSecureProducerSocket(interface::ProducerSocket *producer_socket, + bool rtc, std::string &keystore_path, + std::string &keystore_pwd); ~P2PSecureProducerSocket(); @@ -56,10 +55,6 @@ class P2PSecureProducerSocket : public ProducerSocket { bool is_last = true, uint32_t start_offset = 0) override; - void asyncProduce(Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr) override; - int setSocketOption(int socket_option_key, ProducerInterestCallback socket_option_value) override; diff --git a/libtransport/src/implementation/socket.cc b/libtransport/src/implementation/socket.cc index 2e21f2bc3..95941da07 100644 --- a/libtransport/src/implementation/socket.cc +++ b/libtransport/src/implementation/socket.cc @@ -14,13 +14,42 @@ */ #include <core/global_configuration.h> +#include <hicn/transport/interfaces/socket_options_default_values.h> #include <implementation/socket.h> namespace transport { namespace implementation { Socket::Socket(std::shared_ptr<core::Portal> &&portal) - : portal_(std::move(portal)), is_async_(false) {} + : portal_(std::move(portal)), + is_async_(false), + packet_format_(interface::default_values::packet_format) {} + +int Socket::setSocketOption(int socket_option_key, + hicn_format_t packet_format) { + switch (socket_option_key) { + case interface::GeneralTransportOptions::PACKET_FORMAT: + packet_format_ = packet_format; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; +} + +int Socket::getSocketOption(int socket_option_key, + hicn_format_t &packet_format) { + switch (socket_option_key) { + case interface::GeneralTransportOptions::PACKET_FORMAT: + packet_format = packet_format_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; +} } // namespace implementation } // namespace transport
\ No newline at end of file diff --git a/libtransport/src/implementation/socket.h b/libtransport/src/implementation/socket.h index cf22c03e1..11c9a704d 100644 --- a/libtransport/src/implementation/socket.h +++ b/libtransport/src/implementation/socket.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -38,7 +38,26 @@ class Socket { virtual void connect() = 0; virtual bool isRunning() = 0; - virtual asio::io_service &getIoService() { return portal_->getIoService(); } + virtual asio::io_service &getIoService() { + return portal_->getThread().getIoService(); + } + + int setSocketOption(int socket_option_key, hicn_format_t packet_format); + int getSocketOption(int socket_option_key, hicn_format_t &packet_format); + + int getSocketOption(int socket_option_key, + std::shared_ptr<core::Portal> &socket_option_value) { + switch (socket_option_key) { + case interface::GeneralTransportOptions::PORTAL: + socket_option_value = portal_; + break; + default: + return SOCKET_OPTION_NOT_GET; + ; + } + + return SOCKET_OPTION_GET; + } protected: Socket(std::shared_ptr<core::Portal> &&portal); @@ -48,6 +67,7 @@ class Socket { protected: std::shared_ptr<core::Portal> portal_; bool is_async_; + hicn_format_t packet_format_; }; } // namespace implementation diff --git a/libtransport/src/implementation/socket_consumer.h b/libtransport/src/implementation/socket_consumer.h index e0981af7f..ebdac7f93 100644 --- a/libtransport/src/implementation/socket_consumer.h +++ b/libtransport/src/implementation/socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -20,6 +20,7 @@ #include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/interfaces/statistics.h> #include <hicn/transport/utils/event_thread.h> +#include <implementation/socket.h> #include <protocols/cbr.h> #include <protocols/raaqm.h> #include <protocols/rtc/rtc.h> @@ -38,7 +39,6 @@ class ConsumerSocket : public Socket { std::shared_ptr<core::Portal> &&portal) : Socket(std::move(portal)), consumer_interface_(consumer), - async_downloader_(), interest_lifetime_(default_values::interest_lifetime), min_window_size_(default_values::min_window_size), max_window_size_(default_values::max_window_size), @@ -56,6 +56,7 @@ class ConsumerSocket : public Socket { rate_estimation_observer_(nullptr), rate_estimation_batching_parameter_(default_values::batch), rate_estimation_choice_(0), + max_unverified_delay_(default_values::max_unverified_delay), verifier_(std::make_shared<auth::VoidVerifier>()), verify_signature_(false), reset_window_(false), @@ -64,41 +65,42 @@ class ConsumerSocket : public Socket { on_interest_satisfied_(VOID_HANDLER), on_content_object_input_(VOID_HANDLER), stats_summary_(VOID_HANDLER), + on_fwd_strategy_(VOID_HANDLER), + on_rec_strategy_(VOID_HANDLER), read_callback_(nullptr), timer_interval_milliseconds_(0), + recovery_strategy_(RtcTransportRecoveryStrategies::RTX_ONLY), + aggregated_data_(false), + fec_setting_(""), guard_raaqm_params_() { switch (protocol) { case TransportProtocolAlgorithms::CBR: transport_protocol_ = - std::make_unique<protocol::CbrTransportProtocol>(this); + std::make_shared<protocol::CbrTransportProtocol>(this); break; case TransportProtocolAlgorithms::RTC: transport_protocol_ = - std::make_unique<protocol::rtc::RTCTransportProtocol>(this); + std::make_shared<protocol::rtc::RTCTransportProtocol>(this); break; case TransportProtocolAlgorithms::RAAQM: default: transport_protocol_ = - std::make_unique<protocol::RaaqmTransportProtocol>(this); + std::make_shared<protocol::RaaqmTransportProtocol>(this); break; } } public: ConsumerSocket(interface::ConsumerSocket *consumer, int protocol) - : ConsumerSocket(consumer, protocol, std::make_shared<core::Portal>()) {} + : ConsumerSocket(consumer, protocol, core::Portal::createShared()) {} ConsumerSocket(interface::ConsumerSocket *consumer, int protocol, - asio::io_service &io_service) - : ConsumerSocket(consumer, protocol, - std::make_shared<core::Portal>(io_service)) { + ::utils::EventThread &worker) + : ConsumerSocket(consumer, protocol, core::Portal::createShared(worker)) { is_async_ = true; } - ~ConsumerSocket() { - stop(); - async_downloader_.stop(); - } + ~ConsumerSocket() { stop(); } interface::ConsumerSocket *getInterface() { return consumer_interface_; @@ -122,18 +124,6 @@ class ConsumerSocket : public Socket { transport_protocol_->start(); - return is_async_ ? CONSUMER_RUNNING : CONSUMER_FINISHED; - } - - virtual int asyncConsume(const Name &name) { - if (!async_downloader_.stopped()) { - async_downloader_.add([this, name]() { - network_name_ = std::move(name); - network_name_.setSuffix(0); - transport_protocol_->start(); - }); - } - return CONSUMER_RUNNING; } @@ -149,6 +139,9 @@ class ConsumerSocket : public Socket { } } + using Socket::getSocketOption; + using Socket::setSocketOption; + virtual int setSocketOption(int socket_option_key, ReadCallback *socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -245,6 +238,10 @@ class ConsumerSocket : public Socket { interest_lifetime_ = socket_option_value; break; + case GeneralTransportOptions::MAX_UNVERIFIED_TIME: + max_unverified_delay_ = socket_option_value; + break; + case RateEstimationOptions::RATE_ESTIMATION_BATCH_PARAMETER: if (socket_option_value > 0) { rate_estimation_batching_parameter_ = socket_option_value; @@ -265,6 +262,11 @@ class ConsumerSocket : public Socket { timer_interval_milliseconds_ = socket_option_value; break; + case RtcTransportOptions::RECOVERY_STRATEGY: + recovery_strategy_ = + (RtcTransportRecoveryStrategies)socket_option_value; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -328,6 +330,11 @@ class ConsumerSocket : public Socket { result = SOCKET_OPTION_SET; break; + case RtcTransportOptions::AGGREGATED_DATA: + aggregated_data_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; + default: return result; } @@ -406,36 +413,37 @@ class ConsumerSocket : public Socket { int setSocketOption( int socket_option_key, const std::shared_ptr<auth::Verifier> &socket_option_value) { - int result = SOCKET_OPTION_NOT_SET; if (!transport_protocol_->isRunning()) { switch (socket_option_key) { case GeneralTransportOptions::VERIFIER: verifier_.reset(); verifier_ = socket_option_value; - result = SOCKET_OPTION_SET; break; default: - return result; + return SOCKET_OPTION_NOT_SET; } } - - return result; + return SOCKET_OPTION_SET; } int setSocketOption(int socket_option_key, const std::string &socket_option_value) { int result = SOCKET_OPTION_NOT_SET; - if (!transport_protocol_->isRunning()) { - switch (socket_option_key) { - case DataLinkOptions::OUTPUT_INTERFACE: + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + if (!transport_protocol_->isRunning()) { output_interface_ = socket_option_value; portal_->setOutputInterface(output_interface_); result = SOCKET_OPTION_SET; - break; + } + break; + case GeneralTransportOptions::FEC_TYPE: + fec_setting_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; - default: - return result; - } + default: + return result; } return result; } @@ -461,6 +469,29 @@ class ConsumerSocket : public Socket { }); } + int setSocketOption(int socket_option_key, + StrategyCallback socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + StrategyCallback socket_option_value) -> int { + switch (socket_option_key) { + case ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE: + on_fwd_strategy_ = socket_option_value; + break; + case ConsumerCallbacksOptions::REC_STRATEGY_CHANGE: + on_rec_strategy_ = socket_option_value; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; + }); + } + int getSocketOption(int socket_option_key, double &socket_option_value) { utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { @@ -516,6 +547,10 @@ class ConsumerSocket : public Socket { socket_option_value = interest_lifetime_; break; + case GeneralTransportOptions::MAX_UNVERIFIED_TIME: + socket_option_value = max_unverified_delay_; + break; + case RaaqmTransportOptions::SAMPLE_NUMBER: socket_option_value = sample_number_; break; @@ -532,6 +567,10 @@ class ConsumerSocket : public Socket { socket_option_value = timer_interval_milliseconds_; break; + case RtcTransportOptions::RECOVERY_STRATEGY: + socket_option_value = recovery_strategy_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -553,6 +592,10 @@ class ConsumerSocket : public Socket { socket_option_value = reset_window_; break; + case RtcTransportOptions::AGGREGATED_DATA: + socket_option_value = aggregated_data_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -628,20 +671,6 @@ class ConsumerSocket : public Socket { } int getSocketOption(int socket_option_key, - std::shared_ptr<core::Portal> &socket_option_value) { - switch (socket_option_key) { - case PORTAL: - socket_option_value = portal_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - } - - int getSocketOption(int socket_option_key, IcnObserver **socket_option_value) { utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { @@ -665,7 +694,6 @@ class ConsumerSocket : public Socket { default: return SOCKET_OPTION_NOT_GET; } - return SOCKET_OPTION_GET; } @@ -674,6 +702,9 @@ class ConsumerSocket : public Socket { case DataLinkOptions::OUTPUT_INTERFACE: socket_option_value = output_interface_; break; + case GeneralTransportOptions::FEC_TYPE: + socket_option_value = fec_setting_; + break; default: return SOCKET_OPTION_NOT_GET; } @@ -714,6 +745,29 @@ class ConsumerSocket : public Socket { }); } + int getSocketOption(int socket_option_key, + StrategyCallback **socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + StrategyCallback **socket_option_value) -> int { + switch (socket_option_key) { + case ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE: + *socket_option_value = &on_fwd_strategy_; + break; + case ConsumerCallbacksOptions::REC_STRATEGY_CHANGE: + *socket_option_value = &on_rec_strategy_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; + }); + } + protected: template <typename Lambda, typename arg2> int rescheduleOnIOService(int socket_option_key, arg2 socket_option_value, @@ -726,9 +780,9 @@ class ConsumerSocket : public Socket { /* Condition variable for the wait */ std::condition_variable cv; bool done = false; - portal_->getIoService().dispatch([&socket_option_key, - &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getThread().tryRunHandlerNow([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock<std::mutex> lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -748,8 +802,6 @@ class ConsumerSocket : public Socket { protected: interface::ConsumerSocket *consumer_interface_; - utils::EventThread async_downloader_; - // No need to protect from multiple accesses in the async consumer // The parameter is accessible only with a getSocketOption and // set from the consume @@ -776,6 +828,7 @@ class ConsumerSocket : public Socket { int rate_estimation_choice_; // Verification parameters + int max_unverified_delay_; std::shared_ptr<auth::Verifier> verifier_; transport::auth::KeyId *key_id_; std::atomic_bool verify_signature_; @@ -787,17 +840,26 @@ class ConsumerSocket : public Socket { ConsumerInterestCallback on_interest_satisfied_; ConsumerContentObjectCallback on_content_object_input_; ConsumerTimerCallback stats_summary_; + StrategyCallback on_fwd_strategy_; + StrategyCallback on_rec_strategy_; ReadCallback *read_callback_; uint32_t timer_interval_milliseconds_; // Transport protocol - std::unique_ptr<protocol::TransportProtocol> transport_protocol_; + std::shared_ptr<protocol::TransportProtocol> transport_protocol_; // Statistic TransportStatistics stats_; + // RTC protocol + RtcTransportRecoveryStrategies recovery_strategy_; + bool aggregated_data_; + + // FEC setting + std::string fec_setting_; + utils::SpinLock guard_raaqm_params_; std::string output_interface_; }; diff --git a/libtransport/src/implementation/socket_producer.h b/libtransport/src/implementation/socket_producer.h index 9daf79b9d..37151d497 100644 --- a/libtransport/src/implementation/socket_producer.h +++ b/libtransport/src/implementation/socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -40,6 +40,7 @@ namespace implementation { using namespace core; using namespace interface; +using ProducerCallback = interface::ProducerSocket::Callback; class ProducerSocket : public Socket { private: @@ -48,11 +49,14 @@ class ProducerSocket : public Socket { : Socket(std::move(portal)), producer_interface_(producer_socket), data_packet_size_(default_values::content_object_packet_size), + max_segment_size_(default_values::content_object_packet_size), content_object_expiry_time_(default_values::content_object_expiry_time), - async_thread_(), - making_manifest_(false), + making_manifest_(default_values::manifest_capacity), hash_algorithm_(auth::CryptoHashType::SHA256), - suffix_strategy_(core::NextSegmentCalculationStrategy::INCREMENTAL), + signer_(std::make_shared<auth::VoidSigner>()), + suffix_strategy_(std::make_shared<utils::IncrementalSuffixStrategy>(0)), + aggregated_data_(false), + fec_setting_(""), on_interest_input_(VOID_HANDLER), on_interest_dropped_input_buffer_(VOID_HANDLER), on_interest_inserted_input_buffer_(VOID_HANDLER), @@ -63,29 +67,30 @@ class ProducerSocket : public Socket { on_content_object_in_output_buffer_(VOID_HANDLER), on_content_object_output_(VOID_HANDLER), on_content_object_evicted_from_output_buffer_(VOID_HANDLER), - on_content_produced_(VOID_HANDLER) { + on_content_produced_(VOID_HANDLER), + application_callback_(nullptr) { switch (protocol) { case ProductionProtocolAlgorithms::RTC_PROD: production_protocol_ = - std::make_unique<protocol::RTCProductionProtocol>(this); + std::make_shared<protocol::RTCProductionProtocol>(this); break; case ProductionProtocolAlgorithms::BYTE_STREAM: default: production_protocol_ = - std::make_unique<protocol::ByteStreamProductionProtocol>(this); + std::make_shared<protocol::ByteStreamProductionProtocol>(this); break; } } public: ProducerSocket(interface::ProducerSocket *producer, int protocol) - : ProducerSocket(producer, protocol, std::make_shared<core::Portal>()) {} + : ProducerSocket(producer, protocol, core::Portal::createShared()) { + is_async_ = true; + } ProducerSocket(interface::ProducerSocket *producer, int protocol, - asio::io_service &io_service) - : ProducerSocket(producer, protocol, - std::make_shared<core::Portal>(io_service)) { - is_async_ = true; + ::utils::EventThread &worker) + : ProducerSocket(producer, protocol, core::Portal::createShared(worker)) { } virtual ~ProducerSocket() {} @@ -98,31 +103,9 @@ class ProducerSocket : public Socket { producer_interface_ = producer_socket; } - void connect() override { - portal_->connect(false); - production_protocol_->start(); - } - - bool isRunning() override { return !production_protocol_->isRunning(); }; + void connect() override { portal_->connect(false); } - virtual void asyncProduce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr) { - if (!async_thread_.stopped()) { - auto a = buffer.release(); - async_thread_.add([this, content_name, a, is_last, offset, - last_segment]() { - auto buf = std::unique_ptr<utils::MemBuf>(a); - if (last_segment != NULL) { - **last_segment = offset + produceStream(content_name, std::move(buf), - is_last, offset); - } else { - produceStream(content_name, std::move(buf), is_last, offset); - } - }); - } - } + bool isRunning() override { return production_protocol_->isRunning(); }; virtual uint32_t produceStream(const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer, @@ -156,12 +139,38 @@ class ProducerSocket : public Socket { production_protocol_->produce(content_object); } + void sendMapme() { production_protocol_->sendMapme(); } + void registerPrefix(const Prefix &producer_namespace) { - production_protocol_->registerNamespaceWithNetwork(producer_namespace); + portal_->registerRoute(producer_namespace); } + void start() { production_protocol_->start(); } void stop() { production_protocol_->stop(); } + using Socket::getSocketOption; + using Socket::setSocketOption; + + virtual int setSocketOption(int socket_option_key, + ProducerCallback *socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + ProducerCallback *socket_option_value) -> int { + switch (socket_option_key) { + case ProducerCallbacksOptions::PRODUCER_CALLBACK: + application_callback_ = socket_option_value; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; + }); + } + virtual int setSocketOption(int socket_option_key, uint32_t socket_option_value) { switch (socket_option_key) { @@ -172,6 +181,17 @@ class ProducerSocket : public Socket { } break; + case GeneralTransportOptions::MAKE_MANIFEST: + making_manifest_ = socket_option_value; + break; + + case GeneralTransportOptions::MAX_SEGMENT_SIZE: + if (socket_option_value <= default_values::max_content_object_size && + socket_option_value > 0) { + max_segment_size_ = socket_option_value; + } + break; + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: production_protocol_->setOutputBufferSize(socket_option_value); break; @@ -260,8 +280,8 @@ class ProducerSocket : public Socket { virtual int setSocketOption(int socket_option_key, bool socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::MAKE_MANIFEST: - making_manifest_ = socket_option_value; + case RtcTransportOptions::AGGREGATED_DATA: + aggregated_data_ = socket_option_value; break; default: @@ -385,7 +405,7 @@ class ProducerSocket : public Socket { virtual int setSocketOption( int socket_option_key, - core::NextSegmentCalculationStrategy socket_option_value) { + const std::shared_ptr<utils::SuffixStrategy> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SUFFIX_STRATEGY: suffix_strategy_ = socket_option_value; @@ -413,9 +433,33 @@ class ProducerSocket : public Socket { return SOCKET_OPTION_SET; } + int getSocketOption(int socket_option_key, + ProducerCallback **socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + ProducerCallback **socket_option_value) -> int { + switch (socket_option_key) { + case ProducerCallbacksOptions::PRODUCER_CALLBACK: + *socket_option_value = application_callback_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; + }); + } + virtual int getSocketOption(int socket_option_key, uint32_t &socket_option_value) { switch (socket_option_key) { + case GeneralTransportOptions::MAKE_MANIFEST: + socket_option_value = making_manifest_; + break; + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: socket_option_value = production_protocol_->getOutputBufferSize(); break; @@ -424,6 +468,10 @@ class ProducerSocket : public Socket { socket_option_value = (uint32_t)data_packet_size_; break; + case GeneralTransportOptions::MAX_SEGMENT_SIZE: + socket_option_value = (uint32_t)max_segment_size_; + break; + case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: socket_option_value = content_object_expiry_time_; break; @@ -438,14 +486,14 @@ class ProducerSocket : public Socket { virtual int getSocketOption(int socket_option_key, bool &socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::MAKE_MANIFEST: - socket_option_value = making_manifest_; - break; - case GeneralTransportOptions::ASYNC_MODE: socket_option_value = is_async_; break; + case RtcTransportOptions::AGGREGATED_DATA: + socket_option_value = aggregated_data_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -547,21 +595,6 @@ class ProducerSocket : public Socket { }); } - virtual int getSocketOption( - int socket_option_key, - std::shared_ptr<core::Portal> &socket_option_value) { - switch (socket_option_key) { - case PORTAL: - socket_option_value = portal_; - break; - default: - return SOCKET_OPTION_NOT_GET; - ; - } - - return SOCKET_OPTION_GET; - } - virtual int getSocketOption(int socket_option_key, auth::CryptoHashType &socket_option_value) { switch (socket_option_key) { @@ -577,7 +610,7 @@ class ProducerSocket : public Socket { virtual int getSocketOption( int socket_option_key, - core::NextSegmentCalculationStrategy &socket_option_value) { + std::shared_ptr<utils::SuffixStrategy> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SUFFIX_STRATEGY: socket_option_value = suffix_strategy_; @@ -603,9 +636,31 @@ class ProducerSocket : public Socket { return SOCKET_OPTION_GET; } + int getSocketOption(int socket_option_key, std::string &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::FEC_TYPE: + socket_option_value = fec_setting_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; + } + virtual int setSocketOption(int socket_option_key, const std::string &socket_option_value) { - return SOCKET_OPTION_NOT_SET; + int result = SOCKET_OPTION_NOT_SET; + switch (socket_option_key) { + case GeneralTransportOptions::FEC_TYPE: + fec_setting_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; + + default: + return result; + } + return result; } // If the thread calling lambda_func is not the same of io_service, this @@ -623,9 +678,9 @@ class ProducerSocket : public Socket { std::condition_variable cv; bool done = false; - portal_->getIoService().dispatch([&socket_option_key, - &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getThread().tryRunHandlerNow([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock<std::mutex> lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -655,9 +710,9 @@ class ProducerSocket : public Socket { /* Condition variable for the wait */ std::condition_variable cv; bool done = false; - portal_->getIoService().dispatch([&socket_option_key, - &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getThread().tryRunHandlerNow([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock<std::mutex> lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -677,20 +732,24 @@ class ProducerSocket : public Socket { // Threads protected: interface::ProducerSocket *producer_interface_; - asio::io_service io_service_; std::atomic<size_t> data_packet_size_; + std::atomic<size_t> max_segment_size_; std::atomic<uint32_t> content_object_expiry_time_; - utils::EventThread async_thread_; - - std::atomic<bool> making_manifest_; + std::atomic<uint32_t> making_manifest_; std::atomic<auth::CryptoHashType> hash_algorithm_; std::atomic<auth::CryptoSuite> crypto_suite_; utils::SpinLock signer_lock_; std::shared_ptr<auth::Signer> signer_; - core::NextSegmentCalculationStrategy suffix_strategy_; + std::shared_ptr<utils::SuffixStrategy> suffix_strategy_; + + std::shared_ptr<protocol::ProductionProtocol> production_protocol_; - std::unique_ptr<protocol::ProductionProtocol> production_protocol_; + // RTC transport + bool aggregated_data_; + + // FEC setting + std::string fec_setting_; // callbacks ProducerInterestCallback on_interest_input_; @@ -706,6 +765,8 @@ class ProducerSocket : public Socket { ProducerContentObjectCallback on_content_object_evicted_from_output_buffer_; ProducerContentCallback on_content_produced_; + + ProducerCallback *application_callback_; }; } // namespace implementation diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.cc b/libtransport/src/implementation/tls_rtc_socket_producer.cc index db62b10c1..06d613ef0 100644 --- a/libtransport/src/implementation/tls_rtc_socket_producer.cc +++ b/libtransport/src/implementation/tls_rtc_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -90,11 +90,11 @@ int TLSRTCProducerSocket::writeOld(BIO *b, const char *buf, int num) { socket = (TLSRTCProducerSocket *)BIO_get_data(b); if (socket->getHandshakeState() != SERVER_FINISHED && socket->first_) { - bool making_manifest = socket->parent_->making_manifest_; + uint32_t making_manifest = socket->parent_->making_manifest_; socket->tls_chunks_--; socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - false); + 0U); socket->parent_->ProducerSocket::produce( socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, 0); socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.h b/libtransport/src/implementation/tls_rtc_socket_producer.h index 92c657afc..f6dc425e4 100644 --- a/libtransport/src/implementation/tls_rtc_socket_producer.h +++ b/libtransport/src/implementation/tls_rtc_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/implementation/tls_socket_consumer.cc b/libtransport/src/implementation/tls_socket_consumer.cc index 65472b41d..b368c4b88 100644 --- a/libtransport/src/implementation/tls_socket_consumer.cc +++ b/libtransport/src/implementation/tls_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -43,7 +43,7 @@ int readOldTLS(BIO *b, char *buf, int size) { if (!socket->something_to_read_) { if (!socket->transport_protocol_->isRunning()) { socket->network_name_.setSuffix(socket->random_suffix_); - socket->ConsumerSocket::asyncConsume(socket->network_name_); + socket->ConsumerSocket::consume(socket->network_name_); } if (!socket->something_to_read_) socket->cv_.wait(lck); @@ -284,31 +284,6 @@ int TLSConsumerSocket::download_content(const Name &name) { return CONSUMER_FINISHED; } -int TLSConsumerSocket::asyncConsume(const Name &name, - std::unique_ptr<utils::MemBuf> &&buffer) { - this->payload_ = std::move(buffer); - - this->ConsumerSocket::setSocketOption( - ConsumerCallbacksOptions::INTEREST_OUTPUT, - (ConsumerInterestCallback)std::bind( - &TLSConsumerSocket::setInterestPayload, this, std::placeholders::_1, - std::placeholders::_2)); - - return asyncConsume(name); -} - -int TLSConsumerSocket::asyncConsume(const Name &name) { - if ((SSL_in_before(this->ssl_) || SSL_in_init(this->ssl_))) { - throw errors::RuntimeException("Handshake not performed"); - } - - if (!async_downloader_tls_.stopped()) { - async_downloader_tls_.add([this, name]() { download_content(name); }); - } - - return CONSUMER_RUNNING; -} - void TLSConsumerSocket::registerPrefix(const Prefix &producer_namespace) { producer_namespace_ = producer_namespace; } @@ -353,7 +328,7 @@ void TLSConsumerSocket::readBufferAvailable( cv_.notify_one(); } -void TLSConsumerSocket::readError(const std::error_code ec) noexcept {} +void TLSConsumerSocket::readError(const std::error_code &ec) noexcept {} void TLSConsumerSocket::readSuccess(std::size_t total_size) noexcept { std::unique_lock<std::mutex> lck(this->mtx_); diff --git a/libtransport/src/implementation/tls_socket_consumer.h b/libtransport/src/implementation/tls_socket_consumer.h index be08ec47d..a74f1ee10 100644 --- a/libtransport/src/implementation/tls_socket_consumer.h +++ b/libtransport/src/implementation/tls_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -47,9 +47,6 @@ class TLSConsumerSocket : public ConsumerSocket, int consume(const Name &name, std::unique_ptr<utils::MemBuf> &&buffer); int consume(const Name &name) override; - int asyncConsume(const Name &name, std::unique_ptr<utils::MemBuf> &&buffer); - int asyncConsume(const Name &name) override; - void registerPrefix(const Prefix &producer_namespace); int setSocketOption( @@ -100,7 +97,7 @@ class TLSConsumerSocket : public ConsumerSocket, virtual void readBufferAvailable( std::unique_ptr<utils::MemBuf> &&buffer) noexcept override; - virtual void readError(const std::error_code ec) noexcept override; + virtual void readError(const std::error_code &ec) noexcept override; virtual void readSuccess(std::size_t total_size) noexcept override; diff --git a/libtransport/src/implementation/tls_socket_producer.cc b/libtransport/src/implementation/tls_socket_producer.cc index 3992ca45c..47f3b43a6 100644 --- a/libtransport/src/implementation/tls_socket_producer.cc +++ b/libtransport/src/implementation/tls_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -99,12 +99,12 @@ int TLSProducerSocket::writeOld(BIO *b, const char *buf, int num) { socket = (TLSProducerSocket *)BIO_get_data(b); if (socket->getHandshakeState() != SERVER_FINISHED && socket->first_) { - bool making_manifest = socket->parent_->making_manifest_; + uint32_t making_manifest = socket->parent_->making_manifest_; //! socket->tls_chunks_ corresponds to is_last socket->tls_chunks_--; socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - false); + 0U); socket->parent_->ProducerSocket::produceStream( socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, socket->last_segment_); @@ -358,7 +358,7 @@ uint32_t TLSProducerSocket::produceStream( } size_t buf_size = buffer->length(); - name_ = production_protocol_->getNamespaces().front().mapName(content_name); + name_ = portal_->getServedNamespaces().begin()->mapName(content_name); tls_chunks_ = to_call_oncontentproduced_ = (int)ceil((float)buf_size / (float)SSL3_RT_MAX_PLAIN_LENGTH); @@ -394,8 +394,7 @@ int TLSProducerSocket::addHicnKeyIdCb(SSL *s, unsigned int ext_type, << "On addHicnKeyIdCb, for the prefix registration."; if (ext_type == 100) { - auto &prefix = - socket->parent_->production_protocol_->getNamespaces().front(); + auto &prefix = *socket->parent_->portal_->getServedNamespaces().begin(); const ip_prefix_t &ip_prefix = prefix.toIpPrefixStruct(); int inet_family = prefix.getAddressFamily(); uint16_t prefix_len_bits = prefix.getPrefixLength(); diff --git a/libtransport/src/implementation/tls_socket_producer.h b/libtransport/src/implementation/tls_socket_producer.h index a542a4d9f..0e958b321 100644 --- a/libtransport/src/implementation/tls_socket_producer.h +++ b/libtransport/src/implementation/tls_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/CMakeLists.txt b/libtransport/src/interfaces/CMakeLists.txt index 7ec024fec..0a0603ac8 100644 --- a/libtransport/src/interfaces/CMakeLists.txt +++ b/libtransport/src/interfaces/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/callbacks.cc b/libtransport/src/interfaces/callbacks.cc index 6869ac3f7..776b4cbe7 100644 --- a/libtransport/src/interfaces/callbacks.cc +++ b/libtransport/src/interfaces/callbacks.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/p2psecure_socket_consumer.cc b/libtransport/src/interfaces/p2psecure_socket_consumer.cc index e473a1e2e..e329a50f1 100644 --- a/libtransport/src/interfaces/p2psecure_socket_consumer.cc +++ b/libtransport/src/interfaces/p2psecure_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/p2psecure_socket_producer.cc b/libtransport/src/interfaces/p2psecure_socket_producer.cc index 10d8a1367..5f98302d0 100644 --- a/libtransport/src/interfaces/p2psecure_socket_producer.cc +++ b/libtransport/src/interfaces/p2psecure_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -23,10 +23,11 @@ P2PSecureProducerSocket::P2PSecureProducerSocket() { socket_ = std::make_unique<implementation::P2PSecureProducerSocket>(this); } -P2PSecureProducerSocket::P2PSecureProducerSocket( - bool rtc, const std::shared_ptr<auth::Identity> &identity) { - socket_ = std::make_unique<implementation::P2PSecureProducerSocket>(this, rtc, - identity); +P2PSecureProducerSocket::P2PSecureProducerSocket(bool rtc, + std::string &keystore_path, + std::string &keystore_pwd) { + socket_ = std::make_unique<implementation::P2PSecureProducerSocket>( + this, rtc, keystore_path, keystore_pwd); } } // namespace interface diff --git a/libtransport/src/interfaces/portal.cc b/libtransport/src/interfaces/portal.cc index 9db0621f6..84634a282 100644 --- a/libtransport/src/interfaces/portal.cc +++ b/libtransport/src/interfaces/portal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -13,83 +13,106 @@ * limitations under the License. */ +#include <core/portal.h> #include <hicn/transport/interfaces/portal.h> -#include <implementation/socket.h> namespace transport { namespace interface { -Portal::Portal() { implementation_ = new core::Portal(); } +class Portal::Impl { + public: + Impl() : portal_(core::Portal::createShared()) {} + Impl(::utils::EventThread &worker) + : portal_(core::Portal::createShared(worker)) {} -Portal::Portal(asio::io_service &io_service) { - implementation_ = new core::Portal(io_service); -} + void registerTransportCallback(TransportCallback *transport_callback) { + portal_->registerTransportCallback(transport_callback); + } + + void connect(bool is_consumer) { portal_->connect(is_consumer); } + + bool interestIsPending(const core::Name &name) { + return portal_->interestIsPending(name); + } + + void sendInterest(core::Interest::Ptr &&interest, + OnContentObjectCallback &&on_content_object_callback, + OnInterestTimeoutCallback &&on_interest_timeout_callback) { + portal_->sendInterest(std::move(interest), + std::move(on_content_object_callback), + std::move(on_interest_timeout_callback)); + } + + void sendContentObject(core::ContentObject &content_object) { + portal_->sendContentObject(content_object); + } + + void killConnection() { portal_->killConnection(); } + + void clear() { portal_->clear(); } -Portal::~Portal() { delete reinterpret_cast<core::Portal *>(implementation_); } + utils::EventThread &getThread() { return portal_->getThread(); } -void Portal::setConsumerCallback(ConsumerCallback *consumer_callback) { - reinterpret_cast<core::Portal *>(implementation_) - ->setConsumerCallback(consumer_callback); + void registerRoute(core::Prefix &prefix) { portal_->registerRoute(prefix); } + + void sendMapme() { portal_->sendMapme(); } + + void setForwardingStrategy(core::Prefix &prefix, std::string &strategy) { + portal_->setForwardingStrategy(prefix, strategy); + } + + private: + std::shared_ptr<core::Portal> portal_; +}; + +Portal::Portal() { implementation_ = new Impl(); } + +Portal::Portal(::utils::EventThread &worker) { + implementation_ = new Impl(worker); } -void Portal::setProducerCallback(ProducerCallback *producer_callback) { - reinterpret_cast<core::Portal *>(implementation_) - ->setProducerCallback(producer_callback); +Portal::~Portal() { delete implementation_; } + +void Portal::registerTransportCallback(TransportCallback *transport_callback) { + implementation_->registerTransportCallback(transport_callback); } void Portal::connect(bool is_consumer) { - reinterpret_cast<core::Portal *>(implementation_)->connect(is_consumer); + implementation_->connect(is_consumer); } bool Portal::interestIsPending(const core::Name &name) { - return reinterpret_cast<core::Portal *>(implementation_) - ->interestIsPending(name); + return implementation_->interestIsPending(name); } void Portal::sendInterest( core::Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object_callback, OnInterestTimeoutCallback &&on_interest_timeout_callback) { - reinterpret_cast<core::Portal *>(implementation_) - ->sendInterest(std::move(interest), std::move(on_content_object_callback), - std::move(on_interest_timeout_callback)); -} - -void Portal::bind(const BindConfig &config) { - reinterpret_cast<core::Portal *>(implementation_)->bind(config); -} - -void Portal::runEventsLoop() { - reinterpret_cast<core::Portal *>(implementation_)->runEventsLoop(); -} - -void Portal::runOneEvent() { - reinterpret_cast<core::Portal *>(implementation_)->runOneEvent(); + implementation_->sendInterest(std::move(interest), + std::move(on_content_object_callback), + std::move(on_interest_timeout_callback)); } void Portal::sendContentObject(core::ContentObject &content_object) { - reinterpret_cast<core::Portal *>(implementation_) - ->sendContentObject(content_object); + implementation_->sendContentObject(content_object); } -void Portal::stopEventsLoop() { - reinterpret_cast<core::Portal *>(implementation_)->stopEventsLoop(); -} +void Portal::killConnection() { implementation_->killConnection(); } -void Portal::killConnection() { - reinterpret_cast<core::Portal *>(implementation_)->killConnection(); -} +void Portal::clear() { implementation_->clear(); } -void Portal::clear() { - reinterpret_cast<core::Portal *>(implementation_)->clear(); -} +utils::EventThread &Portal::getThread() { return implementation_->getThread(); } -asio::io_service &Portal::getIoService() { - return reinterpret_cast<core::Portal *>(implementation_)->getIoService(); +void Portal::registerRoute(core::Prefix &prefix) { + implementation_->registerRoute(prefix); } -void Portal::registerRoute(core::Prefix &prefix) { - reinterpret_cast<core::Portal *>(implementation_)->registerRoute(prefix); +void Portal::sendMapme() { implementation_->sendMapme(); } + +void Portal::setForwardingStrategy(core::Prefix &prefix, + std::string &strategy) { + implementation_->setForwardingStrategy(prefix, strategy); } } // namespace interface diff --git a/libtransport/src/interfaces/socket_consumer.cc b/libtransport/src/interfaces/socket_consumer.cc index 4eee73cab..747dc0974 100644 --- a/libtransport/src/interfaces/socket_consumer.cc +++ b/libtransport/src/interfaces/socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -23,23 +23,28 @@ ConsumerSocket::ConsumerSocket(int protocol) { socket_ = std::make_unique<implementation::ConsumerSocket>(this, protocol); } -ConsumerSocket::ConsumerSocket(int protocol, asio::io_service &io_service) { - socket_ = std::make_unique<implementation::ConsumerSocket>(this, protocol, - io_service); +ConsumerSocket::ConsumerSocket(int protocol, ::utils::EventThread &worker) { + socket_ = + std::make_unique<implementation::ConsumerSocket>(this, protocol, worker); } ConsumerSocket::ConsumerSocket() {} -ConsumerSocket::~ConsumerSocket() { socket_->stop(); } +ConsumerSocket::ConsumerSocket(ConsumerSocket &&other) noexcept + : socket_(std::move(other.socket_)) {} + +ConsumerSocket::~ConsumerSocket() { + if (socket_) { + socket_->stop(); + } +} void ConsumerSocket::connect() { socket_->connect(); } bool ConsumerSocket::isRunning() { return socket_->isRunning(); } -int ConsumerSocket::consume(const Name &name) { return socket_->consume(name); } - -int ConsumerSocket::asyncConsume(const Name &name) { - return socket_->asyncConsume(name); +int ConsumerSocket::consume(const Name &name, bool blocking) { + return socket_->consume(name); } void ConsumerSocket::stop() { socket_->stop(); } @@ -111,6 +116,16 @@ int ConsumerSocket::setSocketOption(int socket_option_key, return socket_->setSocketOption(socket_option_key, socket_option_value); } +int ConsumerSocket::setSocketOption(int socket_option_key, + StrategyCallback socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + Packet::Format socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + int ConsumerSocket::getSocketOption(int socket_option_key, double &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); @@ -169,6 +184,16 @@ int ConsumerSocket::getSocketOption( return socket_->getSocketOption(socket_option_key, socket_option_value); } +int ConsumerSocket::getSocketOption(int socket_option_key, + StrategyCallback **socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + Packet::Format &socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + } // namespace interface } // namespace transport diff --git a/libtransport/src/interfaces/socket_producer.cc b/libtransport/src/interfaces/socket_producer.cc index b04947dfd..10613c0e1 100644 --- a/libtransport/src/interfaces/socket_producer.cc +++ b/libtransport/src/interfaces/socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -33,14 +33,21 @@ ProducerSocket::ProducerSocket(int protocol) { socket_ = std::make_unique<implementation::ProducerSocket>(this, protocol); } -ProducerSocket::ProducerSocket(int protocol, asio::io_service &io_service) { - socket_ = std::make_unique<implementation::ProducerSocket>(this, protocol, - io_service); +ProducerSocket::ProducerSocket(int protocol, ::utils::EventThread &worker) { + socket_ = + std::make_unique<implementation::ProducerSocket>(this, protocol, worker); } +ProducerSocket::ProducerSocket(ProducerSocket &&other) noexcept + : socket_(std::move(other.socket_)) {} + ProducerSocket::ProducerSocket(bool) {} -ProducerSocket::~ProducerSocket() { socket_->stop(); } +ProducerSocket::~ProducerSocket() { + if (socket_) { + socket_->stop(); + } +} void ProducerSocket::connect() { socket_->connect(); } @@ -76,18 +83,14 @@ void ProducerSocket::produce(ContentObject &content_object) { return socket_->produce(content_object); } -void ProducerSocket::asyncProduce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment) { - return socket_->asyncProduce(content_name, std::move(buffer), is_last, offset, - last_segment); -} +void ProducerSocket::sendMapme() { return socket_->sendMapme(); } void ProducerSocket::registerPrefix(const Prefix &producer_namespace) { return socket_->registerPrefix(producer_namespace); } +void ProducerSocket::start() { return socket_->start(); } + void ProducerSocket::stop() { return socket_->stop(); } asio::io_service &ProducerSocket::getIoService() { @@ -95,6 +98,11 @@ asio::io_service &ProducerSocket::getIoService() { }; int ProducerSocket::setSocketOption(int socket_option_key, + Callback *socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + +int ProducerSocket::setSocketOption(int socket_option_key, uint32_t socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } @@ -140,9 +148,14 @@ int ProducerSocket::setSocketOption( return socket_->setSocketOption(socket_option_key, socket_option_value); } +int ProducerSocket::setSocketOption(int socket_option_key, + Packet::Format socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + int ProducerSocket::getSocketOption(int socket_option_key, uint32_t &socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); + return socket_->getSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption(int socket_option_key, @@ -181,6 +194,11 @@ int ProducerSocket::getSocketOption( return socket_->getSocketOption(socket_option_key, socket_option_value); } +int ProducerSocket::getSocketOption(int socket_option_key, + Packet::Format &socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + } // namespace interface } // namespace transport diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.cc b/libtransport/src/interfaces/tls_rtc_socket_producer.cc index 7326fcbcb..6bf1b011c 100644 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.cc +++ b/libtransport/src/interfaces/tls_rtc_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.h b/libtransport/src/interfaces/tls_rtc_socket_producer.h index 3ea84095b..b8b6ec298 100644 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.h +++ b/libtransport/src/interfaces/tls_rtc_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/tls_socket_consumer.cc b/libtransport/src/interfaces/tls_socket_consumer.cc index 6c1c535b5..24060d1d8 100644 --- a/libtransport/src/interfaces/tls_socket_consumer.cc +++ b/libtransport/src/interfaces/tls_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/tls_socket_consumer.h b/libtransport/src/interfaces/tls_socket_consumer.h index 845a9181f..242dc91a5 100644 --- a/libtransport/src/interfaces/tls_socket_consumer.h +++ b/libtransport/src/interfaces/tls_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/tls_socket_producer.cc b/libtransport/src/interfaces/tls_socket_producer.cc index 037702f72..b2b9e723a 100644 --- a/libtransport/src/interfaces/tls_socket_producer.cc +++ b/libtransport/src/interfaces/tls_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/interfaces/tls_socket_producer.h b/libtransport/src/interfaces/tls_socket_producer.h index 3c662176a..9b31cb483 100644 --- a/libtransport/src/interfaces/tls_socket_producer.h +++ b/libtransport/src/interfaces/tls_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/io_modules/CMakeLists.txt b/libtransport/src/io_modules/CMakeLists.txt index 29aec236a..f4143de04 100644 --- a/libtransport/src/io_modules/CMakeLists.txt +++ b/libtransport/src/io_modules/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -11,25 +11,69 @@ # See the License for the specific language governing permissions and # limitations under the License. + +############################################################## +# Android case: no submodules +############################################################## if (${CMAKE_SYSTEM_NAME} MATCHES Android) list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/udp/hicn_forwarder_module.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp/udp_socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hicn-light-ng/hicn_forwarder_module.cc ) list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/udp/hicn_forwarder_module.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp/udp_socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn-light-ng/hicn_forwarder_module.h + ) + + if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(Libhicnctrl ${CURRENT_VERSION} REQUIRED NO_MODULE) + + if (DISABLE_SHARED_LIBRARIES) + set(LIBTYPE static) + else() + set(LIBTYPE shared) + endif() + + list(APPEND LIBHICNCTRL_LIBRARIES hicn::hicnctrl.${LIBTYPE}) + else() + if (DISABLE_SHARED_LIBRARIES) + if (WIN32) + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC}) + else () + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC} log) + endif () + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_STATIC} + ) + else() + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_SHARED}) + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_SHARED} + ) + endif() + endif() + + list(APPEND LIBRARIES + PRIVATE ${LIBHICNCTRL_LIBRARIES} + ) + + list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS + PUBLIC + $<BUILD_INTERFACE:${LIBHICNCTRL_INCLUDE_DIRS}> ) set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) + set(LIBRARIES ${LIBRARIES} PARENT_SCOPE) + set(LIBTRANSPORT_INTERNAL_INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} PARENT_SCOPE) else() - add_subdirectory(udp) +############################################################## +# Compile submodules +############################################################## + add_subdirectory(hicn-light-ng) add_subdirectory(loopback) add_subdirectory(forwarder) if (__vpp__) add_subdirectory(memif) endif() -endif()
\ No newline at end of file +endif() diff --git a/libtransport/src/io_modules/forwarder/CMakeLists.txt b/libtransport/src/io_modules/forwarder/CMakeLists.txt index a1d0c5db5..3922316d3 100644 --- a/libtransport/src/io_modules/forwarder/CMakeLists.txt +++ b/libtransport/src/io_modules/forwarder/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -17,8 +17,6 @@ list(APPEND MODULE_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.h ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.h ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel_listener.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel.h ${CMAKE_CURRENT_SOURCE_DIR}/global_counter.h ) @@ -26,16 +24,13 @@ list(APPEND MODULE_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.cc ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel_listener.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel.cc ) build_module(forwarder_module - SHARED SOURCES ${MODULE_SOURCE_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT}-io-modules - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} ${Libhicn_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ) diff --git a/libtransport/src/io_modules/forwarder/errors.cc b/libtransport/src/io_modules/forwarder/errors.cc index b5f131499..6e93d0453 100644 --- a/libtransport/src/io_modules/forwarder/errors.cc +++ b/libtransport/src/io_modules/forwarder/errors.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #include <io_modules/forwarder/errors.h> diff --git a/libtransport/src/io_modules/forwarder/forwarder.cc b/libtransport/src/io_modules/forwarder/forwarder.cc index 0546cb8b3..3ae5bf397 100644 --- a/libtransport/src/io_modules/forwarder/forwarder.cc +++ b/libtransport/src/io_modules/forwarder/forwarder.cc @@ -15,11 +15,11 @@ #include <core/global_configuration.h> #include <core/local_connector.h> +#include <core/udp_connector.h> +#include <core/udp_listener.h> #include <glog/logging.h> #include <io_modules/forwarder/forwarder.h> #include <io_modules/forwarder/global_id_counter.h> -#include <io_modules/forwarder/udp_tunnel.h> -#include <io_modules/forwarder/udp_tunnel_listener.h> namespace transport { @@ -89,11 +89,12 @@ void Forwarder::initConnectors() { Connector::Id Forwarder::registerLocalConnector( asio::io_service &io_service, Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback) { utils::SpinLock::Acquire locked(connector_lock_); auto id = GlobalCounter<Connector::Id>::getInstance().getNext(); auto connector = std::make_shared<LocalConnector>( - io_service, receive_callback, nullptr, nullptr, reconnect_callback); + io_service, receive_callback, sent_callback, nullptr, reconnect_callback); connector->setConnectorId(id); local_connectors_.emplace(id, std::move(connector)); return id; @@ -105,6 +106,7 @@ Forwarder &Forwarder::deleteConnector(Connector::Id id) { if (it != local_connectors_.end()) { it->second->close(); local_connectors_.erase(it); + } else { } return *this; @@ -120,9 +122,9 @@ Connector::Ptr Forwarder::getConnector(Connector::Id id) { return nullptr; } -void Forwarder::onPacketFromListener(Connector *connector, - utils::MemBuf &packet_buffer, - const std::error_code &ec) { +void Forwarder::onPacketFromListener( + Connector *connector, const std::vector<utils::MemBuf::Ptr> &packets, + const std::error_code &ec) { // Create connector connector->setReceiveCallback( std::bind(&Forwarder::onPacketReceived, this, std::placeholders::_1, @@ -135,37 +137,47 @@ void Forwarder::onPacketFromListener(Connector *connector, remote_connectors_.emplace(connector->getConnectorId(), connector->shared_from_this()); } + // TODO Check if control packet or not. For the moment it is not. - onPacketReceived(connector, packet_buffer, ec); + onPacketReceived(connector, packets, ec); } void Forwarder::onPacketReceived(Connector *connector, - utils::MemBuf &packet_buffer, + const std::vector<utils::MemBuf::Ptr> &packets, const std::error_code &ec) { - // Figure out the type of packet we received - bool is_interest = Packet::isInterest(packet_buffer.data()); - - Packet *packet = nullptr; - if (is_interest) { - packet = static_cast<Interest *>(&packet_buffer); - } else { - packet = static_cast<ContentObject *>(&packet_buffer); + if (ec) { + LOG(ERROR) << "Error receiving packet: " << ec.message(); + return; } - for (auto &c : local_connectors_) { - auto role = c.second->getRole(); - auto is_producer = role == Connector::Role::PRODUCER; - if ((is_producer && is_interest) || (!is_producer && !is_interest)) { - c.second->send(*packet); + for (auto &packet_buffer_ptr : packets) { + auto &packet_buffer = *packet_buffer_ptr; + + // Figure out the type of packet we received + bool is_interest = Packet::isInterest(packet_buffer.data()); + + Packet *packet = nullptr; + if (is_interest) { + packet = static_cast<Interest *>(&packet_buffer); } else { - LOG(ERROR) << "Error sending packet to local connector. is_interest = " - << is_interest << " - is_producer = " << is_producer; + packet = static_cast<ContentObject *>(&packet_buffer); + } + + for (auto &c : local_connectors_) { + auto role = c.second->getRole(); + auto is_producer = role == Connector::Role::PRODUCER; + if ((is_producer && is_interest) || (!is_producer && !is_interest)) { + c.second->send(*packet); + } else { + LOG(ERROR) << "Error sending packet to local connector. is_interest = " + << is_interest << " - is_producer = " << is_producer; + } } - } - // PCS Lookup + FIB lookup. Skip for now + // PCS Lookup + FIB lookup. Skip for now - // Forward packet to local connectors + // Forward packet to local connectors + } } void Forwarder::send(Packet &packet) { diff --git a/libtransport/src/io_modules/forwarder/forwarder.h b/libtransport/src/io_modules/forwarder/forwarder.h index 5b564bb5e..38b4260b3 100644 --- a/libtransport/src/io_modules/forwarder/forwarder.h +++ b/libtransport/src/io_modules/forwarder/forwarder.h @@ -15,13 +15,13 @@ #pragma once +#include <core/udp_listener.h> #include <hicn/transport/core/io_module.h> #include <hicn/transport/core/prefix.h> #include <hicn/transport/utils/event_thread.h> #include <hicn/transport/utils/singleton.h> #include <hicn/transport/utils/spinlock.h> #include <io_modules/forwarder/configuration.h> -#include <io_modules/forwarder/udp_tunnel_listener.h> #include <atomic> #include <libconfig.h++> @@ -31,9 +31,8 @@ namespace transport { namespace core { -class Forwarder : public utils::Singleton<Forwarder> { +class Forwarder { static constexpr char forwarder_config_section[] = "forwarder"; - friend class utils::Singleton<Forwarder>; public: Forwarder(); @@ -47,6 +46,7 @@ class Forwarder : public utils::Singleton<Forwarder> { Connector::Id registerLocalConnector( asio::io_service &io_service, Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback); Forwarder &deleteConnector(Connector::Id id); @@ -58,9 +58,11 @@ class Forwarder : public utils::Singleton<Forwarder> { void stop(); private: - void onPacketFromListener(Connector *connector, utils::MemBuf &packet_buffer, + void onPacketFromListener(Connector *connector, + const std::vector<utils::MemBuf::Ptr> &packets, const std::error_code &ec); - void onPacketReceived(Connector *connector, utils::MemBuf &packet_buffer, + void onPacketReceived(Connector *connector, + const std::vector<utils::MemBuf::Ptr> &packets, const std::error_code &ec); void onPacketSent(Connector *connector, const std::error_code &ec); void onConnectorClosed(Connector *connector); @@ -86,5 +88,20 @@ class Forwarder : public utils::Singleton<Forwarder> { Configuration config_; }; +class ForwarderGlobal : public ::utils::Singleton<ForwarderGlobal> { + friend class utils::Singleton<ForwarderGlobal>; + + public: + ~ForwarderGlobal() {} + std::shared_ptr<Forwarder> &getReference() { return forwarder_; } + + private: + ForwarderGlobal() : forwarder_(std::make_shared<Forwarder>()) {} + + private: + std::shared_ptr<Forwarder> forwarder_; +}; + } // namespace core -} // namespace transport
\ No newline at end of file + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.cc b/libtransport/src/io_modules/forwarder/forwarder_module.cc index 4f95b9ca0..0ced84ab4 100644 --- a/libtransport/src/io_modules/forwarder/forwarder_module.cc +++ b/libtransport/src/io_modules/forwarder/forwarder_module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -25,11 +25,10 @@ ForwarderModule::ForwarderModule() : IoModule(), name_(""), connector_id_(Connector::invalid_connector), - forwarder_(Forwarder::getInstance()) {} + forwarder_ptr_(ForwarderGlobal::getInstance().getReference()), + forwarder_(*forwarder_ptr_) {} -ForwarderModule::~ForwarderModule() { - forwarder_.deleteConnector(connector_id_); -} +ForwarderModule::~ForwarderModule() {} bool ForwarderModule::isConnected() { return true; } @@ -42,7 +41,7 @@ void ForwarderModule::send(Packet &packet) { // local_faces_.at(1 - local_id_).onPacket(packet); } -void ForwarderModule::send(const uint8_t *packet, std::size_t len) { +void ForwarderModule::send(const utils::MemBuf::Ptr &buffer) { // not supported throw errors::NotImplementedException(); } @@ -58,11 +57,13 @@ void ForwarderModule::closeConnection() { } void ForwarderModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name) { connector_id_ = forwarder_.registerLocalConnector( - io_service, std::move(receive_callback), std::move(reconnect_callback)); + io_service, std::move(receive_callback), std::move(sent_callback), + std::move(reconnect_callback)); name_ = app_name; } @@ -78,7 +79,9 @@ void ForwarderModule::connect(bool is_consumer) { std::uint32_t ForwarderModule::getMtu() { return interface_mtu; } -bool ForwarderModule::isControlMessage(const uint8_t *message) { return false; } +bool ForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { + return false; +} extern "C" IoModule *create_module(void) { return new ForwarderModule(); } diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.h b/libtransport/src/io_modules/forwarder/forwarder_module.h index 58bfb7996..52a12b67e 100644 --- a/libtransport/src/io_modules/forwarder/forwarder_module.h +++ b/libtransport/src/io_modules/forwarder/forwarder_module.h @@ -38,11 +38,12 @@ class ForwarderModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; @@ -51,15 +52,19 @@ class ForwarderModule : public IoModule { std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; void closeConnection() override; private: + static void initForwarder(); + + private: std::string name_; Connector::Id connector_id_; + std::shared_ptr<Forwarder> forwarder_ptr_; Forwarder &forwarder_; }; diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel.cc b/libtransport/src/io_modules/forwarder/udp_tunnel.cc deleted file mode 100644 index bf6a69b92..000000000 --- a/libtransport/src/io_modules/forwarder/udp_tunnel.cc +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - */ - -#include <glog/logging.h> -#include <hicn/transport/utils/branch_prediction.h> -#include <io_modules/forwarder/errors.h> -#include <io_modules/forwarder/udp_tunnel.h> - -#include <iostream> -#include <thread> -#include <vector> - -namespace transport { -namespace core { - -UdpTunnelConnector::~UdpTunnelConnector() {} - -void UdpTunnelConnector::connect(const std::string &hostname, uint16_t port, - const std::string &bind_address, - uint16_t bind_port) { - if (state_ == State::CLOSED) { - state_ = State::CONNECTING; - endpoint_iterator_ = resolver_.resolve({hostname, std::to_string(port)}); - remote_endpoint_send_ = *endpoint_iterator_; - socket_->open(remote_endpoint_send_.protocol()); - - if (!bind_address.empty() && bind_port != 0) { - using namespace asio::ip; - socket_->bind( - udp::endpoint(address::from_string(bind_address), bind_port)); - } - - state_ = State::CONNECTED; - - remote_endpoint_ = Endpoint(remote_endpoint_send_); - local_endpoint_ = Endpoint(socket_->local_endpoint()); - - doRecvPacket(); - -#ifdef LINUX - send_timer_.expires_from_now(std::chrono::microseconds(50)); - send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, - std::placeholders::_1)); -#endif - } -} - -void UdpTunnelConnector::send(Packet &packet) { - strand_->post([this, pkt{packet.shared_from_this()}]() { - bool write_in_progress = !output_buffer_.empty(); - output_buffer_.push_back(std::move(pkt)); - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - if (!write_in_progress) { - doSendPacket(); - } - } else { - data_available_ = true; - } - }); -} - -void UdpTunnelConnector::send(const uint8_t *packet, std::size_t len) {} - -void UdpTunnelConnector::close() { - DLOG_IF(INFO, VLOG_IS_ON(2)) << "UDPTunnelConnector::close"; - state_ = State::CLOSED; - bool is_socket_owned = socket_.use_count() == 1; - if (is_socket_owned) { - io_service_.dispatch([this]() { - this->socket_->close(); - // on_close_callback_(shared_from_this()); - }); - } -} - -void UdpTunnelConnector::doSendPacket() { -#ifdef LINUX - send_timer_.expires_from_now(std::chrono::microseconds(50)); - send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, - std::placeholders::_1)); -#else - auto packet = output_buffer_.front().get(); - auto array = std::vector<asio::const_buffer>(); - - const ::utils::MemBuf *current = packet; - do { - array.push_back(asio::const_buffer(current->data(), current->length())); - current = current->next(); - } while (current != packet); - - socket_->async_send_to( - std::move(array), remote_endpoint_send_, - strand_->wrap([this](std::error_code ec, std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - sent_callback_(this, make_error_code(forwarder_error::success)); - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - sendFailed(); - sent_callback_(this, ec); - } - - output_buffer_.pop_front(); - if (!output_buffer_.empty()) { - doSendPacket(); - } - })); -#endif -} - -#ifdef LINUX -void UdpTunnelConnector::writeHandler(std::error_code ec) { - if (TRANSPORT_EXPECT_FALSE(state_ != State::CONNECTED)) { - return; - } - - auto len = std::min(output_buffer_.size(), std::size_t(Connector::max_burst)); - - if (len) { - int m = 0; - for (auto &p : output_buffer_) { - auto packet = p.get(); - ::utils::MemBuf *current = packet; - int b = 0; - do { - // array.push_back(asio::const_buffer(current->data(), - // current->length())); - tx_iovecs_[m][b].iov_base = current->writableData(); - tx_iovecs_[m][b].iov_len = current->length(); - current = current->next(); - b++; - } while (current != packet); - - tx_msgs_[m].msg_hdr.msg_iov = tx_iovecs_[m]; - tx_msgs_[m].msg_hdr.msg_iovlen = b; - tx_msgs_[m].msg_hdr.msg_name = remote_endpoint_send_.data(); - tx_msgs_[m].msg_hdr.msg_namelen = remote_endpoint_send_.size(); - m++; - - if (--len == 0) { - break; - } - } - - int retval = sendmmsg(socket_->native_handle(), tx_msgs_, m, MSG_DONTWAIT); - if (retval > 0) { - while (retval--) { - output_buffer_.pop_front(); - } - } else if (retval != EWOULDBLOCK && retval != EAGAIN) { - LOG(ERROR) << "Error sending messages! " << strerror(errno) - << " << retval"; - return; - } - } - - if (!output_buffer_.empty()) { - send_timer_.expires_from_now(std::chrono::microseconds(50)); - send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, - std::placeholders::_1)); - } -} - -void UdpTunnelConnector::readHandler(std::error_code ec) { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; - - if (TRANSPORT_EXPECT_TRUE(!ec)) { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - if (current_position_ == 0) { - for (int i = 0; i < max_burst; i++) { - auto read_buffer = getRawBuffer(); - rx_iovecs_[i][0].iov_base = read_buffer.first; - rx_iovecs_[i][0].iov_len = read_buffer.second; - rx_msgs_[i].msg_hdr.msg_iov = rx_iovecs_[i]; - rx_msgs_[i].msg_hdr.msg_iovlen = 1; - } - } - - int res = recvmmsg(socket_->native_handle(), rx_msgs_ + current_position_, - max_burst - current_position_, MSG_DONTWAIT, nullptr); - if (res < 0) { - LOG(ERROR) << "Error receiving messages! " << strerror(errno) << " " - << res; - return; - } - - for (int i = 0; i < res; i++) { - auto packet = getPacketFromBuffer( - reinterpret_cast<uint8_t *>( - rx_msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), - rx_msgs_[current_position_].msg_len); - receiveSuccess(*packet); - receive_callback_(this, *packet, - make_error_code(forwarder_error::success)); - ++current_position_; - } - - doRecvPacket(); - } else { - LOG(ERROR) - << "Error in UDP: Receiving packets from a not connected socket."; - } - } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { - LOG(ERROR) << "The connection has been closed by the application."; - return; - } else { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - // receive_callback_(this, *read_msg_, ec); - LOG(ERROR) << "Error in UDP connector: " << ec.value() << " " - << ec.message(); - } else { - LOG(ERROR) << "Error in connector while not connected. " << ec.value() - << " " << ec.message(); - } - } -} -#endif - -void UdpTunnelConnector::doRecvPacket() { -#ifdef LINUX - if (state_ == State::CONNECTED) { -#if ((ASIO_VERSION / 100 % 1000) < 11) - socket_->async_receive(asio::null_buffers(), -#else - socket_->async_wait(asio::ip::tcp::socket::wait_read, -#endif - std::bind(&UdpTunnelConnector::readHandler, this, - std::placeholders::_1)); - } -#else - DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; - read_msg_ = getRawBuffer(); - socket_->async_receive_from( - asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_recv_, - [this](std::error_code ec, std::size_t length) { - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "UdpTunnelConnector received packet length=" << length; - if (TRANSPORT_EXPECT_TRUE(!ec)) { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - auto packet = getPacketFromBuffer(read_msg_.first, length); - receiveSuccess(*packet); - receive_callback_(this, *packet, - make_error_code(forwarder_error::success)); - doRecvPacket(); - } else { - LOG(ERROR) << "Error in UDP: Receiving packets from a not " - "connected socket."; - } - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - LOG(ERROR) << "The connection has been closed by the application."; - return; - } else { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - LOG(ERROR) << "Error in UDP connector: " << ec.value() - << ec.message(); - } else { - LOG(ERROR) << "Error while not connected"; - } - } - }); -#endif -} - -void UdpTunnelConnector::doConnect() { - asio::async_connect( - *socket_, endpoint_iterator_, - [this](std::error_code ec, asio::ip::udp::resolver::iterator) { - if (!ec) { - state_ = State::CONNECTED; - doRecvPacket(); - - if (data_available_) { - data_available_ = false; - doSendPacket(); - } - } else { - LOG(ERROR) << "UDP Connection failed!!!"; - timer_.expires_from_now(std::chrono::milliseconds(500)); - timer_.async_wait(std::bind(&UdpTunnelConnector::doConnect, this)); - } - }); -} - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/hicn-light-ng/CMakeLists.txt b/libtransport/src/io_modules/hicn-light-ng/CMakeLists.txt new file mode 100644 index 000000000..325a8bd1d --- /dev/null +++ b/libtransport/src/io_modules/hicn-light-ng/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (c) 2021-2022 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. + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(Libhicnctrl ${CURRENT_VERSION} REQUIRED NO_MODULE) + + if (DISABLE_SHARED_LIBRARIES) + set(LIBTYPE static) + else() + set(LIBTYPE shared) + endif() + + list(APPEND LIBHICNCTRL_LIBRARIES hicn::hicnctrl.${LIBTYPE}) +else() + if (DISABLE_SHARED_LIBRARIES) + if (WIN32) + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC}) + else () + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC} log) + endif () + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_STATIC} + ) + else() + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_SHARED}) + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_SHARED} + ) + endif() +endif() + +list(APPEND MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.cc +) + +build_module(hicnlightng_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT ${LIBTRANSPORT_COMPONENT} + LINK_LIBRARIES PRIVATE ${LIBHICNCTRL_LIBRARIES} + INCLUDE_DIRS + PRIVATE + ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + ${Libhicnctrl_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} +) diff --git a/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.cc b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.cc new file mode 100644 index 000000000..f67bd9447 --- /dev/null +++ b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.cc @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2021 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 <core/udp_connector.h> +#include <io_modules/hicn-light-ng/hicn_forwarder_module.h> + +extern "C" { +#include <hicn/ctrl/hicn-light-ng.h> +} + +namespace transport { + +namespace core { + +HicnForwarderModule::HicnForwarderModule() + : IoModule(), connector_(nullptr), seq_(0) {} + +HicnForwarderModule::~HicnForwarderModule() {} + +void HicnForwarderModule::connect(bool is_consumer) { + connector_->connect("localhost", 9695); + connector_->setRole(is_consumer ? Connector::Role::CONSUMER + : Connector::Role::PRODUCER); +} + +bool HicnForwarderModule::isConnected() { return connector_->isConnected(); } + +void HicnForwarderModule::send(Packet &packet) { + IoModule::send(packet); + packet.setChecksum(); + connector_->send(packet); +} + +void HicnForwarderModule::send(const utils::MemBuf::Ptr &packet) { + counters_.tx_packets++; + counters_.tx_bytes += packet->length(); + + // Perfect forwarding + connector_->send(packet); +} + +void HicnForwarderModule::registerRoute(const Prefix &prefix) { + auto command = createCommandRoute(prefix.toSockaddr(), + (uint8_t)prefix.getPrefixLength()); + if (!command) { + // TODO error + return; + } + send(command); +} + +void HicnForwarderModule::sendMapme() { + auto command = createCommandMapmeSendUpdate(); + if (!command) { + // TODO error + return; + } + send(command); +} + +void HicnForwarderModule::setForwardingStrategy(const Prefix &prefix, + std::string &strategy) { + auto command = createCommandSetForwardingStrategy( + prefix.toSockaddr(), (uint8_t)prefix.getPrefixLength(), strategy); + if (!command) { + // TODO error + return; + } + send(command); +} + +void HicnForwarderModule::closeConnection() { + auto command = createCommandDeleteConnection(); + if (!command) { + // TODO error + return; + } + + connector_->setSentCallback([](Connector *c, const std::error_code &ec) { + if (!ec) { + c->close(); + } + }); + + send(command); +} + +void HicnForwarderModule::init( + Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, const std::string &app_name) { + if (!connector_) { + connector_.reset(new UdpTunnelConnector( + io_service, std::move(receive_callback), std::move(sent_callback), + nullptr, std::move(reconnect_callback))); + } +} + +void HicnForwarderModule::processControlMessageReply( + utils::MemBuf &packet_buffer) { + if (packet_buffer.data()[0] == NACK_LIGHT) { + throw errors::RuntimeException( + "Received Nack message from hicn light forwarder."); + } +} + +std::uint32_t HicnForwarderModule::getMtu() { return interface_mtu; } + +bool HicnForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { + return packet_buffer.data()[0] == ACK_LIGHT || + packet_buffer.data()[0] == NACK_LIGHT; +} + +/** + * @return A valid msg_route_add_t structure if the command was successful, or + * with .command_id == COMMAND_TYPE_UNDEFINED in case of error. + */ +utils::MemBuf::Ptr HicnForwarderModule::createCommandRoute( + std::unique_ptr<sockaddr> &&addr, uint8_t prefix_length) { + utils::MemBuf::Ptr ret = utils::MemBuf::create(sizeof(msg_route_add_t)); + auto command = reinterpret_cast<msg_route_add_t *>(ret->writableData()); + ret->append(sizeof(msg_route_add_t)); + std::memset(command, 0, sizeof(*command)); + + if (!IS_VALID_FAMILY(addr->sa_family)) return nullptr; + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .cost = 1, + .family = (uint8_t)addr->sa_family, + .len = prefix_length, + }, + }; + + switch (addr->sa_family) { + case AF_INET: + command->payload.address.v4.as_inaddr = + ((sockaddr_in *)addr.get())->sin_addr; + break; + case AF_INET6: + command->payload.address.v6.as_in6addr = + ((sockaddr_in6 *)addr.get())->sin6_addr; + break; + } + snprintf(command->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + "SELF"); + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandDeleteConnection() { + utils::MemBuf::Ptr ret = + utils::MemBuf::create(sizeof(msg_connection_remove_t)); + auto command = + reinterpret_cast<msg_connection_remove_t *>(ret->writableData()); + ret->append(sizeof(msg_connection_remove_t)); + std::memset(command, 0, sizeof(*command)); + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_REMOVE, + .length = 1, + .seq_num = 0, + }, + }; + + snprintf(command->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + "SELF"); + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandMapmeSendUpdate() { + utils::MemBuf::Ptr ret = + utils::MemBuf::create(sizeof(msg_mapme_send_update_t)); + auto command = + reinterpret_cast<msg_mapme_send_update_t *>(ret->writableData()); + ret->append(sizeof(msg_mapme_send_update_t)); + std::memset(command, 0, sizeof(*command)); + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SEND_UPDATE, + .length = 1, + .seq_num = seq_++, + }, + }; + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandSetForwardingStrategy( + std::unique_ptr<sockaddr> &&addr, uint32_t prefix_len, + std::string strategy) { + utils::MemBuf::Ptr ret = utils::MemBuf::create(sizeof(msg_strategy_set_t)); + auto command = reinterpret_cast<msg_strategy_set_t *>(ret->writableData()); + ret->append(sizeof(msg_strategy_set_t)); + std::memset(command, 0, sizeof(*command)); + + if (!IS_VALID_FAMILY(addr->sa_family)) return nullptr; + + strategy_type_t strategy_type = strategy_type_from_str(strategy.c_str()); + if (strategy_type == STRATEGY_TYPE_UNDEFINED) return nullptr; + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_STRATEGY_SET, + .length = 1, + .seq_num = seq_++, + }, + .payload = + { + .family = (uint8_t)addr->sa_family, + .len = (uint8_t)prefix_len, + .type = (uint8_t)strategy_type, + }, + }; + + switch (addr->sa_family) { + case AF_INET: + command->payload.address.v4.as_inaddr = + ((sockaddr_in *)addr.get())->sin_addr; + break; + case AF_INET6: + command->payload.address.v6.as_in6addr = + ((sockaddr_in6 *)addr.get())->sin6_addr; + break; + } + + return ret; +} + +extern "C" IoModule *create_module(void) { return new HicnForwarderModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/udp/hicn_forwarder_module.h b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.h index 845db73bf..0bf82757d 100644 --- a/libtransport/src/io_modules/udp/hicn_forwarder_module.h +++ b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -18,15 +18,17 @@ #include <hicn/transport/core/io_module.h> #include <hicn/transport/core/prefix.h> +extern "C" { +#include <hicn/ctrl/hicn-light-ng.h> +} + namespace transport { namespace core { -class UdpSocketConnector; +class UdpTunnelConnector; class HicnForwarderModule : public IoModule { - static constexpr uint8_t ack_code = 0xc2; - static constexpr uint8_t nack_code = 0xc3; static constexpr std::uint16_t interface_mtu = 1500; public: @@ -56,27 +58,45 @@ class HicnForwarderModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; void registerRoute(const Prefix &prefix) override; + void sendMapme() override; + + void setForwardingStrategy(const Prefix &prefix, + std::string &strategy) override; + std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; void closeConnection() override; private: - UdpSocketConnector *connector_; + utils::MemBuf::Ptr createCommandRoute(std::unique_ptr<sockaddr> &&addr, + uint8_t prefix_length); + utils::MemBuf::Ptr createCommandDeleteConnection(); + utils::MemBuf::Ptr createCommandMapmeSendUpdate(); + utils::MemBuf::Ptr createCommandSetForwardingStrategy( + std::unique_ptr<sockaddr> &&addr, uint32_t prefix_len, + std::string strategy); + + private: + std::shared_ptr<UdpTunnelConnector> connector_; + + /* Sequence number used for sending control messages */ + uint32_t seq_; }; extern "C" IoModule *create_module(void); diff --git a/libtransport/src/io_modules/loopback/CMakeLists.txt b/libtransport/src/io_modules/loopback/CMakeLists.txt index b5ae0b7f7..817effb3b 100644 --- a/libtransport/src/io_modules/loopback/CMakeLists.txt +++ b/libtransport/src/io_modules/loopback/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -20,11 +20,11 @@ list(APPEND MODULE_SOURCE_FILES ) build_module(loopback_module - SHARED SOURCES ${MODULE_SOURCE_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + INCLUDE_DIRS + PRIVATE ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} ${Libhicn_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ) diff --git a/libtransport/src/io_modules/loopback/local_face.cc b/libtransport/src/io_modules/loopback/local_face.cc index b73444330..7ef3f1a59 100644 --- a/libtransport/src/io_modules/loopback/local_face.cc +++ b/libtransport/src/io_modules/loopback/local_face.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/io_modules/loopback/local_face.h b/libtransport/src/io_modules/loopback/local_face.h index 1f4101447..f54f38afa 100644 --- a/libtransport/src/io_modules/loopback/local_face.h +++ b/libtransport/src/io_modules/loopback/local_face.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/io_modules/loopback/loopback_module.cc b/libtransport/src/io_modules/loopback/loopback_module.cc index f7dd5e7b0..5b7ed5f61 100644 --- a/libtransport/src/io_modules/loopback/loopback_module.cc +++ b/libtransport/src/io_modules/loopback/loopback_module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -41,7 +41,7 @@ void LoopbackModule::send(Packet &packet) { local_faces_.at(1 - local_id_)->send(packet); } -void LoopbackModule::send(const uint8_t *packet, std::size_t len) { +void LoopbackModule::send(const utils::MemBuf::Ptr &buffer) { // not supported throw errors::NotImplementedException(); } @@ -57,6 +57,7 @@ void LoopbackModule::closeConnection() { } void LoopbackModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name) { @@ -64,8 +65,9 @@ void LoopbackModule::init(Connector::PacketReceivedCallback &&receive_callback, local_id_ = global_counter_++; local_faces_.emplace( local_faces_.begin() + local_id_, - new LocalConnector(io_service, std::move(receive_callback), nullptr, - nullptr, std::move(reconnect_callback))); + new LocalConnector(io_service, std::move(receive_callback), + std::move(sent_callback), nullptr, + std::move(reconnect_callback))); } } @@ -75,7 +77,9 @@ void LoopbackModule::processControlMessageReply(utils::MemBuf &packet_buffer) { std::uint32_t LoopbackModule::getMtu() { return interface_mtu; } -bool LoopbackModule::isControlMessage(const uint8_t *message) { return false; } +bool LoopbackModule::isControlMessage(utils::MemBuf &packet_buffer) { + return false; +} extern "C" IoModule *create_module(void) { return new LoopbackModule(); } diff --git a/libtransport/src/io_modules/loopback/loopback_module.h b/libtransport/src/io_modules/loopback/loopback_module.h index 219fa8841..2779ae7e3 100644 --- a/libtransport/src/io_modules/loopback/loopback_module.h +++ b/libtransport/src/io_modules/loopback/loopback_module.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -36,11 +36,12 @@ class LoopbackModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; @@ -49,7 +50,7 @@ class LoopbackModule : public IoModule { std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; diff --git a/libtransport/src/io_modules/memif/CMakeLists.txt b/libtransport/src/io_modules/memif/CMakeLists.txt index fc1c1f135..134ac1db6 100644 --- a/libtransport/src/io_modules/memif/CMakeLists.txt +++ b/libtransport/src/io_modules/memif/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -11,18 +11,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -find_package(Vpp REQUIRED) -find_package(Libmemif REQUIRED) + +############################################################## +# Dependencies and third party libs +############################################################## +find_package(Vpp ${VPP_DEFAULT_VERSION} EXACT REQUIRED) if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - find_package(HicnPlugin REQUIRED) - find_package(SafeVapi REQUIRED) + find_package(HicnPlugin ${CURRENT_VERSION} REQUIRED) + find_package(SafeVapi ${CURRENT_VERSION} REQUIRED) else() list(APPEND DEPENDENCIES ${SAFE_VAPI_SHARED} ) endif() +list(APPEND DEPENDENCIES + ${MEMIF_THIRD_PARTY_DEPENDENCIES} +) + +############################################################## +# Sources +############################################################## list(APPEND MODULE_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.h ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h @@ -32,23 +42,23 @@ list(APPEND MODULE_HEADER_FILES list(APPEND MODULE_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.c - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.c ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_module.cc ) build_module(memif_module - SHARED SOURCES ${MODULE_SOURCE_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT}-io-modules - LINK_LIBRARIES ${LIBMEMIF_LIBRARIES} ${SAFE_VAPI_LIBRARIES} + OBJECT_LIBRARIES ${MEMIF_THIRD_PARTY_OBJECT_LIBRARIES} + LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} ${SAFE_VAPI_LIBRARIES} INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - ${VPP_INCLUDE_DIRS} - ${LIBMEMIF_INCLUDE_DIRS} - ${SAFE_VAPI_INCLUDE_DIRS} + PUBLIC + ${MEMIF_THIRD_PARTY_INCLUDE_DIRS} + ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + ${VPP_INCLUDE_DIRS} + ${LIBMEMIF_INCLUDE_DIRS} + ${SAFE_VAPI_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} ) diff --git a/libtransport/src/io_modules/memif/hicn_vapi.c b/libtransport/src/io_modules/memif/hicn_vapi.c index 6d78026ab..753679f54 100644 --- a/libtransport/src/io_modules/memif/hicn_vapi.c +++ b/libtransport/src/io_modules/memif/hicn_vapi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -27,8 +27,6 @@ #include <vlibapi/api.h> #include <vlibmemory/api.h> #include <vnet/ip/format.h> -#include <vnet/ip/ip4_packet.h> -#include <vnet/ip/ip6_packet.h> #include <vpp_plugins/hicn/error.h> #include <vppinfra/error.h> @@ -46,9 +44,6 @@ u8 *format_vl_api_address_union(u8 *s, va_list *args) { return NULL; } /*********************************************************************************/ -DEFINE_VAPI_MSG_IDS_HICN_API_JSON -DEFINE_VAPI_MSG_IDS_IP_API_JSON - static vapi_error_e register_prod_app_cb( vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, vapi_payload_hicn_api_register_prod_app_reply *reply) { @@ -78,7 +73,7 @@ int hicn_vapi_register_prod_app(vapi_ctx_t ctx, vapi_msg_hicn_api_register_prod_app *msg = vapi_alloc_hicn_api_register_prod_app(ctx); - if (ip46_address_is_ip4((ip46_address_t *)&input_params->prefix->address)) { + if (ip_address_is_v4((ip_address_t *)&input_params->prefix->address)) { memcpy(&msg->payload.prefix.address.un.ip4, &input_params->prefix->address, sizeof(ip4_address_t)); msg->payload.prefix.address.af = ADDRESS_IP4; @@ -190,7 +185,7 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, vapi_msg_ip_route_add_del *msg = vapi_alloc_ip_route_add_del(ctx, 1); msg->payload.is_add = 1; - if (ip46_address_is_ip4((ip46_address_t *)(input_params->prod_addr))) { + if (ip_address_is_v4((ip_address_t *)(input_params->prod_addr))) { memcpy(&msg->payload.route.prefix.address.un.ip4, &input_params->prefix->address.v4, sizeof(ip4_address_t)); msg->payload.route.prefix.address.af = ADDRESS_IP4; @@ -204,7 +199,7 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, msg->payload.route.paths[0].sw_if_index = ~0; msg->payload.route.paths[0].table_id = 0; - if (ip46_address_is_ip4((ip46_address_t *)(input_params->prod_addr))) { + if (ip_address_is_v4((ip_address_t *)(input_params->prod_addr))) { memcpy(&(msg->payload.route.paths[0].nh.address.ip4), input_params->prod_addr->v4.as_u8, sizeof(ip4_address_t)); msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; @@ -214,7 +209,7 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; } - msg->payload.route.paths[0].type = FIB_API_PATH_FLAG_NONE; + msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; int ret = vapi_ip_route_add_del(ctx, msg, reigster_route_cb, NULL); diff --git a/libtransport/src/io_modules/memif/hicn_vapi.h b/libtransport/src/io_modules/memif/hicn_vapi.h index e94c97749..967179f68 100644 --- a/libtransport/src/io_modules/memif/hicn_vapi.h +++ b/libtransport/src/io_modules/memif/hicn_vapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/io_modules/memif/memif_connector.cc b/libtransport/src/io_modules/memif/memif_connector.cc deleted file mode 100644 index 68ad52b63..000000000 --- a/libtransport/src/io_modules/memif/memif_connector.cc +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright (c) 2017-2019 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 <glog/logging.h> -#include <hicn/transport/errors/not_implemented_exception.h> -#include <io_modules/memif/memif_connector.h> -#include <sys/epoll.h> - -#include <cstdlib> - -extern "C" { -#include <memif/libmemif.h> -}; - -#define CANCEL_TIMER 1 - -namespace transport { - -namespace core { - -struct memif_connection { - uint16_t index; - /* memif conenction handle */ - memif_conn_handle_t conn; - /* transmit queue id */ - uint16_t tx_qid; - /* tx buffers */ - memif_buffer_t *tx_bufs; - /* allocated tx buffers counter */ - /* number of tx buffers pointing to shared memory */ - uint16_t tx_buf_num; - /* rx buffers */ - memif_buffer_t *rx_bufs; - /* allcoated rx buffers counter */ - /* number of rx buffers pointing to shared memory */ - uint16_t rx_buf_num; - /* interface ip address */ - uint8_t ip_addr[4]; -}; - -std::once_flag MemifConnector::flag_; -utils::EpollEventReactor MemifConnector::main_event_reactor_; - -MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, - PacketSentCallback &&packet_sent, - OnCloseCallback &&close_callback, - OnReconnectCallback &&on_reconnect, - asio::io_service &io_service, - std::string app_name) - : Connector(std::move(receive_callback), std::move(packet_sent), - std::move(close_callback), std::move(on_reconnect)), - memif_worker_(nullptr), - timer_set_(false), - send_timer_(std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), - disconnect_timer_( - std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), - io_service_(io_service), - work_(asio::make_work_guard(io_service_)), - memif_connection_(std::make_unique<memif_connection_t>()), - tx_buf_counter_(0), - is_reconnection_(false), - data_available_(false), - app_name_(app_name), - socket_filename_("") { - std::call_once(MemifConnector::flag_, &MemifConnector::init, this); -} - -MemifConnector::~MemifConnector() { close(); } - -void MemifConnector::init() { - /* initialize memory interface */ - int err = memif_init(controlFdUpdate, const_cast<char *>(app_name_.c_str()), - nullptr, nullptr, nullptr); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_init: " << memif_strerror(err); - } -} - -void MemifConnector::connect(uint32_t memif_id, long memif_mode) { - state_ = State::CONNECTING; - - memif_id_ = memif_id; - socket_filename_ = "/run/vpp/memif.sock"; - - createMemif(memif_id, memif_mode, nullptr); - - while (state_ != State::CONNECTED) { - MemifConnector::main_event_reactor_.runOneEvent(); - } - - int err; - - /* get interrupt queue id */ - int fd = -1; - err = memif_get_queue_efd(memif_connection_->conn, 0, &fd); - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_get_queue_efd: " << memif_strerror(err); - return; - } - - // Remove fd from main epoll - main_event_reactor_.delFileDescriptor(fd); - - // Add fd to epoll of instance - event_reactor_.addFileDescriptor( - fd, EPOLLIN, [this](const utils::Event &evt) -> int { - return onInterrupt(memif_connection_->conn, this, 0); - }); - - memif_worker_ = std::make_unique<std::thread>( - std::bind(&MemifConnector::threadMain, this)); -} - -int MemifConnector::createMemif(uint32_t index, uint8_t mode, char *s) { - memif_connection_t *c = memif_connection_.get(); - - /* setting memif connection arguments */ - memif_conn_args_t args; - memset(&args, 0, sizeof(args)); - - args.is_master = mode; - args.log2_ring_size = MEMIF_LOG2_RING_SIZE; - args.buffer_size = MEMIF_BUF_SIZE; - args.num_s2m_rings = 1; - args.num_m2s_rings = 1; - strncpy((char *)args.interface_name, IF_NAME, strlen(IF_NAME) + 1); - args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; - - int err; - - err = memif_create_socket(&args.socket, socket_filename_.c_str(), nullptr); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - throw errors::RuntimeException(memif_strerror(err)); - } - - args.interface_id = index; - /* last argument for memif_create (void * private_ctx) is used by user - to identify connection. this context is returned with callbacks */ - - /* default interrupt */ - if (s == nullptr) { - err = memif_create(&c->conn, &args, onConnect, onDisconnect, onInterrupt, - this); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - throw errors::RuntimeException(memif_strerror(err)); - } - } - - c->index = (uint16_t)index; - c->tx_qid = 0; - /* alloc memif buffers */ - c->rx_buf_num = 0; - c->rx_bufs = static_cast<memif_buffer_t *>( - malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); - c->tx_buf_num = 0; - c->tx_bufs = static_cast<memif_buffer_t *>( - malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); - - // memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, 0); - - return 0; -} - -int MemifConnector::deleteMemif() { - memif_connection_t *c = memif_connection_.get(); - - if (c->rx_bufs) { - free(c->rx_bufs); - } - - c->rx_bufs = nullptr; - c->rx_buf_num = 0; - - if (c->tx_bufs) { - free(c->tx_bufs); - } - - c->tx_bufs = nullptr; - c->tx_buf_num = 0; - - int err; - /* disconenct then delete memif connection */ - err = memif_delete(&c->conn); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_delete: " << memif_strerror(err); - } - - if (TRANSPORT_EXPECT_FALSE(c->conn != nullptr)) { - LOG(ERROR) << "memif delete fail"; - } - - return 0; -} - -int MemifConnector::controlFdUpdate(int fd, uint8_t events, void *private_ctx) { - /* convert memif event definitions to epoll events */ - if (events & MEMIF_FD_EVENT_DEL) { - return MemifConnector::main_event_reactor_.delFileDescriptor(fd); - } - - uint32_t evt = 0; - - if (events & MEMIF_FD_EVENT_READ) { - evt |= EPOLLIN; - } - - if (events & MEMIF_FD_EVENT_WRITE) { - evt |= EPOLLOUT; - } - - if (events & MEMIF_FD_EVENT_MOD) { - return MemifConnector::main_event_reactor_.modFileDescriptor(fd, evt); - } - - return MemifConnector::main_event_reactor_.addFileDescriptor( - fd, evt, [](const utils::Event &evt) -> int { - uint32_t event = 0; - int memif_err = 0; - - if (evt.events & EPOLLIN) { - event |= MEMIF_FD_EVENT_READ; - } - - if (evt.events & EPOLLOUT) { - event |= MEMIF_FD_EVENT_WRITE; - } - - if (evt.events & EPOLLERR) { - event |= MEMIF_FD_EVENT_ERROR; - } - - memif_err = memif_control_fd_handler(evt.data.fd, event); - - if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_control_fd_handler: " - << memif_strerror(memif_err); - } - - return 0; - }); -} - -int MemifConnector::bufferAlloc(long n, uint16_t qid) { - memif_connection_t *c = memif_connection_.get(); - int err; - uint16_t r; - /* set data pointer to shared memory and set buffer_len to shared mmeory - * buffer len */ - err = memif_buffer_alloc(c->conn, qid, c->tx_bufs, n, &r, 2000); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_buffer_alloc: " << memif_strerror(err); - return -1; - } - - c->tx_buf_num += r; - return r; -} - -int MemifConnector::txBurst(uint16_t qid) { - memif_connection_t *c = memif_connection_.get(); - int err; - uint16_t r; - /* inform peer memif interface about data in shared memory buffers */ - /* mark memif buffers as free */ - err = memif_tx_burst(c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_tx_burst: " << memif_strerror(err); - } - - // err = memif_refill_queue(c->conn, qid, r, 0); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_tx_burst: " << memif_strerror(err); - c->tx_buf_num -= r; - return -1; - } - - c->tx_buf_num -= r; - return 0; -} - -void MemifConnector::sendCallback(const std::error_code &ec) { - timer_set_ = false; - - if (TRANSPORT_EXPECT_TRUE(!ec && state_ == State::CONNECTED)) { - doSend(); - } -} - -void MemifConnector::processInputBuffer(std::uint16_t total_packets) { - utils::MemBuf::Ptr ptr; - - for (; total_packets > 0; total_packets--) { - if (input_buffer_.pop(ptr)) { - receive_callback_(this, *ptr, std::make_error_code(std::errc(0))); - } - } -} - -/* informs user about connected status. private_ctx is used by user to identify - connection (multiple connections WIP) */ -int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { - MemifConnector *connector = (MemifConnector *)private_ctx; - connector->state_ = State::CONNECTED; - memif_refill_queue(conn, 0, -1, 0); - - return 0; -} - -/* informs user about disconnected status. private_ctx is used by user to - identify connection (multiple connections WIP) */ -int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { - MemifConnector *connector = (MemifConnector *)private_ctx; - connector->state_ = State::CLOSED; - return 0; -} - -void MemifConnector::threadMain() { event_reactor_.runEventLoop(1000); } - -int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, - uint16_t qid) { - MemifConnector *connector = (MemifConnector *)private_ctx; - - memif_connection_t *c = connector->memif_connection_.get(); - int err = MEMIF_ERR_SUCCESS, ret_val; - uint16_t total_packets = 0; - uint16_t rx; - - do { - err = memif_rx_burst(conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); - ret_val = err; - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && - err != MEMIF_ERR_NOBUF)) { - LOG(ERROR) << "memif_rx_burst: " << memif_strerror(err); - goto error; - } - - c->rx_buf_num += rx; - - if (TRANSPORT_EXPECT_FALSE(connector->io_service_.stopped())) { - LOG(ERROR) << "socket stopped: ignoring " << rx << " packets"; - goto error; - } - - std::size_t packet_length; - for (int i = 0; i < rx; i++) { - auto buffer = connector->getRawBuffer(); - packet_length = (c->rx_bufs + i)->len; - std::memcpy(buffer.first, (c->rx_bufs + i)->data, packet_length); - auto packet = connector->getPacketFromBuffer(buffer.first, packet_length); - - if (!connector->input_buffer_.push(std::move(packet))) { - LOG(ERROR) << "Error pushing packet. Ring buffer full."; - - // TODO Here we should consider the possibility to signal the congestion - // to the application, that would react properly (e.g. slow down - // message) - } - } - - /* mark memif buffers and shared memory buffers as free */ - /* free processed buffers */ - - err = memif_refill_queue(conn, qid, rx, 0); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); - } - - c->rx_buf_num -= rx; - total_packets += rx; - - } while (ret_val == MEMIF_ERR_NOBUF); - - connector->io_service_.post( - std::bind(&MemifConnector::processInputBuffer, connector, total_packets)); - - return 0; - -error: - err = memif_refill_queue(c->conn, qid, rx, 0); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); - } - c->rx_buf_num -= rx; - - return 0; -} - -void MemifConnector::close() { - if (state_ != State::CLOSED) { - disconnect_timer_->expiresFromNow(std::chrono::microseconds(50)); - disconnect_timer_->asyncWait([this](const std::error_code &ec) { - deleteMemif(); - event_reactor_.stop(); - }); - - if (memif_worker_ && memif_worker_->joinable()) { - memif_worker_->join(); - } - } -} - -void MemifConnector::send(Packet &packet) { - { - utils::SpinLock::Acquire locked(write_msgs_lock_); - output_buffer_.push_back(packet.shared_from_this()); - } -#if CANCEL_TIMER - if (!timer_set_) { - timer_set_ = true; - send_timer_->expiresFromNow(std::chrono::microseconds(50)); - send_timer_->asyncWait( - std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); - } -#endif -} - -int MemifConnector::doSend() { - std::size_t max = 0; - int32_t n = 0; - std::size_t size = 0; - - { - utils::SpinLock::Acquire locked(write_msgs_lock_); - size = output_buffer_.size(); - } - - do { - max = size < MAX_MEMIF_BUFS ? size : MAX_MEMIF_BUFS; - n = bufferAlloc(max, memif_connection_->tx_qid); - - if (TRANSPORT_EXPECT_FALSE(n < 0)) { - LOG(ERROR) << "Error allocating buffers."; - return -1; - } - - for (uint16_t i = 0; i < n; i++) { - utils::SpinLock::Acquire locked(write_msgs_lock_); - - auto packet = output_buffer_.front().get(); - const utils::MemBuf *current = packet; - std::size_t offset = 0; - uint8_t *shared_buffer = - reinterpret_cast<uint8_t *>(memif_connection_->tx_bufs[i].data); - do { - std::memcpy(shared_buffer + offset, current->data(), current->length()); - offset += current->length(); - current = current->next(); - } while (current != packet); - - memif_connection_->tx_bufs[i].len = uint32_t(offset); - - output_buffer_.pop_front(); - } - - txBurst(memif_connection_->tx_qid); - - utils::SpinLock::Acquire locked(write_msgs_lock_); - size = output_buffer_.size(); - } while (size > 0); - - return 0; -} - -void MemifConnector::send(const uint8_t *packet, std::size_t len) { - throw errors::NotImplementedException(); -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/memif/memif_vapi.c b/libtransport/src/io_modules/memif/memif_vapi.c index b3da2b012..54e2c3134 100644 --- a/libtransport/src/io_modules/memif/memif_vapi.c +++ b/libtransport/src/io_modules/memif/memif_vapi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -22,8 +22,6 @@ #include <vapi/vapi_safe.h> #include <vppinfra/clib.h> -DEFINE_VAPI_MSG_IDS_MEMIF_API_JSON - static vapi_error_e memif_details_cb(vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, vapi_payload_memif_details *reply) { @@ -45,6 +43,9 @@ static vapi_error_e memif_details_cb(vapi_ctx_t ctx, void *callback_ctx, int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id) { vapi_lock(); vapi_msg_memif_dump *msg = vapi_alloc_memif_dump(ctx); + + // Initialize memif id to 0 + *memif_id = 0; int ret = vapi_memif_dump(ctx, msg, memif_details_cb, memif_id); vapi_unlock(); return ret; diff --git a/libtransport/src/io_modules/memif/memif_vapi.h b/libtransport/src/io_modules/memif/memif_vapi.h index bcf06ed43..f5f0639e7 100644 --- a/libtransport/src/io_modules/memif/memif_vapi.h +++ b/libtransport/src/io_modules/memif/memif_vapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -44,6 +44,10 @@ typedef struct memif_output_params_s { int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id); +int memif_vapi_create_socket(vapi_ctx_t ctx, + memif_create_params_t *input_params, + memif_output_params_t *output_params); + int memif_vapi_create_memif(vapi_ctx_t ctx, memif_create_params_t *input_params, memif_output_params_t *output_params); diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.cc b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc index 44c8376df..65260077a 100644 --- a/libtransport/src/io_modules/memif/vpp_forwarder_module.cc +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -13,16 +13,16 @@ * limitations under the License. */ +#include <core/memif_connector.h> #include <glog/logging.h> #include <hicn/transport/config.h> #include <hicn/transport/errors/not_implemented_exception.h> #include <io_modules/memif/hicn_vapi.h> -#include <io_modules/memif/memif_connector.h> #include <io_modules/memif/memif_vapi.h> #include <io_modules/memif/vpp_forwarder_module.h> extern "C" { -#include <memif/libmemif.h> +#include <libmemif.h> }; typedef enum { MASTER = 0, SLAVE = 1 } memif_role_t; @@ -39,21 +39,24 @@ namespace core { VPPForwarderModule::VPPForwarderModule() : IoModule(), connector_(nullptr), + memif_id_(0), sw_if_index_(~0), face_id1_(~0), face_id2_(~0), is_consumer_(false) {} -VPPForwarderModule::~VPPForwarderModule() { delete connector_; } +VPPForwarderModule::~VPPForwarderModule() {} void VPPForwarderModule::init( Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name) { if (!connector_) { - connector_ = - new MemifConnector(std::move(receive_callback), 0, 0, - std::move(reconnect_callback), io_service, app_name); + connector_ = std::make_unique<MemifConnector>( + std::move(receive_callback), std::move(sent_callback), + Connector::OnCloseCallback(0), std::move(reconnect_callback), + io_service, app_name); } } @@ -62,7 +65,7 @@ void VPPForwarderModule::processControlMessageReply( throw errors::NotImplementedException(); } -bool VPPForwarderModule::isControlMessage(const uint8_t *message) { +bool VPPForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { return false; } @@ -73,12 +76,12 @@ void VPPForwarderModule::send(Packet &packet) { connector_->send(packet); } -void VPPForwarderModule::send(const uint8_t *packet, std::size_t len) { +void VPPForwarderModule::send(const utils::MemBuf::Ptr &buffer) { counters_.tx_packets++; - counters_.tx_bytes += len; + counters_.tx_bytes += buffer->length(); // Perfect forwarding - connector_->send(packet, len); + connector_->send(buffer); } std::uint32_t VPPForwarderModule::getMtu() { return interface_mtu; } @@ -170,7 +173,8 @@ void VPPForwarderModule::connect(bool is_consumer) { consumerConnection(); } - connector_->connect(memif_id_, 0); + connector_->connect(memif_id_, 0 /* is_master = false */, + memif_socket_filename); connector_->setRole(is_consumer_ ? Connector::Role::CONSUMER : Connector::Role::PRODUCER); } @@ -207,7 +211,8 @@ void VPPForwarderModule::registerRoute(const Prefix &prefix) { throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); } - inet6_address_ = *output.prod_addr; + std::memcpy(inet6_address_.v6.as_u8, output.prod_addr->v6.as_u8, + sizeof(inet6_address_)); face_id1_ = output.face_id; } else { @@ -228,8 +233,6 @@ void VPPForwarderModule::registerRoute(const Prefix &prefix) { void VPPForwarderModule::closeConnection() { if (VPPForwarderModule::sock_) { - connector_->close(); - if (is_consumer_) { hicn_del_face_app_input_params params; params.face_id = face_id1_; @@ -242,6 +245,8 @@ void VPPForwarderModule::closeConnection() { hicn_vapi_face_prod_del(VPPForwarderModule::sock_, ¶ms); } + connector_->close(); + if (sw_if_index_ != uint32_t(~0)) { int ret = memif_vapi_delete_memif(VPPForwarderModule::sock_, sw_if_index_); diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.h b/libtransport/src/io_modules/memif/vpp_forwarder_module.h index 8c4114fed..162ee0ca5 100644 --- a/libtransport/src/io_modules/memif/vpp_forwarder_module.h +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -32,7 +32,8 @@ namespace core { class MemifConnector; class VPPForwarderModule : public IoModule { - static constexpr std::uint16_t interface_mtu = 1500; + static inline std::uint16_t interface_mtu = 1500; + static inline std::string const memif_socket_filename = "/run/vpp/memif.sock"; public: VPPForwarderModule(); @@ -41,11 +42,12 @@ class VPPForwarderModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; @@ -54,7 +56,7 @@ class VPPForwarderModule : public IoModule { std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; @@ -66,7 +68,7 @@ class VPPForwarderModule : public IoModule { void producerConnection(); private: - MemifConnector *connector_; + std::shared_ptr<MemifConnector> connector_; uint32_t memif_id_; uint32_t sw_if_index_; // A consumer socket in vpp has two faces (ipv4 and ipv6) diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc b/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc deleted file mode 100644 index 62efdc3a5..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2017-2019 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 <core/raw_socket_connector.h> -#include <hicn/transport/utils/conversions.h> -#include <net/if.h> -#include <netdb.h> -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#define MY_DEST_MAC0 0x0a -#define MY_DEST_MAC1 0x7b -#define MY_DEST_MAC2 0x7c -#define MY_DEST_MAC3 0x1c -#define MY_DEST_MAC4 0x4a -#define MY_DEST_MAC5 0x14 - -namespace transport { - -namespace core { - -RawSocketConnector::RawSocketConnector( - PacketReceivedCallback &&receive_callback, - OnReconnect &&on_reconnect_callback, asio::io_service &io_service, - std::string app_name) - : Connector(std::move(receive_callback), std::move(on_reconnect_callback)), - io_service_(io_service), - socket_(io_service_, raw_protocol(PF_PACKET, SOCK_RAW)), - // resolver_(io_service_), - timer_(io_service_), - read_msg_(packet_pool_.makePtr(nullptr)), - data_available_(false), - app_name_(app_name) { - memset(&link_layer_address_, 0, sizeof(link_layer_address_)); -} - -RawSocketConnector::~RawSocketConnector() {} - -void RawSocketConnector::connect(const std::string &interface_name, - const std::string &mac_address_str) { - state_ = ConnectorState::CONNECTING; - memset(ðernet_header_, 0, sizeof(ethernet_header_)); - struct ifreq ifr; - struct ifreq if_mac; - uint8_t mac_address[6]; - - utils::convertStringToMacAddress(mac_address_str, mac_address); - - // Get interface mac address - int fd = static_cast<int>(socket_.native_handle()); - - /* Get the index of the interface to send on */ - memset(&ifr, 0, sizeof(struct ifreq)); - strncpy(ifr.ifr_name, interface_name.c_str(), interface_name.size()); - - // if (ioctl(fd, SIOCGIFINDEX, &if_idx) < 0) { - // perror("SIOCGIFINDEX"); - // } - - /* Get the MAC address of the interface to send on */ - memset(&if_mac, 0, sizeof(struct ifreq)); - strncpy(if_mac.ifr_name, interface_name.c_str(), interface_name.size()); - if (ioctl(fd, SIOCGIFHWADDR, &if_mac) < 0) { - perror("SIOCGIFHWADDR"); - throw errors::RuntimeException("Interface does not exist"); - } - - /* Ethernet header */ - for (int i = 0; i < 6; i++) { - ethernet_header_.ether_shost[i] = - ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[i]; - ethernet_header_.ether_dhost[i] = mac_address[i]; - } - - /* Ethertype field */ - ethernet_header_.ether_type = htons(ETH_P_IPV6); - - strcpy(ifr.ifr_name, interface_name.c_str()); - - if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) { - memcpy(link_layer_address_.sll_addr, ifr.ifr_hwaddr.sa_data, 6); - } - - // memset(&ifr, 0, sizeof(ifr)); - // ioctl(fd, SIOCGIFFLAGS, &ifr); - // ifr.ifr_flags |= IFF_PROMISC; - // ioctl(fd, SIOCSIFFLAGS, &ifr); - - link_layer_address_.sll_family = AF_PACKET; - link_layer_address_.sll_protocol = htons(ETH_P_ALL); - link_layer_address_.sll_ifindex = if_nametoindex(interface_name.c_str()); - link_layer_address_.sll_hatype = 1; - link_layer_address_.sll_halen = 6; - - // startConnectionTimer(); - doConnect(); - doRecvPacket(); -} - -void RawSocketConnector::send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent) { - if (packet_sent != 0) { - socket_.async_send( - asio::buffer(packet, len), - [packet_sent](std::error_code ec, std::size_t /*length*/) { - packet_sent(); - }); - } else { - if (state_ == ConnectorState::CONNECTED) { - socket_.send(asio::buffer(packet, len)); - } - } -} - -void RawSocketConnector::send(const Packet::MemBufPtr &packet) { - io_service_.post([this, packet]() { - bool write_in_progress = !output_buffer_.empty(); - output_buffer_.push_back(std::move(packet)); - if (TRANSPORT_EXPECT_TRUE(state_ == ConnectorState::CONNECTED)) { - if (!write_in_progress) { - doSendPacket(); - } else { - // Tell the handle connect it has data to write - data_available_ = true; - } - } - }); -} - -void RawSocketConnector::close() { - io_service_.post([this]() { socket_.close(); }); -} - -void RawSocketConnector::doSendPacket() { - auto packet = output_buffer_.front().get(); - auto array = std::vector<asio::const_buffer>(); - - const utils::MemBuf *current = packet; - do { - array.push_back(asio::const_buffer(current->data(), current->length())); - current = current->next(); - } while (current != packet); - - socket_.async_send( - std::move(array), - [this /*, packet*/](std::error_code ec, std::size_t bytes_transferred) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - output_buffer_.pop_front(); - if (!output_buffer_.empty()) { - doSendPacket(); - } - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - } - }); -} - -void RawSocketConnector::doRecvPacket() { - read_msg_ = getPacket(); - socket_.async_receive( - asio::buffer(read_msg_->writableData(), packet_size), - [this](std::error_code ec, std::size_t bytes_transferred) mutable { - if (!ec) { - // Ignore packets that are not for us - uint8_t *dst_mac_address = const_cast<uint8_t *>(read_msg_->data()); - if (!std::memcmp(dst_mac_address, ethernet_header_.ether_shost, - ETHER_ADDR_LEN)) { - read_msg_->append(bytes_transferred); - read_msg_->trimStart(sizeof(struct ether_header)); - receive_callback_(std::move(read_msg_)); - } - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - } - doRecvPacket(); - }); -} - -void RawSocketConnector::doConnect() { - state_ = ConnectorState::CONNECTED; - socket_.bind(raw_endpoint(&link_layer_address_, sizeof(link_layer_address_))); -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_connector.h b/libtransport/src/io_modules/raw_socket/raw_socket_connector.h deleted file mode 100644 index 06892b3d8..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_connector.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2017-2019 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. - */ - -#pragma once - -#include <core/connector.h> -#include <hicn/transport/config.h> -#include <hicn/transport/core/asio_wrapper.h> -#include <hicn/transport/core/name.h> -#include <linux/if_packet.h> -#include <net/ethernet.h> -#include <sys/socket.h> - -#include <deque> - -namespace transport { - -namespace core { - -using asio::generic::raw_protocol; -using raw_endpoint = asio::generic::basic_endpoint<raw_protocol>; - -class RawSocketConnector : public Connector { - public: - RawSocketConnector(PacketReceivedCallback &&receive_callback, - OnReconnect &&reconnect_callback, - asio::io_service &io_service, - std::string app_name = "Libtransport"); - - ~RawSocketConnector() override; - - void send(const Packet::MemBufPtr &packet) override; - - void send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent = 0) override; - - void close() override; - - void connect(const std::string &interface_name, - const std::string &mac_address_str); - - private: - void doConnect(); - - void doRecvPacket(); - - void doSendPacket(); - - private: - asio::io_service &io_service_; - raw_protocol::socket socket_; - - struct ether_header ethernet_header_; - - struct sockaddr_ll link_layer_address_; - - asio::steady_timer timer_; - - utils::ObjectPool<utils::MemBuf>::Ptr read_msg_; - - bool data_available_; - std::string app_name_; -}; - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc b/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc deleted file mode 100644 index dcf489f59..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017-2019 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 <core/raw_socket_interface.h> -#include <hicn/transport/utils/linux.h> - -#include <fstream> - -namespace transport { - -namespace core { - -static std::string config_folder_path = "/etc/transport/interface.conf.d"; - -RawSocketInterface::RawSocketInterface(RawSocketConnector &connector) - : ForwarderInterface<RawSocketInterface, RawSocketConnector>(connector) {} - -RawSocketInterface::~RawSocketInterface() {} - -void RawSocketInterface::connect(bool is_consumer) { - std::string complete_filename = - config_folder_path + std::string("/") + output_interface_; - - std::ifstream is(complete_filename); - std::string interface; - - if (is) { - is >> remote_mac_address_; - } - - // Get interface ip address - struct sockaddr_in6 address = {0}; - utils::retrieveInterfaceAddress(output_interface_, &address); - - std::memcpy(&inet6_address_.v6.as_u8, &address.sin6_addr, - sizeof(address.sin6_addr)); - connector_.connect(output_interface_, remote_mac_address_); -} - -void RawSocketInterface::registerRoute(Prefix &prefix) { return; } - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_interface.h b/libtransport/src/io_modules/raw_socket/raw_socket_interface.h deleted file mode 100644 index 7036cac7e..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_interface.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017-2019 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. - */ - -#pragma once - -#include <core/forwarder_interface.h> -#include <core/raw_socket_connector.h> -#include <hicn/transport/core/prefix.h> - -#include <atomic> -#include <deque> - -namespace transport { - -namespace core { - -class RawSocketInterface - : public ForwarderInterface<RawSocketInterface, RawSocketConnector> { - public: - typedef RawSocketConnector ConnectorType; - - RawSocketInterface(RawSocketConnector &connector); - - ~RawSocketInterface(); - - void connect(bool is_consumer); - - void registerRoute(Prefix &prefix); - - std::uint16_t getMtu() { return interface_mtu; } - - TRANSPORT_ALWAYS_INLINE static bool isControlMessageImpl( - const uint8_t *message) { - return false; - } - - TRANSPORT_ALWAYS_INLINE void processControlMessageReplyImpl( - Packet::MemBufPtr &&packet_buffer) {} - - TRANSPORT_ALWAYS_INLINE void closeConnection(){}; - - private: - static constexpr std::uint16_t interface_mtu = 1500; - std::string remote_mac_address_; -}; - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/udp/CMakeLists.txt b/libtransport/src/io_modules/udp/CMakeLists.txt deleted file mode 100644 index b9c19d063..000000000 --- a/libtransport/src/io_modules/udp/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021 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. - -list(APPEND MODULE_HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.h -) - -list(APPEND MODULE_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.cc -) - -build_module(hicnlight_module - SHARED - SOURCES ${MODULE_SOURCE_FILES} - DEPENDS ${DEPENDENCIES} - COMPONENT ${LIBTRANSPORT_COMPONENT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} -) diff --git a/libtransport/src/io_modules/udp/hicn_forwarder_module.cc b/libtransport/src/io_modules/udp/hicn_forwarder_module.cc deleted file mode 100644 index ba08dd8c0..000000000 --- a/libtransport/src/io_modules/udp/hicn_forwarder_module.cc +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2017-2020 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 <io_modules/udp/hicn_forwarder_module.h> -#include <io_modules/udp/udp_socket_connector.h> - -union AddressLight { - uint32_t ipv4; - struct in6_addr ipv6; -}; - -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; - uint32_t seq_num; -} CommandHeader; - -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; - uint32_t seq_num; - char symbolic_or_connid[16]; - union AddressLight address; - uint16_t cost; - uint8_t address_type; - uint8_t len; -} RouteToSelfCommand; - -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; - uint32_t seq_num; - char symbolic_or_connid[16]; -} DeleteSelfConnectionCommand; - -namespace { -static constexpr uint8_t addr_inet = 1; -static constexpr uint8_t addr_inet6 = 2; -static constexpr uint8_t add_route_command = 3; -static constexpr uint8_t delete_connection_command = 5; -static constexpr uint8_t request_light = 0xc0; -static constexpr char identifier[] = "SELF"; - -void fillCommandHeader(CommandHeader *header) { - // Allocate and fill the header - header->message_type = request_light; - header->length = 1; -} - -RouteToSelfCommand createCommandRoute(std::unique_ptr<sockaddr> &&addr, - uint8_t prefix_length) { - RouteToSelfCommand command = {0}; - - // check and set IP address - if (addr->sa_family == AF_INET) { - command.address_type = addr_inet; - command.address.ipv4 = ((sockaddr_in *)addr.get())->sin_addr.s_addr; - } else if (addr->sa_family == AF_INET6) { - command.address_type = addr_inet6; - command.address.ipv6 = ((sockaddr_in6 *)addr.get())->sin6_addr; - } - - // Fill remaining payload fields -#ifndef _WIN32 - strcpy(command.symbolic_or_connid, identifier); -#else - strcpy_s(command.symbolic_or_connid, 16, identifier); -#endif - command.cost = 1; - command.len = (uint8_t)prefix_length; - - // Allocate and fill the header - command.command_id = add_route_command; - fillCommandHeader((CommandHeader *)&command); - - return command; -} - -DeleteSelfConnectionCommand createCommandDeleteConnection() { - DeleteSelfConnectionCommand command = {0}; - fillCommandHeader((CommandHeader *)&command); - command.command_id = delete_connection_command; - -#ifndef _WIN32 - strcpy(command.symbolic_or_connid, identifier); -#else - strcpy_s(command.symbolic_or_connid, 16, identifier); -#endif - - return command; -} - -} // namespace - -namespace transport { - -namespace core { - -HicnForwarderModule::HicnForwarderModule() : IoModule(), connector_(nullptr) {} - -HicnForwarderModule::~HicnForwarderModule() {} - -void HicnForwarderModule::connect(bool is_consumer) { - connector_->connect(); - connector_->setRole(is_consumer ? Connector::Role::CONSUMER - : Connector::Role::PRODUCER); -} - -bool HicnForwarderModule::isConnected() { return connector_->isConnected(); } - -void HicnForwarderModule::send(Packet &packet) { - IoModule::send(packet); - packet.setChecksum(); - connector_->send(packet); -} - -void HicnForwarderModule::send(const uint8_t *packet, std::size_t len) { - counters_.tx_packets++; - counters_.tx_bytes += len; - - // Perfect forwarding - connector_->send(packet, len); -} - -void HicnForwarderModule::registerRoute(const Prefix &prefix) { - auto command = createCommandRoute(prefix.toSockaddr(), - (uint8_t)prefix.getPrefixLength()); - send((uint8_t *)&command, sizeof(RouteToSelfCommand)); -} - -void HicnForwarderModule::closeConnection() { - auto command = createCommandDeleteConnection(); - send((uint8_t *)&command, sizeof(DeleteSelfConnectionCommand)); - connector_->close(); -} - -void HicnForwarderModule::init( - Connector::PacketReceivedCallback &&receive_callback, - Connector::OnReconnectCallback &&reconnect_callback, - asio::io_service &io_service, const std::string &app_name) { - if (!connector_) { - connector_ = new UdpSocketConnector(std::move(receive_callback), nullptr, - nullptr, std::move(reconnect_callback), - io_service, app_name); - } -} - -void HicnForwarderModule::processControlMessageReply( - utils::MemBuf &packet_buffer) { - if (packet_buffer.data()[0] == nack_code) { - throw errors::RuntimeException( - "Received Nack message from hicn light forwarder."); - } -} - -std::uint32_t HicnForwarderModule::getMtu() { return interface_mtu; } - -bool HicnForwarderModule::isControlMessage(const uint8_t *message) { - return message[0] == ack_code || message[0] == nack_code; -} - -extern "C" IoModule *create_module(void) { return new HicnForwarderModule(); } - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/udp/udp_socket_connector.cc b/libtransport/src/io_modules/udp/udp_socket_connector.cc deleted file mode 100644 index 1412d8c07..000000000 --- a/libtransport/src/io_modules/udp/udp_socket_connector.cc +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2017-2019 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. - */ - -#ifdef _WIN32 -#include <hicn/transport/portability/win_portability.h> -#endif - -#include <glog/logging.h> -#include <hicn/transport/errors/errors.h> -#include <hicn/transport/utils/object_pool.h> -#include <io_modules/udp/udp_socket_connector.h> - -#include <thread> -#include <vector> - -namespace transport { - -namespace core { - -UdpSocketConnector::UdpSocketConnector( - PacketReceivedCallback &&receive_callback, PacketSentCallback &&packet_sent, - OnCloseCallback &&close_callback, OnReconnectCallback &&on_reconnect, - asio::io_service &io_service, std::string app_name) - : Connector(std::move(receive_callback), std::move(packet_sent), - std::move(close_callback), std::move(on_reconnect)), - io_service_(io_service), - socket_(io_service_), - resolver_(io_service_), - connection_timer_(io_service_), - read_msg_(std::make_pair(nullptr, 0)), - is_reconnection_(false), - data_available_(false), - app_name_(app_name) {} - -UdpSocketConnector::~UdpSocketConnector() {} - -void UdpSocketConnector::connect(std::string ip_address, std::string port) { - endpoint_iterator_ = resolver_.resolve( - {ip_address, port, asio::ip::resolver_query_base::numeric_service}); - - state_ = Connector::State::CONNECTING; - doConnect(); -} - -void UdpSocketConnector::send(const uint8_t *packet, std::size_t len) { - socket_.async_send(asio::buffer(packet, len), - [this](std::error_code ec, std::size_t /*length*/) { - if (sent_callback_) { - sent_callback_(this, ec); - } - }); -} - -void UdpSocketConnector::send(Packet &packet) { - io_service_.post([this, _packet{packet.shared_from_this()}]() { - bool write_in_progress = !output_buffer_.empty(); - output_buffer_.push_back(std::move(_packet)); - if (TRANSPORT_EXPECT_TRUE(state_ == Connector::State::CONNECTED)) { - if (!write_in_progress) { - doWrite(); - } - } else { - // Tell the handle connect it has data to write - data_available_ = true; - } - }); -} - -void UdpSocketConnector::close() { - if (io_service_.stopped()) { - doClose(); - } else { - io_service_.dispatch(std::bind(&UdpSocketConnector::doClose, this)); - } -} - -void UdpSocketConnector::doClose() { - if (state_ != Connector::State::CLOSED) { - state_ = Connector::State::CLOSED; - if (socket_.is_open()) { - socket_.shutdown(asio::ip::tcp::socket::shutdown_type::shutdown_both); - socket_.close(); - } - } -} - -void UdpSocketConnector::doWrite() { - auto packet = output_buffer_.front().get(); - auto array = std::vector<asio::const_buffer>(); - - const utils::MemBuf *current = packet; - do { - array.push_back(asio::const_buffer(current->data(), current->length())); - current = current->next(); - } while (current != packet); - - socket_.async_send(std::move(array), [this](std::error_code ec, - std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - output_buffer_.pop_front(); - if (!output_buffer_.empty()) { - doWrite(); - } - } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - tryReconnect(); - } - }); -} - -void UdpSocketConnector::doRead() { - read_msg_ = getRawBuffer(); - socket_.async_receive( - asio::buffer(read_msg_.first, read_msg_.second), - [this](std::error_code ec, std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - auto packet = getPacketFromBuffer(read_msg_.first, length); - receive_callback_(this, *packet, std::make_error_code(std::errc(0))); - doRead(); - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - tryReconnect(); - } - }); -} - -void UdpSocketConnector::tryReconnect() { - if (state_ == Connector::State::CONNECTED) { - LOG(ERROR) << "Connection lost. Trying to reconnect..."; - state_ = Connector::State::CONNECTING; - is_reconnection_ = true; - io_service_.post([this]() { - if (socket_.is_open()) { - socket_.shutdown(asio::ip::tcp::socket::shutdown_type::shutdown_both); - socket_.close(); - } - - doConnect(); - startConnectionTimer(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - }); - } -} - -void UdpSocketConnector::doConnect() { - asio::async_connect( - socket_, endpoint_iterator_, - [this](std::error_code ec, udp::resolver::iterator) { - if (!ec) { - connection_timer_.cancel(); - state_ = Connector::State::CONNECTED; - doRead(); - - if (data_available_) { - data_available_ = false; - doWrite(); - } - - if (is_reconnection_) { - is_reconnection_ = false; - } - - on_reconnect_callback_(this); - } else { - doConnect(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - }); -} - -bool UdpSocketConnector::checkConnected() { - return state_ == Connector::State::CONNECTED; -} - -void UdpSocketConnector::startConnectionTimer() { - connection_timer_.expires_from_now(std::chrono::seconds(60)); - connection_timer_.async_wait(std::bind(&UdpSocketConnector::handleDeadline, - this, std::placeholders::_1)); -} - -void UdpSocketConnector::handleDeadline(const std::error_code &ec) { - if (!ec) { - io_service_.post([this]() { - socket_.close(); - LOG(ERROR) << "Error connecting. Is the forwarder running?"; - }); - } -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/udp/udp_socket_connector.h b/libtransport/src/io_modules/udp/udp_socket_connector.h deleted file mode 100644 index c483e14aa..000000000 --- a/libtransport/src/io_modules/udp/udp_socket_connector.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2017-2019 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. - */ - -#pragma once - -#include <hicn/transport/config.h> -#include <hicn/transport/core/asio_wrapper.h> -#include <hicn/transport/core/connector.h> -#include <hicn/transport/core/content_object.h> -#include <hicn/transport/core/global_object_pool.h> -#include <hicn/transport/core/interest.h> -#include <hicn/transport/core/name.h> -#include <hicn/transport/core/packet.h> -#include <hicn/transport/utils/branch_prediction.h> - -#include <deque> - -namespace transport { -namespace core { - -using asio::ip::udp; - -class UdpSocketConnector : public Connector { - public: - UdpSocketConnector(PacketReceivedCallback &&receive_callback, - PacketSentCallback &&packet_sent, - OnCloseCallback &&close_callback, - OnReconnectCallback &&on_reconnect, - asio::io_service &io_service, - std::string app_name = "Libtransport"); - - ~UdpSocketConnector() override; - - void send(Packet &packet) override; - - void send(const uint8_t *packet, std::size_t len) override; - - void close() override; - - void connect(std::string ip_address = "127.0.0.1", std::string port = "9695"); - - private: - void doConnect(); - - void doRead(); - - void doWrite(); - - void doClose(); - - bool checkConnected(); - - private: - void handleDeadline(const std::error_code &ec); - - void startConnectionTimer(); - - void tryReconnect(); - - asio::io_service &io_service_; - asio::ip::udp::socket socket_; - asio::ip::udp::resolver resolver_; - asio::ip::udp::resolver::iterator endpoint_iterator_; - asio::steady_timer connection_timer_; - - std::pair<uint8_t *, std::size_t> read_msg_; - - bool is_reconnection_; - bool data_available_; - - std::string app_name_; -}; - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/libhicntransport-config.cmake.in b/libtransport/src/libhicntransport-config.cmake.in new file mode 100644 index 000000000..9b8c51962 --- /dev/null +++ b/libtransport/src/libhicntransport-config.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +set(Libhicntransport_VERSION_MAJOR "@VERSION_MAJOR@") +set(Libhicntransport_VERSION_MINOR "@VERSION_MINOR@") +set(Libhicntransport_VERSION_PATCH "@VERSION_PATCH@") + +set_and_check(Libhicntransport_INCLUDE_DIRS "@PACKAGE_Libhicntransport_INCLUDE_DIRS@") +include("${CMAKE_CURRENT_LIST_DIR}/libhicntransport-targets.cmake") diff --git a/libtransport/src/protocols/CMakeLists.txt b/libtransport/src/protocols/CMakeLists.txt index b763e95e2..51879a9ed 100644 --- a/libtransport/src/protocols/CMakeLists.txt +++ b/libtransport/src/protocols/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -52,9 +52,15 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc ) -set(RAAQM_CONFIG_INSTALL_PREFIX - ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn -) +if (${CMAKE_SYSTEM_NAME} MATCHES Darwin OR ${CMAKE_SYSTEM_NAME} MATCHES Linux) + set(RAAQM_CONFIG_INSTALL_PREFIX + ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + ) +else() + set(RAAQM_CONFIG_INSTALL_PREFIX + ${CMAKE_INSTALL_PREFIX}/etc/hicn + ) +endif() set(raaqm_config_path ${RAAQM_CONFIG_INSTALL_PREFIX}/consumer.conf @@ -67,7 +73,7 @@ set(TRANSPORT_CONFIG install( FILES ${TRANSPORT_CONFIG} - DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + DESTINATION ${RAAQM_CONFIG_INSTALL_PREFIX} COMPONENT ${LIBTRANSPORT_COMPONENT} ) diff --git a/libtransport/src/protocols/byte_stream_reassembly.cc b/libtransport/src/protocols/byte_stream_reassembly.cc index ac36d4e61..3278595b7 100644 --- a/libtransport/src/protocols/byte_stream_reassembly.cc +++ b/libtransport/src/protocols/byte_stream_reassembly.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/byte_stream_reassembly.h b/libtransport/src/protocols/byte_stream_reassembly.h index 278740bd3..bfcac3181 100644 --- a/libtransport/src/protocols/byte_stream_reassembly.h +++ b/libtransport/src/protocols/byte_stream_reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/cbr.cc b/libtransport/src/protocols/cbr.cc index 1548cc68d..446ea8b99 100644 --- a/libtransport/src/protocols/cbr.cc +++ b/libtransport/src/protocols/cbr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -37,11 +37,11 @@ void CbrTransportProtocol::afterDataUnsatisfied(uint64_t segment) {} void CbrTransportProtocol::afterContentReception( const Interest &interest, const ContentObject &content_object) { auto segment = content_object.getName().getSuffix(); - auto now = utils::SteadyClock::now(); - auto rtt = std::chrono::duration_cast<utils::Microseconds>( - now - interest_timepoints_[segment & mask]); + auto now = utils::SteadyTime::Clock::now(); + auto rtt = utils::SteadyTime::getDurationMs( + interest_timepoints_[segment & mask], now); // Update stats - updateStats(segment, rtt.count(), now); + updateStats(segment, rtt, now); } } // end namespace protocol diff --git a/libtransport/src/protocols/cbr.h b/libtransport/src/protocols/cbr.h index 41cdbc98c..c178dbf60 100644 --- a/libtransport/src/protocols/cbr.h +++ b/libtransport/src/protocols/cbr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/congestion_window_protocol.h b/libtransport/src/protocols/congestion_window_protocol.h index 36ac6eb17..f9ff208cc 100644 --- a/libtransport/src/protocols/congestion_window_protocol.h +++ b/libtransport/src/protocols/congestion_window_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/data_processing_events.h b/libtransport/src/protocols/data_processing_events.h index 28732502b..182de3ed8 100644 --- a/libtransport/src/protocols/data_processing_events.h +++ b/libtransport/src/protocols/data_processing_events.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/datagram_reassembly.cc b/libtransport/src/protocols/datagram_reassembly.cc index 069873a52..3a32c81f5 100644 --- a/libtransport/src/protocols/datagram_reassembly.cc +++ b/libtransport/src/protocols/datagram_reassembly.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/datagram_reassembly.h b/libtransport/src/protocols/datagram_reassembly.h index de294df06..0def32dd2 100644 --- a/libtransport/src/protocols/datagram_reassembly.h +++ b/libtransport/src/protocols/datagram_reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/errors.cc b/libtransport/src/protocols/errors.cc index 183fcc574..a7dd26e16 100644 --- a/libtransport/src/protocols/errors.cc +++ b/libtransport/src/protocols/errors.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/errors.h b/libtransport/src/protocols/errors.h index 58dadae5a..33d5fbee4 100644 --- a/libtransport/src/protocols/errors.h +++ b/libtransport/src/protocols/errors.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/fec/CMakeLists.txt b/libtransport/src/protocols/fec/CMakeLists.txt index 6d61ae043..8ae0a7360 100644 --- a/libtransport/src/protocols/fec/CMakeLists.txt +++ b/libtransport/src/protocols/fec/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/fec/fec.cc b/libtransport/src/protocols/fec/fec.cc index 16a04cb98..912e7a40f 100644 --- a/libtransport/src/protocols/fec/fec.cc +++ b/libtransport/src/protocols/fec/fec.cc @@ -44,6 +44,7 @@ #include "fec.h" +#include <assert.h> #include <hicn/transport/portability/platform.h> #include <stdio.h> #include <stdlib.h> @@ -60,72 +61,6 @@ #endif /* - * compatibility stuff - */ -#ifdef MSDOS /* but also for others, e.g. sun... */ -#define NEED_BCOPY -#define bcmp(a, b, n) memcmp(a, b, n) -#endif - -#ifdef ANDROID -#define bcmp(a, b, n) memcmp(a, b, n) -#endif - -#ifdef NEED_BCOPY -#define bcopy(s, d, siz) memcpy((d), (s), (siz)) -#define bzero(d, siz) memset((d), '\0', (siz)) -#endif - -/* - * stuff used for testing purposes only - */ - -#ifdef TEST -#define DEB(x) -#define DDB(x) x -#define DEBUG 0 /* minimal debugging */ -#ifdef MSDOS -#include <time.h> -struct timeval { - unsigned long ticks; -}; -#define gettimeofday(x, dummy) \ - { (x)->ticks = clock(); } -#define DIFF_T(a, b) (1 + 1000000 * (a.ticks - b.ticks) / CLOCKS_PER_SEC) -typedef unsigned long u_long; -typedef unsigned short u_short; -#else /* typically, unix systems */ -#include <sys/time.h> -#define DIFF_T(a, b) \ - (1 + 1000000 * (a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec)) -#endif - -#define TICK(t) \ - { \ - struct timeval x; \ - gettimeofday(&x, NULL); \ - t = x.tv_usec + 1000000 * (x.tv_sec & 0xff); \ - } -#define TOCK(t) \ - { \ - u_long t1; \ - TICK(t1); \ - if (t1 < t) \ - t = 256000000 + t1 - t; \ - else \ - t = t1 - t; \ - if (t == 0) t = 1; \ - } - -u_long ticks[10]; /* vars for timekeeping */ -#else -#define DEB(x) -#define DDB(x) -#define TICK(x) -#define TOCK(x) -#endif /* TEST */ - -/* * You should not need to change anything beyond this point. * The first part of the file implements linear algebra in GF. * @@ -402,31 +337,17 @@ static void matmul(gf *a, gf *b, gf *c, int n, int k, int m) { } } -#ifdef DEBUGG -/* - * returns 1 if the square matrix is identiy - * (only for test) - */ -static int is_identity(gf *m, int k) { - int row, col; - for (row = 0; row < k; row++) - for (col = 0; col < k; col++) - if ((row == col && *m != 1) || (row != col && *m != 0)) - return 0; - else - m++; - return 1; -} -#endif /* debug */ - /* * invert_mat() takes a matrix and produces its inverse * k is the size of the matrix. * (Gauss-Jordan, adapted from Numerical Recipes in C) * Return non-zero if singular. */ -DEB(int pivloops = 0; int pivswaps = 0; /* diagnostic */) +int pivloops = 0; +int pivswaps = 0; /* diagnostic */ static int invert_mat(gf *src, int k) { + assert(k > 0); + gf c, *p; int irow, icol, row, col, i, ix; @@ -436,9 +357,9 @@ static int invert_mat(gf *src, int k) { int *ipiv = (int *)my_malloc(k * sizeof(int), "ipiv"); gf *id_row = NEW_GF_MATRIX(1, k); gf *temp_row = NEW_GF_MATRIX(1, k); - - bzero(id_row, k * sizeof(gf)); - DEB(pivloops = 0; pivswaps = 0; /* diagnostic */) + memset(id_row, '\0', k * sizeof(gf)); + pivloops = 0; + pivswaps = 0; /* diagnostic */ /* * ipiv marks elements already used as pivots. */ @@ -459,7 +380,7 @@ static int invert_mat(gf *src, int k) { for (row = 0; row < k; row++) { if (ipiv[row] != 1) { for (ix = 0; ix < k; ix++) { - DEB(pivloops++;) + pivloops++; if (ipiv[ix] == 0) { if (src[row * k + ix] != 0) { irow = row; @@ -497,12 +418,9 @@ static int invert_mat(gf *src, int k) { fprintf(stderr, "singular matrix 2\n"); goto fail; } - if (c != 1) { /* otherwhise this is a NOP */ - /* - * this is done often , but optimizing is not so - * fruitful, at least in the obvious ways (unrolling) - */ - DEB(pivswaps++;) + + if (c != 1) { + pivswaps++; c = inverse[c]; pivot_row[icol] = 1; for (ix = 0; ix < k; ix++) pivot_row[ix] = gf_mul(c, pivot_row[ix]); @@ -515,7 +433,7 @@ static int invert_mat(gf *src, int k) { * we can optimize the addmul). */ id_row[icol] = 1; - if (bcmp(pivot_row, id_row, k * sizeof(gf)) != 0) { + if (memcmp(pivot_row, id_row, k * sizeof(gf)) != 0) { for (p = src, ix = 0; ix < k; ix++, p += k) { if (ix != icol) { c = p[icol]; @@ -560,6 +478,8 @@ fail: */ int invert_vdm(gf *src, int k) { + assert(k > 0); + int i, j, row, col; gf *b, *c, *p; gf t, xx; @@ -614,14 +534,8 @@ int invert_vdm(gf *src, int k) { static int fec_initialized = 0; static void init_fec() { - TICK(ticks[0]); generate_gf(); - TOCK(ticks[0]); - DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);) - TICK(ticks[0]); init_mul_table(); - TOCK(ticks[0]); - DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);) fec_initialized = 1; } @@ -680,19 +594,14 @@ struct fec_parms *fec_new(int k, int n) { * k*k vandermonde matrix, multiply right the bottom n-k rows * by the inverse, and construct the identity matrix at the top. */ - TICK(ticks[3]); invert_vdm(tmp_m, k); /* much faster than invert_mat */ matmul(tmp_m + k * k, tmp_m, retval->enc_matrix + k * k, n - k, k, k); /* * the upper matrix is I so do not bother with a slow multiply */ - bzero(retval->enc_matrix, k * k * sizeof(gf)); + memset(retval->enc_matrix, '\0', k * k * sizeof(gf)); for (p = retval->enc_matrix, col = 0; col < k; col++, p += k + 1) *p = 1; free(tmp_m); - TOCK(ticks[3]); - - DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", ticks[3]);) - DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) return retval; } @@ -708,10 +617,10 @@ void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) { if (GF_BITS > 8) sz /= 2; if (index < k) - bcopy(src[index], fec, sz * sizeof(gf)); + memcpy(fec, src[index], sz * sizeof(gf)); else if (index < code->n) { p = &(code->enc_matrix[index * k]); - bzero(fec, sz * sizeof(gf)); + memset(fec, '\0', sz * sizeof(gf)); for (i = 0; i < k; i++) addmul(fec, src[i], p[i], sz); } else fprintf(stderr, "Invalid index %d (max %d)\n", index, code->n - 1); @@ -733,22 +642,13 @@ static int shuffle(gf *pkt[], int index[], int k) { int c = index[i]; if (index[c] == c) { - DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) + fprintf(stderr, "\nshuffle, error at %d\n", i); return 1; } SWAP(index[i], index[c], int); SWAP(pkt[i], pkt[c], gf *); } } - DEB(/* just test that it works... */ - for (i = 0; i < k; i++) { - if (index[i] < k && index[i] != i) { - fprintf(stderr, "shuffle: after\n"); - for (i = 0; i < k; i++) fprintf(stderr, "%3d ", index[i]); - fprintf(stderr, "\n"); - return 1; - } - }) return 0; } @@ -757,20 +657,16 @@ static int shuffle(gf *pkt[], int index[], int k) { * indexes. The matrix must be already allocated as * a vector of k*k elements, in row-major order */ -static gf *build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[]) { +static gf *build_decode_matrix(struct fec_parms *code, int index[]) { int i, k = code->k; gf *p, *matrix = NEW_GF_MATRIX(k, k); - TICK(ticks[9]); for (i = 0, p = matrix; i < k; i++, p += k) { -#if 1 /* this is simply an optimization, not very useful indeed */ if (index[i] < k) { - bzero(p, k * sizeof(gf)); + memset(p, '\0', k * sizeof(gf)); p[i] = 1; - } else -#endif - if (index[i] < code->n) - bcopy(&(code->enc_matrix[index[i] * k]), p, k * sizeof(gf)); + } else if (index[i] < code->n) + memcpy(p, &(code->enc_matrix[index[i] * k]), k * sizeof(gf)); else { fprintf(stderr, "decode: invalid index %d (max %d)\n", index[i], code->n - 1); @@ -778,12 +674,10 @@ static gf *build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[]) { return NULL; } } - TICK(ticks[9]); if (invert_mat(matrix, k)) { free(matrix); matrix = NULL; } - TOCK(ticks[9]); return matrix; } @@ -800,39 +694,29 @@ static gf *build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[]) { */ int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) { gf *m_dec; - gf **new_pkt; + gf **new_pkt = nullptr; int row, col, k = code->k; + int i = 0; if (GF_BITS > 8) sz /= 2; if (shuffle(pkt, index, k)) /* error if true */ return 1; - m_dec = build_decode_matrix(code, pkt, index); + m_dec = build_decode_matrix(code, index); if (m_dec == NULL) return 1; /* error */ /* * do the actual decoding */ - new_pkt = (gf **)my_malloc(k * sizeof(gf *), "new pkt pointers"); + new_pkt = pkt + k; for (row = 0; row < k; row++) { if (index[row] >= k) { - new_pkt[row] = (gf *)my_malloc(sz * sizeof(gf), "new pkt buffer"); - bzero(new_pkt[row], sz * sizeof(gf)); + memset(new_pkt[i], '\0', sz * sizeof(gf)); for (col = 0; col < k; col++) - addmul(new_pkt[row], pkt[col], m_dec[row * k + col], sz); - } - } - /* - * move pkts to their final destination - */ - for (row = 0; row < k; row++) { - if (index[row] >= k) { - bcopy(new_pkt[row], pkt[row], sz * sizeof(gf)); - free(new_pkt[row]); + addmul(new_pkt[i], pkt[col], m_dec[row * k + col], sz); + i++; } } - free(new_pkt); free(m_dec); - return 0; } diff --git a/libtransport/src/protocols/fec/rely.cc b/libtransport/src/protocols/fec/rely.cc index 7a30a62e2..d4d98a90b 100644 --- a/libtransport/src/protocols/fec/rely.cc +++ b/libtransport/src/protocols/fec/rely.cc @@ -23,38 +23,39 @@ namespace transport { namespace protocol { namespace fec { -RelyEncoder::RelyEncoder(uint32_t k, uint32_t n, uint32_t seq_offset) +RelyEncoder::RelyEncoder(uint32_t k, uint32_t n, uint32_t /* seq_offset */) : RelyBase(k, n) { configure(kmtu, ktimeout, kmax_stream_size); set_repair_trigger(k_, n_ - k_, n_ - k_); } void RelyEncoder::onPacketProduced(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { // Get pointer to payload, leaving space to insert FEC header. // TODO Check if this additional header is really needed. - auto data = content_object.writableData() + offset - sizeof(fec_header); - auto length = content_object.length() - offset + sizeof(fec_header); + auto data = content_object.writableData() + offset - sizeof(fec_metadata); + auto length = content_object.length() - offset + sizeof(fec_metadata); // Check packet length does not exceed maximum length supported by the // encoder (otherwise segmentation would take place). - assert(length < max_packet_bytes()); + DCHECK(length < max_packet_bytes()); DLOG_IF(INFO, VLOG_IS_ON(4)) - << "Encoding packet of length " << length - sizeof(fec_header); + << "Encoding packet of length " << length - sizeof(fec_metadata); - // Get the suffix. With rely we need to write it in the fec_header in order to - // be able to recognize the seq number upon recovery. + // Get the suffix. With rely we need to write it in the fec_metadata in order + // to be able to recognize the seq number upon recovery. auto suffix = content_object.getName().getSuffix(); DLOG_IF(INFO, VLOG_IS_ON(4)) << "Producing packet " << suffix << " (index == " << current_index_ << ")"; - // Consume payload. Add fec_header in front before feeding payload to encoder, - // and copy original content of packet - fec_header *h = reinterpret_cast<fec_header *>(data); - fec_header copy = *h; + // Consume payload. Add fec_metadata in front before feeding payload to + // encoder, and copy original content of packet + fec_metadata *h = reinterpret_cast<fec_metadata *>(data); + fec_metadata copy = *h; h->setSeqNumberBase(suffix); + h->setMetadataBase(metadata); auto packets = consume(data, length, getCurrentTime()); - assert(packets == 1); + DCHECK(packets == 1); // Update packet counter current_index_ += packets; @@ -62,7 +63,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, // Restore original packet content and increment data pointer to the correct // position *h = copy; - data += sizeof(fec_header); + data += sizeof(fec_metadata); // Check position of this packet inside N size block auto i = current_index_ % n_; @@ -74,24 +75,24 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, // TODO Optimize it by copying only the RELY header // Be sure encoder can produce - assert(can_produce()); + DCHECK(can_produce()); // Check new payload size and make sure it fits in packet buffer auto new_payload_size = produce_bytes(); int difference = new_payload_size - length; - assert(difference > 0); - assert(content_object.ensureCapacity(difference)); + DCHECK(difference > 0); + DCHECK(content_object.ensureCapacity(difference)); // Update length DLOG_IF(INFO, VLOG_IS_ON(4)) << "The packet length will be incremented by " - << difference + sizeof(fec_header); - content_object.append(difference + sizeof(fec_header)); + << difference + sizeof(fec_metadata); + content_object.append(difference + sizeof(fec_metadata)); content_object.updateLength(); // Make sure we got a source packet, otherwise we would put a repair symbol // in a source packet - assert(rely::packet_is_systematic(produce_data())); + DCHECK(rely::packet_is_systematic(produce_data())); // Copy rely packet replacing old source packet. std::memcpy(data, produce_data(), new_payload_size); @@ -111,7 +112,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, while (can_produce()) { // The current index MUST be k_, because we enforce n - k repair to be // produced after k sources - assert(current_index_ == k_); + DCHECK(current_index_ == k_); buffer packet; if (!buffer_callback_) { @@ -130,7 +131,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, std::memcpy(packet->writableData(), produce_data(), produce_bytes()); // Push symbol in repair_packets - packets_.emplace_back(0, std::move(packet)); + packets_.emplace_back(0, metadata, std::move(packet)); // Advance the encoder produce_next(); @@ -143,7 +144,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, // If we have generated repair symbols, let's notify caller via the installed // callback if (packets_.size()) { - assert(packets_.size() == n_ - k_); + DCHECK(packets_.size() == n_ - k_); fec_callback_(packets_); packets_.clear(); current_index_ = 0; @@ -156,7 +157,7 @@ RelyDecoder::RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) } void RelyDecoder::onDataPacket(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { // Adjust pointers to point to packet payload auto data = content_object.writableData() + offset; auto size = content_object.length() - offset; @@ -164,30 +165,38 @@ void RelyDecoder::onDataPacket(core::ContentObject &content_object, // Pass payload to decoder consume(data, size, getCurrentTime()); + producePackets(); +} + +void RelyDecoder::producePackets() { // Drain decoder if possible while (can_produce()) { - // Get size of decoded packet - auto size = produce_bytes(); - - // Get buffer to copy packet in - auto packet = core::PacketManager<>::getInstance().getMemBuf(); + auto fec_header_size = sizeof(fec_metadata); + auto payload_size = produce_bytes() - sizeof(fec_metadata); - // Copy buffer - packet->append(size); - std::memcpy(packet->writableData(), produce_data(), size); + buffer packet; + if (!buffer_callback_) { + packet = core::PacketManager<>::getInstance().getMemBuf(); + packet->append(payload_size); + } else { + packet = buffer_callback_(payload_size); + } // Read seq number - fec_header *h = reinterpret_cast<fec_header *>(packet->writableData()); + const fec_metadata *h = + reinterpret_cast<const fec_metadata *>(produce_data()); uint32_t index = h->getSeqNumberBase(); + uint32_t metadata = h->getMetadataBase(); DLOG_IF(INFO, VLOG_IS_ON(4)) << "The index written in the packet is " << index; - // Remove FEC header - packet->trimStart(sizeof(fec_header)); + // Copy payload + std::memcpy(packet->writableData(), produce_data() + fec_header_size, + payload_size); // Save packet in buffer - packets_.emplace_back(index, std::move(packet)); + packets_.emplace_back(index, metadata, std::move(packet)); // Advance to next packet produce_next(); @@ -198,8 +207,28 @@ void RelyDecoder::onDataPacket(core::ContentObject &content_object, fec_callback_(packets_); packets_.clear(); } + + flushOutOfOrder(); +} + +void RelyDecoder::flushOutOfOrder() { + if (flush_timer_ == nullptr) return; + flush_timer_->cancel(); + + if (has_upcoming_flush()) { + flush_timer_->expires_from_now(std::chrono::milliseconds( + std::max((int64_t)0, upcoming_flush(getCurrentTime())))); + + flush_timer_->async_wait([this](const std::error_code &ec) { + if (ec) return; + if (has_upcoming_flush()) { + flush(getCurrentTime()); + producePackets(); + } + }); + } } } // namespace fec } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/fec/rely.h b/libtransport/src/protocols/fec/rely.h index bfbdb30bc..001a26002 100644 --- a/libtransport/src/protocols/fec/rely.h +++ b/libtransport/src/protocols/fec/rely.h @@ -33,8 +33,12 @@ namespace fec { * @brief Table of used codes. */ #define foreach_rely_fec_type \ + _(Rely, 1, 2) \ _(Rely, 1, 3) \ + _(Rely, 1, 4) \ _(Rely, 2, 3) \ + _(Rely, 2, 6) \ + _(Rely, 3, 9) \ _(Rely, 4, 5) \ _(Rely, 4, 6) \ _(Rely, 4, 7) \ @@ -74,11 +78,17 @@ class RelyBase : public virtual FECBase { * decoding operations. It may be removed once we know the meaning of the * fields in the rely header. */ - struct fec_header { - uint32_t seq_number; - + class fec_metadata { + public: void setSeqNumberBase(uint32_t suffix) { seq_number = htonl(suffix); } - uint32_t getSeqNumberBase() { return ntohl(seq_number); } + uint32_t getSeqNumberBase() const { return ntohl(seq_number); } + + void setMetadataBase(uint32_t value) { metadata = htonl(value); } + uint32_t getMetadataBase() const { return ntohl(metadata); } + + private: + uint32_t seq_number; + uint32_t metadata; }; /** @@ -112,21 +122,20 @@ class RelyBase : public virtual FECBase { #if RELY_DEBUG return time_++; #else - auto _time = utils::SteadyClock::now().time_since_epoch(); - auto time = std::chrono::duration_cast<utils::Milliseconds>(_time).count(); - return time; + return utils::SteadyTime::nowMs().count(); #endif } - protected: uint32_t k_; uint32_t n_; + std::uint32_t seq_offset_; + /** * @brief Vector of packets to be passed to caller callbacks. For encoder it * will contain the repair packets, for decoder the recovered sources. */ - std::vector<std::pair<uint32_t, buffer>> packets_; + BufferArray packets_; /** * @brief Current index to be used for local packet count. @@ -142,50 +151,54 @@ class RelyBase : public virtual FECBase { * @brief The Rely Encoder implementation. * */ -class RelyEncoder : private RelyBase, - private rely::encoder, - public ProducerFEC { +class RelyEncoder : RelyBase, rely::encoder, public ProducerFEC { public: RelyEncoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0); /** * Producers will call this function when they produce a data packet. */ - void onPacketProduced(core::ContentObject &content_object, - uint32_t offset) override; + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets */ std::size_t getFecHeaderSize() override { - return header_bytes() + sizeof(fec_header) + 4; + return header_bytes() + sizeof(fec_metadata) + 4; } - void reset() override {} + void reset() override { + // Nothing to do here + } }; -class RelyDecoder : private RelyBase, - private rely::decoder, - public ConsumerFEC { +class RelyDecoder : RelyBase, rely::decoder, public ConsumerFEC { public: RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0); /** * Consumers will call this function when they receive a data packet */ - void onDataPacket(core::ContentObject &content_object, - uint32_t offset) override; + void onDataPacket(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets */ std::size_t getFecHeaderSize() override { - return header_bytes() + sizeof(fec_header); + return header_bytes() + sizeof(fec_metadata); + } + + void reset() override { + // Nothing to do here } - void reset() override {} + private: + void producePackets(); + void flushOutOfOrder(); }; } // namespace fec } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/fec/rs.cc b/libtransport/src/protocols/fec/rs.cc index 2c23d515d..9c0a3d4fb 100644 --- a/libtransport/src/protocols/fec/rs.cc +++ b/libtransport/src/protocols/fec/rs.cc @@ -46,23 +46,26 @@ bool BlockCode::addRepairSymbol(const fec::buffer &packet, uint32_t i, to_decode_ = true; DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding symbol of size " << packet->length(); return addSymbol(packet, i, offset, - packet->length() - sizeof(fec_header) - offset); + packet->length() - sizeof(fec_header) - offset, + FECBase::INVALID_METADATA); } bool BlockCode::addSourceSymbol(const fec::buffer &packet, uint32_t i, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding source symbol of size " << packet->length() << ", offset " << offset; - return addSymbol(packet, i, offset, packet->length() - offset); + return addSymbol(packet, i, offset, packet->length() - offset, metadata); } bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i, - uint32_t offset, std::size_t size) { + uint32_t offset, std::size_t size, + uint32_t metadata) { if (size > max_buffer_size_) { max_buffer_size_ = size; } - operator[](current_block_size_++) = std::make_tuple(i, packet, offset); + operator[](current_block_size_) = RSBufferInfo(offset, i, metadata, packet); + current_block_size_++; if (current_block_size_ >= k_) { if (to_decode_) { @@ -80,12 +83,13 @@ bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i, void BlockCode::encode() { gf *data[n_]; - uint32_t base = std::get<0>(operator[](0)); + uint32_t base = operator[](0).getIndex(); // Set packet length in first 2 bytes for (uint32_t i = 0; i < k_; i++) { - auto &packet = std::get<1>(operator[](i)); - auto offset = std::get<2>(operator[](i)); + auto &packet = operator[](i).getBuffer(); + auto offset = operator[](i).getOffset(); + auto metadata_base = operator[](i).getMetadata(); auto ret = packet->ensureCapacityAndFillUnused(max_buffer_size_ + offset, 0); @@ -98,10 +102,11 @@ void BlockCode::encode() { // Buffers should hold 2 *after* the padding, in order to be // able to set the length for the encoding operation. // packet->trimStart(offset); - uint16_t *length = reinterpret_cast<uint16_t *>(packet->writableData() + - max_buffer_size_ + offset); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ + offset); auto buffer_length = packet->length() - offset; - *length = htons(buffer_length); + metadata->setPacketLength(buffer_length); + metadata->setMetadataBase(metadata_base); DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current buffer size: " << packet->length(); @@ -109,7 +114,7 @@ void BlockCode::encode() { } // Finish to fill source block with the buffers to hold the repair symbols - auto length = max_buffer_size_ + sizeof(fec_header) + LEN_SIZE_BYTES; + auto length = max_buffer_size_ + sizeof(fec_header) + METADATA_BYTES; for (uint32_t i = k_; i < n_; i++) { buffer packet; if (!params_.buffer_callback_) { @@ -133,19 +138,20 @@ void BlockCode::encode() { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current symbol size: " << packet->length(); data[i] = packet->writableData(); - operator[](i) = std::make_tuple(i, std::move(packet), uint32_t(0)); + operator[](i) = RSBufferInfo(uint32_t(0), i, FECBase::INVALID_METADATA, + std::move(packet)); } // Generate repair symbols and put them in corresponding buffers DLOG_IF(INFO, VLOG_IS_ON(4)) << "Calling encode with max_buffer_size_ = " << max_buffer_size_; for (uint32_t i = k_; i < n_; i++) { - fec_encode(code_, data, data[i], i, max_buffer_size_ + LEN_SIZE_BYTES); + fec_encode(code_, data, data[i], i, max_buffer_size_ + METADATA_BYTES); } // Re-include header in repair packets for (uint32_t i = k_; i < n_; i++) { - auto &packet = std::get<1>(operator[](i)); + auto &packet = operator[](i).getBuffer(); packet->prepend(sizeof(fec_header)); DLOG_IF(INFO, VLOG_IS_ON(4)) << "Produced repair symbol of size = " << packet->length(); @@ -153,16 +159,31 @@ void BlockCode::encode() { } void BlockCode::decode() { - gf *data[k_]; + gf *data[n_]; uint32_t index[k_]; + buffer aux_fec_packets[n_ - k_]; + // FEC packet number k0 + uint32_t k0 = 0; + + // Reorder block by index with in-place sorting + for (uint32_t i = 0; i < k_;) { + uint32_t idx = operator[](i).getIndex(); + if (idx >= k_ || idx == i) { + i++; + } else { + std::swap(operator[](i), operator[](idx)); + } + } for (uint32_t i = 0; i < k_; i++) { - auto &packet = std::get<1>(operator[](i)); - index[i] = std::get<0>(operator[](i)); - auto offset = std::get<2>(operator[](i)); + auto &packet = operator[](i).getBuffer(); + index[i] = operator[](i).getIndex(); + auto offset = operator[](i).getOffset(); + auto metadata_base = operator[](i).getMetadata(); sorted_index_[i] = index[i]; if (index[i] < k_) { + operator[](i).setReceived(); DLOG_IF(INFO, VLOG_IS_ON(4)) << "DECODE SOURCE - index " << index[i] << " - Current buffer size: " << packet->length(); @@ -173,49 +194,51 @@ void BlockCode::decode() { // able to set the length for the encoding operation packet->trimStart(offset); packet->ensureCapacityAndFillUnused(max_buffer_size_, 0); - uint16_t *length = reinterpret_cast<uint16_t *>( - packet->writableData() + max_buffer_size_ - LEN_SIZE_BYTES); - - *length = htons(packet->length()); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ - METADATA_BYTES); + metadata->setPacketLength(packet->length()); + metadata->setMetadataBase(metadata_base); } else { DLOG_IF(INFO, VLOG_IS_ON(4)) << "DECODE SYMBOL - index " << index[i] << " - Current buffer size: " << packet->length(); packet->trimStart(sizeof(fec_header) + offset); + aux_fec_packets[k0] = core::PacketManager<>::getInstance().getMemBuf(); + data[k_ + k0] = aux_fec_packets[k0]->writableData(); + k0++; } - data[i] = packet->writableData(); } - // We decode the source block DLOG_IF(INFO, VLOG_IS_ON(4)) << "Calling decode with max_buffer_size_ = " << max_buffer_size_; + fec_decode(code_, data, reinterpret_cast<int *>(index), max_buffer_size_); // Find the index in the block for recovered packets - for (uint32_t i = 0; i < k_; i++) { - if (index[i] != i) { - for (uint32_t j = 0; j < k_; j++) - if (sorted_index_[j] == uint32_t(index[i])) { - sorted_index_[j] = i; - } - } - } - - // Reorder block by index with in-place sorting - for (uint32_t i = 0; i < k_; i++) { - for (uint32_t j = sorted_index_[i]; j != i; j = sorted_index_[i]) { - std::swap(sorted_index_[j], sorted_index_[i]); - std::swap(operator[](j), operator[](i)); + for (uint32_t i = 0, j = 0; i < k_; i++) { + if (index[i] >= k_) { + operator[](i).setBuffer(aux_fec_packets[j++]); + operator[](i).setIndex(i); } } // Adjust length according to the one written in the source packet for (uint32_t i = 0; i < k_; i++) { - auto &packet = std::get<1>(operator[](i)); - uint16_t *length = reinterpret_cast<uint16_t *>( - packet->writableData() + max_buffer_size_ - LEN_SIZE_BYTES); - packet->setLength(ntohs(*length)); + auto &packet = operator[](i).getBuffer(); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ - METADATA_BYTES); + // Adjust buffer length + packet->setLength(metadata->getPacketLength()); + // Adjust metadata + operator[](i).setMetadata(metadata->getMetadataBase()); + + // reset the point to the beginning of the packets for all received packets + if (operator[](i).getReceived()) { + auto &packet = operator[](i).getBuffer(); + auto offset = operator[](i).getOffset(); + packet->prepend(offset); + } } } @@ -252,12 +275,11 @@ RSEncoder::RSEncoder(uint32_t k, uint32_t n, uint32_t seq_offset) source_block_(k_, n_, seq_offset_, current_code_, *this) {} void RSEncoder::consume(const fec::buffer &packet, uint32_t index, - uint32_t offset) { - if (!source_block_.addSourceSymbol(packet, index, offset)) { - std::vector<std::pair<uint32_t, buffer>> repair_packets; + uint32_t offset, uint32_t metadata) { + if (!source_block_.addSourceSymbol(packet, index, offset, metadata)) { + fec::BufferArray repair_packets; for (uint32_t i = k_; i < n_; i++) { - repair_packets.emplace_back(std::move(std::get<0>(source_block_[i])), - std::move(std::get<1>(source_block_[i]))); + repair_packets.emplace_back(std::move(source_block_[i])); } fec_callback_(repair_packets); @@ -265,9 +287,9 @@ void RSEncoder::consume(const fec::buffer &packet, uint32_t index, } void RSEncoder::onPacketProduced(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { consume(content_object.shared_from_this(), - content_object.getName().getSuffix(), offset); + content_object.getName().getSuffix(), offset, metadata); } RSDecoder::RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) @@ -276,10 +298,15 @@ RSDecoder::RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) void RSDecoder::recoverPackets(SourceBlocks::iterator &src_block_it) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "recoverPackets for " << k_; auto &src_block = src_block_it->second; - std::vector<std::pair<uint32_t, buffer>> source_packets(k_); + auto base_index = src_block_it->first; + BufferArray source_packets(k_); + + // Iterate over packets in the block and adjust indexed accordingly. This must + // be done because indexes are from 0 to (n - k - 1), but we need indexes from + // base_index to base_index + (n - k - 1) for (uint32_t i = 0; i < src_block.getK(); i++) { - source_packets[i] = std::make_pair(src_block_it->first + i, - std::move(std::get<1>(src_block[i]))); + src_block[i].setIndex(base_index + src_block[i].getIndex()); + source_packets[i] = FECBufferInfo(std::move(src_block[i])); } setProcessed(src_block_it->first); @@ -296,9 +323,9 @@ void RSDecoder::recoverPackets(SourceBlocks::iterator &src_block_it) { } void RSDecoder::consumeSource(const fec::buffer &packet, uint32_t index, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { // Normalize index - assert(index >= seq_offset_); + DCHECK(index >= seq_offset_); auto i = (index - seq_offset_) % n_; // Get base @@ -324,16 +351,19 @@ void RSDecoder::consumeSource(const fec::buffer &packet, uint32_t index, // protocol that may come in the future. auto it = src_blocks_.find(base); if (it != src_blocks_.end()) { - auto ret = it->second.addSourceSymbol(packet, i, offset); + auto ret = it->second.addSourceSymbol(packet, i, offset, metadata); if (!ret) { recoverPackets(it); } } else { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding to parked source packets"; - auto ret = parked_packets_.emplace( - base, std::vector<std::pair<buffer, uint32_t>>()); - ret.first->second.emplace_back(packet, i); + auto ret = parked_packets_.emplace(base, BufferInfoArray()); + ret.first->second.emplace_back(offset, i, metadata, packet); + /** + * If we reached k source packets, we do not have any missing packet to + * recover via FEC. Delete the block. + */ if (ret.first->second.size() >= k_) { setProcessed(ret.first->first); parked_packets_.erase(ret.first); @@ -356,8 +386,8 @@ void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Decoder consume called for repair symbol. BASE = " << base - << ", index = " << base + i << " and i = " << i << ". K=" << k - << ", N=" << n; + << ", index = " << base + i << " and i = " << (int)i << ". K=" << (int)k + << ", N=" << (int)n; // check if a source block already exist for this symbol auto it = src_blocks_.find(base); @@ -380,8 +410,9 @@ void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { auto it2 = parked_packets_.find(base); if (it2 != parked_packets_.end()) { for (auto &packet_index : it2->second) { - auto ret = it->second.addSourceSymbol(packet_index.first, - packet_index.second, offset); + auto ret = it->second.addSourceSymbol( + packet_index.getBuffer(), packet_index.getIndex(), + packet_index.getOffset(), packet_index.getMetadata()); if (!ret) { recoverPackets(it); // Finish to delete packets in same source block that were @@ -399,7 +430,7 @@ void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { } void RSDecoder::onDataPacket(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Calling fec for data packet " << content_object.getName() << ". Offset: " << offset; @@ -409,7 +440,7 @@ void RSDecoder::onDataPacket(core::ContentObject &content_object, if (isSymbol(suffix)) { consumeRepair(content_object.shared_from_this(), offset); } else { - consumeSource(content_object.shared_from_this(), suffix, offset); + consumeSource(content_object.shared_from_this(), suffix, offset, metadata); } } diff --git a/libtransport/src/protocols/fec/rs.h b/libtransport/src/protocols/fec/rs.h index e159ad9f7..034c32bdc 100644 --- a/libtransport/src/protocols/fec/rs.h +++ b/libtransport/src/protocols/fec/rs.h @@ -34,10 +34,28 @@ namespace protocol { namespace fec { #define foreach_rs_fec_type \ + _(RS, 1, 2) \ _(RS, 1, 3) \ + _(RS, 1, 4) \ + _(RS, 2, 3) \ + _(RS, 2, 4) \ + _(RS, 2, 5) \ + _(RS, 2, 6) \ + _(RS, 3, 6) \ + _(RS, 3, 7) \ + _(RS, 3, 8) \ + _(RS, 3, 9) \ + _(RS, 3, 10) \ + _(RS, 3, 11) \ + _(RS, 3, 12) \ _(RS, 4, 5) \ _(RS, 4, 6) \ _(RS, 4, 7) \ + _(RS, 4, 8) \ + _(RS, 4, 9) \ + _(RS, 4, 10) \ + _(RS, 4, 11) \ + _(RS, 4, 12) \ _(RS, 6, 10) \ _(RS, 8, 10) \ _(RS, 8, 11) \ @@ -45,6 +63,8 @@ namespace fec { _(RS, 8, 14) \ _(RS, 8, 16) \ _(RS, 8, 32) \ + _(RS, 10, 20) \ + _(RS, 10, 25) \ _(RS, 10, 30) \ _(RS, 10, 40) \ _(RS, 10, 60) \ @@ -56,12 +76,24 @@ namespace fec { _(RS, 16, 27) \ _(RS, 17, 21) \ _(RS, 17, 34) \ + _(RS, 20, 45) \ + _(RS, 20, 50) \ + _(RS, 20, 60) \ + _(RS, 20, 70) \ + _(RS, 30, 70) \ + _(RS, 30, 75) \ + _(RS, 30, 85) \ + _(RS, 30, 95) \ _(RS, 32, 36) \ _(RS, 32, 41) \ _(RS, 32, 46) \ _(RS, 32, 54) \ _(RS, 34, 42) \ _(RS, 35, 70) \ + _(RS, 40, 95) \ + _(RS, 40, 100) \ + _(RS, 40, 110) \ + _(RS, 40, 120) \ _(RS, 52, 62) static const constexpr uint16_t MAX_SOURCE_BLOCK_SIZE = 128; @@ -73,9 +105,24 @@ static const constexpr uint16_t MAX_SOURCE_BLOCK_SIZE = 128; * std::array allows to be constructed in place, saving the allocation at the * price os knowing in advance its size. */ -using Packets = std::array<std::tuple</* index */ uint32_t, /* buffer */ buffer, - uint32_t /* offset */>, - MAX_SOURCE_BLOCK_SIZE>; +class RSBufferInfo : public FECBufferInfo { + public: + RSBufferInfo() : FECBufferInfo() {} + + RSBufferInfo(uint32_t offset, uint32_t index, uint32_t metadata, + buffer buffer) + : FECBufferInfo(index, metadata, buffer), offset_(offset) {} + + uint32_t getOffset() { return offset_; } + RSBufferInfo &setOffset(uint32_t offset) { + offset_ = offset; + return *this; + } + + private: + uint32_t offset_ = 0; +}; +using Packets = std::array<RSBufferInfo, MAX_SOURCE_BLOCK_SIZE>; /** * FEC Header, prepended to symbol packets. @@ -123,10 +170,32 @@ class rs; */ class BlockCode : public Packets { /** + * @brief Metadata to include when encoding the buffers. This does not need to + * be sent over the network, but just to be included in the FEC protected + * bytes. + * + */ + class __attribute__((__packed__)) fec_metadata { + public: + void setPacketLength(uint16_t length) { packet_length = htons(length); } + uint32_t getPacketLength() { return ntohs(packet_length); } + + void setMetadataBase(uint32_t value) { metadata = htonl(value); } + uint32_t getMetadataBase() { return ntohl(metadata); } + + private: + uint16_t packet_length; /* Used to get the real size of the packet after we + pad it */ + uint32_t + metadata; /* Caller may specify an integer for storing additional + metadata that can be used when recovering the packet. */ + }; + + /** * For variable length packet we need to prepend to the padded payload the * real length of the packet. This is *not* sent over the network. */ - static constexpr std::size_t LEN_SIZE_BYTES = 2; + static constexpr std::size_t METADATA_BYTES = sizeof(fec_metadata); public: BlockCode(uint32_t k, uint32_t n, uint32_t seq_offset, struct fec_parms *code, @@ -142,7 +211,8 @@ class BlockCode : public Packets { * Add a source symbol to the source block. */ bool addSourceSymbol(const fec::buffer &packet, uint32_t i, - uint32_t offset = 0); + uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); /** * Get current length of source block. @@ -169,7 +239,7 @@ class BlockCode : public Packets { * Add symbol to source block **/ bool addSymbol(const fec::buffer &packet, uint32_t i, uint32_t offset, - std::size_t size); + std::size_t size, uint32_t metadata); /** * Starting from k source symbols, get the n - k repair symbols @@ -310,10 +380,11 @@ class RSEncoder : public rs, public ProducerFEC { /** * Always consume source symbols. */ - void consume(const fec::buffer &packet, uint32_t index, uint32_t offset = 0); + void consume(const fec::buffer &packet, uint32_t index, uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); - void onPacketProduced(core::ContentObject &content_object, - uint32_t offset) override; + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets @@ -348,8 +419,8 @@ class RSDecoder : public rs, public ConsumerFEC { /** * Consume source symbol */ - void consumeSource(const fec::buffer &packet, uint32_t i, - uint32_t offset = 0); + void consumeSource(const fec::buffer &packet, uint32_t i, uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); /** * Consume repair symbol @@ -359,8 +430,8 @@ class RSDecoder : public rs, public ConsumerFEC { /** * Consumers will call this function when they receive a data packet */ - void onDataPacket(core::ContentObject &content_object, - uint32_t offset) override; + void onDataPacket(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets @@ -398,8 +469,8 @@ class RSDecoder : public rs, public ConsumerFEC { * not make any sense to build the source block, since we received all the * source packet of the block. */ - std::unordered_map<uint32_t, std::vector<std::pair<buffer, uint32_t>>> - parked_packets_; + using BufferInfoArray = std::vector<RSBufferInfo>; + std::unordered_map<uint32_t, BufferInfoArray> parked_packets_; }; } // namespace fec diff --git a/libtransport/src/protocols/fec_base.h b/libtransport/src/protocols/fec_base.h index a1929d85e..bda3ee756 100644 --- a/libtransport/src/protocols/fec_base.h +++ b/libtransport/src/protocols/fec_base.h @@ -15,6 +15,7 @@ #pragma once +#include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/errors/not_implemented_exception.h> @@ -25,12 +26,67 @@ namespace protocol { namespace fec { -using buffer = typename utils::MemBuf::Ptr; -using BufferArray = std::vector<std::pair<uint32_t, buffer>>; +using buffer = utils::MemBuf::Ptr; + +class FECBufferInfo { + public: + FECBufferInfo() + : index_(~0), metadata_(~0), received_(false), buffer_(nullptr) {} + + template <typename T> + FECBufferInfo(uint32_t index, uint32_t metadata, T &&buffer) + : index_(index), + metadata_(metadata), + received_(false), + buffer_(std::forward<T>(buffer)) {} + + // Getters + uint32_t getIndex() const { return index_; } + + uint32_t getMetadata() const { return metadata_; } + + bool getReceived() const { return received_; } + + buffer &getBuffer() { return buffer_; } + + // Setters + void setReceived() { received_ = true; } + + FECBufferInfo &setIndex(uint32_t index) { + index_ = index; + return *this; + } + + FECBufferInfo &setMetadata(uint32_t metadata) { + metadata_ = metadata; + return *this; + } + + FECBufferInfo &setBuffer(buffer &buffer) { + buffer_ = buffer; + return *this; + } + + FECBufferInfo &setBuffer(buffer &&buffer) { + buffer_ = std::move(buffer); + return *this; + } + + private: + uint32_t index_; + uint32_t metadata_; + bool received_; + buffer buffer_; +}; + +using BufferArray = typename std::vector<FECBufferInfo>; class FECBase { public: - virtual ~FECBase() = default; + static inline uint32_t INVALID_METADATA = ~0; + static inline uint32_t INVALID_INDEX = ~0; + + virtual ~FECBase() {} /** * Callback to be called after the encode or the decode operations. In the * former case it will contain the symbols, while in the latter the sources. @@ -64,11 +120,21 @@ class FECBase { buffer_callback_ = buffer_callback; } + /** + * Creates the timer to flush packets. So far needed only if using Rely and + * want to avoid expired packets blocked by missing pkts to wait for a new + * packet to arrive and trigger the flush + */ + void setIOService(asio::io_service &io_service) { + flush_timer_ = std::make_unique<asio::steady_timer>(io_service); + } + virtual void reset() = 0; protected: PacketsReady fec_callback_{0}; BufferRequested buffer_callback_{0}; + std::unique_ptr<asio::steady_timer> flush_timer_; }; /** @@ -80,8 +146,9 @@ class ProducerFEC : public virtual FECBase { /** * Producers will call this function upon production of a new packet. */ - virtual void onPacketProduced(core::ContentObject &content_object, - uint32_t offset) = 0; + virtual void onPacketProduced( + core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) = 0; }; /** @@ -95,9 +162,10 @@ class ConsumerFEC : public virtual FECBase { * Consumers will call this function when they receive a data packet */ virtual void onDataPacket(core::ContentObject &content_object, - uint32_t offset) = 0; + uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) = 0; }; } // namespace fec } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.cc b/libtransport/src/protocols/incremental_indexer_bytestream.cc index cc302a98a..b94f229e5 100644 --- a/libtransport/src/protocols/incremental_indexer_bytestream.cc +++ b/libtransport/src/protocols/incremental_indexer_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -29,9 +29,9 @@ void IncrementalIndexer::onContentObject(core::Interest &interest, DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content " << content_object.getName(); - assert(reassembly_); + DCHECK(reassembly_); - if (TRANSPORT_EXPECT_FALSE(content_object.testRst())) { + if (TRANSPORT_EXPECT_FALSE(content_object.isLast())) { final_suffix_ = content_object.getName().getSuffix(); } diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.h b/libtransport/src/protocols/incremental_indexer_bytestream.h index c6a669629..422e49ecd 100644 --- a/libtransport/src/protocols/incremental_indexer_bytestream.h +++ b/libtransport/src/protocols/incremental_indexer_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -62,7 +62,7 @@ class IncrementalIndexer : public Indexer { next_reassembly_suffix_ = first_suffix_; } - virtual uint32_t checkNextSuffix() override { + virtual uint32_t checkNextSuffix() const override { return next_download_suffix_ <= final_suffix_ ? next_download_suffix_ : Indexer::invalid_index; } @@ -76,7 +76,7 @@ class IncrementalIndexer : public Indexer { first_suffix_ = suffix; } - uint32_t getFirstSuffix() override { return first_suffix_; } + uint32_t getFirstSuffix() const override { return first_suffix_; } virtual uint32_t jumpToIndex(uint32_t index) override { next_download_suffix_ = index; @@ -95,14 +95,14 @@ class IncrementalIndexer : public Indexer { return final_suffix_ != Indexer::invalid_index; } - virtual uint32_t getFinalSuffix() override { return final_suffix_; } + virtual uint32_t getFinalSuffix() const override { return final_suffix_; } void enableFec(fec::FECType fec_type) override {} void disableFec() override {} void setNFec(uint32_t n_fec) override {} - virtual uint32_t getNFec() override { return 0; } + virtual uint32_t getNFec() const override { return 0; } virtual void onContentObject(core::Interest &interest, core::ContentObject &content_object, diff --git a/libtransport/src/protocols/index_manager_bytestream.cc b/libtransport/src/protocols/index_manager_bytestream.cc index c78dc634d..952f36e0e 100644 --- a/libtransport/src/protocols/index_manager_bytestream.cc +++ b/libtransport/src/protocols/index_manager_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/index_manager_bytestream.h b/libtransport/src/protocols/index_manager_bytestream.h index e14c8845b..7ea31dfa5 100644 --- a/libtransport/src/protocols/index_manager_bytestream.h +++ b/libtransport/src/protocols/index_manager_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -40,7 +40,9 @@ class IndexManager : public IncrementalIndexer { indexer_->setFirstSuffix(suffix); } - uint32_t getFirstSuffix() override { return indexer_->getFirstSuffix(); } + uint32_t getFirstSuffix() const override { + return indexer_->getFirstSuffix(); + } uint32_t getNextReassemblySegment() override { return indexer_->getNextReassemblySegment(); @@ -50,22 +52,26 @@ class IndexManager : public IncrementalIndexer { return indexer_->isFinalSuffixDiscovered(); } - uint32_t getFinalSuffix() override { return indexer_->getFinalSuffix(); } + uint32_t getFinalSuffix() const override { + return indexer_->getFinalSuffix(); + } uint32_t jumpToIndex(uint32_t index) override { return indexer_->jumpToIndex(index); } void setNFec(uint32_t n_fec) override { return indexer_->setNFec(n_fec); } - uint32_t getNFec() override { return indexer_->getNFec(); } + uint32_t getNFec() const override { return indexer_->getNFec(); } void enableFec(fec::FECType fec_type) override { return indexer_->enableFec(fec_type); } - double getFecOverhead() override { return indexer_->getFecOverhead(); } + double getFecOverhead() const override { return indexer_->getFecOverhead(); } - double getMaxFecOverhead() override { return indexer_->getMaxFecOverhead(); } + double getMaxFecOverhead() const override { + return indexer_->getMaxFecOverhead(); + } void disableFec() override { return indexer_->disableFec(); } diff --git a/libtransport/src/protocols/indexer.cc b/libtransport/src/protocols/indexer.cc index 8d4cf04d7..41465755b 100644 --- a/libtransport/src/protocols/indexer.cc +++ b/libtransport/src/protocols/indexer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -14,10 +14,10 @@ */ #include <implementation/socket_consumer.h> +#include <protocols/errors.h> #include <protocols/indexer.h> namespace transport { - namespace protocol { using namespace interface; @@ -36,6 +36,35 @@ void Indexer::setVerifier() { } } -} // end namespace protocol +void Indexer::applyPolicy(core::Interest &interest, + core::ContentObject &content_object, bool reassembly, + auth::VerificationPolicy policy) const { + DCHECK(reassembly_ != nullptr); + + switch (policy) { + case auth::VerificationPolicy::ACCEPT: { + if (reassembly) { + reassembly_->reassemble(content_object); + } + break; + } + case auth::VerificationPolicy::UNKNOWN: + if (reassembly && reassembly_->reassembleUnverified()) { + reassembly_->reassemble(content_object); + } + break; + case auth::VerificationPolicy::DROP: + transport_->onPacketDropped( + interest, content_object, + make_error_code(protocol_error::verification_failed)); + break; + case auth::VerificationPolicy::ABORT: { + transport_->onContentReassembled( + make_error_code(protocol_error::session_aborted)); + break; + } + } +} +} // end namespace protocol } // end namespace transport diff --git a/libtransport/src/protocols/indexer.h b/libtransport/src/protocols/indexer.h index 7e3a52fb0..1bacb13aa 100644 --- a/libtransport/src/protocols/indexer.h +++ b/libtransport/src/protocols/indexer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,6 +15,7 @@ #pragma once +#include <hicn/transport/auth/policies.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <protocols/fec_utils.h> @@ -44,7 +45,7 @@ class Indexer { /** * Suffix getters */ - virtual uint32_t checkNextSuffix() = 0; + virtual uint32_t checkNextSuffix() const = 0; virtual uint32_t getNextSuffix() = 0; virtual uint32_t getNextReassemblySegment() = 0; @@ -52,24 +53,24 @@ class Indexer { * Set first suffix from where to start. */ virtual void setFirstSuffix(uint32_t suffix) = 0; - virtual uint32_t getFirstSuffix() = 0; + virtual uint32_t getFirstSuffix() const = 0; /** * Functions to set/enable/disable fec */ virtual void setNFec(uint32_t n_fec) = 0; - virtual uint32_t getNFec() = 0; + virtual uint32_t getNFec() const = 0; virtual void enableFec(fec::FECType fec_type) = 0; virtual void disableFec() = 0; virtual bool isFec(uint32_t index) { return false; } - virtual double getFecOverhead() { return 0.0; } - virtual double getMaxFecOverhead() { return 0.0; } + virtual double getFecOverhead() const { return 0.0; } + virtual double getMaxFecOverhead() const { return 0.0; } /** * Final suffix helpers. */ virtual bool isFinalSuffixDiscovered() = 0; - virtual uint32_t getFinalSuffix() = 0; + virtual uint32_t getFinalSuffix() const = 0; /** * Set reassembly protocol @@ -84,9 +85,15 @@ class Indexer { virtual void setVerifier(); /** + * Apply a verification policy + */ + virtual void applyPolicy(core::Interest &interest, + core::ContentObject &content_object, bool reassembly, + auth::VerificationPolicy policy) const; + /** * Jump to suffix. This may be useful if, for any protocol dependent - * mechanism, we need to suddenly change current suffix. This does not modify - * the way suffixes re incremented/decremented (that's part of the + * mechanism, we need to suddenly change current suffix. This does not + * modify the way suffixes re incremented/decremented (that's part of the * implementation). */ virtual uint32_t jumpToIndex(uint32_t index) = 0; @@ -108,6 +115,7 @@ class Indexer { TransportProtocol *transport_; Reassembly *reassembly_; std::shared_ptr<auth::Verifier> verifier_; + auth::CryptoHashType manifest_hash_type_; }; } // end namespace protocol diff --git a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc index 168aa57af..b5ab8184f 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc +++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -31,8 +31,7 @@ ManifestIncrementalIndexer::ManifestIncrementalIndexer( implementation::ConsumerSocket *icn_socket, TransportProtocol *transport) : IncrementalIndexer(icn_socket, transport), suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy( - NextSegmentCalculationStrategy::INCREMENTAL, next_download_suffix_, - 0)) {} + utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) {} void ManifestIncrementalIndexer::onContentObject( core::Interest &interest, core::ContentObject &content_object, @@ -59,12 +58,7 @@ void ManifestIncrementalIndexer::onContentObject( void ManifestIncrementalIndexer::onUntrustedManifest( core::Interest &interest, core::ContentObject &content_object, bool reassembly) { - auto manifest = - std::make_unique<ContentObjectManifest>(std::move(content_object)); - - auth::VerificationPolicy policy = verifier_->verifyPackets(manifest.get()); - - manifest->decode(); + auth::VerificationPolicy policy = verifier_->verifyPackets(&content_object); if (policy != auth::VerificationPolicy::ACCEPT) { transport_->onContentReassembled( @@ -72,6 +66,10 @@ void ManifestIncrementalIndexer::onUntrustedManifest( return; } + auto manifest = + std::make_unique<ContentObjectManifest>(std::move(content_object)); + manifest->decode(); + processTrustedManifest(interest, std::move(manifest), reassembly); } @@ -83,9 +81,10 @@ void ManifestIncrementalIndexer::processTrustedManifest( throw errors::RuntimeException("Received manifest with unknown version."); } - switch (manifest->getManifestType()) { + switch (manifest->getType()) { case core::ManifestType::INLINE_MANIFEST: { - suffix_strategy_->setFinalSuffix(manifest->getFinalBlockNumber()); + suffix_strategy_->setFinalSuffix( + manifest->getParamsBytestream().final_segment); // The packets to verify with the received manifest std::vector<auth::PacketPtr> packets; @@ -162,45 +161,15 @@ void ManifestIncrementalIndexer::onUntrustedContentObject( applyPolicy(interest, content_object, reassembly, policy); } -void ManifestIncrementalIndexer::applyPolicy( - core::Interest &interest, core::ContentObject &content_object, - bool reassembly, auth::VerificationPolicy policy) { - assert(reassembly_); - switch (policy) { - case auth::VerificationPolicy::ACCEPT: { - if (reassembly && !reassembly_->reassembleUnverified()) { - reassembly_->reassemble(content_object); - } - break; - } - case auth::VerificationPolicy::DROP: { - transport_->onPacketDropped( - interest, content_object, - make_error_code(protocol_error::verification_failed)); - break; - } - case auth::VerificationPolicy::ABORT: { - transport_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } - case auth::VerificationPolicy::UNKNOWN: { - if (reassembly && reassembly_->reassembleUnverified()) { - reassembly_->reassemble(content_object); - } - } - } -} - -uint32_t ManifestIncrementalIndexer::checkNextSuffix() { - return suffix_strategy_->getNextSuffix(); +uint32_t ManifestIncrementalIndexer::checkNextSuffix() const { + return suffix_strategy_->checkNextSuffix(); } uint32_t ManifestIncrementalIndexer::getNextSuffix() { auto ret = suffix_strategy_->getNextSuffix(); if (ret <= suffix_strategy_->getFinalSuffix() && - ret != utils::SuffixStrategy::INVALID_SUFFIX) { + ret != utils::SuffixStrategy::MAX_SUFFIX) { suffix_queue_.push(ret); return ret; } @@ -208,7 +177,7 @@ uint32_t ManifestIncrementalIndexer::getNextSuffix() { return Indexer::invalid_index; } -uint32_t ManifestIncrementalIndexer::getFinalSuffix() { +uint32_t ManifestIncrementalIndexer::getFinalSuffix() const { return suffix_strategy_->getFinalSuffix(); } diff --git a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h index d8cf5892f..12876f35c 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h +++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -39,8 +39,7 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { ManifestIncrementalIndexer(IncrementalIndexer &&indexer) : IncrementalIndexer(std::move(indexer)), suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy( - core::NextSegmentCalculationStrategy::INCREMENTAL, - next_download_suffix_, 0)) { + utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) { for (uint32_t i = first_suffix_; i < next_download_suffix_; i++) { suffix_queue_.push(i); } @@ -54,7 +53,7 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { core::ContentObject &content_object, bool reassembly) override; - uint32_t checkNextSuffix() override; + uint32_t checkNextSuffix() const override; uint32_t getNextSuffix() override; @@ -62,7 +61,7 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { bool isFinalSuffixDiscovered() override; - uint32_t getFinalSuffix() override; + uint32_t getFinalSuffix() const override; protected: std::unique_ptr<utils::SuffixStrategy> suffix_strategy_; @@ -82,9 +81,6 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { void onUntrustedContentObject(core::Interest &interest, core::ContentObject &content_object, bool reassembly); - void applyPolicy(core::Interest &interest, - core::ContentObject &content_object, bool reassembly, - auth::VerificationPolicy policy); }; } // end namespace protocol diff --git a/libtransport/src/protocols/prod_protocol_bytestream.cc b/libtransport/src/protocols/prod_protocol_bytestream.cc index f659cb37c..2a3ec07e1 100644 --- a/libtransport/src/protocols/prod_protocol_bytestream.cc +++ b/libtransport/src/protocols/prod_protocol_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -29,12 +29,7 @@ ByteStreamProductionProtocol::ByteStreamProductionProtocol( implementation::ProducerSocket *icn_socket) : ProductionProtocol(icn_socket) {} -ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { - stop(); - if (listening_thread_.joinable()) { - listening_thread_.join(); - } -} +ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { stop(); } uint32_t ByteStreamProductionProtocol::produceDatagram( const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) { @@ -68,16 +63,16 @@ uint32_t ByteStreamProductionProtocol::produceStream( return 0; } - Name name(content_name); - - // Get the atomic variables to ensure they keep the same value - // during the production - // Total size of the data packet uint32_t data_packet_size; socket_->getSocketOption(GeneralTransportOptions::DATA_PACKET_SIZE, data_packet_size); + // Maximum size of a segment + uint32_t max_segment_size; + socket_->getSocketOption(GeneralTransportOptions::MAX_SEGMENT_SIZE, + max_segment_size); + // Expiry time uint32_t content_object_expiry_time; socket_->getSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, @@ -88,106 +83,90 @@ uint32_t ByteStreamProductionProtocol::produceStream( socket_->getSocketOption(GeneralTransportOptions::HASH_ALGORITHM, hash_algo); // Suffix calculation strategy - core::NextSegmentCalculationStrategy _suffix_strategy; + std::shared_ptr<utils::SuffixStrategy> suffix_strategy; socket_->getSocketOption(GeneralTransportOptions::SUFFIX_STRATEGY, - _suffix_strategy); - auto suffix_strategy = utils::SuffixStrategyFactory::getSuffixStrategy( - _suffix_strategy, start_offset); + suffix_strategy); + suffix_strategy->reset(start_offset); - auto buffer_size = buffer->length(); + // Default format + core::Packet::Format default_format; + socket_->getSocketOption(GeneralTransportOptions::PACKET_FORMAT, + default_format); + + Name name(content_name); + size_t buffer_size = buffer->length(); + size_t signature_length = signer_->getSignatureFieldSize(); + uint32_t final_block_number = start_offset; + + // Content-related + core::Packet::Format content_format; + uint32_t content_header_size; + uint64_t content_free_space; + uint32_t nb_segments; int bytes_segmented = 0; - std::size_t header_size; - std::size_t manifest_header_size = 0; - std::size_t signature_length = 0; - std::uint32_t final_block_number = start_offset; - uint64_t free_space_for_content = 0; - core::Packet::Format format; + // Manifest-related + core::Packet::Format manifest_format; + uint32_t manifest_header_size; + uint64_t manifest_free_space; + uint32_t nb_manifests; std::shared_ptr<core::ContentObjectManifest> manifest; + uint32_t manifest_capacity = making_manifest_; bool is_last_manifest = false; - - // TODO Manifest may still be used for indexing - if (making_manifest_ && !signer_) { - LOG(FATAL) << "Making manifests without setting producer identity."; - } - - core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC; - core::Packet::Format hf_format_ah = core::Packet::Format::HF_UNSPEC; - - if (name.getType() == HNT_CONTIGUOUS_V4 || name.getType() == HNT_IOV_V4) { - hf_format = core::Packet::Format::HF_INET_TCP; - hf_format_ah = core::Packet::Format::HF_INET_TCP_AH; - } else if (name.getType() == HNT_CONTIGUOUS_V6 || - name.getType() == HNT_IOV_V6) { - hf_format = core::Packet::Format::HF_INET6_TCP; - hf_format_ah = core::Packet::Format::HF_INET6_TCP_AH; - } else { - throw errors::RuntimeException("Unknown name format."); - } - - format = hf_format; - if (making_manifest_) { - manifest_header_size = core::Packet::getHeaderSizeFromFormat( - signer_ ? hf_format_ah : hf_format, - signer_ ? signer_->getSignatureFieldSize() : 0); - } else if (signer_) { - format = hf_format_ah; - signature_length = signer_->getSignatureFieldSize(); + ParamsBytestream transport_params; + + manifest_format = Packet::toAHFormat(default_format); + content_format = + !making_manifest_ ? Packet::toAHFormat(default_format) : default_format; + + content_header_size = + core::Packet::getHeaderSizeFromFormat(content_format, signature_length); + manifest_header_size = + core::Packet::getHeaderSizeFromFormat(manifest_format, signature_length); + content_free_space = + std::min(max_segment_size, data_packet_size - content_header_size); + manifest_free_space = + std::min(max_segment_size, data_packet_size - manifest_header_size); + + // Compute the number of segments the data will be split into + nb_segments = + uint32_t(std::ceil(double(buffer_size) / double(content_free_space))); + if (content_free_space * nb_segments < buffer_size) { + nb_segments++; } - header_size = core::Packet::getHeaderSizeFromFormat(format, signature_length); - free_space_for_content = data_packet_size - header_size; - uint32_t number_of_segments = - uint32_t(std::ceil(double(buffer_size) / double(free_space_for_content))); - if (free_space_for_content * number_of_segments < buffer_size) { - number_of_segments++; - } - - // TODO allocate space for all the headers if (making_manifest_) { - uint32_t segment_in_manifest = static_cast<uint32_t>( - std::floor(double(data_packet_size - manifest_header_size - - ContentObjectManifest::getManifestHeaderSize()) / - ContentObjectManifest::getManifestEntrySize()) - - 1.0); - uint32_t number_of_manifests = static_cast<uint32_t>( - std::ceil(float(number_of_segments) / segment_in_manifest)); - final_block_number += number_of_segments + number_of_manifests - 1; + nb_manifests = static_cast<uint32_t>( + std::ceil(float(nb_segments) / manifest_capacity)); + final_block_number += nb_segments + nb_manifests - 1; + transport_params.final_segment = + is_last ? final_block_number : utils::SuffixStrategy::MAX_SUFFIX; manifest.reset(ContentObjectManifest::createManifest( + manifest_format, name.setSuffix(suffix_strategy->getNextManifestSuffix()), core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, - hash_algo, is_last_manifest, name, _suffix_strategy, - signer_ ? signer_->getSignatureFieldSize() : 0)); - manifest->setLifetime(content_object_expiry_time); + is_last_manifest, name, hash_algo, signature_length)); - if (is_last) { - manifest->setFinalBlockNumber(final_block_number); - } else { - manifest->setFinalBlockNumber(utils::SuffixStrategy::INVALID_SUFFIX); - } + manifest->setLifetime(content_object_expiry_time); + manifest->setParamsBytestream(transport_params); } - for (unsigned int packaged_segments = 0; - packaged_segments < number_of_segments; packaged_segments++) { + auto self = shared_from_this(); + for (unsigned int packaged_segments = 0; packaged_segments < nb_segments; + packaged_segments++) { if (making_manifest_) { - if (manifest->estimateManifestSize(2) > - data_packet_size - manifest_header_size) { + if (manifest->estimateManifestSize(1) > manifest_free_space) { manifest->encode(); - - // If identity set, sign manifest - if (signer_) { - signer_->signPacket(manifest.get()); - } + signer_->signPacket(manifest.get()); // Send the current manifest - passContentObjectToCallbacks(manifest); - + passContentObjectToCallbacks(manifest, self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest->getName(); // Send content objects stored in the queue while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); + passContentObjectToCallbacks(content_queue_.front(), self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content " << content_queue_.front()->getName(); content_queue_.pop(); @@ -195,86 +174,84 @@ uint32_t ByteStreamProductionProtocol::produceStream( // Create new manifest. The reference to the last manifest has been // acquired in the passContentObjectToCallbacks function, so we can - // safely release this reference + // safely release this reference. manifest.reset(ContentObjectManifest::createManifest( + manifest_format, name.setSuffix(suffix_strategy->getNextManifestSuffix()), core::ManifestVersion::VERSION_1, - core::ManifestType::INLINE_MANIFEST, hash_algo, is_last_manifest, - name, _suffix_strategy, - signer_ ? signer_->getSignatureFieldSize() : 0)); + core::ManifestType::INLINE_MANIFEST, is_last_manifest, name, + hash_algo, signature_length)); manifest->setLifetime(content_object_expiry_time); - manifest->setFinalBlockNumber( - is_last ? final_block_number - : utils::SuffixStrategy::INVALID_SUFFIX); + manifest->setParamsBytestream(transport_params); } } - auto content_suffix = suffix_strategy->getNextContentSuffix(); + // Create content object + uint32_t content_suffix = suffix_strategy->getNextContentSuffix(); auto content_object = std::make_shared<ContentObject>( - name.setSuffix(content_suffix), format, - signer_ && !making_manifest_ ? signer_->getSignatureFieldSize() : 0); + name.setSuffix(content_suffix), content_format, + !making_manifest_ ? signature_length : 0); content_object->setLifetime(content_object_expiry_time); auto b = buffer->cloneOne(); - b->trimStart(free_space_for_content * packaged_segments); + b->trimStart(content_free_space * packaged_segments); b->trimEnd(b->length()); - if (TRANSPORT_EXPECT_FALSE(packaged_segments == number_of_segments - 1)) { + // Segment the input data + if (TRANSPORT_EXPECT_FALSE(packaged_segments == nb_segments - 1)) { b->append(buffer_size - bytes_segmented); bytes_segmented += (int)(buffer_size - bytes_segmented); if (is_last && making_manifest_) { is_last_manifest = true; } else if (is_last) { - content_object->setRst(); + content_object->setLast(); } } else { - b->append(free_space_for_content); - bytes_segmented += (int)(free_space_for_content); + b->append(content_free_space); + bytes_segmented += (int)(content_free_space); } + // Set the segmented data as payload content_object->appendPayload(std::move(b)); + // Either we sign the content object or we save its hash into the current + // manifest if (making_manifest_) { - using namespace std::chrono_literals; auth::CryptoHash hash = content_object->computeDigest(hash_algo); manifest->addSuffixHash(content_suffix, hash); content_queue_.push(content_object); } else { - if (signer_) { - signer_->signPacket(content_object.get()); - } - passContentObjectToCallbacks(content_object); + signer_->signPacket(content_object.get()); + passContentObjectToCallbacks(content_object, self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content " << content_object->getName(); } } + // We send the manifest that hasn't been fully filled yet if (making_manifest_) { if (is_last_manifest) { - manifest->setFinalManifest(is_last_manifest); + manifest->setIsLast(is_last_manifest); } manifest->encode(); + signer_->signPacket(manifest.get()); - if (signer_) { - signer_->signPacket(manifest.get()); - } - - passContentObjectToCallbacks(manifest); + passContentObjectToCallbacks(manifest, self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest->getName(); while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); + passContentObjectToCallbacks(content_queue_.front(), self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content " << content_queue_.front()->getName(); content_queue_.pop(); } } - portal_->getIoService().post([this]() { + portal_->getThread().add([this, self]() { std::shared_ptr<ContentObject> co; while (object_queue_for_callbacks_.pop(co)) { if (*on_new_segment_) { @@ -296,7 +273,7 @@ uint32_t ByteStreamProductionProtocol::produceStream( } }); - portal_->getIoService().dispatch([this, buffer_size]() { + portal_->getThread().add([this, buffer_size, self]() { if (*on_content_produced_) { on_content_produced_->operator()(*socket_->getInterface(), std::make_error_code(std::errc(0)), @@ -307,9 +284,10 @@ uint32_t ByteStreamProductionProtocol::produceStream( return suffix_strategy->getTotalCount(); } -void ByteStreamProductionProtocol::scheduleSendBurst() { - portal_->getIoService().post([this]() { - std::shared_ptr<ContentObject> co; +void ByteStreamProductionProtocol::scheduleSendBurst( + const std::shared_ptr<ByteStreamProductionProtocol> &self) { + portal_->getThread().add([this, self]() { + ContentObject::Ptr co; for (uint32_t i = 0; i < burst_size; i++) { if (object_queue_for_callbacks_.pop(co)) { @@ -321,11 +299,15 @@ void ByteStreamProductionProtocol::scheduleSendBurst() { on_content_object_to_sign_->operator()(*socket_->getInterface(), *co); } + output_buffer_.insert(co); + if (*on_content_object_in_output_buffer_) { on_content_object_in_output_buffer_->operator()( *socket_->getInterface(), *co); } + portal_->sendContentObject(*co); + if (*on_content_object_output_) { on_content_object_output_->operator()(*socket_->getInterface(), *co); } @@ -337,13 +319,12 @@ void ByteStreamProductionProtocol::scheduleSendBurst() { } void ByteStreamProductionProtocol::passContentObjectToCallbacks( - const std::shared_ptr<ContentObject> &content_object) { - output_buffer_.insert(content_object); - portal_->sendContentObject(*content_object); + const std::shared_ptr<ContentObject> &content_object, + const std::shared_ptr<ByteStreamProductionProtocol> &self) { object_queue_for_callbacks_.push(std::move(content_object)); if (object_queue_for_callbacks_.size() >= burst_size) { - scheduleSendBurst(); + scheduleSendBurst(self); } } @@ -376,7 +357,5 @@ void ByteStreamProductionProtocol::onInterest(Interest &interest) { } } -void ByteStreamProductionProtocol::onError(std::error_code ec) {} - } // namespace protocol } // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_bytestream.h b/libtransport/src/protocols/prod_protocol_bytestream.h index cf36b90a5..809ad8d5c 100644 --- a/libtransport/src/protocols/prod_protocol_bytestream.h +++ b/libtransport/src/protocols/prod_protocol_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -50,16 +50,19 @@ class ByteStreamProductionProtocol : public ProductionProtocol { uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer, size_t buffer_size) override; + auto shared_from_this() { return utils::shared_from(this); } + protected: // Consumer Callback // void reset() override; void onInterest(core::Interest &i) override; - void onError(std::error_code ec) override; private: void passContentObjectToCallbacks( - const std::shared_ptr<ContentObject> &content_object); - void scheduleSendBurst(); + const std::shared_ptr<ContentObject> &content_object, + const std::shared_ptr<ByteStreamProductionProtocol> &self); + void scheduleSendBurst( + const std::shared_ptr<ByteStreamProductionProtocol> &self); private: // While manifests are being built, contents are stored in a queue diff --git a/libtransport/src/protocols/prod_protocol_rtc.cc b/libtransport/src/protocols/prod_protocol_rtc.cc index cdc882d81..242abd30d 100644 --- a/libtransport/src/protocols/prod_protocol_rtc.cc +++ b/libtransport/src/protocols/prod_protocol_rtc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -13,9 +13,11 @@ * limitations under the License. */ +#include <glog/logging.h> #include <hicn/transport/core/global_object_pool.h> #include <implementation/socket_producer.h> #include <protocols/prod_protocol_rtc.h> +#include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_consts.h> #include <stdlib.h> #include <time.h> @@ -38,71 +40,89 @@ RTCProductionProtocol::RTCProductionProtocol( bytes_production_rate_(0), packets_production_rate_(0), fec_packets_production_rate_(0), - last_round_(std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count()), + last_produced_data_ts_(0), + last_round_(utils::SteadyTime::nowMs().count()), allow_delayed_nacks_(false), queue_timer_on_(false), consumer_in_sync_(false), - on_consumer_in_sync_(nullptr) { - srand((unsigned int)time(NULL)); - prod_label_ = rand() % 256; + on_consumer_in_sync_(nullptr), + pending_fec_pace_(false), + max_len_(0), + queue_len_(0), + data_aggregation_(true), + data_aggregation_timer_switch_(false) { + std::uniform_int_distribution<> dis(0, 255); + prod_label_ = dis(gen_); cache_label_ = (prod_label_ + 1) % 256; interests_queue_timer_ = - std::make_unique<asio::steady_timer>(portal_->getIoService()); - round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + round_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + fec_pacing_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + app_packets_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); setOutputBufferSize(10000); - scheduleRoundTimer(); +} + +RTCProductionProtocol::~RTCProductionProtocol() {} + +void RTCProductionProtocol::setProducerParam() { + // Flow name: here we assume there is only one prefix registered in the portal + flow_name_ = portal_->getServedNamespaces().begin()->getName(); + + // Manifest + uint32_t making_manifest; + socket_->getSocketOption(interface::GeneralTransportOptions::MAKE_MANIFEST, + making_manifest); + + // Signer + std::shared_ptr<auth::Signer> signer; + socket_->getSocketOption(interface::GeneralTransportOptions::SIGNER, signer); + + // Default format + core::Packet::Format default_format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + default_format); // FEC using namespace std::placeholders; enableFEC(std::bind(&RTCProductionProtocol::onFecPackets, this, _1), std::bind(&RTCProductionProtocol::getBuffer, this, _1)); -} -RTCProductionProtocol::~RTCProductionProtocol() {} + // Aggregated data + socket_->getSocketOption(interface::RtcTransportOptions::AGGREGATED_DATA, + data_aggregation_); -void RTCProductionProtocol::registerNamespaceWithNetwork( - const Prefix &producer_namespace) { - ProductionProtocol::registerNamespaceWithNetwork(producer_namespace); - - flow_name_ = producer_namespace.getName(); - auto family = flow_name_.getAddressFamily(); - - switch (family) { - case AF_INET6: - data_header_size_ = - signer_ && !making_manifest_ - ? (uint32_t)Packet::getHeaderSizeFromFormat( - HF_INET6_TCP_AH, signer_->getSignatureFieldSize()) - : (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET6_TCP); - ; - break; - case AF_INET: - data_header_size_ = - signer_ && !making_manifest_ - ? (uint32_t)Packet::getHeaderSizeFromFormat( - HF_INET_TCP_AH, signer_->getSignatureFieldSize()) - : (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET_TCP); - break; - default: - throw errors::RuntimeException("Unknown name format."); - } + size_t signature_size = signer->getSignatureFieldSize(); + data_header_format_ = { + !making_manifest ? Packet::toAHFormat(default_format) : default_format, + !making_manifest ? signature_size : 0}; + manifest_header_format_ = {Packet::toAHFormat(default_format), + signature_size}; + nack_header_format_ = {Packet::toAHFormat(default_format), signature_size}; + fec_header_format_ = {Packet::toAHFormat(default_format), signature_size}; + + // Schedule round timer + scheduleRoundTimer(); } void RTCProductionProtocol::scheduleRoundTimer() { round_timer_->expires_from_now( std::chrono::milliseconds(rtc::PRODUCER_STATS_INTERVAL)); - round_timer_->async_wait([this](std::error_code ec) { + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + round_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - updateStats(); + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + sp->updateStats(); + } }); } void RTCProductionProtocol::updateStats() { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t duration = now - last_round_; if (duration == 0) duration = 1; double per_second = rtc::MILLI_IN_A_SEC / duration; @@ -125,11 +145,12 @@ void RTCProductionProtocol::updateStats() { if (max_packet_production_ < rtc::WIN_MIN) max_packet_production_ = rtc::WIN_MIN; - if (packets_production_rate_ != 0) { - allow_delayed_nacks_ = false; - } else if (prev_packets_production_rate == 0) { - // at least 2 rounds with production rate = 0 + if (packets_production_rate_ <= rtc::MIN_PRODUCTION_RATE || + prev_packets_production_rate <= rtc::MIN_PRODUCTION_RATE) { allow_delayed_nacks_ = true; + } else { + // at least 2 rounds with enough packets + allow_delayed_nacks_ = false; } // check if the production rate is decreased. if yes send nacks if needed @@ -170,42 +191,237 @@ uint32_t RTCProductionProtocol::produceDatagram( socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE, data_packet_size); - if (TRANSPORT_EXPECT_FALSE((buffer_size + data_header_size_ + - rtc::DATA_HEADER_SIZE) > data_packet_size)) { + if (TRANSPORT_EXPECT_FALSE( + (Packet::getHeaderSizeFromFormat(data_header_format_.first, + data_header_format_.second) + + rtc::DATA_HEADER_SIZE + buffer_size) > data_packet_size)) { return 0; } + if (!data_aggregation_) { + // if data aggregation is off emptyQueue will always return doing nothing + emptyQueue(); + + sendManifest(content_name); + + // create content object + auto content_object = + core::PacketManager<>::getInstance().getPacket<ContentObject>( + data_header_format_.first, data_header_format_.second); + + // add rtc header to the payload + struct rtc::data_packet_t header; + content_object->appendPayload((const uint8_t *)&header, + rtc::DATA_HEADER_SIZE); + content_object->appendPayload(buffer->data(), buffer->length()); + + // schedule actual sending on internal thread + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(content_object)}, + content_name]() mutable { + produceInternal(std::move(content_object), content_name); + }); + } else { + // XXX here we assume that all the packets that we push to the queue have + // the same name + auto app_pkt = utils::MemBuf::copyBuffer(buffer->data(), buffer->length()); + addPacketToQueue(std::move(app_pkt)); + } + + return 1; +} + +void RTCProductionProtocol::addPacketToQueue( + std::unique_ptr<utils::MemBuf> &&buffer) { + std::size_t buffer_size = buffer->length(); + if ((queue_len_ + buffer_size) > rtc::MAX_RTC_PAYLOAD_SIZE) { + emptyQueue(); // this should guaranty that the generated packet will never + // be larger than an MTU + } + + waiting_app_packets_.push(std::move(buffer)); + if (max_len_ < buffer_size) max_len_ = buffer_size; + queue_len_ += buffer_size; + + if (waiting_app_packets_.size() >= rtc::MAX_AGGREGATED_PACKETS) { + emptyQueue(); + } + + if (waiting_app_packets_.size() >= 1 && !data_aggregation_timer_switch_) { + data_aggregation_timer_switch_ = true; + app_packets_timer_->expires_from_now( + std::chrono::milliseconds(rtc::AGGREGATED_PACKETS_TIMER)); + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + app_packets_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->data_aggregation_timer_switch_) return; + ptr->emptyQueue(); + } + }); + } +} + +void RTCProductionProtocol::emptyQueue() { + if (waiting_app_packets_.size() == 0) return; // queue is empty + + Name n(flow_name_); + + // cancel timer is scheduled + if (data_aggregation_timer_switch_) { + data_aggregation_timer_switch_ = false; + app_packets_timer_->cancel(); + } + + // send a manifest beforehand if the hash buffer if full + sendManifest(n); + + // create content object auto content_object = core::PacketManager<>::getInstance().getPacket<ContentObject>( - signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP, - signer_ ? signer_->getSignatureFieldSize() : 0); + data_header_format_.first, data_header_format_.second); + // add rtc header to the payload struct rtc::data_packet_t header; content_object->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); - content_object->appendPayload(buffer->data(), buffer->length()); - // schedule actual sending on internal thread - portal_->getIoService().dispatch([this, - content_object{std::move(content_object)}, - content_name]() mutable { - produceInternal(std::move(content_object), content_name); + // init aggregated header + rtc::AggrPktHeader hdr( + (uint8_t *)(content_object->getPayload()->data() + rtc::DATA_HEADER_SIZE), + max_len_, waiting_app_packets_.size()); + uint32_t header_size = hdr.getHeaderLen(); + content_object->append(header_size); // leave space for the aggregated header + + uint8_t index = 0; + while (waiting_app_packets_.size() != 0) { + std::unique_ptr<utils::MemBuf> pkt = + std::move(waiting_app_packets_.front()); + waiting_app_packets_.pop(); + // XXX for the moment we have a single name, so this works, otherwise we + // need to do something else + hdr.addPacketToHeader(index, pkt->length()); + // append packet + content_object->appendPayload(pkt->data(), pkt->length()); + index++; + } + + // reset queue values + max_len_ = 0; + queue_len_ = 0; + + // the packet is ready we need to send it + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(content_object)}, n]() mutable { + produceInternal(std::move(content_object), n); + }); +} + +void RTCProductionProtocol::sendManifest(const Name &name) { + if (!making_manifest_) { + return; + } + + Name manifest_name(name); + + uint32_t data_packet_size; + socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE, + data_packet_size); + + // The maximum number of entries a manifest can hold + uint32_t manifest_capacity = making_manifest_; + + // If there is not enough hashes to fill a manifest, return early + if (manifest_entries_.size() < manifest_capacity) { + return; + } + + // Create a new manifest + std::shared_ptr<core::ContentObjectManifest> manifest = + createManifest(manifest_name.setSuffix(current_seg_)); + + // Fill the manifest with packet hashes that were previously saved + uint32_t nb_entries; + for (nb_entries = 0; nb_entries < manifest_capacity; ++nb_entries) { + if (manifest_entries_.empty()) { + break; + } + std::pair<uint32_t, auth::CryptoHash> front = manifest_entries_.front(); + manifest->addSuffixHash(front.first, front.second); + manifest_entries_.pop(); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending manifest " << manifest->getName().getSuffix() << " of size " + << nb_entries; + + // Encode and send the manifest + manifest->encode(); + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(manifest)}, manifest_name]() mutable { + produceInternal(std::move(content_object), manifest_name); + }); +} + +std::shared_ptr<core::ContentObjectManifest> +RTCProductionProtocol::createManifest(const Name &content_name) const { + Name name(content_name); + + auth::CryptoHashType hash_algo; + socket_->getSocketOption(interface::GeneralTransportOptions::HASH_ALGORITHM, + hash_algo); + + uint64_t now = utils::SteadyTime::nowMs().count(); + + // Create a new manifest + std::shared_ptr<core::ContentObjectManifest> manifest( + ContentObjectManifest::createManifest( + manifest_header_format_.first, name, core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, false, name, hash_algo, + manifest_header_format_.second)); + + // Set connection parameters + manifest->setParamsRTC(ParamsRTC{ + .timestamp = now, + .prod_rate = bytes_production_rate_, + .prod_seg = current_seg_, + .support_fec = false, }); - return 1; + return manifest; } void RTCProductionProtocol::produceInternal( std::shared_ptr<ContentObject> &&content_object, const Name &content_name, bool fec) { + uint64_t now = utils::SteadyTime::nowMs().count(); + + if (fec && (now - last_produced_data_ts_) < rtc::FEC_PACING_TIME) { + paced_fec_packets_.push(std::pair<uint64_t, ContentObject::Ptr>( + now, std::move(content_object))); + postponeFecPacket(); + } else { + // need to check if there are FEC packets waiting to be sent + flushFecPkts(current_seg_); + producePktInternal(std::move(content_object), content_name, fec); + } +} + +void RTCProductionProtocol::producePktInternal( + std::shared_ptr<ContentObject> &&content_object, const Name &content_name, + bool fec) { + bool is_manifest = content_object->getPayloadType() == PayloadType::MANIFEST; + uint64_t now = utils::SteadyTime::nowMs().count(); + // set rtc header - struct rtc::data_packet_t *data_pkt = - (struct rtc::data_packet_t *)content_object->getPayload()->data(); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - data_pkt->setTimestamp(now); - data_pkt->setProductionRate(bytes_production_rate_); + if (!is_manifest) { + struct rtc::data_packet_t *data_pkt = + (struct rtc::data_packet_t *)content_object->getPayload()->data(); + data_pkt->setTimestamp(now); + data_pkt->setProductionRate(bytes_production_rate_); + } // set hicn stuff Name n(content_name); @@ -213,11 +429,6 @@ void RTCProductionProtocol::produceInternal( content_object->setLifetime(500); // XXX this should be set by the APP content_object->setPathLabel(prod_label_); - // sign packet - if (signer_) { - signer_->signPacket(content_object.get()); - } - // update stats if (!fec) { produced_bytes_ += @@ -227,7 +438,7 @@ void RTCProductionProtocol::produceInternal( produced_fec_packets_++; } - if (produced_packets_ >= max_packet_production_) { + if (!data_aggregation_ && produced_packets_ >= max_packet_production_) { // in this case all the pending interests may be used to accomodate the // sudden increase in the production rate. calling the updateStats we will // notify all the clients @@ -240,8 +451,12 @@ void RTCProductionProtocol::produceInternal( // pass packet to FEC encoder if (fec_encoder_ && !fec) { - fec_encoder_->onPacketProduced( - *content_object, content_object->headerSize() + rtc::DATA_HEADER_SIZE); + uint32_t offset = + is_manifest ? content_object->headerSize() + : content_object->headerSize() + rtc::DATA_HEADER_SIZE; + uint32_t metadata = static_cast<uint32_t>(content_object->getPayloadType()); + + fec_encoder_->onPacketProduced(*content_object, offset, metadata); } output_buffer_.insert(content_object); @@ -253,7 +468,7 @@ void RTCProductionProtocol::produceInternal( auto seq_it = seqs_map_.find(current_seg_); if (seq_it != seqs_map_.end()) { - portal_->sendContentObject(*content_object); + sendContentObject(content_object, false, fec); } if (*on_content_object_output_) { @@ -264,6 +479,8 @@ void RTCProductionProtocol::produceInternal( // remove interests from the interest cache if it exists removeFromInterestQueue(current_seg_); + if (!fec) last_produced_data_ts_ = now; + // Update current segment current_seg_ = (current_seg_ + 1) % rtc::MIN_PROBE_SEQ; @@ -277,6 +494,55 @@ void RTCProductionProtocol::produceInternal( } } +void RTCProductionProtocol::flushFecPkts(uint32_t current_seq_num) { + // Currently we immediately send all the pending fec packets + // A pacing policy may be helpful, but we do not want to delay too much + // the packets at this moment. + while (paced_fec_packets_.size() > 0) { + producePktInternal(std::move(paced_fec_packets_.front().second), flow_name_, + true); + paced_fec_packets_.pop(); + } + fec_pacing_timer_->cancel(); + pending_fec_pace_ = false; + postponeFecPacket(); +} + +void RTCProductionProtocol::postponeFecPacket() { + if (paced_fec_packets_.size() == 0) return; + if (pending_fec_pace_) { + return; + } + + uint64_t produced_time = paced_fec_packets_.front().first; + uint64_t now = utils::SteadyTime::nowMs().count(); + + uint64_t wait_time = 0; + if ((produced_time + rtc::FEC_PACING_TIME) > now) + wait_time = produced_time + rtc::FEC_PACING_TIME - now; + + fec_pacing_timer_->expires_from_now(std::chrono::milliseconds(wait_time)); + pending_fec_pace_ = true; + + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + fec_pacing_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + if (!sp->pending_fec_pace_) return; + + if (sp->paced_fec_packets_.size() > 0) { + sp->producePktInternal(std::move(sp->paced_fec_packets_.front().second), + sp->flow_name_, true); + sp->paced_fec_packets_.pop(); + } + sp->pending_fec_pace_ = false; + sp->postponeFecPacket(); + } + }); +} + void RTCProductionProtocol::onInterest(Interest &interest) { if (*on_interest_input_) { on_interest_input_->operator()(*socket_->getInterface(), interest); @@ -284,7 +550,7 @@ void RTCProductionProtocol::onInterest(Interest &interest) { auto suffix = interest.firstSuffix(); // numberOfSuffixes returns only the prefixes in the payalod - // we add + 1 to count anche the seq in the name + // we add + 1 to count also the seq in the name auto n_suffixes = interest.numberOfSuffixes() + 1; Name name = interest.getName(); bool prev_consumer_state = consumer_in_sync_; @@ -293,6 +559,7 @@ void RTCProductionProtocol::onInterest(Interest &interest) { if (i > 0) { name.setSuffix(*(suffix + (i - 1))); } + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received interest " << name; const std::shared_ptr<ContentObject> content_object = @@ -312,7 +579,7 @@ void RTCProductionProtocol::onInterest(Interest &interest) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content %u (onInterest) " << content_object->getName(); content_object->setPathLabel(cache_label_); - portal_->sendContentObject(*content_object); + sendContentObject(content_object); } else { if (*on_interest_process_) { on_interest_process_->operator()(*socket_->getInterface(), interest); @@ -332,14 +599,19 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, consumer_in_sync_ = false; } - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); - if (interest_seg > rtc::MIN_PROBE_SEQ) { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << interest_seg; - sendNack(interest_seg); - return; + switch (rtc::ProbeHandler::getProbeType(interest_seg)) { + case rtc::ProbeType::INIT: + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received init probe " << interest_seg; + sendManifestProbe(interest_seg); + return; + case rtc::ProbeType::RTT: + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received RTT probe " << interest_seg; + sendNack(interest_seg); + return; + default: + break; } // if the production rate 0 use delayed nacks @@ -349,7 +621,7 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, next_timer = timers_map_.begin()->first; } - uint64_t expiration = now + rtc::SENTINEL_TIMER_INTERVAL; + uint64_t expiration = now + rtc::NACK_DELAY; addToInterestQueue(interest_seg, expiration); // here we have at least one interest in the queue, we need to start or @@ -369,8 +641,8 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, } if (queue_timer_on_) { - // the producer is producing. Send nacks to packets that will expire before - // the data production and remove the timer + // the producer is producing. Send nacks to packets that will expire + // before the data production and remove the timer queue_timer_on_ = false; interests_queue_timer_->cancel(); sendNacksForPendingInterests(); @@ -387,8 +659,8 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, sendNack(interest_seg); } else { if (!consumer_in_sync_ && on_consumer_in_sync_) { - // we consider the remote consumer to be in sync as soon as it covers 70% - // of the production window with interests + // we consider the remote consumer to be in sync as soon as it covers + // 70% of the production window with interests uint32_t perc = ceil((double)max_gap * 0.7); if (interest_seg > (perc + current_seg_)) { consumer_in_sync_ = true; @@ -401,13 +673,18 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, } } -void RTCProductionProtocol::onError(std::error_code ec) {} - void RTCProductionProtocol::scheduleQueueTimer(uint64_t wait) { interests_queue_timer_->expires_from_now(std::chrono::milliseconds(wait)); - interests_queue_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - interestQueueTimer(); + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + interests_queue_timer_->async_wait([self](const std::error_code &ec) { + if (ec) { + return; + } + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + sp->interestQueueTimer(); + } }); } @@ -450,9 +727,7 @@ void RTCProductionProtocol::sendNacksForPendingInterests() { if (packets_production_rate_ != 0) packet_gap = ceil(rtc::MILLI_IN_A_SEC / (double)packets_production_rate_); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); for (auto it = seqs_map_.begin(); it != seqs_map_.end(); it++) { if (it->first > current_seg_) { @@ -486,9 +761,7 @@ void RTCProductionProtocol::removeFromInterestQueue(uint32_t interest_seg) { } void RTCProductionProtocol::interestQueueTimer() { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); for (auto it_timers = timers_map_.begin(); it_timers != timers_map_.end();) { uint64_t expire = it_timers->first; @@ -511,20 +784,37 @@ void RTCProductionProtocol::interestQueueTimer() { } } +void RTCProductionProtocol::sendManifestProbe(uint32_t sequence) { + Name manifest_name(flow_name_); + manifest_name.setSuffix(sequence); + + std::shared_ptr<core::ContentObjectManifest> manifest_probe = + createManifest(manifest_name); + + manifest_probe->setLifetime(0); + manifest_probe->setPathLabel(prod_label_); + manifest_probe->encode(); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *manifest_probe); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send init probe " << sequence; + sendContentObject(manifest_probe, true, false); +} + void RTCProductionProtocol::sendNack(uint32_t sequence) { auto nack = core::PacketManager<>::getInstance().getPacket<ContentObject>( - signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP, - signer_ ? signer_->getSignatureFieldSize() : 0); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + nack_header_format_.first, nack_header_format_.second); + uint64_t now = utils::SteadyTime::nowMs().count(); uint32_t next_packet = current_seg_; uint32_t prod_rate = bytes_production_rate_; struct rtc::nack_packet_t header; header.setTimestamp(now); header.setProductionRate(prod_rate); - header.setProductionSegement(next_packet); + header.setProductionSegment(next_packet); nack->appendPayload((const uint8_t *)&header, rtc::NACK_HEADER_SIZE); Name n(flow_name_); @@ -533,14 +823,16 @@ void RTCProductionProtocol::sendNack(uint32_t sequence) { nack->setLifetime(0); nack->setPathLabel(prod_label_); - if (signer_) { - signer_->signPacket(nack.get()); - } - if (!consumer_in_sync_ && on_consumer_in_sync_ && - sequence < rtc::MIN_PROBE_SEQ && sequence > next_packet) { + rtc::ProbeHandler::getProbeType(sequence) == rtc::ProbeType::NOT_PROBE && + sequence > next_packet) { consumer_in_sync_ = true; - auto interest = core::PacketManager<>::getInstance().getPacket<Interest>(); + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + + auto interest = + core::PacketManager<>::getInstance().getPacket<Interest>(format); interest->setName(n); on_consumer_in_sync_(*socket_->getInterface(), *interest); } @@ -550,16 +842,37 @@ void RTCProductionProtocol::sendNack(uint32_t sequence) { } DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send nack " << sequence; - portal_->sendContentObject(*nack); + sendContentObject(nack, true, false); } -void RTCProductionProtocol::onFecPackets( - std::vector<std::pair<uint32_t, fec::buffer>> &packets) { +void RTCProductionProtocol::sendContentObject( + std::shared_ptr<ContentObject> content_object, bool nack, bool fec) { + bool is_ah = _is_ah(content_object->getFormat()); + + // Compute signature + if (is_ah) { + signer_->signPacket(content_object.get()); + } + + portal_->sendContentObject(*content_object); + + // Compute and save data packet digest + if (making_manifest_ && !is_ah) { + auth::CryptoHashType hash_algo; + socket_->getSocketOption(interface::GeneralTransportOptions::HASH_ALGORITHM, + hash_algo); + manifest_entries_.push({content_object->getName().getSuffix(), + content_object->computeDigest(hash_algo)}); + } +} + +void RTCProductionProtocol::onFecPackets(fec::BufferArray &packets) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Produced " << packets.size() << " FEC packets"; + for (auto &packet : packets) { auto content_object = - std::static_pointer_cast<ContentObject>(packet.second); + std::static_pointer_cast<ContentObject>(packet.getBuffer()); content_object->prepend(content_object->headerSize() + rtc::DATA_HEADER_SIZE); pending_fec_packets_.push(std::move(content_object)); @@ -569,19 +882,21 @@ void RTCProductionProtocol::onFecPackets( fec::buffer RTCProductionProtocol::getBuffer(std::size_t size) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Asked buffer for FEC symbol of size " << size; + auto ret = core::PacketManager<>::getInstance().getPacket<ContentObject>( - signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP, - signer_ ? signer_->getSignatureFieldSize() : 0); + fec_header_format_.first, fec_header_format_.second); + ret->updateLength(rtc::DATA_HEADER_SIZE + size); ret->append(rtc::DATA_HEADER_SIZE + size); ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Responding with buffer of length " << ret->length(); - assert(ret->length() >= size); + DCHECK(ret->length() >= size); return ret; } } // namespace protocol -} // end namespace transport +} // namespace transport diff --git a/libtransport/src/protocols/prod_protocol_rtc.h b/libtransport/src/protocols/prod_protocol_rtc.h index 96ad5673d..7f50a2505 100644 --- a/libtransport/src/protocols/prod_protocol_rtc.h +++ b/libtransport/src/protocols/prod_protocol_rtc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -32,6 +32,7 @@ class RTCProductionProtocol : public ProductionProtocol { using ProductionProtocol::start; using ProductionProtocol::stop; + void setProducerParam() override; void produce(ContentObject &content_object) override; uint32_t produceStream(const Name &content_name, @@ -49,21 +50,31 @@ class RTCProductionProtocol : public ProductionProtocol { buffer, buffer_size, buffer_size)); } - void registerNamespaceWithNetwork(const Prefix &producer_namespace) override; - void setConsumerInSyncCallback( interface::ProducerInterestCallback &&callback) { on_consumer_in_sync_ = std::move(callback); } + auto shared_from_this() { return utils::shared_from(this); } + private: // packet handlers void onInterest(Interest &interest) override; - void onError(std::error_code ec) override; + void onError(const std::error_code &ec) override{}; void processInterest(uint32_t interest_seg, uint32_t lifetime); + void producePktInternal(std::shared_ptr<ContentObject> &&content_object, + const Name &content_name, bool fec = false); void produceInternal(std::shared_ptr<ContentObject> &&content_object, const Name &content_name, bool fec = false); void sendNack(uint32_t sequence); + void sendContentObject(std::shared_ptr<ContentObject> content_object, + bool nac = false, bool fec = false); + + // manifests + void sendManifestProbe(uint32_t sequence); + void sendManifest(const Name &content_name); + std::shared_ptr<core::ContentObjectManifest> createManifest( + const Name &name) const; // stats void updateStats(); @@ -77,15 +88,25 @@ class RTCProductionProtocol : public ProductionProtocol { void interestQueueTimer(); // FEC functions - void onFecPackets(std::vector<std::pair<uint32_t, fec::buffer>> &packets); + void onFecPackets(fec::BufferArray &packets); fec::buffer getBuffer(std::size_t size); + void postponeFecPacket(); + void dispatchFecPacket(); + void flushFecPkts(uint32_t current_seq_num); + // aggregated data functions + void emptyQueue(); + void addPacketToQueue(std::unique_ptr<utils::MemBuf> &&buffer); core::Name flow_name_; - uint32_t current_seg_; // seq id of the next packet produced - uint32_t prod_label_; // path lable of the producer - uint32_t cache_label_; // path lable for content from the producer cache - uint16_t data_header_size_; // hicn data header size + std::pair<core::Packet::Format, size_t> data_header_format_; + std::pair<core::Packet::Format, size_t> manifest_header_format_; + std::pair<core::Packet::Format, size_t> fec_header_format_; + std::pair<core::Packet::Format, size_t> nack_header_format_; + + uint32_t current_seg_; // seq id of the next packet produced + uint32_t prod_label_; // path label of the producer + uint32_t cache_label_; // path label for content from the producer cache uint32_t produced_bytes_; // bytes produced in the last round uint32_t produced_packets_; // packet produed in the last round @@ -98,7 +119,11 @@ class RTCProductionProtocol : public ProductionProtocol { uint32_t packets_production_rate_; // pps uint32_t fec_packets_production_rate_; // pps + uint64_t last_produced_data_ts_; // ms + std::unique_ptr<asio::steady_timer> round_timer_; + std::unique_ptr<asio::steady_timer> fec_pacing_timer_; + uint64_t last_round_; // delayed nacks are used by the producer to avoid to send too @@ -131,6 +156,21 @@ class RTCProductionProtocol : public ProductionProtocol { // Save FEC packets here before sending them std::queue<ContentObject::Ptr> pending_fec_packets_; + std::queue<std::pair<uint64_t, ContentObject::Ptr>> paced_fec_packets_; + bool pending_fec_pace_; + + // Save application packets if they are small + std::queue<std::unique_ptr<utils::MemBuf>> waiting_app_packets_; + uint16_t max_len_; // len of the largest packet + uint16_t queue_len_; // total size of all packet in the queue + bool data_aggregation_; // turns on/off data aggregation + // timer to check the queue len + std::unique_ptr<asio::steady_timer> app_packets_timer_; + bool data_aggregation_timer_switch_; // bool to check if the timer is on + + // Manifest + std::queue<std::pair<uint32_t, auth::CryptoHash>> + manifest_entries_; // map a packet suffix to a packet hash }; } // namespace protocol diff --git a/libtransport/src/protocols/production_protocol.cc b/libtransport/src/protocols/production_protocol.cc index 6b317d47d..8b781e38a 100644 --- a/libtransport/src/protocols/production_protocol.cc +++ b/libtransport/src/protocols/production_protocol.cc @@ -24,8 +24,8 @@ using namespace interface; ProductionProtocol::ProductionProtocol( implementation::ProducerSocket *icn_socket) - : socket_(icn_socket), - is_running_(false), + : Protocol(), + socket_(icn_socket), fec_encoder_(nullptr), on_interest_input_(VOID_HANDLER), on_interest_dropped_input_buffer_(VOID_HANDLER), @@ -38,101 +38,92 @@ ProductionProtocol::ProductionProtocol( on_content_object_output_(VOID_HANDLER), on_content_object_evicted_from_output_buffer_(VOID_HANDLER), on_content_produced_(VOID_HANDLER), + producer_callback_(VOID_HANDLER), fec_type_(fec::FECType::UNKNOWN) { socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); // TODO add statistics for producer // socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); } -ProductionProtocol::~ProductionProtocol() { - if (!is_async_ && is_running_) { - stop(); - } +ProductionProtocol::~ProductionProtocol() {} - if (listening_thread_.joinable()) { - listening_thread_.join(); +int ProductionProtocol::start() { + if (isRunning()) { + return -1; } -} -int ProductionProtocol::start() { - socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, - &on_interest_input_); - socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_DROP, - &on_interest_dropped_input_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_PASS, - &on_interest_inserted_input_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::CACHE_HIT, - &on_interest_satisfied_output_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::CACHE_MISS, - &on_interest_process_); - socket_->getSocketOption(ProducerCallbacksOptions::NEW_CONTENT_OBJECT, - &on_new_segment_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_READY, - &on_content_object_in_output_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT, - &on_content_object_output_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN, - &on_content_object_to_sign_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, - &on_content_produced_); - - socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); - socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_); - socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - making_manifest_); - - bool first = true; - - for (core::Prefix &producer_namespace : served_namespaces_) { - if (first) { - core::BindConfig bind_config(producer_namespace, 1000); - portal_->bind(bind_config); - portal_->setProducerCallback(this); - first = !first; - } else { - portal_->registerRoute(producer_namespace); + portal_->getThread().addAndWaitForExecution([this]() { + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, + &on_interest_input_); + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_DROP, + &on_interest_dropped_input_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_PASS, + &on_interest_inserted_input_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CACHE_HIT, + &on_interest_satisfied_output_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CACHE_MISS, + &on_interest_process_); + socket_->getSocketOption(ProducerCallbacksOptions::NEW_CONTENT_OBJECT, + &on_new_segment_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_READY, + &on_content_object_in_output_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT, + &on_content_object_output_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN, + &on_content_object_to_sign_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, + &on_content_produced_); + socket_->getSocketOption(ProducerCallbacksOptions::PRODUCER_CALLBACK, + &producer_callback_); + + socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); + socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_); + socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST, + making_manifest_); + + std::string fec_type_str = ""; + socket_->getSocketOption(GeneralTransportOptions::FEC_TYPE, fec_type_str); + if (fec_type_str != "") { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_type_str.c_str()); } - } - is_running_ = true; + portal_->registerTransportCallback(this); + setProducerParam(); - if (!is_async_) { - listening_thread_ = std::thread([this]() { portal_->runEventsLoop(); }); - } + setRunning(); + }); return 0; } -void ProductionProtocol::stop() { - is_running_ = false; - - if (!is_async_) { - portal_->stopEventsLoop(); - } else { - portal_->clear(); - } -} - void ProductionProtocol::produce(ContentObject &content_object) { - if (*on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), - content_object); - } + auto content_object_ptr = content_object.shared_from_this(); + portal_->getThread().add([this, co = std::move(content_object_ptr)]() { + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), + *co); + } - output_buffer_.insert(std::static_pointer_cast<ContentObject>( - content_object.shared_from_this())); + output_buffer_.insert(co); - if (*on_content_object_output_) { - on_content_object_output_->operator()(*socket_->getInterface(), - content_object); - } + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *co); + } - portal_->sendContentObject(content_object); + portal_->sendContentObject(*co); + }); } -void ProductionProtocol::registerNamespaceWithNetwork( - const Prefix &producer_namespace) { - served_namespaces_.push_back(producer_namespace); +void ProductionProtocol::sendMapme() { portal_->sendMapme(); } + +void ProductionProtocol::onError(const std::error_code &ec) { + // Stop production protocol + stop(); + + // Call error callback + if (producer_callback_) { + producer_callback_->produceError(ec); + } } } // namespace protocol diff --git a/libtransport/src/protocols/production_protocol.h b/libtransport/src/protocols/production_protocol.h index 7366311eb..8e10d2f40 100644 --- a/libtransport/src/protocols/production_protocol.h +++ b/libtransport/src/protocols/production_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -22,6 +22,7 @@ #include <implementation/socket.h> #include <protocols/fec_base.h> #include <protocols/fec_utils.h> +#include <protocols/protocol.h> #include <utils/content_store.h> #include <atomic> @@ -33,17 +34,20 @@ namespace protocol { using namespace core; -class ProductionProtocol : public Portal::ProducerCallback { +class ProductionProtocol + : public Protocol, + public std::enable_shared_from_this<ProductionProtocol> { public: ProductionProtocol(implementation::ProducerSocket *icn_socket); virtual ~ProductionProtocol(); - bool isRunning() { return is_running_; } - virtual int start(); - virtual void stop(); + using Protocol::stop; + + virtual void setProducerParam(){}; virtual void produce(ContentObject &content_object); + virtual void sendMapme(); virtual uint32_t produceStream(const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer, bool is_last = true, @@ -61,20 +65,18 @@ class ProductionProtocol : public Portal::ProducerCallback { void setOutputBufferSize(std::size_t size) { output_buffer_.setLimit(size); } std::size_t getOutputBufferSize() { return output_buffer_.getLimit(); } - virtual void registerNamespaceWithNetwork(const Prefix &producer_namespace); - const std::list<Prefix> &getNamespaces() const { return served_namespaces_; } - protected: // Producer callback virtual void onInterest(core::Interest &i) override = 0; - virtual void onError(std::error_code ec) override{}; + virtual void onError(const std::error_code &ec) override; template <typename FECHandler, typename AllocatorHandler> void enableFEC(FECHandler &&fec_handler, AllocatorHandler &&allocator_handler) { if (!fec_encoder_) { // Try to get FEC from environment - if (const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE")) { + const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE"); + if (fec_str && (fec_type_ == fec::FECType::UNKNOWN)) { LOG(INFO) << "Using FEC " << fec_str; fec_type_ = fec::FECUtils::fecTypeFromString(fec_str); } @@ -95,11 +97,6 @@ class ProductionProtocol : public Portal::ProducerCallback { // Thread pool responsible for IO operations (send data / receive interests) std::vector<utils::EventThread> io_threads_; - - // TODO remove this thread - std::thread listening_thread_; - std::shared_ptr<Portal> portal_; - std::atomic<bool> is_running_; interface::ProductionStatistics *stats_; std::unique_ptr<fec::ProducerFEC> fec_encoder_; @@ -119,15 +116,14 @@ class ProductionProtocol : public Portal::ProducerCallback { interface::ProducerContentCallback *on_content_produced_; + interface::ProducerSocket::Callback *producer_callback_; + // Output buffer utils::ContentStore output_buffer_; - // List ot routes served by current producer protocol - std::list<Prefix> served_namespaces_; - // Signature and manifest std::shared_ptr<auth::Signer> signer_; - bool making_manifest_; + uint32_t making_manifest_; bool is_async_; fec::FECType fec_type_; diff --git a/libtransport/src/protocols/protocol.h b/libtransport/src/protocols/protocol.h new file mode 100644 index 000000000..a9f929db9 --- /dev/null +++ b/libtransport/src/protocols/protocol.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +#include <core/portal.h> +#include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/utils/noncopyable.h> + +#include <random> + +namespace transport { + +namespace protocol { + +class Protocol : public core::Portal::TransportCallback, utils::NonCopyable { + public: + virtual void stop() { + portal_->getThread().addAndWaitForExecution([this]() { + unSetRunning(); + portal_->unregisterTransportCallback(); + portal_->clear(); + }); + } + + virtual void onInterest(core::Interest &i) { + throw errors::RuntimeException("Not implemented"); + } + + virtual void onContentObject(core::Interest &i, core::ContentObject &c) { + throw errors::RuntimeException("Not implemented"); + } + + virtual void onTimeout(core::Interest::Ptr &i, const core::Name &n) { + throw errors::RuntimeException("Not implemented"); + } + + virtual void onError(const std::error_code &ec) { + throw errors::RuntimeException("Not implemented"); + } + + bool isRunning() { return is_running_; } + void setRunning() { is_running_ = true; } + void unSetRunning() { is_running_ = false; } + + protected: + Protocol() : portal_(nullptr), is_running_(false), gen_(rd_()) {} + virtual ~Protocol() {} + + protected: + std::shared_ptr<core::Portal> portal_; + std::atomic_bool is_running_; + + // Random engine + std::random_device rd_; + std::mt19937 gen_; +}; + +} // namespace protocol + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/protocols/raaqm.cc b/libtransport/src/protocols/raaqm.cc index 1247af400..131367d78 100644 --- a/libtransport/src/protocols/raaqm.cc +++ b/libtransport/src/protocols/raaqm.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -36,8 +36,9 @@ RaaqmTransportProtocol::RaaqmTransportProtocol( current_window_size_(1), interests_in_flight_(0), cur_path_(nullptr), - t0_(utils::SteadyClock::now()), + t0_(utils::SteadyTime::Clock::now()), rate_estimator_(nullptr), + dis_(0, 1.0), schedule_interests_(true) { init(); } @@ -60,7 +61,7 @@ void RaaqmTransportProtocol::reset() { // Reset protocol variables interests_in_flight_ = 0; - t0_ = utils::SteadyClock::now(); + t0_ = utils::SteadyTime::Clock::now(); // Optionally reset congestion window bool reset_window; @@ -389,6 +390,8 @@ void RaaqmTransportProtocol::sendInterest( uint32_t len) { interests_in_flight_++; interest_retransmissions_[interest_name.getSuffix() & mask]++; + interest_timepoints_[interest_name.getSuffix() & mask] = + utils::SteadyTime::Clock::now(); TransportProtocol::sendInterest(interest_name, additional_suffixes, len); } @@ -477,7 +480,7 @@ void RaaqmTransportProtocol::scheduleNextInterests() { } } -void RaaqmTransportProtocol::onContentReassembled(std::error_code ec) { +void RaaqmTransportProtocol::onContentReassembled(const std::error_code &ec) { rate_estimator_->onDownloadFinished(); TransportProtocol::onContentReassembled(ec); schedule_interests_ = false; @@ -487,18 +490,18 @@ void RaaqmTransportProtocol::updateRtt(uint64_t segment) { if (TRANSPORT_EXPECT_FALSE(!cur_path_)) { throw std::runtime_error("RAAQM ERROR: no current path found, exit"); } else { - auto now = utils::SteadyClock::now(); - utils::Microseconds rtt = std::chrono::duration_cast<utils::Microseconds>( - now - interest_timepoints_[segment & mask]); + auto now = utils::SteadyTime::Clock::now(); + utils::SteadyTime::Milliseconds rtt = utils::SteadyTime::getDurationMs( + interest_timepoints_[segment & mask], now); // Update stats - updateStats((uint32_t)segment, rtt.count(), now); + updateStats((uint32_t)segment, rtt, now); if (rate_estimator_) { - rate_estimator_->onRttUpdate((double)rtt.count()); + rate_estimator_->onRttUpdate(rtt); } - cur_path_->insertNewRtt(rtt.count(), now); + cur_path_->insertNewRtt(rtt, now); cur_path_->smoothTimer(); if (cur_path_->newPropagationDelayAvailable()) { @@ -510,27 +513,27 @@ void RaaqmTransportProtocol::updateRtt(uint64_t segment) { void RaaqmTransportProtocol::RAAQM() { if (!cur_path_) { throw errors::RuntimeException("ERROR: no current path found, exit"); - exit(EXIT_FAILURE); } else { // Change drop probability according to RTT statistics cur_path_->updateDropProb(); - double coin = ((double)rand() / (RAND_MAX)); + double coin = dis_(gen_); if (coin <= cur_path_->getDropProb()) { decreaseWindow(); } } } -void RaaqmTransportProtocol::updateStats(uint32_t suffix, uint64_t rtt, - utils::TimePoint &now) { +void RaaqmTransportProtocol::updateStats( + uint32_t suffix, const utils::SteadyTime::Milliseconds &rtt, + utils::SteadyTime::TimePoint &now) { // Update RTT statistics stats_->updateAverageRtt(rtt); stats_->updateAverageWindowSize(current_window_size_); // Call statistics callback if (*stats_summary_) { - auto dt = std::chrono::duration_cast<utils::Milliseconds>(now - t0_); + auto dt = utils::SteadyTime::getDurationMs(t0_, now); uint32_t timer_interval_milliseconds = 0; socket_->getSocketOption(GeneralTransportOptions::STATS_INTERVAL, diff --git a/libtransport/src/protocols/raaqm.h b/libtransport/src/protocols/raaqm.h index ffbb30d3a..ec344c23a 100644 --- a/libtransport/src/protocols/raaqm.h +++ b/libtransport/src/protocols/raaqm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -23,6 +23,7 @@ #include <protocols/transport_protocol.h> #include <queue> +#include <random> #include <vector> namespace transport { @@ -55,8 +56,9 @@ class RaaqmTransportProtocol : public TransportProtocol, const ContentObject &content_object); virtual void afterDataUnsatisfied(uint64_t segment); - virtual void updateStats(uint32_t suffix, uint64_t rtt, - utils::TimePoint &now); + virtual void updateStats(uint32_t suffix, + const utils::SteadyTime::Milliseconds &rtt, + utils::SteadyTime::TimePoint &now); private: void init(); @@ -73,7 +75,7 @@ class RaaqmTransportProtocol : public TransportProtocol, *additional_suffixes = nullptr, uint32_t len = 0) override; - void onContentReassembled(std::error_code ec) override; + void onContentReassembled(const std::error_code &ec) override; void updateRtt(uint64_t segment); void RAAQM(); void updatePathTable(const ContentObject &content_object); @@ -81,13 +83,15 @@ class RaaqmTransportProtocol : public TransportProtocol, void checkForStalePaths(); void printRtt(); + auto shared_from_this() { return utils::shared_from(this); } + protected: // Congestion window management double current_window_size_; // Protocol management uint64_t interests_in_flight_; std::array<std::uint32_t, buffer_size> interest_retransmissions_; - std::array<utils::TimePoint, buffer_size> interest_timepoints_; + std::array<utils::SteadyTime::TimePoint, buffer_size> interest_timepoints_; std::queue<uint32_t> interest_to_retransmit_; private: @@ -102,13 +106,16 @@ class RaaqmTransportProtocol : public TransportProtocol, PathTable path_table_; // TimePoints for statistic - utils::TimePoint t0_; + utils::SteadyTime::TimePoint t0_; bool set_interest_filter_; // for rate-estimation at packet level IcnRateEstimator *rate_estimator_; + // Real distribution + std::uniform_real_distribution<> dis_; + // params for autotuning bool raaqm_autotune_; double default_beta_; diff --git a/libtransport/src/protocols/raaqm_data_path.cc b/libtransport/src/protocols/raaqm_data_path.cc index f2c21b9ef..d06fee918 100644 --- a/libtransport/src/protocols/raaqm_data_path.cc +++ b/libtransport/src/protocols/raaqm_data_path.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -23,16 +23,18 @@ namespace protocol { RaaqmDataPath::RaaqmDataPath(double drop_factor, double minimum_drop_probability, unsigned new_timer, unsigned int samples, - uint64_t new_rtt, uint64_t new_rtt_min, - uint64_t new_rtt_max, unsigned new_pd) + const utils::SteadyTime::Milliseconds &new_rtt, + const utils::SteadyTime::Milliseconds &new_rtt_min, + const utils::SteadyTime::Milliseconds &new_rtt_max, + unsigned new_pd) : drop_factor_(drop_factor), minimum_drop_probability_(minimum_drop_probability), timer_(new_timer), samples_(samples), - rtt_(new_rtt), - rtt_min_(new_rtt_min), - rtt_max_(new_rtt_max), + rtt_(new_rtt.count()), + rtt_min_(new_rtt_min.count()), + rtt_max_(new_rtt_max.count()), prop_delay_(new_pd), new_prop_delay_(false), drop_prob_(0), @@ -43,14 +45,15 @@ RaaqmDataPath::RaaqmDataPath(double drop_factor, raw_data_bytes_received_(0), last_raw_data_bytes_received_(0), rtt_samples_(samples_), - last_received_pkt_(utils::SteadyClock::now()), + last_received_pkt_(utils::SteadyTime::Clock::now()), average_rtt_(0), alpha_(ALPHA) {} -RaaqmDataPath &RaaqmDataPath::insertNewRtt(uint64_t new_rtt, - const utils::TimePoint &now) { - rtt_ = new_rtt; - rtt_samples_.pushBack(new_rtt); +RaaqmDataPath &RaaqmDataPath::insertNewRtt( + const utils::SteadyTime::Milliseconds &new_rtt, + const utils::SteadyTime::TimePoint &now) { + rtt_ = new_rtt.count(); + rtt_samples_.pushBack(rtt_); rtt_max_ = rtt_samples_.rBegin(); rtt_min_ = rtt_samples_.begin(); @@ -143,10 +146,8 @@ unsigned int RaaqmDataPath::getPropagationDelay() { } bool RaaqmDataPath::isStale() { - utils::TimePoint now = utils::SteadyClock::now(); - auto time = - std::chrono::duration_cast<utils::Microseconds>(now - last_received_pkt_) - .count(); + utils::SteadyTime::TimePoint now = utils::SteadyTime::Clock::now(); + auto time = utils::SteadyTime::getDurationUs(last_received_pkt_, now).count(); if (time > 2000000) { return true; } diff --git a/libtransport/src/protocols/raaqm_data_path.h b/libtransport/src/protocols/raaqm_data_path.h index c0b53a690..b6f7c5ac1 100644 --- a/libtransport/src/protocols/raaqm_data_path.h +++ b/libtransport/src/protocols/raaqm_data_path.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -34,8 +34,13 @@ class RaaqmDataPath { public: RaaqmDataPath(double drop_factor, double minimum_drop_probability, unsigned new_timer, unsigned int samples, - uint64_t new_rtt = 1000, uint64_t new_rtt_min = 1000, - uint64_t new_rtt_max = 1000, unsigned new_pd = UINT_MAX); + const utils::SteadyTime::Milliseconds &new_rtt = + utils::SteadyTime::Milliseconds(1000), + const utils::SteadyTime::Milliseconds &new_rtt_min = + utils::SteadyTime::Milliseconds(1000), + const utils::SteadyTime::Milliseconds &new_rtt_max = + utils::SteadyTime::Milliseconds(1000), + unsigned new_pd = UINT_MAX); public: /* @@ -44,7 +49,8 @@ class RaaqmDataPath { * max of RTT. * @param new_rtt is the value of the new RTT */ - RaaqmDataPath &insertNewRtt(uint64_t new_rtt, const utils::TimePoint &now); + RaaqmDataPath &insertNewRtt(const utils::SteadyTime::Milliseconds &new_rtt, + const utils::SteadyTime::TimePoint &now); /** * @brief Update the path statistics @@ -214,7 +220,7 @@ class RaaqmDataPath { /** * Time of the last call to the path reporter method */ - utils::TimePoint last_received_pkt_; + utils::SteadyTime::TimePoint last_received_pkt_; double average_rtt_; double alpha_; diff --git a/libtransport/src/protocols/rate_estimation.cc b/libtransport/src/protocols/rate_estimation.cc index 2337e18be..d834b53e6 100644 --- a/libtransport/src/protocols/rate_estimation.cc +++ b/libtransport/src/protocols/rate_estimation.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -14,6 +14,7 @@ */ #include <glog/logging.h> +#include <hicn/transport/errors/runtime_exception.h> #include <hicn/transport/interfaces/socket_options_default_values.h> #include <protocols/rate_estimation.h> @@ -92,8 +93,8 @@ InterRttEstimator::InterRttEstimator(double alpha_arg) { this->win_current_ = 1.0; pthread_mutex_init(&(this->mutex_), NULL); - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } InterRttEstimator::~InterRttEstimator() { @@ -105,33 +106,35 @@ InterRttEstimator::~InterRttEstimator() { pthread_mutex_destroy(&(this->mutex_)); } -void InterRttEstimator::onRttUpdate(double rtt) { +void InterRttEstimator::onRttUpdate( + const utils::SteadyTime::Milliseconds &rtt) { pthread_mutex_lock(&(this->mutex_)); - this->rtt_ = rtt; + this->rtt_ = rtt.count(); this->number_of_packets_++; - this->avg_rtt_ += rtt; + this->avg_rtt_ += this->rtt_; pthread_mutex_unlock(&(this->mutex_)); if (!thread_is_running_) { my_th_ = (pthread_t *)malloc(sizeof(pthread_t)); if (!my_th_) { - LOG(ERROR) << "Error allocating thread."; - my_th_ = NULL; + throw errors::RuntimeException("Error allocating thread."); } - if (/*int err = */ pthread_create(my_th_, NULL, transport::protocol::Timer, - (void *)this)) { - LOG(ERROR) << "Error creating the thread"; + + if (pthread_create(my_th_, NULL, transport::protocol::Timer, + (void *)this)) { + free(my_th_); my_th_ = NULL; + throw errors::RuntimeException("Error allocating thread."); } + thread_is_running_ = true; } } void InterRttEstimator::onWindowIncrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); pthread_mutex_lock(&(this->mutex_)); this->avg_win_ += this->win_current_ * delay; @@ -139,14 +142,13 @@ void InterRttEstimator::onWindowIncrease(double win_current) { this->win_change_ += delay; pthread_mutex_unlock(&(this->mutex_)); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void InterRttEstimator::onWindowDecrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); pthread_mutex_lock(&(this->mutex_)); this->avg_win_ += this->win_current_ * delay; @@ -154,25 +156,24 @@ void InterRttEstimator::onWindowDecrease(double win_current) { this->win_change_ += delay; pthread_mutex_unlock(&(this->mutex_)); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } ALaTcpEstimator::ALaTcpEstimator() { this->estimation_ = 0.0; this->observer_ = NULL; - this->start_time_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); this->totalSize_ = 0.0; } void ALaTcpEstimator::onStart() { this->totalSize_ = 0.0; - this->start_time_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); } void ALaTcpEstimator::onDownloadFinished() { - TimePoint end = std::chrono::steady_clock::now(); - auto delay = - std::chrono::duration_cast<Microseconds>(end - this->start_time_).count(); + auto end = utils::SteadyTime::now(); + auto delay = utils::SteadyTime::getDurationUs(this->start_time_, end).count(); this->estimation_ = this->totalSize_ * 8 * 1000000 / delay; if (observer_) { observer_->notifyStats(this->estimation_); @@ -192,22 +193,21 @@ SimpleEstimator::SimpleEstimator(double alphaArg, int batching_param) { this->number_of_packets_ = 0; this->base_alpha_ = alphaArg; this->alpha_ = alphaArg; - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void SimpleEstimator::onStart() { this->estimated_ = false; this->number_of_packets_ = 0; this->total_size_ = 0.0; - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void SimpleEstimator::onDownloadFinished() { - TimePoint end = std::chrono::steady_clock::now(); - auto delay = - std::chrono::duration_cast<Microseconds>(end - this->start_time_).count(); + auto end = utils::SteadyTime::now(); + auto delay = utils::SteadyTime::getDurationUs(this->start_time_, end).count(); if (observer_) { observer_->notifyDownloadTime((double)delay); } @@ -229,8 +229,7 @@ void SimpleEstimator::onDownloadFinished() { } else { if (this->number_of_packets_ >= (int)(75.0 * (double)this->batching_param_ / 100.0)) { - delay = std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + delay = utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); // Assuming all packets carry max_packet_size_ bytes of data // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds if (this->estimation_) { @@ -249,22 +248,21 @@ void SimpleEstimator::onDownloadFinished() { } this->number_of_packets_ = 0; this->total_size_ = 0.0; - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void SimpleEstimator::onDataReceived(int packet_size) { this->total_size_ += packet_size; } -void SimpleEstimator::onRttUpdate(double rtt) { +void SimpleEstimator::onRttUpdate(const utils::SteadyTime::Milliseconds &rtt) { this->number_of_packets_++; if (this->number_of_packets_ == this->batching_param_) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); // Assuming all packets carry max_packet_size_ bytes of data // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds if (this->estimation_) { @@ -280,7 +278,7 @@ void SimpleEstimator::onRttUpdate(double rtt) { this->alpha_ = this->base_alpha_; this->number_of_packets_ = 0; this->total_size_ = 0.0; - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } } @@ -297,13 +295,14 @@ BatchingPacketsEstimator::BatchingPacketsEstimator(double alpha_arg, this->max_packet_size_ = 0; this->estimation_ = 0.0; this->win_current_ = 1.0; - this->begin_batch_ = std::chrono::steady_clock::now(); - this->start_time_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); + this->start_time_ = utils::SteadyTime::now(); } -void BatchingPacketsEstimator::onRttUpdate(double rtt) { +void BatchingPacketsEstimator::onRttUpdate( + const utils::SteadyTime::Milliseconds &rtt) { this->number_of_packets_++; - this->avg_rtt_ += rtt; + this->avg_rtt_ += rtt.count(); if (number_of_packets_ == this->batching_param_) { if (estimation_ == 0) { @@ -329,25 +328,23 @@ void BatchingPacketsEstimator::onRttUpdate(double rtt) { } void BatchingPacketsEstimator::onWindowIncrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); this->avg_win_ += this->win_current_ * delay; this->win_current_ = win_current; this->win_change_ += delay; - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void BatchingPacketsEstimator::onWindowDecrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); this->avg_win_ += this->win_current_ * delay; this->win_current_ = win_current; this->win_change_ += delay; - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } } // end namespace protocol diff --git a/libtransport/src/protocols/rate_estimation.h b/libtransport/src/protocols/rate_estimation.h index 42ae74194..b71de12e4 100644 --- a/libtransport/src/protocols/rate_estimation.h +++ b/libtransport/src/protocols/rate_estimation.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/interfaces/statistics.h> +#include <hicn/transport/utils/noncopyable.h> #include <protocols/raaqm_data_path.h> #include <chrono> @@ -24,16 +25,13 @@ namespace transport { namespace protocol { -class IcnRateEstimator { +class IcnRateEstimator : utils::NonCopyable { public: - using TimePoint = std::chrono::steady_clock::time_point; - using Microseconds = std::chrono::microseconds; - IcnRateEstimator(){}; virtual ~IcnRateEstimator(){}; - virtual void onRttUpdate(double rtt){}; + virtual void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt){}; virtual void onDataReceived(int packetSize){}; @@ -48,9 +46,10 @@ class IcnRateEstimator { virtual void setObserver(interface::IcnObserver *observer) { this->observer_ = observer; }; + interface::IcnObserver *observer_; - TimePoint start_time_; - TimePoint begin_batch_; + utils::SteadyTime::TimePoint start_time_; + utils::SteadyTime::TimePoint begin_batch_; double base_alpha_; double alpha_; double estimation_; @@ -67,7 +66,7 @@ class InterRttEstimator : public IcnRateEstimator { ~InterRttEstimator(); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt); void onDataReceived(int packet_size) { if (packet_size > this->max_packet_size_) { @@ -102,7 +101,7 @@ class BatchingPacketsEstimator : public IcnRateEstimator { public: BatchingPacketsEstimator(double alpha_arg, int batchingParam); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt); void onDataReceived(int packet_size) { if (packet_size > this->max_packet_size_) { @@ -149,7 +148,7 @@ class SimpleEstimator : public IcnRateEstimator { public: SimpleEstimator(double alpha, int batching_param); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt); void onDataReceived(int packet_size); diff --git a/libtransport/src/protocols/reassembly.cc b/libtransport/src/protocols/reassembly.cc index ce24fce1b..065458f7e 100644 --- a/libtransport/src/protocols/reassembly.cc +++ b/libtransport/src/protocols/reassembly.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/protocols/reassembly.h b/libtransport/src/protocols/reassembly.h index e072ad123..b0879201d 100644 --- a/libtransport/src/protocols/reassembly.h +++ b/libtransport/src/protocols/reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -36,7 +36,7 @@ class Reassembly { public: class ContentReassembledCallback { public: - virtual void onContentReassembled(std::error_code ec) = 0; + virtual void onContentReassembled(const std::error_code &ec) = 0; }; Reassembly(implementation::ConsumerSocket *icn_socket, @@ -47,12 +47,12 @@ class Reassembly { virtual ~Reassembly() = default; /** - * Hanle reassembly of content object. + * Handle reassembly of content object. */ virtual void reassemble(core::ContentObject &content_object) = 0; /** - * Hanle reassembly of content object. + * Handle reassembly of content object. */ virtual void reassemble(utils::MemBuf &buffer, uint32_t suffix) = 0; diff --git a/libtransport/src/protocols/rtc/CMakeLists.txt b/libtransport/src/protocols/rtc/CMakeLists.txt index 873b345d0..be8e0189c 100644 --- a/libtransport/src/protocols/rtc/CMakeLists.txt +++ b/libtransport/src/protocols/rtc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -16,22 +16,43 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_consts.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_forwarding_strategy.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_indexer.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_packet.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_congestion_detection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_iat.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_recovery_strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_delay.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_fec_only.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_low_rate.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_recovery_off.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_rtx_only.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_verifier.h ) list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_forwarding_strategy.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_congestion_detection.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_iat.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_recovery_strategy.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_delay.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_fec_only.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_low_rate.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_recovery_off.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_rtx_only.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_verifier.cc ) set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/rtc/probe_handler.cc b/libtransport/src/protocols/rtc/probe_handler.cc index abaca6ad9..abb234757 100644 --- a/libtransport/src/protocols/rtc/probe_handler.cc +++ b/libtransport/src/protocols/rtc/probe_handler.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -13,6 +13,7 @@ * limitations under the License. */ +#include <hicn/transport/utils/chrono_typedefs.h> #include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_consts.h> @@ -27,6 +28,7 @@ ProbeHandler::ProbeHandler(SendProbeCallback &&send_callback, : probe_interval_(0), max_probes_(0), sent_probes_(0), + recv_probes_(0), probe_timer_(std::make_unique<asio::steady_timer>(io_service)), rand_eng_((std::random_device())()), distr_(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ), @@ -39,17 +41,25 @@ uint64_t ProbeHandler::getRtt(uint32_t seq) { if (it == pending_probes_.end()) return 0; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t rtt = now - it->second; if (rtt < 1) rtt = 1; pending_probes_.erase(it); + recv_probes_++; return rtt; } +double ProbeHandler::getProbeLossRate() { + return 1.0 - ((double)recv_probes_ / (double)sent_probes_); +} + +void ProbeHandler::setSuffixRange(uint32_t min, uint32_t max) { + assert(min <= max && min >= MIN_PROBE_SEQ); + distr_ = std::uniform_int_distribution<uint32_t>(min, max); +} + void ProbeHandler::setProbes(uint32_t probe_interval, uint32_t max_probes) { stopProbes(); probe_interval_ = probe_interval; @@ -60,6 +70,7 @@ void ProbeHandler::stopProbes() { probe_interval_ = 0; max_probes_ = 0; sent_probes_ = 0; + recv_probes_ = 0; probe_timer_->cancel(); } @@ -67,9 +78,7 @@ void ProbeHandler::sendProbes() { if (probe_interval_ == 0) return; if (max_probes_ != 0 && sent_probes_ >= max_probes_) return; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint32_t seq = distr_(rand_eng_); pending_probes_.insert(std::pair<uint32_t, uint64_t>(seq, now)); @@ -92,14 +101,25 @@ void ProbeHandler::sendProbes() { std::weak_ptr<ProbeHandler> self(shared_from_this()); probe_timer_->expires_from_now(std::chrono::microseconds(probe_interval_)); - probe_timer_->async_wait([self](std::error_code ec) { + probe_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - if (auto s = self.lock()) { + auto s = self.lock(); + if (s) { s->sendProbes(); } }); } +ProbeType ProbeHandler::getProbeType(uint32_t seq) { + if (MIN_INIT_PROBE_SEQ <= seq && seq <= MAX_INIT_PROBE_SEQ) { + return ProbeType::INIT; + } + if (MIN_RTT_PROBE_SEQ <= seq && seq <= MAX_RTT_PROBE_SEQ) { + return ProbeType::RTT; + } + return ProbeType::NOT_PROBE; +} + } // namespace rtc } // namespace protocol diff --git a/libtransport/src/protocols/rtc/probe_handler.h b/libtransport/src/protocols/rtc/probe_handler.h index e34b23df0..2de908176 100644 --- a/libtransport/src/protocols/rtc/probe_handler.h +++ b/libtransport/src/protocols/rtc/probe_handler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -26,6 +26,12 @@ namespace protocol { namespace rtc { +enum class ProbeType { + NOT_PROBE, + INIT, + RTT, +}; + class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> { public: using SendProbeCallback = std::function<void(uint32_t)>; @@ -35,31 +41,39 @@ class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> { ~ProbeHandler(); - // if the function returns 0 the probe is not valaid + // If the function returns 0 the probe is not valid. uint64_t getRtt(uint32_t seq); - // reset the probes parameters. it stop the current probing. - // to restar call sendProbes. - // probe_interval = 0 means that no event will be scheduled - // max_probe = 0 means no limit to the number of probe to send + // this function may return a residual loss rate higher than the real one if + // we don't wait enough time for the probes to come back + double getProbeLossRate(); + + // Set the probe suffix range [min, max] + void setSuffixRange(uint32_t min, uint32_t max); + + // Reset the probes parameters and stops the current probing. + // probe_interval = 0 means that no event will be scheduled. + // max_probe = 0 means no limit to the number of probe to send. void setProbes(uint32_t probe_interval, uint32_t max_probes); - // stop to schedule probes void stopProbes(); void sendProbes(); + static ProbeType getProbeType(uint32_t seq); + private: uint32_t probe_interval_; // us uint32_t max_probes_; // packets uint32_t sent_probes_; // packets + uint32_t recv_probes_; // packets std::unique_ptr<asio::steady_timer> probe_timer_; - // map from seqnumber to timestamp + // Map from packet suffixes to timestamp std::unordered_map<uint32_t, uint64_t> pending_probes_; - // random generator + // Random generator std::default_random_engine rand_eng_; std::uniform_int_distribution<uint32_t> distr_; diff --git a/libtransport/src/protocols/rtc/rtc.cc b/libtransport/src/protocols/rtc/rtc.cc index 0cb4cda1d..df6522471 100644 --- a/libtransport/src/protocols/rtc/rtc.cc +++ b/libtransport/src/protocols/rtc/rtc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -22,7 +22,7 @@ #include <protocols/rtc/rtc.h> #include <protocols/rtc/rtc_consts.h> #include <protocols/rtc/rtc_indexer.h> -#include <protocols/rtc/rtc_rc_queue.h> +#include <protocols/rtc/rtc_rc_congestion_detection.h> #include <algorithm> @@ -37,13 +37,15 @@ using namespace interface; RTCTransportProtocol::RTCTransportProtocol( implementation::ConsumerSocket *icn_socket) : TransportProtocol(icn_socket, new RtcIndexer<>(icn_socket, this), - new DatagramReassembly(icn_socket, this)), + new RtcReassembly(icn_socket, this)), number_(0) { icn_socket->getSocketOption(PORTAL, portal_); - round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); + round_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); scheduler_timer_ = - std::make_unique<asio::steady_timer>(portal_->getIoService()); - pacing_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + pacing_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); } RTCTransportProtocol::~RTCTransportProtocol() {} @@ -61,25 +63,52 @@ std::size_t RTCTransportProtocol::transportHeaderLength() { // private void RTCTransportProtocol::initParams() { TransportProtocol::reset(); + fwd_strategy_.setCallback(on_fwd_strategy_); - rc_ = std::make_shared<RTCRateControlQueue>(); + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + + std::shared_ptr<auth::Verifier> verifier; + socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier); + + uint32_t max_unverified_delay; + socket_->getSocketOption(GeneralTransportOptions::MAX_UNVERIFIED_TIME, + max_unverified_delay); + + rc_ = std::make_shared<RTCRateControlCongestionDetection>(); ldr_ = std::make_shared<RTCLossDetectionAndRecovery>( - indexer_verifier_.get(), - std::bind(&RTCTransportProtocol::sendRtxInterest, this, - std::placeholders::_1), - portal_->getIoService()); + indexer_verifier_.get(), portal_->getThread().getIoService(), + interface::RtcTransportRecoveryStrategies::RTX_ONLY, + [self](uint32_t seq) { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + ptr->sendRtxInterest(seq); + } + }, + on_rec_strategy_); + verifier_ = std::make_shared<RTCVerifier>(verifier, max_unverified_delay); state_ = std::make_shared<RTCState>( indexer_verifier_.get(), - std::bind(&RTCTransportProtocol::sendProbeInterest, this, - std::placeholders::_1), - std::bind(&RTCTransportProtocol::discoveredRtt, this), - portal_->getIoService()); + [self](uint32_t seq) { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + ptr->sendProbeInterest(seq); + } + }, + [self]() { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + ptr->discoveredRtt(); + } + }, + portal_->getThread().getIoService()); + state_->initParams(); rc_->setState(state_); - // TODO: for the moment we keep the congestion control disabled - // rc_->tunrOnRateControl(); - ldr_->setState(state_); + rc_->turnOnRateControl(); + ldr_->setState(state_.get()); + ldr_->setRateControl(rc_.get()); + verifier_->setState(state_); // protocol state start_send_interest_ = false; @@ -102,6 +131,10 @@ void RTCTransportProtocol::initParams() { } #else max_aggregated_interest_ = 1; + if (const char *max_aggr = std::getenv("MAX_AGGREGATED_INTERESTS")) { + LOG(INFO) << "Max Aggregated: " << max_aggr; + max_aggregated_interest_ = std::stoul(std::string(max_aggr)); + } #endif max_sent_int_ = @@ -131,6 +164,7 @@ void RTCTransportProtocol::initParams() { indexer_verifier_->setNFec(0); ldr_->setFecParams(fec::FECUtils::getBlockSymbols(fec_type_), fec::FECUtils::getSourceSymbols(fec_type_)); + fec_decoder_->setIOService(portal_->getThread().getIoService()); } else { indexer_verifier_->disableFec(); } @@ -162,61 +196,97 @@ void RTCTransportProtocol::inactiveProducer() { void RTCTransportProtocol::newRound() { round_timer_->expires_from_now(std::chrono::milliseconds(ROUND_LEN)); - // TODO pass weak_ptr here - round_timer_->async_wait([this, n{number_}](std::error_code ec) { - if (ec) return; - if (n != number_) { + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + round_timer_->async_wait([self](const std::error_code &ec) { + if (ec) { return; } + auto ptr = self.lock(); + + if (!ptr || !ptr->isRunning()) { + return; + } + + auto &state = ptr->state_; + // saving counters that will be reset on new round - uint32_t sent_retx = state_->getSentRtxInRound(); - uint32_t received_bytes = state_->getReceivedBytesInRound(); - uint32_t sent_interest = state_->getSentInterestInRound(); - uint32_t lost_data = state_->getLostData(); - uint32_t definitely_lost = state_->getDefinitelyLostPackets(); - uint32_t recovered_losses = state_->getRecoveredLosses(); - uint32_t received_nacks = state_->getReceivedNacksInRound(); - uint32_t received_fec = state_->getReceivedFecPackets(); - - bool in_sync = (current_state_ == SyncState::in_sync); - ldr_->onNewRound(in_sync); - state_->onNewRound((double)ROUND_LEN, in_sync); - rc_->onNewRound((double)ROUND_LEN); + uint32_t sent_retx = state->getSentRtxInRound(); + uint32_t received_bytes = + (state->getReceivedBytesInRound() + // data packets received + state->getReceivedFecBytesInRound()); // fec packets received + uint32_t sent_interest = state->getSentInterestInRound(); + uint32_t lost_data = state->getLostData(); + uint32_t definitely_lost = state->getDefinitelyLostPackets(); + uint32_t recovered_losses = state->getRecoveredLosses(); + uint32_t received_nacks = state->getReceivedNacksInRound(); + uint32_t received_fec = state->getReceivedFecPackets(); + + bool in_sync = (ptr->current_state_ == SyncState::in_sync); + ptr->ldr_->onNewRound(in_sync); + ptr->state_->onNewRound((double)ROUND_LEN, in_sync); + ptr->rc_->onNewRound((double)ROUND_LEN); // update sync state if needed - if (current_state_ == SyncState::in_sync) { - double cache_rate = state_->getPacketFromCacheRatio(); + if (ptr->current_state_ == SyncState::in_sync) { + double cache_rate = state->getPacketFromCacheRatio(); if (cache_rate > MAX_DATA_FROM_CACHE) { - current_state_ = SyncState::catch_up; + ptr->current_state_ = SyncState::catch_up; } } else { - double target_rate = state_->getProducerRate() * PRODUCTION_RATE_FRACTION; - double received_rate = state_->getReceivedRate(); - uint32_t round_without_nacks = state_->getRoundsWithoutNacks(); - double cache_ratio = state_->getPacketFromCacheRatio(); + double target_rate = state->getProducerRate() * PRODUCTION_RATE_FRACTION; + double received_rate = + state->getReceivedRate() + state->getRecoveredFecRate(); + uint32_t round_without_nacks = state->getRoundsWithoutNacks(); + double cache_ratio = state->getPacketFromCacheRatio(); if (round_without_nacks >= ROUNDS_IN_SYNC_BEFORE_SWITCH && received_rate >= target_rate && cache_ratio < MAX_DATA_FROM_CACHE) { - current_state_ = SyncState::in_sync; + ptr->current_state_ = SyncState::in_sync; } } DLOG_IF(INFO, VLOG_IS_ON(3)) << "Calling updateSyncWindow in newRound function"; - updateSyncWindow(); + ptr->updateSyncWindow(); - sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data, - definitely_lost, recovered_losses, received_nacks, - received_fec); - newRound(); + ptr->sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data, + definitely_lost, recovered_losses, received_nacks, + received_fec); + ptr->fwd_strategy_.checkStrategy(); + ptr->newRound(); }); } void RTCTransportProtocol::discoveredRtt() { start_send_interest_ = true; - ldr_->turnOnRTX(); + uint32_t strategy; + socket_->getSocketOption(RtcTransportOptions::RECOVERY_STRATEGY, strategy); + ldr_->changeRecoveryStrategy( + (interface::RtcTransportRecoveryStrategies)strategy); + ldr_->turnOnRecovery(); ldr_->onNewRound(false); + + // set forwarding strategy switch if selected + Name *name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + Prefix prefix(*name, 128); + if ((interface::RtcTransportRecoveryStrategies)strategy == + interface::RtcTransportRecoveryStrategies::LOW_RATE_AND_BESTPATH) { + fwd_strategy_.initFwdStrategy(portal_, prefix, state_.get(), + RTCForwardingStrategy::BEST_PATH); + } else if ((interface::RtcTransportRecoveryStrategies)strategy == + interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_REPLICATION) { + fwd_strategy_.initFwdStrategy(portal_, prefix, state_.get(), + RTCForwardingStrategy::REPLICATION); + } else if ((interface::RtcTransportRecoveryStrategies)strategy == + interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES) { + fwd_strategy_.initFwdStrategy(portal_, prefix, state_.get(), + RTCForwardingStrategy::BOTH); + } + updateSyncWindow(); } @@ -244,7 +314,7 @@ void RTCTransportProtocol::computeMaxSyncWindow() { (production_rate * lifetime_ms * INTEREST_LIFETIME_REDUCTION_FACTOR) / packet_size); - max_sync_win_ = std::min(max_sync_win_, rc_->getCongesionWindow()); + max_sync_win_ = std::min(max_sync_win_, rc_->getCongestionWindow()); } void RTCTransportProtocol::updateSyncWindow() { @@ -259,25 +329,14 @@ void RTCTransportProtocol::updateSyncWindow() { } double prod_rate = state_->getProducerRate(); - double rtt = (double)state_->getRTT() / MILLI_IN_A_SEC; + double rtt = (double)state_->getMinRTT() / MILLI_IN_A_SEC; double packet_size = state_->getAveragePacketSize(); // if some of the info are not available do not update the current win if (prod_rate != 0.0 && rtt != 0.0 && packet_size != 0.0) { - double fec_interest_overhead = (double)state_->getPendingFecPackets() / - (double)(state_->getPendingInterestNumber() - - state_->getPendingFecPackets()); - - double fec_overhead = - std::max(indexer_verifier_->getFecOverhead(), fec_interest_overhead); - - prod_rate += (prod_rate * fec_overhead); - current_sync_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size); uint32_t buffer = PRODUCER_BUFFER_MS; - if (rtt > 150) - buffer = buffer * 2; // if the RTT is too large we increase the - // the size of the buffer + current_sync_win_ += ceil(prod_rate * (buffer / MILLI_IN_A_SEC) / packet_size); @@ -285,8 +344,17 @@ void RTCTransportProtocol::updateSyncWindow() { current_sync_win_ = current_sync_win_ * CATCH_UP_WIN_INCREMENT; } + uint32_t min_win = WIN_MIN; + bool aggregated_data_on; + socket_->getSocketOption(RtcTransportOptions::AGGREGATED_DATA, + aggregated_data_on); + if (aggregated_data_on) { + min_win = WIN_MIN_WITH_AGGREGARED_DATA; + min_win += (min_win * (1 - (std::max(0.3, rtt) - rtt) / 0.3)); + } + current_sync_win_ = std::min(current_sync_win_, max_sync_win_); - current_sync_win_ = std::max(current_sync_win_, WIN_MIN); + current_sync_win_ = std::max(current_sync_win_, min_win); } scheduleNextInterests(); @@ -322,7 +390,7 @@ void RTCTransportProtocol::sendProbeInterest(uint32_t seq) { socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &interest_name); - DLOG_IF(INFO, VLOG_IS_ON(3)) << "send probe " << seq; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send probe " << seq; interest_name->setSuffix(seq); sendInterest(*interest_name); } @@ -330,13 +398,18 @@ void RTCTransportProtocol::sendProbeInterest(uint32_t seq) { void RTCTransportProtocol::scheduleNextInterests() { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Schedule next interests"; - if (!isRunning() && !is_first_) return; + if (!isRunning() && !is_first_) { + return; + } - if (pacing_timer_on_) return; // wait pacing timer for the next send + if (pacing_timer_on_) { + return; // wait pacing timer for the next send + } - if (!start_send_interest_) + if (!start_send_interest_) { return; // RTT discovering phase is not finished so // do not start to send interests + } if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Inactive producer."; @@ -352,7 +425,7 @@ void RTCTransportProtocol::scheduleNextInterests() { &interest_name); uint32_t next_seg = 0; - DLOG_IF(INFO, VLOG_IS_ON(3)) << "send interest " << next_seg; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send interest " << next_seg; interest_name->setSuffix(next_seg); if (portal_->interestIsPending(*interest_name)) { @@ -370,28 +443,37 @@ void RTCTransportProtocol::scheduleNextInterests() { << " -- current_sync_win_: " << current_sync_win_; uint32_t pending = state_->getPendingInterestNumber(); - if (pending >= current_sync_win_) return; // no space in the window + uint32_t pending_fec = state_->getPendingFecPackets(); + + if ((pending - pending_fec) >= current_sync_win_) + return; // no space in the window - if ((current_sync_win_ - pending) < max_aggregated_interest_) { + // XXX double check if aggregated interests are still working here + if ((current_sync_win_ - (pending - pending_fec)) < + max_aggregated_interest_) { if (scheduler_timer_on_) return; // timer already scheduled - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t time = now - last_interest_sent_time_; if (time < WAIT_FOR_INTEREST_BATCH) { uint64_t next = WAIT_FOR_INTEREST_BATCH - time; scheduler_timer_on_ = true; scheduler_timer_->expires_from_now(std::chrono::milliseconds(next)); - scheduler_timer_->async_wait([this](std::error_code ec) { + + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + scheduler_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - if (!scheduler_timer_on_) return; - scheduler_timer_on_ = false; - scheduleNextInterests(); + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->scheduler_timer_on_) return; + + ptr->scheduler_timer_on_ = false; + ptr->scheduleNextInterests(); + } }); - return; // whait for the timer + return; // wait for the timer } } @@ -403,7 +485,7 @@ void RTCTransportProtocol::scheduleNextInterests() { indexer_verifier_->jumpToIndex(state_->getLastSeqNacked() + 1); } - // skipe received packets + // skip received packets if (indexer_verifier_->checkNextSuffix() <= state_->getHighestSeqReceivedInOrder()) { indexer_verifier_->jumpToIndex(state_->getHighestSeqReceivedInOrder() + 1); @@ -417,7 +499,8 @@ void RTCTransportProtocol::scheduleNextInterests() { socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); std::array<uint32_t, MAX_AGGREGATED_INTEREST> additional_suffixes; - while ((state_->getPendingInterestNumber() < current_sync_win_) && + while (((state_->getPendingInterestNumber() - + state_->getPendingFecPackets()) < current_sync_win_) && (sent_interests < max_sent_int_)) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "In while loop. Window size: " << current_sync_win_; @@ -428,19 +511,20 @@ void RTCTransportProtocol::scheduleNextInterests() { // send the packet only if: // 1) it is not pending yet (not true for rtx) - // 2) the packet is not received or lost + // 2) the packet is not received or def lost // 3) is not in the rtx list // 4) is fec and is not in order (!= last sent + 1) + PacketState packet_state = state_->getPacketState(next_seg); if (portal_->interestIsPending(*name) || - state_->isReceivedOrLost(next_seg) != PacketState::UNKNOWN || - ldr_->isRtx(next_seg) || + packet_state == PacketState::RECEIVED || + packet_state == PacketState::DEFINITELY_LOST || ldr_->isRtx(next_seg) || (indexer_verifier_->isFec(next_seg) && next_seg != last_interest_sent_seq_ + 1)) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "skip interest " << next_seg << " because: pending " - << portal_->interestIsPending(*name) << ", recv " - << (state_->isReceivedOrLost(next_seg) != PacketState::UNKNOWN) - << ", rtx " << (ldr_->isRtx(next_seg)) << ", is old fec " + << portal_->interestIsPending(*name) << ", recv or lost" + << (int)packet_state << ", rtx " << (ldr_->isRtx(next_seg)) + << ", is old fec " << ((indexer_verifier_->isFec(next_seg) && next_seg != last_interest_sent_seq_ + 1)); continue; @@ -462,10 +546,7 @@ void RTCTransportProtocol::scheduleNextInterests() { sent_packets++; sent_interests++; sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1); - last_interest_sent_time_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + last_interest_sent_time_ = utils::SteadyTime::nowMs().count(); aggregated_counter = 0; } } @@ -473,25 +554,29 @@ void RTCTransportProtocol::scheduleNextInterests() { // exiting the while we may have some pending interest to send if (aggregated_counter != 0) { sent_packets++; - last_interest_sent_time_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + last_interest_sent_time_ = utils::SteadyTime::nowMs().count(); sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1); } - if (state_->getPendingInterestNumber() < current_sync_win_) { + if ((state_->getPendingInterestNumber() - state_->getPendingFecPackets()) < + current_sync_win_) { // we still have space in the window but we already sent too many packets // wait PACING_WAIT to avoid drops in the kernel pacing_timer_on_ = true; pacing_timer_->expires_from_now(std::chrono::microseconds(PACING_WAIT)); - scheduler_timer_->async_wait([this](std::error_code ec) { + + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + scheduler_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - if (!pacing_timer_on_) return; - pacing_timer_on_ = false; - scheduleNextInterests(); + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->pacing_timer_on_) return; + + ptr->pacing_timer_on_ = false; + ptr->scheduleNextInterests(); + } }); } } @@ -500,13 +585,13 @@ void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, const Name &name) { uint32_t segment_number = name.getSuffix(); - if (segment_number >= MIN_PROBE_SEQ) { + if (ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE) { // this is a timeout on a probe, do nothing return; } - PacketState state = state_->isReceivedOrLost(segment_number); - if (state != PacketState::UNKNOWN) { + PacketState state = state_->getPacketState(segment_number); + if (state == PacketState::RECEIVED || state == PacketState::DEFINITELY_LOST) { // we may recover a packets using fec, ignore this timer return; } @@ -524,13 +609,18 @@ void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, DLOG_IF(INFO, VLOG_IS_ON(3)) << "handle timeout for packet " << segment_number << " using rtx"; if (ldr_->isRtxOn()) { - ldr_->onTimeout(segment_number); - if (indexer_verifier_->isFec(segment_number)) + if (indexer_verifier_->isFec(segment_number)) { + // if this is a fec packet we do not recover it with rtx so we consider + // the packet to be lost + ldr_->onTimeout(segment_number, true); state_->onTimeout(segment_number, true); - else + } else { + ldr_->onTimeout(segment_number, false); state_->onTimeout(segment_number, false); + } } else { // in this case we wil never recover the timeout + ldr_->onTimeout(segment_number, true); state_->onTimeout(segment_number, true); } scheduleNextInterests(); @@ -559,7 +649,7 @@ void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, void RTCTransportProtocol::onNack(const ContentObject &content_object) { struct nack_packet_t *nack = (struct nack_packet_t *)content_object.getPayload()->data(); - uint32_t production_seg = nack->getProductionSegement(); + uint32_t production_seg = nack->getProductionSegment(); uint32_t nack_segment = content_object.getName().getSuffix(); bool is_rtx = ldr_->isRtx(nack_segment); @@ -592,6 +682,8 @@ void RTCTransportProtocol::onNack(const ContentObject &content_object) { // remove the nack is it exists if (tn_it != timeouts_or_nacks_.end()) timeouts_or_nacks_.erase(tn_it); + state_->onJumpForward(production_seg); + verifier_->onJumpForward(production_seg); // the client is asking for content in the past // switch to catch up state and increase the window // this is true only if the packet is not an RTX @@ -618,11 +710,9 @@ void RTCTransportProtocol::onProbe(const ContentObject &content_object) { bool valid = state_->onProbePacketReceived(content_object); if (!valid) return; - struct nack_packet_t *probe = - (struct nack_packet_t *)content_object.getPayload()->data(); - uint32_t production_seg = probe->getProductionSegement(); + uint32_t production_seg = RTCState::getProbeParams(content_object).prod_seg; - // as for the nacks set next_segment + // As for the nacks set next_segment DLOG_IF(INFO, VLOG_IS_ON(3)) << "on probe next seg = " << indexer_verifier_->checkNextSuffix() << ", jump to " << production_seg; @@ -636,12 +726,39 @@ void RTCTransportProtocol::onContentObjectReceived( Interest &interest, ContentObject &content_object, std::error_code &ec) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content object of size: " << content_object.payloadSize(); - uint32_t payload_size = content_object.payloadSize(); + uint32_t segment_number = content_object.getName().getSuffix(); + PayloadType payload_type = content_object.getPayloadType(); + PacketState state; + + ContentObject *content_ptr = &content_object; + ContentObject::Ptr manifest_ptr = nullptr; + + bool is_probe = + ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE; + bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE; + bool is_fec = indexer_verifier_->isFec(segment_number); + bool is_manifest = + !is_probe && !is_nack && !is_fec && payload_type == PayloadType::MANIFEST; + bool is_data = + !is_probe && !is_nack && !is_fec && payload_type == PayloadType::DATA; + bool compute_stats = is_data || is_manifest; ec = make_error_code(protocol_error::not_reassemblable); - if (segment_number >= MIN_PROBE_SEQ) { + // A helper function to process manifests or data packets received + auto onDataPacketReceived = [this](ContentObject &content_object, + bool compute_stats) { + ldr_->onDataPacketReceived(content_object); + rc_->onDataPacketReceived(content_object, compute_stats); + updateSyncWindow(); + }; + + // First verify the packet signature and apply the corresponding policy + auth::VerificationPolicy policy = verifier_->verify(content_object, is_fec); + indexer_verifier_->applyPolicy(interest, content_object, false, policy); + + if (is_probe) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << segment_number; if (*on_content_object_input_) { (*on_content_object_input_)(*socket_->getInterface(), content_object); @@ -650,7 +767,7 @@ void RTCTransportProtocol::onContentObjectReceived( return; } - if (payload_size == NACK_HEADER_SIZE) { + if (is_nack) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received nack " << segment_number; if (*on_content_object_input_) { (*on_content_object_input_)(*socket_->getInterface(), content_object); @@ -659,57 +776,122 @@ void RTCTransportProtocol::onContentObjectReceived( return; } + // content_ptr will point either to the input data packet or to a manifest + // whose FEC header has been removed + if (is_manifest) { + manifest_ptr = removeFecHeader(content_object); + if (manifest_ptr) { + content_ptr = manifest_ptr.get(); + } + } + + // From there, the packet is either a FEC, a manifest or a data packet. DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content " << segment_number; - bool compute_stats = true; + // Do not count timed out packets in stats auto tn_it = timeouts_or_nacks_.find(segment_number); if (tn_it != timeouts_or_nacks_.end()) { compute_stats = false; timeouts_or_nacks_.erase(tn_it); } - if (ldr_->isRtx(segment_number)) { + + // Do not count retransmissions or losses in stats + if (ldr_->isRtx(segment_number) || + ldr_->isPossibleLossWithNoRtx(segment_number)) { compute_stats = false; } - // check if the packet was already received - PacketState state = state_->isReceivedOrLost(segment_number); + // Fetch packet state + state = state_->getPacketState(segment_number); - if (state != PacketState::RECEIVED) { - // send packet to decoder - if (fec_decoder_) { - DLOG_IF(INFO, VLOG_IS_ON(4)) - << "send packet " << segment_number << " to FEC decoder"; - fec_decoder_->onDataPacket( - content_object, content_object.headerSize() + rtc::DATA_HEADER_SIZE); - } - if (!indexer_verifier_->isFec(segment_number)) { - // the packet may be alredy sent to the ap by the decoder, check again if - // it is already received - state = state_->isReceivedOrLost(segment_number); - if (state != PacketState::RECEIVED) { - DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received content " << segment_number; + // Check if the packet is a retransmission + if (ldr_->isRtx(segment_number) && state != PacketState::RECEIVED) { + if (is_data || is_manifest) { + state_->onPacketRecoveredRtx(segment_number); - state_->onDataPacketReceived(content_object, compute_stats); + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } - if (*on_content_object_input_) { - (*on_content_object_input_)(*socket_->getInterface(), content_object); - } - ec = make_error_code(protocol_error::success); + if (is_manifest) { + processManifest(interest, *content_ptr); } - } else { - DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received fec " << segment_number; - state_->onFecPacketReceived(content_object); + + ec = is_manifest ? make_error_code(protocol_error::not_reassemblable) + : make_error_code(protocol_error::success); + + // The packet is considered received, return early + onDataPacketReceived(*content_ptr, compute_stats); + return; } - } else { + + if (is_fec) { + state_->onFecPacketRecoveredRtx(segment_number); + } + } + + // Fetch packet state again; it may have changed + state = state_->getPacketState(segment_number); + + // Check if the packet was already received + if (state == PacketState::RECEIVED || state == PacketState::TO_BE_RECEIVED) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received duplicated content " << segment_number << ", drop it"; ec = make_error_code(protocol_error::duplicated_content); + onDataPacketReceived(*content_ptr, compute_stats); + return; } - ldr_->onDataPacketReceived(content_object); - rc_->onDataPacketReceived(content_object); + if (!is_fec) { + state_->dataToBeReceived(segment_number); + } - updateSyncWindow(); + // Send packet to FEC decoder + if (fec_decoder_) { + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Send packet " << segment_number << " to FEC decoder"; + + uint32_t offset = is_manifest + ? content_object.headerSize() + : content_object.headerSize() + rtc::DATA_HEADER_SIZE; + uint32_t metadata = static_cast<uint32_t>(content_object.getPayloadType()); + + fec_decoder_->onDataPacket(content_object, offset, metadata); + } + + // We can return early if FEC + if (is_fec) { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received FEC " << segment_number; + state_->onFecPacketReceived(content_object); + onDataPacketReceived(*content_ptr, compute_stats); + return; + } + + // The packet may have been already sent to the app by the decoder, check + // again if it is already received + state = state_->getPacketState( + segment_number); // state == RECEIVED or TO_BE_RECEIVED + + if (state != PacketState::RECEIVED) { + DLOG_IF(INFO, VLOG_IS_ON(4)) + << (is_manifest ? "Received manifest " : "Received data ") + << segment_number; + + if (is_manifest) { + processManifest(interest, *content_ptr); + } + + state_->onDataPacketReceived(*content_ptr, compute_stats); + + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } + + ec = is_manifest ? make_error_code(protocol_error::not_reassemblable) + : make_error_code(protocol_error::success); + } + + onDataPacketReceived(*content_ptr, compute_stats); } void RTCTransportProtocol::sendStatsToApp( @@ -729,33 +911,149 @@ void RTCTransportProtocol::sendStatsToApp( stats_->updateReceivedNacks(received_nacks); stats_->updateReceivedFEC(received_fec); - stats_->updateAverageWindowSize(current_sync_win_); - stats_->updateLossRatio(state_->getLossRate()); - stats_->updateAverageRtt(state_->getRTT()); + stats_->updateAverageWindowSize(state_->getPendingInterestNumber()); + stats_->updateLossRatio(state_->getPerSecondLossRate()); + uint64_t rtt = state_->getAvgRTT(); + stats_->updateAverageRtt(utils::SteadyTime::Milliseconds(rtt)); + stats_->updateQueuingDelay(state_->getQueuing()); stats_->updateLostData(lost_data); stats_->updateDefinitelyLostData(definitely_lost); stats_->updateRecoveredData(recovered_losses); stats_->updateCCState((unsigned int)current_state_ ? 1 : 0); (*stats_summary_)(*socket_->getInterface(), *stats_); + bool in_congestion = rc_->inCongestionState(); + stats_->updateCongestionState(in_congestion); + double residual_losses = state_->getResidualLossRate(); + stats_->updateResidualLossRate(residual_losses); + stats_->updateQualityScore(state_->getQualityScore()); + + // set alerts + if (rtt > MAX_RTT) + stats_->setAlert(interface::TransportStatistics::statsAlerts::LATENCY); + else + stats_->clearAlert(interface::TransportStatistics::statsAlerts::LATENCY); + + if (in_congestion) + stats_->setAlert(interface::TransportStatistics::statsAlerts::CONGESTION); + else + stats_->clearAlert( + interface::TransportStatistics::statsAlerts::CONGESTION); + + if (residual_losses > MAX_RESIDUAL_LOSSES) + stats_->setAlert(interface::TransportStatistics::statsAlerts::LOSSES); + else + stats_->clearAlert(interface::TransportStatistics::statsAlerts::LOSSES); } } -void RTCTransportProtocol::onFecPackets( - std::vector<std::pair<uint32_t, fec::buffer>> &packets) { +void RTCTransportProtocol::onFecPackets(fec::BufferArray &packets) { + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + + Name *name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + for (auto &packet : packets) { - PacketState state = state_->isReceivedOrLost(packet.first); - if (state != PacketState::RECEIVED) { - state_->onPacketRecoveredFec(packet.first); - ldr_->onPacketRecoveredFec(packet.first); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "Recovered packet " << packet.first << " through FEC."; - reassembly_->reassemble(*packet.second, packet.first); - } else { - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "Packet" << packet.first << "already received."; + uint32_t seq_number = packet.getIndex(); + uint32_t metadata = packet.getMetadata(); + fec::buffer buffer = packet.getBuffer(); + + PayloadType payload_type = static_cast<PayloadType>(metadata); + switch (payload_type) { + case PayloadType::DATA: + case PayloadType::MANIFEST: + break; + case PayloadType::UNSPECIFIED: + default: + payload_type = PayloadType::DATA; + break; } + + switch (state_->getPacketState(seq_number)) { + case PacketState::RECEIVED: + case PacketState::TO_BE_RECEIVED: { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet " << seq_number << " already received"; + break; + } + default: { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Recovered packet " << seq_number << " through FEC"; + + if (payload_type == PayloadType::MANIFEST) { + name->setSuffix(seq_number); + + auto interest = + core::PacketManager<>::getInstance().getPacket<Interest>(format); + interest->setName(*name); + + auto content_object = toContentObject( + *name, format, payload_type, buffer->data(), buffer->length()); + + processManifest(*interest, *content_object); + } + + state_->onPacketRecoveredFec(seq_number, buffer->length()); + ldr_->onPacketRecoveredFec(seq_number); + + if (payload_type == PayloadType::DATA) { + verifier_->onDataRecoveredFec(seq_number); + reassembly_->reassemble(*buffer, seq_number); + } + + break; + } + } + } +} + +void RTCTransportProtocol::processManifest(Interest &interest, + ContentObject &manifest) { + auth::VerificationPolicy policy = verifier_->processManifest(manifest); + indexer_verifier_->applyPolicy(interest, manifest, false, policy); +} + +ContentObject::Ptr RTCTransportProtocol::removeFecHeader( + const ContentObject &content_object) { + if (!fec_decoder_ || !fec_decoder_->getFecHeaderSize()) { + return nullptr; } + + size_t fec_header_size = fec_decoder_->getFecHeaderSize(); + const uint8_t *payload = + content_object.data() + content_object.headerSize() + fec_header_size; + size_t payload_size = content_object.payloadSize() - fec_header_size; + + ContentObject::Ptr co = + toContentObject(content_object.getName(), content_object.getFormat(), + content_object.getPayloadType(), payload, payload_size); + + return co; +} + +ContentObject::Ptr RTCTransportProtocol::toContentObject( + const Name &name, Packet::Format format, PayloadType payload_type, + const uint8_t *payload, std::size_t payload_size, + std::size_t additional_header_size) { + // Recreate ContentObject + ContentObject::Ptr co = + core::PacketManager<>::getInstance().getPacket<ContentObject>( + format, additional_header_size); + co->updateLength(payload_size); + co->append(payload_size); + co->trimStart(co->headerSize()); + + // Copy payload + std::memcpy(co->writableData(), payload, payload_size); + + // Restore network headers and some fields + co->prepend(co->headerSize()); + co->setName(name); + co->setPayloadType(payload_type); + + return co; } } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc.h b/libtransport/src/protocols/rtc/rtc.h index e6431264d..37706eb1c 100644 --- a/libtransport/src/protocols/rtc/rtc.h +++ b/libtransport/src/protocols/rtc/rtc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,10 +15,12 @@ #pragma once -#include <protocols/datagram_reassembly.h> +#include <protocols/rtc/rtc_forwarding_strategy.h> #include <protocols/rtc/rtc_ldr.h> #include <protocols/rtc/rtc_rc.h> +#include <protocols/rtc/rtc_reassembly.h> #include <protocols/rtc/rtc_state.h> +#include <protocols/rtc/rtc_verifier.h> #include <protocols/transport_protocol.h> #include <unordered_set> @@ -44,6 +46,8 @@ class RTCTransportProtocol : public TransportProtocol { std::size_t transportHeaderLength() override; + auto shared_from_this() { return utils::shared_from(this); } + private: enum class SyncState { catch_up = 0, in_sync = 1, last }; @@ -76,6 +80,7 @@ class RTCTransportProtocol : public TransportProtocol { void onPacketDropped(Interest &interest, ContentObject &content_object, const std::error_code &reason) override {} void onReassemblyFailed(std::uint32_t missing_segment) override {} + void processManifest(Interest &interest, ContentObject &manifest); // interaction with app functions void sendStatsToApp(uint32_t retx_count, uint32_t received_bytes, @@ -84,11 +89,20 @@ class RTCTransportProtocol : public TransportProtocol { uint32_t received_nacks, uint32_t received_fec); // FEC functions - void onFecPackets(std::vector<std::pair<uint32_t, fec::buffer>> &packets); + void onFecPackets(fec::BufferArray &packets); + + // Utils + ContentObject::Ptr removeFecHeader(const ContentObject &content_object); + ContentObject::Ptr toContentObject(const Name &name, Packet::Format format, + PayloadType payload_type, + const uint8_t *payload, + std::size_t payload_size, + std::size_t additional_header_size = 0); // protocol state bool start_send_interest_; SyncState current_state_; + // cwin vars uint32_t current_sync_win_; uint32_t max_sync_win_; @@ -120,6 +134,10 @@ class RTCTransportProtocol : public TransportProtocol { std::shared_ptr<RTCState> state_; std::shared_ptr<RTCRateControl> rc_; std::shared_ptr<RTCLossDetectionAndRecovery> ldr_; + std::shared_ptr<RTCVerifier> verifier_; + + // forwarding strategy selection + RTCForwardingStrategy fwd_strategy_; uint32_t number_; }; diff --git a/libtransport/src/protocols/rtc/rtc_consts.h b/libtransport/src/protocols/rtc/rtc_consts.h index d04bc1b1f..03efd8e84 100644 --- a/libtransport/src/protocols/rtc/rtc_consts.h +++ b/libtransport/src/protocols/rtc/rtc_consts.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -45,22 +45,22 @@ const uint32_t MAX_INTERESTS_IN_BATCH = 5; // number of seq numbers per const uint32_t WAIT_FOR_INTEREST_BATCH = 20; // msec. timer that we wait to try // to aggregate interest in the // same packet -const uint32_t MAX_PACING_BATCH = 5; -// number of interest that we can send inside -// the loop before they get dropped by the -// kernel. +const uint32_t MAX_PACING_BATCH = 5; // number of interest that we can send + // inside the loop before they get dropped + // by the kernel. const uint32_t PACING_WAIT = 1000; // usec to wait betwing two pacing batch. As // for MAX_PACING_BATCH this value was // computed during tests const uint32_t MAX_RTX_IN_BATCH = 10; // max rtx to send in loop // packet const -const uint32_t HICN_HEADER_SIZE = 40 + 20; // IPv6 + TCP bytes const uint32_t RTC_INTEREST_LIFETIME = 2000; // probes sequence range const uint32_t MIN_PROBE_SEQ = 0xefffffff; -const uint32_t MIN_RTT_PROBE_SEQ = MIN_PROBE_SEQ; +const uint32_t MIN_INIT_PROBE_SEQ = MIN_PROBE_SEQ; +const uint32_t MAX_INIT_PROBE_SEQ = 0xf7ffffff - 1; +const uint32_t MIN_RTT_PROBE_SEQ = MAX_INIT_PROBE_SEQ + 1; const uint32_t MAX_RTT_PROBE_SEQ = 0xffffffff - 1; // RTT_PROBE_INTERVAL will be used during the section while // INIT_RTT_PROBE_INTERVAL is used at the beginning to @@ -78,15 +78,16 @@ const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; // ms const uint32_t MAX_PENDING_PROBES = 10; // congestion -const double MAX_QUEUING_DELAY = 100.0; // ms +const double MAX_QUEUING_DELAY = 50.0; // ms // data from cache const double MAX_DATA_FROM_CACHE = 0.25; // 25% // window const -const uint32_t INITIAL_WIN = 5; // pkts -const uint32_t INITIAL_WIN_MAX = 1000000; // pkts -const uint32_t WIN_MIN = 5; // pkts +const uint32_t INITIAL_WIN = 5; // pkts +const uint32_t INITIAL_WIN_MAX = 1000000; // pkts +const uint32_t WIN_MIN = 5; // pkts +const uint32_t WIN_MIN_WITH_AGGREGARED_DATA = 10; // pkts const double CATCH_UP_WIN_INCREMENT = 1.2; // used in rate control const double WIN_DECREASE_FACTOR = 0.5; @@ -105,10 +106,8 @@ const double MOVING_AVG_ALPHA = 0.8; const double MILLI_IN_A_SEC = 1000.0; const double MICRO_IN_A_SEC = 1000000.0; - -const double MAX_CACHED_PACKETS = 262144; // 2^18 - // about 50 sec of traffic at 50Mbps - // with 1200 bytes packets +const uint32_t ROUNDS_PER_SEC = (uint32_t)(MILLI_IN_A_SEC / ROUND_LEN); +const uint32_t ROUNDS_PER_MIN = (uint32_t)ROUNDS_PER_SEC * 60; const uint32_t MAX_ROUND_WHIOUT_PACKETS = (20 * MILLI_IN_A_SEC) / ROUND_LEN; // 20 sec in rounds; @@ -120,12 +119,24 @@ const uint64_t MAX_TIMER_RTX = ~0; const uint32_t SENTINEL_TIMER_INTERVAL = 100; // ms const uint32_t MAX_RTX_WITH_SENTINEL = 10; // packets const double CATCH_UP_RTT_INCREMENT = 1.2; +const double MAX_RESIDUAL_LOSS_RATE = 2.0; // % +const uint32_t WAIT_BEFORE_FEC_UPDATE = ROUNDS_PER_SEC * 5; // used by producer const uint32_t PRODUCER_STATS_INTERVAL = 200; // ms -const uint32_t MIN_PRODUCTION_RATE = 10; // pps - // min prod rate - // set running several test +const uint32_t MIN_PRODUCTION_RATE = 25; // pps, equal to min window * + // rounds in a second +const uint32_t NACK_DELAY = 1500; // ms +const uint32_t FEC_PACING_TIME = 5; // ms + +// aggregated data consts +const uint16_t MAX_RTC_PAYLOAD_SIZE = 1200; // bytes +const uint16_t MAX_AGGREGATED_PACKETS = 5; // pkt +const uint32_t AGGREGATED_PACKETS_TIMER = 2; // ms + +// alert thresholds +const uint32_t MAX_RTT = 200; // ms +const double MAX_RESIDUAL_LOSSES = 0.05; // % } // namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_data_path.cc b/libtransport/src/protocols/rtc/rtc_data_path.cc index c098088a3..b3abf5ea8 100644 --- a/libtransport/src/protocols/rtc/rtc_data_path.cc +++ b/libtransport/src/protocols/rtc/rtc_data_path.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -13,14 +13,17 @@ * limitations under the License. */ +#include <hicn/transport/utils/chrono_typedefs.h> #include <protocols/rtc/rtc_data_path.h> #include <stdlib.h> #include <algorithm> #include <cfloat> #include <chrono> +#include <cmath> #define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec +#define AVG_RTT_TIME 1000 // (ms) 1sec namespace transport { @@ -32,6 +35,8 @@ RTCDataPath::RTCDataPath(uint32_t path_id) : path_id_(path_id), min_rtt(UINT_MAX), prev_min_rtt(UINT_MAX), + max_rtt(0), + prev_max_rtt(0), min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the // real OWD, but the measured one, that depends on the // clock of sender and receiver. the only meaningful @@ -46,20 +51,46 @@ RTCDataPath::RTCDataPath(uint32_t path_id) largest_recv_seq_(0), largest_recv_seq_time_(0), avg_inter_arrival_(DBL_MAX), + rtt_sum_(0), + last_avg_rtt_compute_(0), + rtt_samples_(0), + avg_rtt_(0.0), received_nacks_(false), - received_packets_(false), + received_packets_(0), rounds_without_packets_(0), last_received_data_packet_(0), - RTT_history_(HISTORY_LEN), + min_RTT_history_(HISTORY_LEN), + max_RTT_history_(HISTORY_LEN), OWD_history_(HISTORY_LEN){}; -void RTCDataPath::insertRttSample(uint64_t rtt) { - // for the rtt we only keep track of the min one +void RTCDataPath::insertRttSample( + const utils::SteadyTime::Milliseconds& rtt_milliseconds, bool is_probe) { + // compute min rtt + uint64_t rtt = rtt_milliseconds.count(); if (rtt < min_rtt) min_rtt = rtt; - last_received_data_packet_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + + uint64_t now = utils::SteadyTime::nowMs().count(); + last_received_data_packet_ = now; + + // compute avg rtt + if (is_probe) { + // max rtt is computed only on probes to avoid to take into account the + // production time at the server + if (rtt > max_rtt) max_rtt = rtt; + + rtt_sum_ += rtt; + rtt_samples_++; + } + + if ((now - last_avg_rtt_compute_) >= AVG_RTT_TIME) { + // compute a new avg rtt + // if rtt_samples_ = 0 keep the same rtt + if (rtt_samples_ != 0) avg_rtt_ = (double)rtt_sum_ / (double)rtt_samples_; + + rtt_sum_ = 0; + rtt_samples_ = 0; + last_avg_rtt_compute_ = now; + } } void RTCDataPath::insertOwdSample(int64_t owd) { @@ -87,15 +118,13 @@ void RTCDataPath::insertOwdSample(int64_t owd) { // owd is computed only for valid data packets so we count only // this for decide if we recevie traffic or not - received_packets_ = true; + received_packets_++; } void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) { // got packet in sequence, compute gap if (largest_recv_seq_ == (segment_number - 1)) { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t delta = now - largest_recv_seq_time_; largest_recv_seq_ = segment_number; largest_recv_seq_time_ = now; @@ -110,10 +139,7 @@ void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) { // ooo packet, update the stasts if needed if (largest_recv_seq_ <= segment_number) { largest_recv_seq_ = segment_number; - largest_recv_seq_time_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + largest_recv_seq_time_ = utils::SteadyTime::nowMs().count(); } } @@ -146,10 +172,20 @@ void RTCDataPath::roundEnd() { min_rtt = prev_min_rtt; } + // same for max_rtt + if (max_rtt != 0) { + prev_max_rtt = max_rtt; + } else { + max_rtt = prev_max_rtt; + } + if (min_rtt == 0) min_rtt = 1; + if (max_rtt == 0) max_rtt = 1; - RTT_history_.pushBack(min_rtt); + min_RTT_history_.pushBack(min_rtt); + max_RTT_history_.pushBack(max_rtt); min_rtt = UINT_MAX; + max_rtt = 0; // do the same for min owd if (min_owd != INT_MAX) { @@ -163,32 +199,47 @@ void RTCDataPath::roundEnd() { min_owd = INT_MAX; } - if (!received_packets_) + if (received_packets_ == 0) rounds_without_packets_++; else rounds_without_packets_ = 0; - received_packets_ = false; + received_packets_ = 0; } uint32_t RTCDataPath::getPathId() { return path_id_; } -double RTCDataPath::getQueuingDealy() { return queuing_delay; } +double RTCDataPath::getQueuingDealy() { + if (queuing_delay == DBL_MAX) return 0; + return queuing_delay; +} uint64_t RTCDataPath::getMinRtt() { - if (RTT_history_.size() != 0) return RTT_history_.begin(); + if (min_RTT_history_.size() != 0) return min_RTT_history_.begin(); + return 0; +} + +uint64_t RTCDataPath::getAvgRtt() { return std::round(avg_rtt_); } + +uint64_t RTCDataPath::getMaxRtt() { + if (max_RTT_history_.size() != 0) return max_RTT_history_.begin(); return 0; } int64_t RTCDataPath::getMinOwd() { if (OWD_history_.size() != 0) return OWD_history_.begin(); - return 0; + return INT_MAX; } double RTCDataPath::getJitter() { return jitter_; } uint64_t RTCDataPath::getLastPacketTS() { return last_received_data_packet_; } -void RTCDataPath::clearRtt() { RTT_history_.clear(); } +uint32_t RTCDataPath::getPacketsLastRound() { return received_packets_; } + +void RTCDataPath::clearRtt() { + min_RTT_history_.clear(); + max_RTT_history_.clear(); +} } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_data_path.h b/libtransport/src/protocols/rtc/rtc_data_path.h index c5c37fc0d..5afbbb87f 100644 --- a/libtransport/src/protocols/rtc/rtc_data_path.h +++ b/libtransport/src/protocols/rtc/rtc_data_path.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,7 +15,9 @@ #pragma once +#include <hicn/transport/utils/chrono_typedefs.h> #include <stdint.h> +#include <utils/max_filter.h> #include <utils/min_filter.h> #include <climits> @@ -34,19 +36,23 @@ class RTCDataPath { RTCDataPath(uint32_t path_id); public: - void insertRttSample(uint64_t rtt); + void insertRttSample(const utils::SteadyTime::Milliseconds &rtt, + bool is_probe); void insertOwdSample(int64_t owd); void computeInterArrivalGap(uint32_t segment_number); void receivedNack(); uint32_t getPathId(); uint64_t getMinRtt(); + uint64_t getAvgRtt(); + uint64_t getMaxRtt(); double getQueuingDealy(); double getInterArrivalGap(); double getJitter(); bool isActive(); bool pathToProducer(); uint64_t getLastPacketTS(); + uint32_t getPacketsLastRound(); void clearRtt(); @@ -60,6 +66,9 @@ class RTCDataPath { uint64_t min_rtt; uint64_t prev_min_rtt; + uint64_t max_rtt; + uint64_t prev_max_rtt; + int64_t min_owd; int64_t prev_min_owd; @@ -74,19 +83,26 @@ class RTCDataPath { uint64_t largest_recv_seq_time_; double avg_inter_arrival_; + // compute the avg rtt over one sec + uint64_t rtt_sum_; + uint64_t last_avg_rtt_compute_; + uint32_t rtt_samples_; + double avg_rtt_; + // flags to check if a path is active // we considere a path active if it reaches a producer //(not a cache) --aka we got at least one nack on this path-- // and if we receives packets bool received_nacks_; - bool received_packets_; - uint8_t rounds_without_packets_; // if we don't get any packet + uint32_t received_packets_; + uint32_t rounds_without_packets_; // if we don't get any packet // for MAX_ROUNDS_WITHOUT_PKTS // we consider the path inactive uint64_t last_received_data_packet_; // timestamp for the last data received // on this path - utils::MinFilter<uint64_t> RTT_history_; + utils::MinFilter<uint64_t> min_RTT_history_; + utils::MaxFilter<uint64_t> max_RTT_history_; utils::MinFilter<int64_t> OWD_history_; }; diff --git a/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc new file mode 100644 index 000000000..9503eed3e --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 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 <hicn/transport/interfaces/notification.h> +#include <protocols/rtc/rtc_forwarding_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace transport::interface; + +RTCForwardingStrategy::RTCForwardingStrategy() + : init_(false), + forwarder_set_(false), + selected_strategy_(NONE), + current_strategy_(NONE), + rounds_since_last_set_(0), + portal_(nullptr), + state_(nullptr) {} + +RTCForwardingStrategy::~RTCForwardingStrategy() {} + +void RTCForwardingStrategy::setCallback(interface::StrategyCallback* callback) { + callback_ = callback; +} + +void RTCForwardingStrategy::initFwdStrategy( + std::shared_ptr<core::Portal> portal, core::Prefix& prefix, RTCState* state, + strategy_t strategy) { + init_ = true; + selected_strategy_ = strategy; + if (strategy == BOTH) + current_strategy_ = BEST_PATH; + else + current_strategy_ = strategy; + rounds_since_last_set_ = 0; + prefix_ = prefix; + portal_ = portal; + state_ = state; +} + +void RTCForwardingStrategy::checkStrategy() { + if (*callback_) { + strategy_t used_strategy = selected_strategy_; + if (used_strategy == BOTH) used_strategy = current_strategy_; + assert(used_strategy == BEST_PATH || used_strategy == REPLICATION || + used_strategy == NONE); + + notification::ForwardingStrategy strategy = + notification::ForwardingStrategy::NONE; + switch (used_strategy) { + case BEST_PATH: + strategy = notification::ForwardingStrategy::BEST_PATH; + break; + case REPLICATION: + strategy = notification::ForwardingStrategy::REPLICATION; + break; + default: + break; + } + + (*callback_)(strategy); + } + + if (!init_) return; + + if (selected_strategy_ == NONE) return; + + if (selected_strategy_ == BEST_PATH) { + checkStrategyBestPath(); + return; + } + + if (selected_strategy_ == REPLICATION) { + checkStrategyReplication(); + return; + } + + checkStrategyBoth(); +} + +void RTCForwardingStrategy::checkStrategyBestPath() { + if (!forwarder_set_) { + setStrategy(BEST_PATH); + forwarder_set_ = true; + return; + } + + uint8_t qs = state_->getQualityScore(); + + if (qs >= 4 || rounds_since_last_set_ < 25) { // wait a least 5 sec + // between each switch + rounds_since_last_set_++; + return; + } + + // try to switch path + setStrategy(BEST_PATH); +} + +void RTCForwardingStrategy::checkStrategyReplication() { + if (!forwarder_set_) { + setStrategy(REPLICATION); + forwarder_set_ = true; + return; + } + + // here we have nothing to do for the moment + return; +} + +void RTCForwardingStrategy::checkStrategyBoth() { + if (!forwarder_set_) { + setStrategy(current_strategy_); + forwarder_set_ = true; + return; + } + + checkStrategyBestPath(); + + // TODO + // for the moment we use only best path. + // but later: + // 1. if both paths are bad use replication + // 2. while using replication compute the effectiveness. if the majority of + // the packets are coming from a single path, try to use bestpath +} + +void RTCForwardingStrategy::setStrategy(strategy_t strategy) { + rounds_since_last_set_ = 0; + current_strategy_ = strategy; + portal_->setForwardingStrategy(prefix_, + string_strategies_[current_strategy_]); +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h new file mode 100644 index 000000000..821b28051 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +#include <core/portal.h> +#include <hicn/transport/interfaces/callbacks.h> +#include <protocols/rtc/rtc_state.h> + +#include <array> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCForwardingStrategy { + public: + enum strategy_t { + BEST_PATH, + REPLICATION, + BOTH, + NONE, + }; + + RTCForwardingStrategy(); + ~RTCForwardingStrategy(); + + void initFwdStrategy(std::shared_ptr<core::Portal> portal, + core::Prefix& prefix, RTCState* state, + strategy_t strategy); + + void checkStrategy(); + void setCallback(interface::StrategyCallback* callback); + + private: + void checkStrategyBestPath(); + void checkStrategyReplication(); + void checkStrategyBoth(); + + void setStrategy(strategy_t strategy); + + std::array<std::string, 4> string_strategies_ = {"bestpath", "replication", + "both", "none"}; + + bool init_; // true if all val are initializes + bool forwarder_set_; // true if the strategy is been set at least + // once + strategy_t selected_strategy_; // this is the strategy selected using socket + // options. this can also be equal to BOTH + strategy_t current_strategy_; // if both strategies can be used this + // indicates the one that is currently in use + // that can be only replication or best path + uint32_t rounds_since_last_set_; + core::Prefix prefix_; + std::shared_ptr<core::Portal> portal_; + RTCState* state_; + interface::StrategyCallback* callback_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_indexer.h b/libtransport/src/protocols/rtc/rtc_indexer.h index 4aee242bb..cda156b22 100644 --- a/libtransport/src/protocols/rtc/rtc_indexer.h +++ b/libtransport/src/protocols/rtc/rtc_indexer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -18,8 +18,10 @@ #include <protocols/errors.h> #include <protocols/fec_utils.h> #include <protocols/indexer.h> +#include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_consts.h> #include <protocols/transport_protocol.h> +#include <utils/suffix_strategy.h> #include <deque> @@ -54,7 +56,7 @@ class RtcIndexer : public Indexer { n_fec_ = 0; } - uint32_t checkNextSuffix() override { return next_suffix_; } + uint32_t checkNextSuffix() const override { return next_suffix_; } uint32_t getNextSuffix() override { if (isFec(next_suffix_)) { @@ -77,7 +79,7 @@ class RtcIndexer : public Indexer { first_suffix_ = suffix % LIMIT; } - uint32_t getFirstSuffix() override { return first_suffix_; } + uint32_t getFirstSuffix() const override { return first_suffix_; } uint32_t jumpToIndex(uint32_t index) override { next_suffix_ = index % LIMIT; @@ -87,30 +89,8 @@ class RtcIndexer : public Indexer { void onContentObject(core::Interest &interest, core::ContentObject &content_object, bool reassembly) override { - setVerifier(); - auto ret = verifier_->verifyPackets(&content_object); - - switch (ret) { - case auth::VerificationPolicy::ACCEPT: { - if (reassembly) { - reassembly_->reassemble(content_object); - } - break; - } - - case auth::VerificationPolicy::UNKNOWN: - case auth::VerificationPolicy::DROP: { - transport_->onPacketDropped( - interest, content_object, - make_error_code(protocol_error::verification_failed)); - break; - } - - case auth::VerificationPolicy::ABORT: { - transport_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } + if (reassembly) { + reassembly_->reassemble(content_object); } } @@ -120,13 +100,12 @@ class RtcIndexer : public Indexer { uint32_t getNextReassemblySegment() override { throw errors::RuntimeException( "Get reassembly segment called on rtc indexer. RTC indexer does not " - "provide " - "reassembly."); + "provide reassembly."); } bool isFinalSuffixDiscovered() override { return true; } - uint32_t getFinalSuffix() override { return LIMIT; } + uint32_t getFinalSuffix() const override { return LIMIT; } void enableFec(fec::FECType fec_type) override { fec_type_ = fec_type; } @@ -137,13 +116,13 @@ class RtcIndexer : public Indexer { n_current_fec_ = n_fec_; } - uint32_t getNFec() override { return n_fec_; } + uint32_t getNFec() const override { return n_fec_; } bool isFec(uint32_t index) override { return isFec(fec_type_, index, first_suffix_); } - double getFecOverhead() override { + double getFecOverhead() const override { if (fec_type_ == fec::FECType::UNKNOWN) { return 0; } @@ -152,7 +131,7 @@ class RtcIndexer : public Indexer { return (double)n_fec_ / k; } - double getMaxFecOverhead() override { + double getMaxFecOverhead() const override { if (fec_type_ == fec::FECType::UNKNOWN) { return 0; } diff --git a/libtransport/src/protocols/rtc/rtc_ldr.cc b/libtransport/src/protocols/rtc/rtc_ldr.cc index f0de48871..1ca1cf48d 100644 --- a/libtransport/src/protocols/rtc/rtc_ldr.cc +++ b/libtransport/src/protocols/rtc/rtc_ldr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,12 @@ #include <glog/logging.h> #include <protocols/rtc/rtc_consts.h> #include <protocols/rtc/rtc_ldr.h> +#include <protocols/rtc/rtc_rs_delay.h> +#include <protocols/rtc/rtc_rs_fec_only.h> +#include <protocols/rtc/rtc_rs_low_rate.h> +#include <protocols/rtc/rtc_rs_recovery_off.h> +#include <protocols/rtc/rtc_rs_rtx_only.h> +#include <protocols/rtc/rtc_state.h> #include <algorithm> #include <unordered_set> @@ -27,146 +33,115 @@ namespace protocol { namespace rtc { RTCLossDetectionAndRecovery::RTCLossDetectionAndRecovery( - Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service) - : rtx_on_(false), - fec_on_(false), - next_rtx_timer_(MAX_TIMER_RTX), - last_event_(0), - sentinel_timer_interval_(MAX_TIMER_RTX), - indexer_(indexer), - send_rtx_callback_(std::move(callback)) { - timer_ = std::make_unique<asio::steady_timer>(io_service); - sentinel_timer_ = std::make_unique<asio::steady_timer>(io_service); + Indexer *indexer, asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies type, + RecoveryStrategy::SendRtxCallback &&callback, + interface::StrategyCallback *external_callback) { + rs_type_ = type; + if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) { + rs_ = std::make_shared<RecoveryStrategyRecoveryOff>( + indexer, std::move(callback), io_service, external_callback); + } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED) { + rs_ = std::make_shared<RecoveryStrategyDelayBased>( + indexer, std::move(callback), io_service, external_callback); + } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY) { + rs_ = std::make_shared<RecoveryStrategyFecOnly>( + indexer, std::move(callback), io_service, external_callback); + } else if (type == interface::RtcTransportRecoveryStrategies::LOW_RATE || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_BESTPATH || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_REPLICATION || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES) { + rs_ = std::make_shared<RecoveryStrategyLowRate>( + indexer, std::move(callback), io_service, external_callback); + } else { + // default + rs_type_ = interface::RtcTransportRecoveryStrategies::RTX_ONLY; + rs_ = std::make_shared<RecoveryStrategyRtxOnly>( + indexer, std::move(callback), io_service, external_callback); + } } RTCLossDetectionAndRecovery::~RTCLossDetectionAndRecovery() {} -void RTCLossDetectionAndRecovery::turnOnRTX() { - rtx_on_ = true; - scheduleSentinelTimer(state_->getRTT() * CATCH_UP_RTT_INCREMENT); -} - -void RTCLossDetectionAndRecovery::turnOffRTX() { - rtx_on_ = false; - clear(); -} - -uint32_t RTCLossDetectionAndRecovery::computeFecPacketsToAsk(bool in_sync) { - uint32_t current_fec = indexer_->getNFec(); - double current_loss_rate = state_->getLossRate(); - double last_loss_rate = state_->getLastRoundLossRate(); - - // when in sync ask for fec only if there are losses for 2 rounds - if (in_sync && current_fec == 0 && - (current_loss_rate == 0 || last_loss_rate == 0)) - return 0; - - double loss_rate = state_->getMaxLossRate() * 1.5; - - if (!in_sync && loss_rate == 0) loss_rate = 0.05; - if (loss_rate > 0.5) loss_rate = 0.5; - - double exp_losses = (double)k_ * loss_rate; - uint32_t fec_to_ask = ceil(exp_losses / (1 - loss_rate)); - - if (fec_to_ask > (n_ - k_)) fec_to_ask = n_ - k_; - - return fec_to_ask; +void RTCLossDetectionAndRecovery::changeRecoveryStrategy( + interface::RtcTransportRecoveryStrategies type) { + if (type == rs_type_) return; + + rs_type_ = type; + if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) { + rs_ = + std::make_shared<RecoveryStrategyRecoveryOff>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED) { + rs_ = std::make_shared<RecoveryStrategyDelayBased>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY) { + rs_ = std::make_shared<RecoveryStrategyFecOnly>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::LOW_RATE || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_BESTPATH || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_REPLICATION || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES) { + rs_ = std::make_shared<RecoveryStrategyLowRate>(std::move(*(rs_.get()))); + } else { + // default + rs_ = std::make_shared<RecoveryStrategyRtxOnly>(std::move(*(rs_.get()))); + } } void RTCLossDetectionAndRecovery::onNewRound(bool in_sync) { - uint64_t rtt = state_->getRTT(); - if (!fec_on_ && rtt >= 100) { - // turn on fec, here we may have no info so ask for all packets - fec_on_ = true; - turnOffRTX(); - indexer_->setNFec(computeFecPacketsToAsk(in_sync)); - return; - } - - if (fec_on_ && rtt > 80) { - // keep using fec, maybe update it - indexer_->setNFec(computeFecPacketsToAsk(in_sync)); - return; - } - - if ((fec_on_ && rtt <= 80) || (!rtx_on_ && rtt <= 100)) { - // turn on rtx - fec_on_ = false; - indexer_->setNFec(0); - turnOnRTX(); - return; - } + rs_->incRoundId(); + rs_->onNewRound(in_sync); } -void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq) { - // always add timeouts to the RTX list to avoid to send the same packet as if - // it was not a rtx - addToRetransmissions(seq, seq + 1); - last_event_ = getNow(); +void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq, bool lost) { + if (!lost) { + detectLoss(seq, seq + 1); + } else { + rs_->onLostTimeout(seq); + } } void RTCLossDetectionAndRecovery::onPacketRecoveredFec(uint32_t seq) { - // if an RTX is scheduled for a packet recovered using FEC delete it - deleteRtx(seq); - recover_with_fec_.erase(seq); + rs_->receivedPacket(seq); } void RTCLossDetectionAndRecovery::onDataPacketReceived( const core::ContentObject &content_object) { - last_event_ = getNow(); - uint32_t seq = content_object.getName().getSuffix(); - if (deleteRtx(seq)) { - state_->onPacketRecoveredRtx(seq); - } else { - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "received data. add from " - << state_->getHighestSeqReceivedInOrder() + 1 << " to " << seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, seq); - } + bool is_rtx = rs_->isRtx(seq); + rs_->receivedPacket(seq); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "received data. add from " + << rs_->getState()->getHighestSeqReceivedInOrder() + 1 << " to " << seq; + if (!is_rtx) + detectLoss(rs_->getState()->getHighestSeqReceivedInOrder() + 1, seq); } void RTCLossDetectionAndRecovery::onNackPacketReceived( const core::ContentObject &nack) { - last_event_ = getNow(); - - uint32_t seq = nack.getName().getSuffix(); - struct nack_packet_t *nack_pkt = (struct nack_packet_t *)nack.getPayload()->data(); - uint32_t production_seq = nack_pkt->getProductionSegement(); + uint32_t production_seq = nack_pkt->getProductionSegment(); + uint32_t seq = nack.getName().getSuffix(); - if (production_seq > seq) { - // this is a past nack, all data before productionSeq are lost. if - // productionSeq > state_->getHighestSeqReceivedInOrder() is impossible to - // recover any packet. If this is not the case we can try to recover the - // packets between state_->getHighestSeqReceivedInOrder() and productionSeq. - // e.g.: the client receives packets 8 10 11 9 where 9 is a nack with - // productionSeq = 14. 9 is lost but we can try to recover packets 12 13 and - // 14 that are not arrived yet - deleteRtx(seq); - DLOG_IF(INFO, VLOG_IS_ON(3)) << "received past nack. add from " - << state_->getHighestSeqReceivedInOrder() + 1 - << " to " << production_seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, - production_seq); - } else { - // future nack. here there should be a gap between the last data received - // and this packet and is it possible to recover the packets between the - // last received data and the production seq. we should not use the seq - // number of the nack since we know that is too early to ask for this seq - // number - // e.g.: // e.g.: the client receives packets 10 11 12 20 where 20 is a nack - // with productionSeq = 18. this says that all the packets between 12 and 18 - // may got lost and we should ask them - deleteRtx(seq); - DLOG_IF(INFO, VLOG_IS_ON(3)) << "received futrue nack. add from " - << state_->getHighestSeqReceivedInOrder() + 1 - << " to " << production_seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, - production_seq); - } + // received a nack. we can try to recover all data packets between the last + // received data and the production seq in the nack. this is similar to the + // recption of a probe + // e.g.: the client receives packets 10 11 12 20 where 20 is a nack + // with productionSeq = 18. this says that all the packets between 12 and 18 + // may got lost and we should ask them + + rs_->receivedPacket(seq); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "received nack. add from " + << rs_->getState()->getHighestSeqReceivedInOrder() + 1 << " to " + << production_seq; + detectLoss(rs_->getState()->getHighestSeqReceivedInOrder() + 1, + production_seq); } void RTCLossDetectionAndRecovery::onProbePacketReceived( @@ -174,336 +149,38 @@ void RTCLossDetectionAndRecovery::onProbePacketReceived( // we don't log the reception of a probe packet for the sentinel timer because // probes are not taken into account into the sync window. we use them as // future nacks to detect possible packets lost - struct nack_packet_t *probe_pkt = - (struct nack_packet_t *)probe.getPayload()->data(); - uint32_t production_seq = probe_pkt->getProductionSegement(); + + uint32_t production_seq = RTCState::getProbeParams(probe).prod_seg; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "received probe. add from " - << state_->getHighestSeqReceivedInOrder() + 1 << " to " << production_seq; + << rs_->getState()->getHighestSeqReceivedInOrder() + 1 << " to " + << production_seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, - production_seq); + detectLoss(rs_->getState()->getHighestSeqReceivedInOrder() + 1, + production_seq); } -void RTCLossDetectionAndRecovery::clear() { - rtx_state_.clear(); - rtx_timers_.clear(); - sentinel_timer_->cancel(); - if (next_rtx_timer_ != MAX_TIMER_RTX) { - next_rtx_timer_ = MAX_TIMER_RTX; - timer_->cancel(); - } -} +void RTCLossDetectionAndRecovery::detectLoss(uint32_t start, uint32_t stop) { + if (start >= stop) return; -void RTCLossDetectionAndRecovery::addToRetransmissions(uint32_t start, - uint32_t stop) { // skip nacked packets - if (start <= state_->getLastSeqNacked()) { - start = state_->getLastSeqNacked() + 1; + if (start <= rs_->getState()->getLastSeqNacked()) { + start = rs_->getState()->getLastSeqNacked() + 1; } // skip received or lost packets - if (start <= state_->getHighestSeqReceivedInOrder()) { - start = state_->getHighestSeqReceivedInOrder() + 1; + if (start <= rs_->getState()->getHighestSeqReceivedInOrder()) { + start = rs_->getState()->getHighestSeqReceivedInOrder() + 1; } for (uint32_t seq = start; seq < stop; seq++) { - if (state_->isReceivedOrLost(seq) == PacketState::UNKNOWN) { - if (rtx_on_) { - if (!indexer_->isFec(seq)) { - // handle it with rtx - if (!isRtx(seq)) { - state_->onLossDetected(seq); - rtxState state; - state.first_send_ = state_->getInterestSentTime(seq); - if (state.first_send_ == 0) // this interest was never sent before - state.first_send_ = getNow(); - state.next_send_ = computeNextSend(seq, true); - state.rtx_count_ = 0; - DLOG_IF(INFO, VLOG_IS_ON(4)) - << "Add " << seq << " to retransmissions. next rtx is %lu " - << state.next_send_ - getNow(); - rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state)); - rtx_timers_.insert( - std::pair<uint64_t, uint32_t>(state.next_send_, seq)); - } - } else { - // is fec, do not send it - auto it = recover_with_fec_.find(seq); - if (it == recover_with_fec_.end()) { - state_->onLossDetected(seq); - recover_with_fec_.insert(seq); - } - } - } else { - // keep track of losses but recover with FEC - auto it = recover_with_fec_.find(seq); - if (it == recover_with_fec_.end()) { - state_->onLossDetected(seq); - recover_with_fec_.insert(seq); - } - } - } - } - scheduleNextRtx(); -} - -uint64_t RTCLossDetectionAndRecovery::computeNextSend(uint32_t seq, - bool new_rtx) { - uint64_t now = getNow(); - if (new_rtx) { - // for the new rtx we wait one estimated IAT after the loss detection. this - // is bacause, assuming that packets arrive with a constant IAT, we should - // get a new packet every IAT - double prod_rate = state_->getProducerRate(); - uint32_t estimated_iat = SENTINEL_TIMER_INTERVAL; - uint32_t jitter = 0; - - if (prod_rate != 0) { - double packet_size = state_->getAveragePacketSize(); - estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); - jitter = ceil(state_->getJitter()); - } - - uint32_t wait = estimated_iat + jitter; - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "first rtx for " << seq << " in " << wait - << " ms, rtt = " << state_->getRTT() << " ait = " << estimated_iat - << " jttr = " << jitter; - - return now + wait; - } else { - // wait one RTT - // however if the IAT is larger than the RTT, wait one IAT - uint32_t wait = SENTINEL_TIMER_INTERVAL; - - double prod_rate = state_->getProducerRate(); - if (prod_rate == 0) { - return now + SENTINEL_TIMER_INTERVAL; - } - - double packet_size = state_->getAveragePacketSize(); - uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); - - uint64_t rtt = state_->getRTT(); - if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL; - wait = rtt; - - if (estimated_iat > rtt) wait = estimated_iat; - - uint32_t jitter = ceil(state_->getJitter()); - wait += jitter; - - // it may happen that the channel is congested and we have some additional - // queuing delay to take into account - uint32_t queue = ceil(state_->getQueuing()); - wait += queue; - - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "next rtx for " << seq << " in " << wait - << " ms, rtt = " << state_->getRTT() << " ait = " << estimated_iat - << " jttr = " << jitter << " queue = " << queue; - - return now + wait; - } -} - -void RTCLossDetectionAndRecovery::retransmit() { - if (rtx_timers_.size() == 0) return; - - uint64_t now = getNow(); - - auto it = rtx_timers_.begin(); - std::unordered_set<uint32_t> lost_pkt; - uint32_t sent_counter = 0; - while (it != rtx_timers_.end() && it->first <= now && - sent_counter < MAX_RTX_IN_BATCH) { - uint32_t seq = it->second; - auto rtx_it = - rtx_state_.find(seq); // this should always return a valid iter - if (rtx_it->second.rtx_count_ >= RTC_MAX_RTX || - (now - rtx_it->second.first_send_) >= RTC_MAX_AGE || - seq < state_->getLastSeqNacked()) { - // max rtx reached or packet too old or packet nacked, this packet is lost - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "packet " << seq << " lost because 1) max rtx: " - << (rtx_it->second.rtx_count_ >= RTC_MAX_RTX) << " 2) max age: " - << ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE) - << " 3) nacked: " << (seq < state_->getLastSeqNacked()); - lost_pkt.insert(seq); - it++; - } else { - // resend the packet - state_->onRetransmission(seq); - double prod_rate = state_->getProducerRate(); - if (prod_rate != 0) rtx_it->second.rtx_count_++; - rtx_it->second.next_send_ = computeNextSend(seq, false); - it = rtx_timers_.erase(it); - rtx_timers_.insert( - std::pair<uint64_t, uint32_t>(rtx_it->second.next_send_, seq)); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "send rtx for sequence " << seq << ", next send in " - << (rtx_it->second.next_send_ - now); - send_rtx_callback_(seq); - sent_counter++; - } - } - - // remove packets if needed - for (auto lost_it = lost_pkt.begin(); lost_it != lost_pkt.end(); lost_it++) { - uint32_t seq = *lost_it; - state_->onPacketLost(seq); - deleteRtx(seq); - } -} - -void RTCLossDetectionAndRecovery::scheduleNextRtx() { - if (rtx_timers_.size() == 0) { - // all the rtx were removed, reset timer - next_rtx_timer_ = MAX_TIMER_RTX; - return; - } - - // check if timer is alreay set - if (next_rtx_timer_ != MAX_TIMER_RTX) { - // a new check for rtx is already scheduled - if (next_rtx_timer_ > rtx_timers_.begin()->first) { - // we need to re-schedule it - timer_->cancel(); - } else { - // wait for the next timer - return; - } - } - - // set a new timer - next_rtx_timer_ = rtx_timers_.begin()->first; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t wait = 1; - if (next_rtx_timer_ != MAX_TIMER_RTX && next_rtx_timer_ > now) - wait = next_rtx_timer_ - now; - - std::weak_ptr<RTCLossDetectionAndRecovery> self(shared_from_this()); - timer_->expires_from_now(std::chrono::milliseconds(wait)); - timer_->async_wait([self](std::error_code ec) { - if (ec) return; - if (auto s = self.lock()) { - s->retransmit(); - s->next_rtx_timer_ = MAX_TIMER_RTX; - s->scheduleNextRtx(); - } - }); -} - -bool RTCLossDetectionAndRecovery::deleteRtx(uint32_t seq) { - auto it_rtx = rtx_state_.find(seq); - if (it_rtx == rtx_state_.end()) return false; // rtx not found - - uint64_t ts = it_rtx->second.next_send_; - auto it_timers = rtx_timers_.find(ts); - while (it_timers != rtx_timers_.end() && it_timers->first == ts) { - if (it_timers->second == seq) { - rtx_timers_.erase(it_timers); - break; - } - it_timers++; - } - - bool lost = it_rtx->second.rtx_count_ > 0; - rtx_state_.erase(it_rtx); - - return lost; -} - -void RTCLossDetectionAndRecovery::scheduleSentinelTimer( - uint64_t expires_from_now) { - std::weak_ptr<RTCLossDetectionAndRecovery> self(shared_from_this()); - sentinel_timer_->expires_from_now( - std::chrono::milliseconds(expires_from_now)); - sentinel_timer_->async_wait([self](std::error_code ec) { - if (ec) return; - if (auto s = self.lock()) { - s->sentinelTimer(); - } - }); -} - -void RTCLossDetectionAndRecovery::sentinelTimer() { - uint64_t now = getNow(); - - bool expired = false; - bool sent = false; - if ((now - last_event_) >= sentinel_timer_interval_) { - // at least a sentinel_timer_interval_ elapsed since last event - expired = true; - if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) { - // this happens at the beginning (or if the producer stops for some - // reason) we need to keep sending interest 0 until we get an answer - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "sentinel timer: the producer is not active, send packet 0"; - state_->onRetransmission(0); - send_rtx_callback_(0); - } else { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "sentinel timer: the producer is active, " - "send the 10 oldest packets"; - sent = true; - uint32_t rtx = 0; - auto it = state_->getPendingInterestsMapBegin(); - auto end = state_->getPendingInterestsMapEnd(); - while (it != end && rtx < MAX_RTX_WITH_SENTINEL) { - uint32_t seq = it->first; - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "sentinel timer, add " << seq << " to the rtx list"; - addToRetransmissions(seq, seq + 1); - rtx++; - it++; - } - } - } else { - // sentinel timer did not expire because we registered at least one event - } - - uint32_t next_timer; - double prod_rate = state_->getProducerRate(); - if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive()) || prod_rate == 0) { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "next timer in " << SENTINEL_TIMER_INTERVAL; - next_timer = SENTINEL_TIMER_INTERVAL; - } else { - double prod_rate = state_->getProducerRate(); - double packet_size = state_->getAveragePacketSize(); - uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); - uint32_t jitter = ceil(state_->getJitter()); - - // try to reduce the number of timers if the estimated IAT is too small - next_timer = std::max((estimated_iat + jitter) * 20, (uint32_t)1); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "next sentinel in " << next_timer - << " ms, rate: " << ((prod_rate * 8.0) / 1000000.0) - << ", iat: " << estimated_iat << ", jitter: " << jitter; - - if (!expired) { - // discount the amout of time that is already passed - uint32_t discount = now - last_event_; - if (next_timer > discount) { - next_timer = next_timer - discount; - } else { - // in this case we trigger the timer in 1 ms - next_timer = 1; + if (rs_->getState()->getPacketState(seq) == PacketState::UNKNOWN) { + if (rs_->lossDetected(seq)) { + rs_->getState()->onLossDetected(seq); } - DLOG_IF(INFO, VLOG_IS_ON(3)) << "timer after discout: " << next_timer; - } else if (sent) { - // wait at least one producer stats interval + owd to check if the - // production rate is reducing. - uint32_t min_wait = PRODUCER_STATS_INTERVAL + ceil(state_->getQueuing()); - next_timer = std::max(next_timer, min_wait); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "wait for updates from prod, next timer: " << next_timer; } } - - scheduleSentinelTimer(next_timer); } } // namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_ldr.h b/libtransport/src/protocols/rtc/rtc_ldr.h index 1b9f9afd6..e7f8ce5db 100644 --- a/libtransport/src/protocols/rtc/rtc_ldr.h +++ b/libtransport/src/protocols/rtc/rtc_ldr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,16 +15,14 @@ #pragma once #include <hicn/transport/config.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +// RtcTransportRecoveryStrategies #include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/name.h> -#include <protocols/indexer.h> -#include <protocols/rtc/rtc_consts.h> -#include <protocols/rtc/rtc_state.h> +#include <protocols/rtc/rtc_recovery_strategy.h> #include <functional> -#include <map> -#include <unordered_map> namespace transport { @@ -34,91 +32,45 @@ namespace rtc { class RTCLossDetectionAndRecovery : public std::enable_shared_from_this<RTCLossDetectionAndRecovery> { - struct rtx_state_ { - uint64_t first_send_; - uint64_t next_send_; - uint32_t rtx_count_; - }; - - using rtxState = struct rtx_state_; - using SendRtxCallback = std::function<void(uint32_t)>; - public: - RTCLossDetectionAndRecovery(Indexer *indexer, SendRtxCallback &&callback, - asio::io_service &io_service); + RTCLossDetectionAndRecovery(Indexer *indexer, asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies type, + RecoveryStrategy::SendRtxCallback &&callback, + interface::StrategyCallback *external_callback); ~RTCLossDetectionAndRecovery(); - void setState(std::shared_ptr<RTCState> state) { state_ = state; } - void setFecParams(uint32_t n, uint32_t k) { - n_ = n; - k_ = k; + void setState(RTCState *state) { rs_->setState(state); } + void setRateControl(RTCRateControl *rateControl) { + rs_->setRateControl(rateControl); } - void turnOnRTX(); - void turnOffRTX(); - bool isRtxOn() { return rtx_on_; } + + void setFecParams(uint32_t n, uint32_t k) { rs_->setFecParams(n, k); } + + void turnOnRecovery() { rs_->tunrOnRecovery(); } + bool isRtxOn() { return rs_->isRtxOn(); } + + void changeRecoveryStrategy(interface::RtcTransportRecoveryStrategies type); void onNewRound(bool in_sync); - void onTimeout(uint32_t seq); + void onTimeout(uint32_t seq, bool lost); void onPacketRecoveredFec(uint32_t seq); void onDataPacketReceived(const core::ContentObject &content_object); void onNackPacketReceived(const core::ContentObject &nack); void onProbePacketReceived(const core::ContentObject &probe); - void clear(); + void clear() { rs_->clear(); } - bool isRtx(uint32_t seq) { - if (rtx_state_.find(seq) != rtx_state_.end()) return true; - return false; + bool isRtx(uint32_t seq) { return rs_->isRtx(seq); } + bool isPossibleLossWithNoRtx(uint32_t seq) { + return rs_->isPossibleLossWithNoRtx(seq); } private: - void addToRetransmissions(uint32_t start, uint32_t stop); - uint64_t computeNextSend(uint32_t seq, bool new_rtx); - void retransmit(); - void scheduleNextRtx(); - bool deleteRtx(uint32_t seq); - void scheduleSentinelTimer(uint64_t expires_from_now); - void sentinelTimer(); - uint32_t computeFecPacketsToAsk(bool in_sync); - - uint64_t getNow() { - using namespace std::chrono; - uint64_t now = - duration_cast<milliseconds>(steady_clock::now().time_since_epoch()) - .count(); - return now; - } - - // this map keeps track of the retransmitted interest, ordered from the oldest - // to the newest one. the state contains the timer of the first send of the - // interest (from pendingIntetests_), the timer of the next send (key of the - // multimap) and the number of rtx - std::map<uint32_t, rtxState> rtx_state_; - // this map stored the rtx by timer. The key is the time at which the rtx - // should be sent, and the val is the interest seq number - std::multimap<uint64_t, uint32_t> rtx_timers_; - - // lost packets that will be recovered with fec - std::unordered_set<uint32_t> recover_with_fec_; - - bool rtx_on_; - bool fec_on_; - uint64_t next_rtx_timer_; - uint64_t last_event_; - uint64_t sentinel_timer_interval_; - - // fec params - uint32_t n_; - uint32_t k_; - - std::unique_ptr<asio::steady_timer> timer_; - std::unique_ptr<asio::steady_timer> sentinel_timer_; - std::shared_ptr<RTCState> state_; - - Indexer *indexer_; + void detectLoss(uint32_t start, uint32_t stop); - SendRtxCallback send_rtx_callback_; + interface::RtcTransportRecoveryStrategies rs_type_; + std::shared_ptr<RecoveryStrategy> rs_; }; } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_packet.h b/libtransport/src/protocols/rtc/rtc_packet.h index 7dc2f82c3..391aedfc6 100644 --- a/libtransport/src/protocols/rtc/rtc_packet.h +++ b/libtransport/src/protocols/rtc/rtc_packet.h @@ -24,6 +24,27 @@ * +-----------------------------------------+ */ +/* aggregated packets + * +---------------------------------+ + * |c| #pkts | len1 | len2 | .... | + * +---------------------------------- + * + * +---------------------------------+ + * |c| #pkts | resv | len 1 | + * +---------------------------------- + * + * aggregated packets header. + * header position. just after the data packet header + * + * c: 1 bit: 0 8bit encoding, 1 16bit encoding + * #pkts: 7 bits: number of application packets contained + * 8bits encoding: + * lenX: 8 bits: len in bites of packet X + * 16bits econding: + * resv: 8 bits: reserved field (unused) + * lenX: 16bits: len in bytes of packet X + */ + #pragma once #ifndef _WIN32 #include <arpa/inet.h> @@ -31,6 +52,8 @@ #include <hicn/transport/portability/win_portability.h> #endif +#include <cstring> + namespace transport { namespace protocol { @@ -82,8 +105,144 @@ struct nack_packet_t { inline uint32_t getProductionRate() const { return ntohl(prod_rate); } inline void setProductionRate(uint32_t rate) { prod_rate = htonl(rate); } - inline uint32_t getProductionSegement() const { return ntohl(prod_seg); } - inline void setProductionSegement(uint32_t seg) { prod_seg = htonl(seg); } + inline uint32_t getProductionSegment() const { return ntohl(prod_seg); } + inline void setProductionSegment(uint32_t seg) { prod_seg = htonl(seg); } +}; + +class AggrPktHeader { + public: + // XXX buf always point to the payload after the data header + AggrPktHeader(uint8_t *buf, uint16_t max_packet_len, uint16_t pkt_number) + : buf_(buf), pkt_num_(pkt_number) { + *buf_ = 0; // reset the first byte to correctly add the header + // encoding and the packet number + if (max_packet_len > 0xff) { + setAggrPktEncoding16bit(); + } else { + setAggrPktEncoding8bit(); + } + setAggrPktNUmber(pkt_number); + header_len_ = computeHeaderLen(); + memset(buf_ + 1, 0, header_len_ - 1); + } + + // XXX buf always point to the payload after the data header + AggrPktHeader(uint8_t *buf) : buf_(buf) { + encoding_ = getAggrPktEncoding(); + pkt_num_ = getAggrPktNumber(); + header_len_ = computeHeaderLen(); + } + + ~AggrPktHeader(){}; + + int addPacketToHeader(uint8_t index, uint16_t len) { + if (index > pkt_num_) return -1; + + setAggrPktLen(index, len); + return 0; + } + + int getPointerToPacket(uint8_t index, uint8_t **pkt_ptr, uint16_t *pkt_len) { + if (index > pkt_num_) return -1; + + uint16_t len = 0; + for (int i = 0; i < index; i++) + len += getAggrPktLen(i); // sum the pkts len from 0 to index - 1 + + uint16_t offset = len + header_len_; + *pkt_ptr = buf_ + offset; + *pkt_len = getAggrPktLen(index); + return 0; + } + + int getPacketOffsets(uint8_t index, uint16_t *pkt_offset, uint16_t *pkt_len) { + if (index > pkt_num_) return -1; + + uint16_t len = 0; + for (int i = 0; i < index; i++) + len += getAggrPktLen(i); // sum the pkts len from 0 to index - 1 + + uint16_t offset = len + header_len_; + *pkt_offset = offset; + *pkt_len = getAggrPktLen(index); + + return 0; + } + + uint8_t *getPayloadAppendPtr() { return buf_ + header_len_; } + + uint16_t getHeaderLen() { return header_len_; } + + uint8_t getNumberOfPackets() { return pkt_num_; } + + private: + inline uint16_t computeHeaderLen() const { + uint16_t len = 4; // min len in bytes + if (!encoding_) { + while (pkt_num_ >= len) { + len += 4; + } + } else { + while (pkt_num_ * 2 >= len) { + len += 4; + } + } + return len; + } + + inline uint8_t getAggrPktEncoding() const { + // get the first bit of the first byte + return (*buf_ >> 7); + } + + inline void setAggrPktEncoding8bit() { + // reset the first bit of the first byte + encoding_ = 0; + *buf_ &= 0x7f; + } + + inline void setAggrPktEncoding16bit() { + // set the first bit of the first byte + encoding_ = 1; + *buf_ ^= 0x80; + } + + inline uint8_t getAggrPktNumber() const { + // return the first byte with the first bit = 0 + return (*buf_ & 0x7f); + } + + inline void setAggrPktNUmber(uint8_t val) { + // set the val without modifying the first bit + *buf_ &= 0x80; // reset everithing but the first bit + val &= 0x7f; // reset the first bit + *buf_ |= val; // or the vals, done! + } + + inline uint16_t getAggrPktLen(uint8_t pkt_index) const { + pkt_index++; + if (!encoding_) { // 8 bits + return (uint16_t) * (buf_ + pkt_index); + } else { // 16 bits + uint16_t *buf_16 = (uint16_t *)buf_; + return ntohs(*(buf_16 + pkt_index)); + } + } + + inline void setAggrPktLen(uint8_t pkt_index, uint16_t len) { + pkt_index++; + if (!encoding_) { // 8 bits + *(buf_ + pkt_index) = (uint8_t)len; + } else { // 16 bits + uint16_t *buf_16 = (uint16_t *)buf_; + *(buf_16 + pkt_index) = htons(len); + } + } + + uint8_t *buf_; + uint8_t encoding_; + uint8_t pkt_num_; + uint16_t header_len_; }; } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_rc.h b/libtransport/src/protocols/rtc/rtc_rc.h index 34d090092..62636ce40 100644 --- a/libtransport/src/protocols/rtc/rtc_rc.h +++ b/libtransport/src/protocols/rtc/rtc_rc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -34,11 +34,15 @@ class RTCRateControl : public std::enable_shared_from_this<RTCRateControl> { void turnOnRateControl() { rc_on_ = true; } void setState(std::shared_ptr<RTCState> state) { protocol_state_ = state; }; - uint32_t getCongesionWindow() { return congestion_win_; }; + uint32_t getCongestionWindow() { return congestion_win_; }; + bool inCongestionState() { + if (congestion_state_ == CongestionState::Congested) return true; + return false; + } virtual void onNewRound(double round_len) = 0; - virtual void onDataPacketReceived( - const core::ContentObject &content_object) = 0; + virtual void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats) = 0; protected: enum class CongestionState { Normal = 0, Underuse = 1, Congested = 2, Last }; diff --git a/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc new file mode 100644 index 000000000..6cd3094b5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 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 <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rc_congestion_detection.h> + +#include <algorithm> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlCongestionDetection::RTCRateControlCongestionDetection() + : rounds_without_congestion_(4), last_queue_(0) {} // must be > 3 + +RTCRateControlCongestionDetection::~RTCRateControlCongestionDetection() {} + +void RTCRateControlCongestionDetection::onNewRound(double round_len) { + if (!rc_on_) return; + + double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC; + double queue = protocol_state_->getQueuing(); + + if (rtt == 0.0) return; // no info from the producer + + if (last_queue_ == queue) { + // if last_queue == queue the consumer didn't receive any + // packet from the producer. we do not change the current congestion state. + // we just increase the counter of rounds whithout congestion if needed + // (in case of congestion the counter is already set to 0) + if (congestion_state_ == CongestionState::Normal) + rounds_without_congestion_++; + } else { + if (queue > MAX_QUEUING_DELAY) { + // here we detect congestion. + congestion_state_ = CongestionState::Congested; + rounds_without_congestion_ = 0; + } else { + // wait 3 rounds before switch back to no congestion + if (rounds_without_congestion_ > 3) { + // nothing bad is happening + congestion_state_ = CongestionState::Normal; + } + rounds_without_congestion_++; + } + last_queue_ = queue; + } +} + +void RTCRateControlCongestionDetection::onDataPacketReceived( + const core::ContentObject &content_object, bool compute_stats) { + // nothing to do + return; +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h new file mode 100644 index 000000000..9afa6c39a --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <hicn/transport/utils/shared_ptr_utils.h> +#include <protocols/rtc/rtc_rc.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCRateControlCongestionDetection : public RTCRateControl { + public: + RTCRateControlCongestionDetection(); + + ~RTCRateControlCongestionDetection(); + + void onNewRound(double round_len); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + uint32_t rounds_without_congestion_; + double last_queue_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_iat.cc b/libtransport/src/protocols/rtc/rtc_rc_iat.cc new file mode 100644 index 000000000..f06f377f3 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_iat.cc @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2021 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 <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rc_iat.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlIAT::RTCRateControlIAT() + : rounds_since_last_drop_(0), + rounds_without_congestion_(0), + rounds_with_congestion_(0), + last_queue_(0), + last_rcv_time_(0), + last_prod_time_(0), + last_seq_number_(0), + target_rate_avg_(0), + round_index_(0), + congestion_cause_(CongestionCause::UNKNOWN) {} + +RTCRateControlIAT::~RTCRateControlIAT() {} + +void RTCRateControlIAT::onNewRound(double round_len) { + if (!rc_on_) return; + + double received_rate = protocol_state_->getReceivedRate() + + protocol_state_->getRecoveredFecRate(); + + double target_rate = + protocol_state_->getProducerRate(); // * PRODUCTION_RATE_FRACTION; + double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC; + // double packet_size = protocol_state_->getAveragePacketSize(); + double queue = protocol_state_->getQueuing(); + + if (rtt == 0.0) return; // no info from the producer + + CongestionState prev_congestion_state = congestion_state_; + + target_rate_avg_ = target_rate_avg_ * (1 - MOVING_AVG_ALPHA) + + target_rate * MOVING_AVG_ALPHA; + + if (prev_congestion_state == CongestionState::Congested) { + if (queue > MAX_QUEUING_DELAY || last_queue_ == queue) { + congestion_state_ = CongestionState::Congested; + + received_rate_.push_back(received_rate); + target_rate_.push_back(target_rate); + + // We assume the cause does not change + // Note that the first assumption about the cause could be wrong + // the cause of congestion could change + if (congestion_cause_ == CongestionCause::UNKNOWN) + if (rounds_with_congestion_ >= 1) + congestion_cause_ = apply_classification_tree( + rounds_with_congestion_ > ROUND_TO_WAIT_FORCE_DECISION); + + rounds_with_congestion_++; + } else { + congestion_state_ = CongestionState::Normal; + + // clear past history + reset_congestion_statistics(); + + // TODO maybe we can use some of these values for the stdev of the + // congestion mode + for (int i = 0; i < ROUND_HISTORY_SIZE; i++) { + iat_on_hold_[i].clear(); + } + } + } else if (queue > MAX_QUEUING_DELAY) { + if (prev_congestion_state == CongestionState::Normal) { + rounds_with_congestion_ = 0; + + if (rounds_without_congestion_ > ROUND_TO_RESET_CAUSE) + congestion_cause_ = CongestionCause::UNKNOWN; + } + congestion_state_ = CongestionState::Congested; + received_rate_.push_back(received_rate); + target_rate_.push_back(target_rate); + } else { + // nothing bad is happening + congestion_state_ = CongestionState::Normal; + reset_congestion_statistics(); + + int past_index = (round_index_ + 1) % ROUND_HISTORY_SIZE; + for (std::vector<double>::iterator it = iat_on_hold_[past_index].begin(); + it != iat_on_hold_[past_index].end(); ++it) { + congestion_free_iat_.push_back(*it); + if (congestion_free_iat_.size() > 50) { + congestion_free_iat_.erase(congestion_free_iat_.begin()); + } + } + iat_on_hold_[past_index].clear(); + round_index_ = (round_index_ + 1) % ROUND_HISTORY_SIZE; + } + + last_queue_ = queue; + + if (congestion_state_ == CongestionState::Congested) { + if (prev_congestion_state == CongestionState::Normal) { + // init the congetion window using the received rate + // disabling for the moment the congestion window setup + // congestion_win_ = (uint32_t)ceil(received_rate * rtt / packet_size); + rounds_since_last_drop_ = ROUNDS_BEFORE_TAKE_ACTION + 1; + } + + if (rounds_since_last_drop_ >= ROUNDS_BEFORE_TAKE_ACTION) { + // disabling for the moment the congestion window setup + // uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR; + // congestion_win_ = std::max(win, WIN_MIN); + rounds_since_last_drop_ = 0; + return; + } + + rounds_since_last_drop_++; + } + + if (congestion_state_ == CongestionState::Normal) { + if (prev_congestion_state == CongestionState::Congested) { + rounds_without_congestion_ = 0; + } + + rounds_without_congestion_++; + if (rounds_without_congestion_ < ROUNDS_BEFORE_TAKE_ACTION) return; + + // disabling for the moment the congestion window setup + // congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR; + // congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX); + } + + if (received_rate_.size() > 1000) + received_rate_.erase(received_rate_.begin()); + if (target_rate_.size() > 1000) target_rate_.erase(target_rate_.begin()); +} + +void RTCRateControlIAT::onDataPacketReceived( + const core::ContentObject &content_object, bool compute_stats) { + core::ParamsRTC params = RTCState::getDataParams(content_object); + + uint64_t now = utils::SteadyTime::nowMs().count(); + + uint32_t segment_number = content_object.getName().getSuffix(); + + if (segment_number == (last_seq_number_ + 1) && compute_stats) { + uint64_t iat = now - last_rcv_time_; + uint64_t ist = params.timestamp - last_prod_time_; + if (now >= last_rcv_time_ && params.timestamp > last_prod_time_) { + if (iat >= ist && ist < MIN_IST_VALUE) { + if (congestion_state_ == CongestionState::Congested) { + iat_.push_back((iat - ist)); + } else { + // no congestion, but we do not always add new values, but only when + // there is no sign of congestion + double queue = protocol_state_->getQueuing(); + if (queue <= CONGESTION_FREE_QUEUEING_DELAY) { + iat_on_hold_[round_index_].push_back((iat - ist)); + } + } + } + } + } + + last_seq_number_ = segment_number; + last_rcv_time_ = now; + last_prod_time_ = params.timestamp; + + if (iat_.size() > 1000) iat_.erase(iat_.begin()); + return; +} + +CongestionCause RTCRateControlIAT::apply_classification_tree(bool force_reply) { + if (iat_.size() <= 2 || received_rate_.size() < 2) + return CongestionCause::UNKNOWN; + + double received_ratio = 0; + double iat_ratio = 0; + double iat_stdev = compute_iat_stdev(iat_); + double iat_congestion_free_stdev = compute_iat_stdev(congestion_free_iat_); + + double iat_avg = 0.0; + + double recv_avg = 0.0; + double recv_max = 0.0; + + double target_rate_avg = 0.0; + + int counter = 0; + std::vector<double>::reverse_iterator target_it = target_rate_.rbegin(); + for (std::vector<double>::reverse_iterator it = received_rate_.rbegin(); + it != received_rate_.rend(); ++it) { + recv_avg += *it; + target_rate_avg += *target_it; + if (counter < ROUND_HISTORY_SIZE) + if (recv_max < *it) { + recv_max = *it; // we consider only the last 2 seconds + } + counter++; + target_it++; + } + recv_avg = recv_avg / received_rate_.size(); + target_rate_avg = target_rate_avg / target_rate_.size(); + + for (std::vector<double>::iterator it = iat_.begin(); it != iat_.end(); + ++it) { + iat_avg += *it; + } + iat_avg = iat_avg / iat_.size(); + + double congestion_free_iat_avg = 0.0; + for (std::vector<double>::iterator it = congestion_free_iat_.begin(); + it != congestion_free_iat_.end(); ++it) { + congestion_free_iat_avg += *it; + } + congestion_free_iat_avg = + congestion_free_iat_avg / congestion_free_iat_.size(); + + received_ratio = recv_avg / target_rate_avg; + + iat_ratio = iat_stdev / iat_congestion_free_stdev; + + CongestionCause congestion_cause = CongestionCause::UNKNOWN; + // applying classification tree model + if (received_ratio <= 0.87) + if (iat_stdev <= 6.48) + if (received_ratio <= 0.83) + congestion_cause = CongestionCause::LINK_CAPACITY; + else if (force_reply) + congestion_cause = CongestionCause::LINK_CAPACITY; + else + congestion_cause = CongestionCause::UNKNOWN; // accuracy is too low + else if (iat_ratio <= 2.46) + if (force_reply) + congestion_cause = CongestionCause::LINK_CAPACITY; + else + congestion_cause = CongestionCause::UNKNOWN; // accuracy is too low + else + congestion_cause = CongestionCause::COMPETING_CROSS_TRAFFIC; + else if (received_ratio <= 0.913 && iat_stdev <= 0.784) + congestion_cause = CongestionCause::LINK_CAPACITY; + else + congestion_cause = CongestionCause::COMPETING_CROSS_TRAFFIC; + + return congestion_cause; +} + +void RTCRateControlIAT::reset_congestion_statistics() { + iat_.clear(); + received_rate_.clear(); + target_rate_.clear(); +} + +double RTCRateControlIAT::compute_iat_stdev(std::vector<double> v) { + if (v.size() == 0) return 0; + + float sum = 0.0, mean, standard_deviation = 0.0; + for (std::vector<double>::iterator it = v.begin(); it != v.end(); it++) { + sum += *it; + } + + mean = sum / v.size(); + for (std::vector<double>::iterator it = v.begin(); it != v.end(); it++) { + standard_deviation += pow(*it - mean, 2); + } + return sqrt(standard_deviation / v.size()); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_iat.h b/libtransport/src/protocols/rtc/rtc_rc_iat.h new file mode 100644 index 000000000..715637807 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_iat.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <hicn/transport/utils/shared_ptr_utils.h> +#include <protocols/rtc/rtc_rc.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +const int ROUND_HISTORY_SIZE = 10; // equivalent to two seconds +const int ROUND_TO_WAIT_FORCE_DECISION = 5; + +// once congestion is gone, we need to wait for k rounds before changing the +// congestion cause in the case it appears again +const int ROUND_TO_RESET_CAUSE = 5; + +const int MIN_IST_VALUE = 150; // samples of ist larger than 150ms are + // discarded +const double CONGESTION_FREE_QUEUEING_DELAY = 10; + +enum class CongestionCause : uint8_t { + COMPETING_CROSS_TRAFFIC, + FRIENDLY_CROSS_TRAFFIC, + UNKNOWN_CROSS_TRAFFIC, + LINK_CAPACITY, + UNKNOWN +}; + +class RTCRateControlIAT : public RTCRateControl { + public: + RTCRateControlIAT(); + + ~RTCRateControlIAT(); + + void onNewRound(double round_len); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + void reset_congestion_statistics(); + + double compute_iat_stdev(std::vector<double> v); + + CongestionCause apply_classification_tree(bool force_reply); + + private: + uint32_t rounds_since_last_drop_; + uint32_t rounds_without_congestion_; + uint32_t rounds_with_congestion_; + double last_queue_; + uint64_t last_rcv_time_; + uint64_t last_prod_time_; + uint32_t last_seq_number_; + double target_rate_avg_; + + // Iat values are not immediately added to the congestion free set of values + std::array<std::vector<double>, ROUND_HISTORY_SIZE> iat_on_hold_; + uint32_t round_index_; + + // with congestion statistics + std::vector<double> iat_; + std::vector<double> received_rate_; + std::vector<double> target_rate_; + + // congestion free statistics + std::vector<double> congestion_free_iat_; + + CongestionCause congestion_cause_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.cc b/libtransport/src/protocols/rtc/rtc_rc_queue.cc index a1c89e329..ecabc5205 100644 --- a/libtransport/src/protocols/rtc/rtc_rc_queue.cc +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -37,7 +37,7 @@ void RTCRateControlQueue::onNewRound(double round_len) { double received_rate = protocol_state_->getReceivedRate(); double target_rate = protocol_state_->getProducerRate() * PRODUCTION_RATE_FRACTION; - double rtt = (double)protocol_state_->getRTT() / MILLI_IN_A_SEC; + double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC; double packet_size = protocol_state_->getAveragePacketSize(); double queue = protocol_state_->getQueuing(); @@ -94,7 +94,7 @@ void RTCRateControlQueue::onNewRound(double round_len) { } void RTCRateControlQueue::onDataPacketReceived( - const core::ContentObject &content_object) { + const core::ContentObject &content_object, bool compute_stats) { // nothing to do return; } diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.h b/libtransport/src/protocols/rtc/rtc_rc_queue.h index 407354d43..cdf78fd47 100644 --- a/libtransport/src/protocols/rtc/rtc_rc_queue.h +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -30,7 +30,8 @@ class RTCRateControlQueue : public RTCRateControl { ~RTCRateControlQueue(); void onNewRound(double round_len); - void onDataPacketReceived(const core::ContentObject &content_object); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); auto shared_from_this() { return utils::shared_from(this); } diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.cc b/libtransport/src/protocols/rtc/rtc_reassembly.cc new file mode 100644 index 000000000..992bab50e --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_reassembly.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 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 <hicn/transport/interfaces/socket_consumer.h> +#include <implementation/socket_consumer.h> +#include <protocols/rtc/rtc_reassembly.h> +#include <protocols/transport_protocol.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RtcReassembly::RtcReassembly(implementation::ConsumerSocket* icn_socket, + TransportProtocol* transport_protocol) + : DatagramReassembly(icn_socket, transport_protocol) { + is_setup_ = false; +} + +void RtcReassembly::reassemble(core::ContentObject& content_object) { + if (!is_setup_) { + is_setup_ = true; + reassembly_consumer_socket_->getSocketOption( + interface::RtcTransportOptions::AGGREGATED_DATA, data_aggregation_); + } + + auto read_buffer = content_object.getPayload(); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Size of payload: " << read_buffer->length(); + + read_buffer->trimStart(transport_protocol_->transportHeaderLength()); + + if (data_aggregation_) { + rtc::AggrPktHeader hdr((uint8_t*)read_buffer->data()); + + for (uint8_t i = 0; i < hdr.getNumberOfPackets(); i++) { + std::unique_ptr<utils::MemBuf> segment = read_buffer->clone(); + + uint16_t pkt_start = 0; + uint16_t pkt_len = 0; + int res = hdr.getPacketOffsets(i, &pkt_start, &pkt_len); + if (res == -1) { + // this should not happen + break; + } + + segment->trimStart(pkt_start); + segment->trimEnd(segment->length() - pkt_len); + + Reassembly::read_buffer_ = std::move(segment); + Reassembly::notifyApplication(); + } + } else { + Reassembly::read_buffer_ = std::move(read_buffer); + Reassembly::notifyApplication(); + } +} + +void RtcReassembly::reassemble(utils::MemBuf& buffer, uint32_t suffix) { + if (!is_setup_) { + is_setup_ = true; + reassembly_consumer_socket_->getSocketOption( + interface::RtcTransportOptions::AGGREGATED_DATA, data_aggregation_); + } + + if (data_aggregation_) { + rtc::AggrPktHeader hdr((uint8_t*)buffer.data()); + + for (uint8_t i = 0; i < hdr.getNumberOfPackets(); i++) { + std::unique_ptr<utils::MemBuf> segment = buffer.clone(); + + uint16_t pkt_start = 0; + uint16_t pkt_len = 0; + int res = hdr.getPacketOffsets(i, &pkt_start, &pkt_len); + if (res == -1) { + // this should not happen + break; + } + + segment->trimStart(pkt_start); + segment->trimEnd(segment->length() - pkt_len); + + Reassembly::read_buffer_ = std::move(segment); + Reassembly::notifyApplication(); + } + + } else { + Reassembly::read_buffer_ = buffer.cloneOne(); + Reassembly::notifyApplication(); + } +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.h b/libtransport/src/protocols/rtc/rtc_reassembly.h index 15722a6d5..132004605 100644 --- a/libtransport/src/protocols/rtc/rtc_reassembly.h +++ b/libtransport/src/protocols/rtc/rtc_reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -28,17 +28,14 @@ namespace rtc { class RtcReassembly : public DatagramReassembly { public: RtcReassembly(implementation::ConsumerSocket *icn_socket, - TransportProtocol *transport_protocol) - : DatagramReassembly(icn_socket, transport_protocol) {} - - void reassemble(core::ContentObject &content_object) override { - auto read_buffer = content_object.getPayload(); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "Size of payload: " << read_buffer->length(); - read_buffer->trimStart(transport_protocol_->transportHeaderLength()); - Reassembly::read_buffer_ = std::move(read_buffer); - Reassembly::notifyApplication(); - } + TransportProtocol *transport_protocol); + + void reassemble(core::ContentObject &content_object) override; + void reassemble(utils::MemBuf &buffer, uint32_t suffix) override; + + private: + bool is_setup_; + bool data_aggregation_; }; } // namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc new file mode 100644 index 000000000..888105eab --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <hicn/transport/interfaces/notification.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace transport::interface; + +RecoveryStrategy::RecoveryStrategy( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + bool use_rtx, bool use_fec, interface::StrategyCallback *external_callback) + : recovery_on_(false), + next_rtx_timer_(MAX_TIMER_RTX), + send_rtx_callback_(std::move(callback)), + indexer_(indexer), + round_id_(0), + last_fec_used_(0), + callback_(external_callback) { + setRtxFec(use_rtx, use_fec); + timer_ = std::make_unique<asio::steady_timer>(io_service); +} + +RecoveryStrategy::RecoveryStrategy(RecoveryStrategy &&rs) + : rtx_state_(std::move(rs.rtx_state_)), + rtx_timers_(std::move(rs.rtx_timers_)), + recover_with_fec_(std::move(rs.recover_with_fec_)), + timer_(std::move(rs.timer_)), + next_rtx_timer_(std::move(rs.next_rtx_timer_)), + send_rtx_callback_(std::move(rs.send_rtx_callback_)), + n_(std::move(rs.n_)), + k_(std::move(rs.k_)), + indexer_(std::move(rs.indexer_)), + state_(std::move(rs.state_)), + rc_(std::move(rs.rc_)), + round_id_(std::move(rs.round_id_)), + last_fec_used_(std::move(rs.last_fec_used_)), + callback_(rs.callback_) { + setFecParams(n_, k_); +} + +RecoveryStrategy::~RecoveryStrategy() {} + +void RecoveryStrategy::setFecParams(uint32_t n, uint32_t k) { + n_ = n; + k_ = k; + + // XXX for the moment we go in steps of 5% loss rate. + // max loss rate = 95% + for (uint32_t loss_rate = 5; loss_rate < 100; loss_rate += 5) { + double dec_loss_rate = (double)loss_rate / 100.0; + double exp_losses = (double)k_ * dec_loss_rate; + uint32_t fec_to_ask = ceil(exp_losses / (1 - dec_loss_rate)); + + fec_state_ f; + f.fec_to_ask = std::min(fec_to_ask, (n_ - k_)); + f.last_update = round_id_; + f.avg_residual_losses = 0.0; + f.consecutive_use = 0; + fec_per_loss_rate_.push_back(f); + } +} + +bool RecoveryStrategy::lossDetected(uint32_t seq) { + if (isRtx(seq)) { + // this packet is already in the list of rtx + return false; + } + + auto it = recover_with_fec_.find(seq); + if (it != recover_with_fec_.end()) { + // this packet is already in list of packets to recover with fec + // this list contians also fec packets that will not be recovered with rtx + return false; + } + + // new loss detected, recover it according to the strategy + newPacketLoss(seq); + return true; +} + +void RecoveryStrategy::clear() { + rtx_state_.clear(); + rtx_timers_.clear(); + recover_with_fec_.clear(); + + if (next_rtx_timer_ != MAX_TIMER_RTX) { + next_rtx_timer_ = MAX_TIMER_RTX; + timer_->cancel(); + } +} + +// rtx functions +void RecoveryStrategy::addNewRtx(uint32_t seq, bool force) { + if (!indexer_->isFec(seq) || force) { + // this packet needs to be re-transmitted + rtxState state; + state.first_send_ = state_->getInterestSentTime(seq); + if (state.first_send_ == 0) // this interest was never sent before + state.first_send_ = getNow(); + state.next_send_ = computeNextSend(seq, true); + state.rtx_count_ = 0; + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Add " << seq << " to retransmissions. next rtx is in " + << state.next_send_ - getNow() << " ms"; + rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state)); + rtx_timers_.insert(std::pair<uint64_t, uint32_t>(state.next_send_, seq)); + + // if a new rtx is introduced, check the rtx timer + scheduleNextRtx(); + } else { + // do not re-send fec packets but keep track of them + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } +} + +uint64_t RecoveryStrategy::computeNextSend(uint32_t seq, bool new_rtx) { + uint64_t now = getNow(); + if (new_rtx) { + // for the new rtx we wait one estimated IAT after the loss detection. this + // is bacause, assuming that packets arrive with a constant IAT, we should + // get a new packet every IAT + double prod_rate = state_->getProducerRate(); + uint32_t estimated_iat = SENTINEL_TIMER_INTERVAL; + uint32_t jitter = 0; + + if (prod_rate != 0) { + double packet_size = state_->getAveragePacketSize(); + estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + jitter = ceil(state_->getJitter()); + } + + uint32_t wait = 1; + if (estimated_iat < 18) { + // for low rate app we do not wait to send a RTX + // we consider low rate stream with less than 50pps (iat >= 20ms) + // (e.g. audio in videoconf, mobile games). + // in the check we use 18ms to accomodate for measurements errors + // for flows with higher rate wait 1 ait + jitter + wait = estimated_iat + jitter; + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "first rtx for " << seq << " in " << wait + << " ms, rtt = " << state_->getMinRTT() << " ait = " << estimated_iat + << " jttr = " << jitter; + + return now + wait; + } else { + // wait one RTT + uint32_t wait = SENTINEL_TIMER_INTERVAL; + + double prod_rate = state_->getProducerRate(); + if (prod_rate == 0) { + return now + SENTINEL_TIMER_INTERVAL; + } + + double packet_size = state_->getAveragePacketSize(); + uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + + uint64_t rtt = state_->getMinRTT(); + if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL; + wait = rtt; + + uint32_t jitter = ceil(state_->getJitter()); + wait += jitter; + + // it may happen that the channel is congested and we have some additional + // queuing delay to take into account + uint32_t queue = ceil(state_->getQueuing()); + wait += queue; + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "next rtx for " << seq << " in " << wait + << " ms, rtt = " << state_->getMinRTT() << " ait = " << estimated_iat + << " jttr = " << jitter << " queue = " << queue; + + return now + wait; + } +} + +void RecoveryStrategy::retransmit() { + if (rtx_timers_.size() == 0) return; + + uint64_t now = getNow(); + + auto it = rtx_timers_.begin(); + std::unordered_set<uint32_t> lost_pkt; + uint32_t sent_counter = 0; + while (it != rtx_timers_.end() && it->first <= now && + sent_counter < MAX_RTX_IN_BATCH) { + uint32_t seq = it->second; + auto rtx_it = + rtx_state_.find(seq); // this should always return a valid iter + if (rtx_it->second.rtx_count_ >= RTC_MAX_RTX || + (now - rtx_it->second.first_send_) >= RTC_MAX_AGE || + seq < state_->getLastSeqNacked()) { + // max rtx reached or packet too old or packet nacked, this packet is lost + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "packet " << seq << " lost because 1) max rtx: " + << (rtx_it->second.rtx_count_ >= RTC_MAX_RTX) << " 2) max age: " + << ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE) + << " 3) nacked: " << (seq < state_->getLastSeqNacked()); + lost_pkt.insert(seq); + it++; + } else { + // resend the packet + state_->onRetransmission(seq); + double prod_rate = state_->getProducerRate(); + if (prod_rate != 0) rtx_it->second.rtx_count_++; + rtx_it->second.next_send_ = computeNextSend(seq, false); + it = rtx_timers_.erase(it); + rtx_timers_.insert( + std::pair<uint64_t, uint32_t>(rtx_it->second.next_send_, seq)); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "send rtx for sequence " << seq << ", next send in " + << (rtx_it->second.next_send_ - now); + send_rtx_callback_(seq); + sent_counter++; + } + } + + // remove packets if needed + for (auto lost_it = lost_pkt.begin(); lost_it != lost_pkt.end(); lost_it++) { + uint32_t seq = *lost_it; + state_->onPacketLost(seq); + deleteRtx(seq); + } +} + +void RecoveryStrategy::scheduleNextRtx() { + if (rtx_timers_.size() == 0) { + // all the rtx were removed, reset timer + next_rtx_timer_ = MAX_TIMER_RTX; + return; + } + + // check if timer is alreay set + if (next_rtx_timer_ != MAX_TIMER_RTX) { + // a new check for rtx is already scheduled + if (next_rtx_timer_ > rtx_timers_.begin()->first) { + // we need to re-schedule it + timer_->cancel(); + } else { + // wait for the next timer + return; + } + } + + // set a new timer + next_rtx_timer_ = rtx_timers_.begin()->first; + uint64_t now = utils::SteadyTime::nowMs().count(); + uint64_t wait = 1; + if (next_rtx_timer_ != MAX_TIMER_RTX && next_rtx_timer_ > now) + wait = next_rtx_timer_ - now; + + std::weak_ptr<RecoveryStrategy> self(shared_from_this()); + timer_->expires_from_now(std::chrono::milliseconds(wait)); + timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + if (auto s = self.lock()) { + s->retransmit(); + s->next_rtx_timer_ = MAX_TIMER_RTX; + s->scheduleNextRtx(); + } + }); +} + +void RecoveryStrategy::deleteRtx(uint32_t seq) { + auto it_rtx = rtx_state_.find(seq); + if (it_rtx == rtx_state_.end()) return; // rtx not found + + // remove the rtx from the timers list + uint64_t ts = it_rtx->second.next_send_; + auto it_timers = rtx_timers_.find(ts); + while (it_timers != rtx_timers_.end() && it_timers->first == ts) { + if (it_timers->second == seq) { + rtx_timers_.erase(it_timers); + break; + } + it_timers++; + } + // remove rtx + rtx_state_.erase(it_rtx); +} + +// fec functions +uint32_t RecoveryStrategy::computeFecPacketsToAsk(bool in_sync) { + double loss_rate = state_->getMaxLossRate() * 100; // use loss rate in % + + if (loss_rate > 95) loss_rate = 95; // max loss rate + + if (loss_rate == 0) return 0; + + // once per minute try to reduce the fec rate. it may happen that for some bin + // we ask too many fec packet. here we try to reduce this values gently + if (round_id_ % ROUNDS_PER_MIN == 0) { + reduceFec(); + } + + // keep track of the last used fec. if we use a new bin on this round reset + // consecutive use and avg loss in the prev bin + uint32_t bin = ceil(loss_rate / 5.0) - 1; + if (bin > fec_per_loss_rate_.size() - 1) bin = fec_per_loss_rate_.size() - 1; + + if (bin != last_fec_used_) { + fec_per_loss_rate_[last_fec_used_].consecutive_use = 0; + fec_per_loss_rate_[last_fec_used_].avg_residual_losses = 0.0; + } + last_fec_used_ = bin; + fec_per_loss_rate_[last_fec_used_].consecutive_use++; + + // we update the stats only once very 5 rounds (1sec) that is the rate at + // which we compute residual losses + if (round_id_ % ROUNDS_PER_SEC == 0) { + double residual_losses = state_->getResidualLossRate() * 100; + // update residual loss rate + fec_per_loss_rate_[bin].avg_residual_losses = + (fec_per_loss_rate_[bin].avg_residual_losses * MOVING_AVG_ALPHA) + + (1 - MOVING_AVG_ALPHA) * residual_losses; + + if ((fec_per_loss_rate_[bin].last_update - round_id_) < + WAIT_BEFORE_FEC_UPDATE) { + // this bin is been updated recently so don't modify it and + // return the current state + return fec_per_loss_rate_[bin].fec_to_ask; + } + + // if the residual loss rate is too high and we can ask more fec packets and + // we are using this configuration since at least 5 sec update fec + if (fec_per_loss_rate_[bin].avg_residual_losses > MAX_RESIDUAL_LOSS_RATE && + fec_per_loss_rate_[bin].fec_to_ask < (n_ - k_) && + fec_per_loss_rate_[bin].consecutive_use > WAIT_BEFORE_FEC_UPDATE) { + // so increase the number of fec packets to ask + fec_per_loss_rate_[bin].fec_to_ask++; + fec_per_loss_rate_[bin].last_update = round_id_; + fec_per_loss_rate_[bin].avg_residual_losses = 0.0; + } + } + + return fec_per_loss_rate_[bin].fec_to_ask; +} + +void RecoveryStrategy::setRtxFec(std::optional<bool> rtx_on, + std::optional<bool> fec_on) { + if (rtx_on) rtx_on_ = *rtx_on; + if (fec_on) fec_on_ = *fec_on; + + if (*callback_) { + notification::RecoveryStrategy strategy = + notification::RecoveryStrategy::RECOVERY_OFF; + + if (rtx_on_ && fec_on_) + strategy = notification::RecoveryStrategy::RTX_AND_FEC; + else if (rtx_on_) + strategy = notification::RecoveryStrategy::RTX_ONLY; + else if (fec_on_) + strategy = notification::RecoveryStrategy::FEC_ONLY; + + (*callback_)(strategy); + } +} + +// common functions +void RecoveryStrategy::onLostTimeout(uint32_t seq) { removePacketState(seq); } + +void RecoveryStrategy::removePacketState(uint32_t seq) { + auto it_fec = recover_with_fec_.find(seq); + if (it_fec != recover_with_fec_.end()) { + recover_with_fec_.erase(it_fec); + return; + } + + deleteRtx(seq); +} + +// private methods + +void RecoveryStrategy::reduceFec() { + for (uint32_t loss_rate = 5; loss_rate < 100; loss_rate += 5) { + double dec_loss_rate = (double)loss_rate / 100.0; + double exp_losses = (double)k_ * dec_loss_rate; + uint32_t fec_to_ask = ceil(exp_losses / (1 - dec_loss_rate)); + + uint32_t bin = ceil(loss_rate / 5.0) - 1; + if (fec_per_loss_rate_[bin].fec_to_ask > fec_to_ask) { + fec_per_loss_rate_[bin].fec_to_ask--; + // std::cout << "reduce fec to ask for bin " << bin << std::endl; + } + } +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_recovery_strategy.h b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h new file mode 100644 index 000000000..9ffc69a1b --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <hicn/transport/interfaces/callbacks.h> +#include <hicn/transport/utils/chrono_typedefs.h> +#include <protocols/indexer.h> +#include <protocols/rtc/rtc_rc.h> +#include <protocols/rtc/rtc_state.h> + +#include <map> +#include <unordered_map> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategy : public std::enable_shared_from_this<RecoveryStrategy> { + protected: + struct rtx_state_ { + uint64_t first_send_; + uint64_t next_send_; + uint32_t rtx_count_; + }; + + using rtxState = struct rtx_state_; + + public: + using SendRtxCallback = std::function<void(uint32_t)>; + + RecoveryStrategy(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, bool use_rtx, bool use_fec, + interface::StrategyCallback *external_callback); + + RecoveryStrategy(RecoveryStrategy &&rs); + + virtual ~RecoveryStrategy(); + + void setRtxFec(std::optional<bool> rtx_on = {}, + std::optional<bool> fec_on = {}); + void setState(RTCState *state) { state_ = state; } + void setRateControl(RTCRateControl *rateControl) { rc_ = rateControl; } + void setFecParams(uint32_t n, uint32_t k); + + void tunrOnRecovery() { recovery_on_ = true; } + + bool isRtx(uint32_t seq) { + if (rtx_state_.find(seq) != rtx_state_.end()) return true; + return false; + } + + bool isPossibleLossWithNoRtx(uint32_t seq) { + if (recover_with_fec_.find(seq) != recover_with_fec_.end()) return true; + return false; + } + + bool isRtxOn() { return rtx_on_; } + + RTCState *getState() { return state_; } + bool lossDetected(uint32_t seq); + void clear(); + + virtual void onNewRound(bool in_sync) = 0; + virtual void newPacketLoss(uint32_t seq) = 0; + virtual void receivedPacket(uint32_t seq) = 0; + void onLostTimeout(uint32_t seq); + + void incRoundId() { round_id_++; } + + // utils + uint64_t getNow() { + uint64_t now = utils::SteadyTime::nowMs().count(); + return now; + } + + protected: + // rtx functions + void addNewRtx(uint32_t seq, bool force); + uint64_t computeNextSend(uint32_t seq, bool new_rtx); + void retransmit(); + void scheduleNextRtx(); + void deleteRtx(uint32_t seq); + + // fec functions + uint32_t computeFecPacketsToAsk(bool in_sync); + + // common functons + void removePacketState(uint32_t seq); + + bool recovery_on_; + bool rtx_on_; + bool fec_on_; + + // this map keeps track of the retransmitted interest, ordered from the oldest + // to the newest one. the state contains the timer of the first send of the + // interest (from pendingIntetests_), the timer of the next send (key of the + // multimap) and the number of rtx + std::map<uint32_t, rtxState> rtx_state_; + // this map stored the rtx by timer. The key is the time at which the rtx + // should be sent, and the val is the interest seq number + std::multimap<uint64_t, uint32_t> rtx_timers_; + + // lost packets that will be recovered with fec + std::unordered_set<uint32_t> recover_with_fec_; + + // rtx vars + std::unique_ptr<asio::steady_timer> timer_; + uint64_t next_rtx_timer_; + SendRtxCallback send_rtx_callback_; + + // fec vars + uint32_t n_; + uint32_t k_; + Indexer *indexer_; + + RTCState *state_; + RTCRateControl *rc_; + + private: + struct fec_state_ { + uint32_t fec_to_ask; + uint32_t last_update; // round id of the last update + // (wait 10 ruonds (2sec) between updates) + uint32_t consecutive_use; // consecutive ruonds where this fec was used + double avg_residual_losses; + }; + + void reduceFec(); + + uint32_t round_id_; // number of rounds + uint32_t last_fec_used_; + std::vector<fec_state_> fec_per_loss_rate_; + interface::StrategyCallback *callback_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_delay.cc b/libtransport/src/protocols/rtc/rtc_rs_delay.cc new file mode 100644 index 000000000..e2c60ca77 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_delay.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_delay.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyDelayBased::RecoveryStrategyDelayBased( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + external_callback), // start with rtx + congestion_state_(false), + probing_state_(false), + switch_rounds_(0) {} + +RecoveryStrategyDelayBased::RecoveryStrategyDelayBased(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(true, false); + // we have to re-init congestion and + // probing + congestion_state_ = false; + probing_state_ = false; +} + +RecoveryStrategyDelayBased::~RecoveryStrategyDelayBased() {} + +void RecoveryStrategyDelayBased::softSwitchToFec(uint32_t fec_to_ask) { + if (fec_to_ask == 0) { + setRtxFec(true, false); + switch_rounds_ = 0; + } else { + switch_rounds_++; + if (switch_rounds_ >= 5) { + setRtxFec(false, true); + } else { + setRtxFec({}, true); + } + } +} + +void RecoveryStrategyDelayBased::onNewRound(bool in_sync) { + if (!recovery_on_) { + // disable fec so that no extra packet will be sent + // for rtx we check if recovery is on in newPacketLoss + setRtxFec(true, false); + indexer_->setNFec(0); + return; + } + + uint64_t rtt = state_->getMinRTT(); + + bool congestion = false; + // XXX at the moment we are not looking at congestion events + // congestion = rc_->inCongestionState(); + + if ((!fec_on_ && rtt >= 100) || (fec_on_ && rtt > 80) || congestion) { + // switch from rtx to fec or keep use fec. Notice that if some rtx are + // waiting to be scheduled, they will be sent normally, but no new rtx will + // be created If the loss rate is 0 keep to use RTX. + uint32_t fec_to_ask = computeFecPacketsToAsk(in_sync); + softSwitchToFec(fec_to_ask); + indexer_->setNFec(fec_to_ask); + return; + } + + if ((fec_on_ && rtt <= 80) || (!rtx_on_ && rtt <= 100)) { + // turn on rtx + softSwitchToFec(0); + indexer_->setNFec(0); + return; + } +} + +void RecoveryStrategyDelayBased::newPacketLoss(uint32_t seq) { + if (rtx_on_ && recovery_on_) { + addNewRtx(seq, false); + } else { + if (!state_->isPending(seq) && !indexer_->isFec(seq)) { + addNewRtx(seq, true); + } else { + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } + } +} + +void RecoveryStrategyDelayBased::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +void RecoveryStrategyDelayBased::probing() { + // TODO + // for the moment ask for all fec and exit the probing phase + probing_state_ = false; + setRtxFec(false, true); + indexer_->setNFec(computeFecPacketsToAsk(true)); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_delay.h b/libtransport/src/protocols/rtc/rtc_rs_delay.h new file mode 100644 index 000000000..0dd199965 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_delay.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyDelayBased : public RecoveryStrategy { + public: + RecoveryStrategyDelayBased(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyDelayBased(RecoveryStrategy &&rs); + + ~RecoveryStrategyDelayBased(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); + + private: + void softSwitchToFec(uint32_t fec_to_ask); + + bool congestion_state_; + bool probing_state_; + uint32_t switch_rounds_; + + void probing(); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc new file mode 100644 index 000000000..36d8e39f0 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_fec_only.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyFecOnly::RecoveryStrategyFecOnly( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, true, + external_callback), + congestion_state_(false), + probing_state_(false), + switch_rounds_(0) {} + +RecoveryStrategyFecOnly::RecoveryStrategyFecOnly(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(false, true); + congestion_state_ = false; + probing_state_ = false; +} + +RecoveryStrategyFecOnly::~RecoveryStrategyFecOnly() {} + +void RecoveryStrategyFecOnly::onNewRound(bool in_sync) { + if (!recovery_on_) { + indexer_->setNFec(0); + return; + } + + // XXX for the moment we are considering congestion events + // if(rc_->inCongestionState()){ + // congestion_state_ = true; + // probing_state_ = false; + // indexer_->setNFec(0); + // return; + // } + + // no congestion + if (congestion_state_) { + // this is the first round after congestion + // enter probing phase + probing_state_ = true; + congestion_state_ = false; + } + + if (probing_state_) { + probing(); + } else { + uint32_t fec_to_ask = computeFecPacketsToAsk(in_sync); + // If fec_to_ask == 0 we use rtx even if in these strategy we use only fec. + // In this way the first packet loss that triggers the usage of fec can be + // recovered using rtx, otherwise it will always be lost + if (fec_to_ask == 0) { + setRtxFec(true, false); + switch_rounds_ = 0; + } else { + switch_rounds_++; + if (switch_rounds_ >= 5) { + setRtxFec(false, true); + } else { + setRtxFec({}, true); + } + } + indexer_->setNFec(fec_to_ask); + } +} + +void RecoveryStrategyFecOnly::newPacketLoss(uint32_t seq) { + if (rtx_on_ && recovery_on_) { + addNewRtx(seq, false); + } else { + if (!state_->isPending(seq) && !indexer_->isFec(seq)) { + addNewRtx(seq, true); + } else { + // if not pending add rtc + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } + } +} + +void RecoveryStrategyFecOnly::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +void RecoveryStrategyFecOnly::probing() { + // TODO + // for the moment ask for all fec and exit the probing phase + probing_state_ = false; + uint32_t fec_to_ask = computeFecPacketsToAsk(true); + indexer_->setNFec(fec_to_ask); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_fec_only.h b/libtransport/src/protocols/rtc/rtc_rs_fec_only.h new file mode 100644 index 000000000..37b505d35 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyFecOnly : public RecoveryStrategy { + public: + RecoveryStrategyFecOnly(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyFecOnly(RecoveryStrategy &&rs); + + ~RecoveryStrategyFecOnly(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); + + private: + bool congestion_state_; + bool probing_state_; + uint32_t switch_rounds_; + + void probing(); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc new file mode 100644 index 000000000..bd153d209 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_low_rate.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyLowRate::RecoveryStrategyLowRate( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, true, + external_callback), // start with fec + fec_consecutive_rounds_((MILLI_IN_A_SEC / ROUND_LEN) * 5), // 5 sec + rtx_allowed_consecutive_rounds_(0) { + initSwitchVector(); +} + +RecoveryStrategyLowRate::RecoveryStrategyLowRate(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)), + fec_consecutive_rounds_((MILLI_IN_A_SEC / ROUND_LEN) * 5), // 5 sec + rtx_allowed_consecutive_rounds_(0) { + setRtxFec(false, true); + initSwitchVector(); +} + +RecoveryStrategyLowRate::~RecoveryStrategyLowRate() {} + +void RecoveryStrategyLowRate::initSwitchVector() { + // TODO adjust thresholds here when new data are collected + // see resutls in + // https://confluence-eng-gpk1.cisco.com/conf/display/SPT/dailyreports + thresholds_t t1; + t1.rtt = 15; // 15ms + t1.loss_rtx_to_fec = 15; // 15% + t1.loss_fec_to_rtx = 10; // 10% + thresholds_t t2; + t2.rtt = 35; // 35ms + t2.loss_rtx_to_fec = 5; // 5% + t2.loss_fec_to_rtx = 1; // 1% + switch_vector.push_back(t1); + switch_vector.push_back(t2); +} + +void RecoveryStrategyLowRate::setRecoveryParameters(bool use_rtx, bool use_fec, + uint32_t fec_to_ask) { + setRtxFec(use_rtx, use_fec); + indexer_->setNFec(fec_to_ask); +} + +void RecoveryStrategyLowRate::selectRecoveryStrategy(bool in_sync) { + uint32_t fec_to_ask = computeFecPacketsToAsk(in_sync); + if (fec_to_ask == 0) { + // fec is off, turn on RTX immediatly to avoid packet losses + setRecoveryParameters(true, false, 0); + fec_consecutive_rounds_ = 0; + return; + } + + uint32_t loss_rate = std::round(state_->getPerSecondLossRate() * 100); + uint32_t rtt = state_->getAvgRTT(); + + bool use_rtx = false; + for (size_t i = 0; i < switch_vector.size(); i++) { + uint32_t max_loss_rate = 0; + if (fec_on_) + max_loss_rate = switch_vector[i].loss_fec_to_rtx; + else + max_loss_rate = switch_vector[i].loss_rtx_to_fec; + + if (rtt < switch_vector[i].rtt && loss_rate < max_loss_rate) { + use_rtx = true; + rtx_allowed_consecutive_rounds_++; + break; + } + } + + if (!use_rtx) rtx_allowed_consecutive_rounds_ = 0; + + if (use_rtx) { + if (fec_on_) { + // here we should swtich from RTX to FEC + // wait 10sec where the switch is allowed before actually switch + if (rtx_allowed_consecutive_rounds_ >= + ((MILLI_IN_A_SEC / ROUND_LEN) * 10)) { // 10 sec + // use RTX + setRecoveryParameters(true, false, 0); + fec_consecutive_rounds_ = 0; + } else { + // keep using FEC (and maybe RTX) + setRecoveryParameters(true, true, fec_to_ask); + fec_consecutive_rounds_++; + } + } else { + // keep using RTX + setRecoveryParameters(true, false, 0); + fec_consecutive_rounds_ = 0; + } + } else { + // use FEC and RTX + setRecoveryParameters(true, true, fec_to_ask); + fec_consecutive_rounds_++; + } + + // everytime that we anable FEC we keep also RTX on. in this way the first + // losses that are not covered by FEC are recovered using RTX. after 5 sec we + // disable fec + if (fec_consecutive_rounds_ >= ((MILLI_IN_A_SEC / ROUND_LEN) * 5)) { + // turn off RTX + setRtxFec(false); + } +} + +void RecoveryStrategyLowRate::onNewRound(bool in_sync) { + if (!recovery_on_) { + // disable fec so that no extra packet will be sent + // for rtx we check if recovery is on in newPacketLoss + setRtxFec(true, false); + indexer_->setNFec(0); + return; + } + + // XXX since this strategy will be used only for flow at low rate we do not + // consider congestion events like in other strategies + + selectRecoveryStrategy(in_sync); +} + +void RecoveryStrategyLowRate::newPacketLoss(uint32_t seq) { + if (rtx_on_ && recovery_on_) { + addNewRtx(seq, false); + } else { + if (!state_->isPending(seq) && !indexer_->isFec(seq)) { + addNewRtx(seq, true); + } else { + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } + } +} + +void RecoveryStrategyLowRate::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_low_rate.h b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h new file mode 100644 index 000000000..f0c7bd0d5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +#include <vector> + +namespace transport { + +namespace protocol { + +namespace rtc { + +struct thresholds_t { + uint32_t rtt; + uint32_t loss_rtx_to_fec; // loss rate used to move from rtx to fec + uint32_t loss_fec_to_rtx; // loss rate used to move from fec to rtx +}; + +class RecoveryStrategyLowRate : public RecoveryStrategy { + public: + RecoveryStrategyLowRate(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyLowRate(RecoveryStrategy &&rs); + + ~RecoveryStrategyLowRate(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); + + private: + void initSwitchVector(); + void setRecoveryParameters(bool use_rtx, bool use_fec, uint32_t fec_to_ask); + void selectRecoveryStrategy(bool in_sync); + + uint32_t fec_consecutive_rounds_; + uint32_t rtx_allowed_consecutive_rounds_; + + // this table contains the thresholds that indicates when to switch from RTX + // to FEC and viceversa. values in the vector are detected with a set of + // experiments. the vector is used in the following way: if rtt and loss rate + // are less than one of the values in the in the vector, losses are + // recovered using RTX. otherwive losses are recovered using FEC. as for FEC + // only and delay based strategy, the swith from RTX to FEC is smooth, + // meaning that FEC and RTX are used together for some rounds + std::vector<thresholds_t> switch_vector; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc new file mode 100644 index 000000000..499e978f1 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_recovery_off.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, false, + external_callback) {} + +RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(false, false); +} + +RecoveryStrategyRecoveryOff::~RecoveryStrategyRecoveryOff() {} + +void RecoveryStrategyRecoveryOff::onNewRound(bool in_sync) { + // nothing to do + return; +} + +void RecoveryStrategyRecoveryOff::newPacketLoss(uint32_t seq) { + // here we only keep track of the lost packets to avoid to + // count them multple times in the counters. for this we + // use the recover_with_fec_ set + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); +} + +void RecoveryStrategyRecoveryOff::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h new file mode 100644 index 000000000..98cd1e6a5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyRecoveryOff : public RecoveryStrategy { + public: + RecoveryStrategyRecoveryOff(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs); + + ~RecoveryStrategyRecoveryOff(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc new file mode 100644 index 000000000..c1ae9b53d --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_rtx_only.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + external_callback) {} + +RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(true, false); +} + +RecoveryStrategyRtxOnly::~RecoveryStrategyRtxOnly() {} + +void RecoveryStrategyRtxOnly::onNewRound(bool in_sync) { + // nothing to do + return; +} + +void RecoveryStrategyRtxOnly::newPacketLoss(uint32_t seq) { + if (!recovery_on_) { + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + return; + } + addNewRtx(seq, false); +} + +void RecoveryStrategyRtxOnly::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h new file mode 100644 index 000000000..7ae909454 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyRtxOnly : public RecoveryStrategy { + public: + RecoveryStrategyRtxOnly(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyRtxOnly(RecoveryStrategy &&rs); + + ~RecoveryStrategyRtxOnly(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_state.cc b/libtransport/src/protocols/rtc/rtc_state.cc index c99205a26..6a21531f8 100644 --- a/libtransport/src/protocols/rtc/rtc_state.cc +++ b/libtransport/src/protocols/rtc/rtc_state.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -24,15 +24,15 @@ namespace protocol { namespace rtc { RTCState::RTCState(Indexer *indexer, - ProbeHandler::SendProbeCallback &&rtt_probes_callback, + ProbeHandler::SendProbeCallback &&probe_callback, DiscoveredRttCallback &&discovered_rtt_callback, asio::io_service &io_service) - : indexer_(indexer), - rtt_probes_(std::make_shared<ProbeHandler>(std::move(rtt_probes_callback), - io_service)), + : loss_history_(10), // log 10sec history + indexer_(indexer), + probe_handler_(std::make_shared<ProbeHandler>(std::move(probe_callback), + io_service)), discovered_rtt_callback_(std::move(discovered_rtt_callback)) { init_rtt_timer_ = std::make_unique<asio::steady_timer>(io_service); - initParams(); } RTCState::~RTCState() {} @@ -55,9 +55,19 @@ void RTCState::initParams() { highest_seq_received_in_order_ = 0; last_seq_nacked_ = 0; loss_rate_ = 0.0; - avg_loss_rate_ = 0.0; + avg_loss_rate_ = -1.0; max_loss_rate_ = 0.0; last_round_loss_rate_ = 0.0; + + // loss rate per sec + lost_per_sec_ = 0; + total_expected_packets_ = 0; + per_sec_loss_rate_ = 0.0; + + // residual losses counters + expected_packets_ = 0; + packets_sent_to_app_ = 0; + rounds_from_last_compute_ = 0; residual_loss_rate_ = 0.0; // fec counters @@ -66,9 +76,13 @@ void RTCState::initParams() { // bw counters received_bytes_ = 0; + received_fec_bytes_ = 0; + recovered_bytes_with_fec_ = 0; + avg_packet_size_ = INIT_PACKET_SIZE; production_rate_ = 0.0; received_rate_ = 0.0; + fec_recovered_rate_ = 0.0; // nack counter nack_on_last_round_ = false; @@ -95,31 +109,30 @@ void RTCState::initParams() { path_table_.clear(); main_path_ = nullptr; - // packet received - received_or_lost_packets_.clear(); + // packet cache (not pending anymore) + packet_cache_.clear(); // pending interests pending_interests_.clear(); - // skipped interest + // used to keep track of the skipped interest last_interest_sent_ = 0; - skipped_interests_.clear(); // init rtt first_interest_sent_time_ = ~0; first_interest_sent_seq_ = 0; + // start probing the producer init_rtt_ = false; - rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); - rtt_probes_->sendProbes(); + probe_handler_->setSuffixRange(MIN_INIT_PROBE_SEQ, MAX_INIT_PROBE_SEQ); + probe_handler_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); + probe_handler_->sendProbes(); setInitRttTimer(INIT_RTT_PROBE_RESTART); } // packet events void RTCState::onSendNewInterest(const core::Name *interest_name) { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint32_t seq = interest_name->getSuffix(); pending_interests_.insert(std::pair<uint32_t, uint64_t>(seq, now)); @@ -137,11 +150,12 @@ void RTCState::onSendNewInterest(const core::Name *interest_name) { } // TODO what happen in case of jumps? - // look for skipped interests - skipped_interests_.erase(seq); // remove seq if it is there + eraseFromPacketCache( + seq); // if we send this interest we don't know its state for (uint32_t i = last_interest_sent_ + 1; i < seq; i++) { if (indexer_->isFec(i)) { - skipped_interests_.insert(i); + // only fec packets can be skipped + addToPacketCache(i, PacketState::SKIPPED); } } @@ -155,6 +169,7 @@ void RTCState::onTimeout(uint32_t seq, bool lost) { auto it = pending_interests_.find(seq); if (it != pending_interests_.end()) { pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; } received_timeouts_++; @@ -162,11 +177,12 @@ void RTCState::onTimeout(uint32_t seq, bool lost) { } void RTCState::onLossDetected(uint32_t seq) { - if (!indexer_->isFec(seq)) { - packets_lost_++; - } else if (skipped_interests_.find(seq) == skipped_interests_.end() && - seq >= first_interest_sent_seq_) { + PacketState state = getPacketState(seq); + + // if the packet is already marked with a state, do nothing + if (state == PacketState::UNKNOWN) { packets_lost_++; + addToPacketCache(seq, PacketState::LOST); } } @@ -178,30 +194,40 @@ void RTCState::onRetransmission(uint32_t seq) { auto it = pending_interests_.find(seq); if (it != pending_interests_.end()) { pending_interests_.erase(it); -#if 0 - packets_lost_++; -#endif + if (indexer_->isFec(seq)) pending_fec_pkt_--; } sent_rtx_++; sent_rtx_last_round_++; } +void RTCState::onPossibleLossWithNoRtx(uint32_t seq) { + // if fec is on or rtx is disable we don't need to do anything to recover a + // packet. however in both cases we need to remove possible missing packets + // from the window of pendinig interest in order to free space without wating + // for the timeout. + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; + } +} + void RTCState::onDataPacketReceived(const core::ContentObject &content_object, bool compute_stats) { uint32_t seq = content_object.getName().getSuffix(); + if (compute_stats) { updatePathStats(content_object, false); received_data_last_round_++; } received_data_++; + packets_sent_to_app_++; - struct data_packet_t *data_pkt = - (struct data_packet_t *)content_object.getPayload()->data(); - uint64_t production_time = data_pkt->getTimestamp(); - if (last_prod_update_ < production_time) { - last_prod_update_ = production_time; - uint32_t production_rate = data_pkt->getProductionRate(); - production_rate_ = (double)production_rate; + core::ParamsRTC params = RTCState::getDataParams(content_object); + + if (last_prod_update_ < params.timestamp) { + last_prod_update_ = params.timestamp; + production_rate_ = (double)params.prod_rate; } updatePacketSize(content_object); @@ -219,9 +245,18 @@ void RTCState::onDataPacketReceived(const core::ContentObject &content_object, void RTCState::onFecPacketReceived(const core::ContentObject &content_object) { uint32_t seq = content_object.getName().getSuffix(); - updateReceivedBytes(content_object); + // updateReceivedBytes(content_object); + received_fec_bytes_ += + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); + + if (seq > highest_seq_received_) highest_seq_received_ = seq; + + PacketState state = getPacketState(seq); + if (state != PacketState::LOST) { + // increase only for not lost packets + received_fec_pkt_++; + } addRecvOrLost(seq, PacketState::RECEIVED); - received_fec_pkt_++; // the producer is responding // it is generating valid data packets so we consider it active producer_is_active_ = true; @@ -233,7 +268,7 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack, struct nack_packet_t *nack_pkt = (struct nack_packet_t *)nack.getPayload()->data(); uint64_t production_time = nack_pkt->getTimestamp(); - uint32_t production_seq = nack_pkt->getProductionSegement(); + uint32_t production_seq = nack_pkt->getProductionSegment(); uint32_t production_rate = nack_pkt->getProductionRate(); if (TRANSPORT_EXPECT_FALSE(main_path_ == nullptr) || @@ -255,6 +290,7 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack, received_nacks_++; received_nacks_last_round_++; + bool to_delete = false; if (production_seq > seq) { // old nack, seq is lost // update last nacked @@ -266,12 +302,19 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack, // future nack // remove the nack from the pending interest map // (the packet is not received/lost yet) - if (indexer_->isFec(seq)) pending_fec_pkt_--; - pending_interests_.erase(seq); + to_delete = true; } else { // this should be a quite rear event. simply remove the // packet from the pending interest list - pending_interests_.erase(seq); + to_delete = true; + } + + if (to_delete) { + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; + } } // the producer is responding @@ -295,44 +338,58 @@ void RTCState::onPacketLost(uint32_t seq) { } #endif if (!indexer_->isFec(seq)) { - definitely_lost_pkt_++; - DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost"; + PacketState state = getPacketState(seq); + if (state == PacketState::LOST || state == PacketState::UNKNOWN) { + definitely_lost_pkt_++; + DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost"; + } } - addRecvOrLost(seq, PacketState::LOST); + addRecvOrLost(seq, PacketState::DEFINITELY_LOST); } void RTCState::onPacketRecoveredRtx(uint32_t seq) { + packets_sent_to_app_++; + if (seq > highest_seq_received_) highest_seq_received_ = seq; losses_recovered_++; addRecvOrLost(seq, PacketState::RECEIVED); } -void RTCState::onPacketRecoveredFec(uint32_t seq) { +void RTCState::onFecPacketRecoveredRtx(uint32_t seq) { + // This is the same as onPacketRecoveredRtx, but in this is case the + // pkt is also a FEC pkt, the addRecvOrLost will be called afterwards + if (seq > highest_seq_received_) highest_seq_received_ = seq; + losses_recovered_++; +} + +void RTCState::onPacketRecoveredFec(uint32_t seq, uint32_t size) { losses_recovered_++; + packets_sent_to_app_++; + recovered_bytes_with_fec_ += size; + + if (seq > highest_seq_received_) highest_seq_received_ = seq; + + // adding header to the count + recovered_bytes_with_fec_ += 60; // XXX get header size some where + + if (getPacketState(seq) == PacketState::UNKNOWN) + onLossDetected(seq); // the pkt was lost but didn't account for it yet + addRecvOrLost(seq, PacketState::RECEIVED); } bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { uint32_t seq = probe.getName().getSuffix(); - uint64_t rtt; - - rtt = rtt_probes_->getRtt(seq); + uint64_t rtt; + rtt = probe_handler_->getRtt(seq); if (rtt == 0) return false; // this is not a valid probe - // like for data and nacks update the path stats. Here the RTT is computed - // by the probe handler. Both probes for rtt and bw are good to esimate - // info on the path + // Like for data and nacks update the path stats. Here the RTT is computed + // by the probe handler. Both probes for rtt and bw are good to estimate + // info on the path. uint32_t path_label = probe.getPathLabel(); - auto path_it = path_table_.find(path_label); - // update production rate and last_seq_nacked like in case of a nack - struct nack_packet_t *probe_pkt = - (struct nack_packet_t *)probe.getPayload()->data(); - uint64_t sender_timestamp = probe_pkt->getTimestamp(); - uint32_t production_seq = probe_pkt->getProductionSegement(); - uint32_t production_rate = probe_pkt->getProductionRate(); - if (path_it == path_table_.end()) { // found a new path std::shared_ptr<RTCDataPath> newPath = @@ -344,26 +401,26 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { auto path = path_it->second; - path->insertRttSample(rtt); + path->insertRttSample(utils::SteadyTime::Milliseconds(rtt), true); path->receivedNack(); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); + + core::ParamsRTC params = RTCState::getProbeParams(probe); - int64_t OWD = now - sender_timestamp; + int64_t OWD = now - params.timestamp; path->insertOwdSample(OWD); - if (last_prod_update_ < sender_timestamp) { - last_production_seq_ = production_seq; - last_prod_update_ = sender_timestamp; - production_rate_ = (double)production_rate; + if (last_prod_update_ < params.timestamp) { + last_production_seq_ = params.prod_seg; + last_prod_update_ = params.timestamp; + production_rate_ = (double)params.prod_rate; } // the producer is responding // we consider it active only if the production rate is not 0 // or the production sequence numner is not 1 - if (production_rate_ != 0 || production_seq != 1) { + if (production_rate_ != 0 || params.prod_seg != 1) { producer_is_active_ = true; } @@ -375,7 +432,7 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { if (!init_rtt_ && received_probes_ <= INIT_RTT_PROBES) { if (received_probes_ == 1) { // we got the first probe, wait at most INIT_RTT_PROBE_WAIT sec for the - // others + // others. main_path_ = path; setInitRttTimer(INIT_RTT_PROBE_WAIT); } @@ -393,11 +450,21 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { return true; } -void RTCState::onNewRound(double round_len, bool in_sync) { - // XXX - // here we take into account only the single path case so we assume that we - // don't use two paths in parellel for this single flow +void RTCState::onJumpForward(uint32_t next_seq) { + for (uint32_t seq = highest_seq_received_in_order_ + 1; seq < next_seq; + seq++) { + auto it = pending_interests_.find(seq); + PacketState packet_state = getPacketState(seq); + if (it == pending_interests_.end() && + packet_state != PacketState::RECEIVED && + packet_state != PacketState::DEFINITELY_LOST) { + onLossDetected(seq); + onPacketLost(seq); + } + } +} +void RTCState::onNewRound(double round_len, bool in_sync) { if (path_table_.empty()) return; double bytes_per_sec = @@ -407,7 +474,24 @@ void RTCState::onNewRound(double round_len, bool in_sync) { else received_rate_ = (received_rate_ * MOVING_AVG_ALPHA) + ((1 - MOVING_AVG_ALPHA) * bytes_per_sec); + double fec_bytes_per_sec = + ((double)received_fec_bytes_ * (MILLI_IN_A_SEC / round_len)); + + if (fec_received_rate_ == 0) + fec_received_rate_ = fec_bytes_per_sec; + else + fec_received_rate_ = (fec_received_rate_ * 0.8) + (0.2 * fec_bytes_per_sec); + + double fec_recovered_bytes_per_sec = + ((double)recovered_bytes_with_fec_ * (MILLI_IN_A_SEC / round_len)); + if (fec_recovered_rate_ == 0) + fec_recovered_rate_ = fec_recovered_bytes_per_sec; + else + fec_recovered_rate_ = + (fec_recovered_rate_ * 0.8) + (0.2 * fec_recovered_bytes_per_sec); + +#if 0 // search for an active path. There should be only one active path (meaning a // path that leads to the producer socket -no cache- and from which we are // currently getting data packets) at any time. However it may happen that @@ -428,9 +512,36 @@ void RTCState::onNewRound(double round_len, bool in_sync) { } } } +#endif + + // search for an active path. Is it possible to have multiple path that are + // used at the same time. We use as reference path the one from where we gets + // more packets. This means that the path should have better lantecy or less + // channel losses + + uint32_t last_round_packets = 0; + std::shared_ptr<RTCDataPath> old_main_path = main_path_; + main_path_ = nullptr; + + for (auto it = path_table_.begin(); it != path_table_.end(); it++) { + if (it->second->isActive()) { + uint32_t pkt = it->second->getPacketsLastRound(); + if (pkt > last_round_packets) { + last_round_packets = pkt; + main_path_ = it->second; + } + } + it->second->roundEnd(); + } - // if (in_sync) updateLossRate(); - updateLossRate(); + if (main_path_ == nullptr) main_path_ = old_main_path; + + // in case we get a new main path we reset the stats of the old one. this is + // beacuse, in case we need to switch back we don't what to take decisions on + // old stats that may be outdated. + if (main_path_ != old_main_path) old_main_path->clearRtt(); + + updateLossRate(in_sync); // handle nacks if (!nack_on_last_round_ && received_bytes_ > 0) { @@ -460,6 +571,8 @@ void RTCState::onNewRound(double round_len, bool in_sync) { // reset counters received_bytes_ = 0; + received_fec_bytes_ = 0; + recovered_bytes_with_fec_ = 0; packets_lost_ = 0; definitely_lost_pkt_ = 0; losses_recovered_ = 0; @@ -516,20 +629,16 @@ void RTCState::updatePathStats(const core::ContentObject &content_object, // it means that we are processing an interest // that is not pending - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t RTT = now - interest_sent_time; - path->insertRttSample(RTT); + path->insertRttSample(utils::SteadyTime::Milliseconds(RTT), false); // compute OWD (the first part of the nack and data packet header are the // same, so we cast to data data packet) - struct data_packet_t *packet = - (struct data_packet_t *)content_object.getPayload()->data(); - uint64_t sender_timestamp = packet->getTimestamp(); - int64_t OWD = now - sender_timestamp; + core::ParamsRTC params = RTCState::getDataParams(content_object); + int64_t OWD = now - params.timestamp; path->insertOwdSample(OWD); // compute IAT or set path to producer @@ -543,59 +652,106 @@ void RTCState::updatePathStats(const core::ContentObject &content_object, } } -void RTCState::updateLossRate() { +void RTCState::updateLossRate(bool in_sync) { last_round_loss_rate_ = loss_rate_; loss_rate_ = 0.0; - residual_loss_rate_ = 0.0; uint32_t number_theorically_received_packets_ = highest_seq_received_ - first_seq_in_round_; - // in this case no new packet was recevied after the previuos round, avoid - // division by 0 - if (number_theorically_received_packets_ == 0) return; - // XXX this may be quite inefficient if the rate is high // maybe is better to iterate over the set? - for (uint32_t i = first_seq_in_round_; i < highest_seq_received_; i++) { - auto it = skipped_interests_.find(i); - if (it != skipped_interests_.end()) { + + uint32_t fec_packets = 0; + for (uint32_t i = (first_seq_in_round_ + 1); i < highest_seq_received_; i++) { + PacketState state = getPacketState(i); + if (state == PacketState::SKIPPED) { if (number_theorically_received_packets_ > 0) number_theorically_received_packets_--; - skipped_interests_.erase(it); } + if (indexer_->isFec(i)) fec_packets++; } + if (indexer_->isFec(highest_seq_received_)) fec_packets++; - loss_rate_ = (double)((double)(packets_lost_) / - (double)number_theorically_received_packets_); + // in this case no new packet was received after the previous round, avoid + // division by 0 + if (number_theorically_received_packets_ == 0 && packets_lost_ == 0) return; - if (rounds_ % 15 == 0) max_loss_rate_ = 0; // reset every 3 sec - if (loss_rate_ > max_loss_rate_) max_loss_rate_ = loss_rate_; + if (number_theorically_received_packets_ != 0) + loss_rate_ = (double)((double)(packets_lost_) / + (double)number_theorically_received_packets_); + else + // we didn't receive anything except NACKs that triggered losses + loss_rate_ = 1.0; - if (avg_loss_rate_ == 0) + if (avg_loss_rate_ == -1.0) avg_loss_rate_ = loss_rate_; else avg_loss_rate_ = avg_loss_rate_ * MOVING_AVG_ALPHA + loss_rate_ * (1 - MOVING_AVG_ALPHA); - residual_loss_rate_ = (double)((double)(packets_lost_ - losses_recovered_) / - (double)number_theorically_received_packets_); + // update counters for loss rate per second + total_expected_packets_ += number_theorically_received_packets_; + lost_per_sec_ += packets_lost_; + + if (in_sync) { + // update counters for residual losses + // fec packets are not sent to the app so we don't want to count them here + expected_packets_ += + ((highest_seq_received_ - first_seq_in_round_) - fec_packets); + } else { + packets_sent_to_app_ = 0; + } + + if (rounds_from_last_compute_ >= (MILLI_IN_A_SEC / ROUND_LEN)) { + // compute loss rate per second + if (lost_per_sec_ > total_expected_packets_) + lost_per_sec_ = total_expected_packets_; + + if (total_expected_packets_ == 0) + per_sec_loss_rate_ = 0; + else + per_sec_loss_rate_ = + (double)((double)(lost_per_sec_) / (double)total_expected_packets_); + + loss_history_.pushBack(per_sec_loss_rate_); + max_loss_rate_ = getMaxLoss(); + + if (in_sync && expected_packets_ != 0) { + // compute residual loss rate + if (packets_sent_to_app_ > expected_packets_) { + // this may happen if we get packet from the prev bin that get recovered + // on the current one + packets_sent_to_app_ = expected_packets_; + } + + residual_loss_rate_ = + 1.0 - ((double)packets_sent_to_app_ / (double)expected_packets_); + if (residual_loss_rate_ < 0.0) residual_loss_rate_ = 0.0; + } + + lost_per_sec_ = 0; + total_expected_packets_ = 0; + expected_packets_ = 0; + packets_sent_to_app_ = 0; + rounds_from_last_compute_ = 0; + } + + rounds_from_last_compute_++; +} - if (residual_loss_rate_ < 0) residual_loss_rate_ = 0; +void RTCState::dataToBeReceived(uint32_t seq) { + addToPacketCache(seq, PacketState::TO_BE_RECEIVED); } void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { - if (indexer_->isFec(seq)) { - pending_fec_pkt_--; + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; } - pending_interests_.erase(seq); - if (received_or_lost_packets_.size() >= MAX_CACHED_PACKETS) { - received_or_lost_packets_.erase(received_or_lost_packets_.begin()); - } - // notice that it may happen that a packet that we consider lost arrives after - // some time, in this case we simply overwrite the packet state. - received_or_lost_packets_[seq] = state; + addToPacketCache(seq, state); // keep track of the last packet received/lost // without holes. @@ -608,16 +764,25 @@ void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { } else if (seq <= highest_seq_received_in_order_) { // here we do nothing } else if (seq > highest_seq_received_in_order_) { - // 1) there is a gap in the sequence so we do not update largest_in_seq_ - // 2) all the packets from largest_in_seq_ to seq are in - // received_or_lost_packets_ an we upate largest_in_seq_ - // or are FEC packets + // 1) there is a gap in the sequence so we do not update + // highest_seq_received_in_order_ + // 2) all the packets from highest_seq_received_in_order_ to seq are + // received or lost or are fec packetis. In this case we increase + // highest_seq_received_in_order_ until we find an hole in the sequence for (uint32_t i = highest_seq_received_in_order_ + 1; i <= seq; i++) { - if (received_or_lost_packets_.find(i) == - received_or_lost_packets_.end() && - !indexer_->isFec(i)) { - break; + PacketState state = getPacketState(i); + if ((state == PacketState::UNKNOWN || state == PacketState::LOST)) { + if (indexer_->isFec(i)) { + // this is a fec packet and we don't care to receive it + // however we may need to increse the number or lost packets + // XXX: in case we want to use rtx to recover fec packets, + // this may prevent to detect a packet loss and no rtx will be sent + onLossDetected(i); + } else { + // this is a data packet and we need to get it + break; + } } // this packet is in order so we can update the // highest_seq_received_in_order_ @@ -629,9 +794,14 @@ void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { void RTCState::setInitRttTimer(uint32_t wait) { init_rtt_timer_->cancel(); init_rtt_timer_->expires_from_now(std::chrono::milliseconds(wait)); - init_rtt_timer_->async_wait([this](std::error_code ec) { + + std::weak_ptr<RTCState> self = shared_from_this(); + init_rtt_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - checkInitRttTimer(); + + if (auto ptr = self.lock()) { + ptr->checkInitRttTimer(); + } }); } @@ -639,19 +809,25 @@ void RTCState::checkInitRttTimer() { if (received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV) { // we didn't received enough probes, restart received_probes_ = 0; - rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); - rtt_probes_->sendProbes(); + probe_handler_->setSuffixRange(MIN_INIT_PROBE_SEQ, MAX_INIT_PROBE_SEQ); + probe_handler_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); + probe_handler_->sendProbes(); setInitRttTimer(INIT_RTT_PROBE_RESTART); return; } + init_rtt_ = true; main_path_->roundEnd(); - rtt_probes_->setProbes(RTT_PROBE_INTERVAL, 0); - rtt_probes_->sendProbes(); + loss_history_.pushBack(probe_handler_->getProbeLossRate()); + max_loss_rate_ = getMaxLoss(); + + probe_handler_->setSuffixRange(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ); + probe_handler_->setProbes(RTT_PROBE_INTERVAL, 0); + probe_handler_->sendProbes(); // init last_seq_nacked_. skip packets that may come from the cache double prod_rate = getProducerRate(); - double rtt = (double)getRTT() / MILLI_IN_A_SEC; + double rtt = (double)getMinRTT() / MILLI_IN_A_SEC; double packet_size = getAveragePacketSize(); uint32_t pkt_in_rtt_ = std::floor(((prod_rate / packet_size) * rtt) * 0.8); last_seq_nacked_ = last_production_seq_ + pkt_in_rtt_; @@ -659,6 +835,68 @@ void RTCState::checkInitRttTimer() { discovered_rtt_callback_(); } +double RTCState::getMaxLoss() { + if (loss_history_.size() != 0) return loss_history_.begin(); + return 0; +} + +core::ParamsRTC RTCState::getProbeParams(const core::ContentObject &probe) { + uint32_t seq = probe.getName().getSuffix(); + core::ParamsRTC params; + + switch (ProbeHandler::getProbeType(seq)) { + case ProbeType::INIT: { + core::ContentObjectManifest manifest( + const_cast<core::ContentObject &>(probe)); + manifest.decode(); + params = manifest.getParamsRTC(); + break; + } + case ProbeType::RTT: { + struct nack_packet_t *probe_pkt = + (struct nack_packet_t *)probe.getPayload()->data(); + params = core::ParamsRTC{ + .timestamp = probe_pkt->getTimestamp(), + .prod_rate = probe_pkt->getProductionRate(), + .prod_seg = probe_pkt->getProductionSegment(), + }; + break; + } + default: + break; + } + + return params; +} + +core::ParamsRTC RTCState::getDataParams(const core::ContentObject &data) { + core::ParamsRTC params; + + switch (data.getPayloadType()) { + case core::PayloadType::DATA: { + struct data_packet_t *data_pkt = + (struct data_packet_t *)data.getPayload()->data(); + params = core::ParamsRTC{ + .timestamp = data_pkt->getTimestamp(), + .prod_rate = data_pkt->getProductionRate(), + .prod_seg = data.getName().getSuffix(), + }; + break; + } + case core::PayloadType::MANIFEST: { + core::ContentObjectManifest manifest( + const_cast<core::ContentObject &>(data)); + manifest.decode(); + params = manifest.getParamsRTC(); + break; + } + default: + break; + } + + return params; +} + } // namespace rtc } // namespace protocol diff --git a/libtransport/src/protocols/rtc/rtc_state.h b/libtransport/src/protocols/rtc/rtc_state.h index 729ba7a1b..8bf48ccc2 100644 --- a/libtransport/src/protocols/rtc/rtc_state.h +++ b/libtransport/src/protocols/rtc/rtc_state.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -14,13 +14,16 @@ */ #pragma once +#include <core/facade.h> #include <hicn/transport/config.h> #include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/name.h> +#include <hicn/transport/utils/rtc_quality_score.h> #include <protocols/indexer.h> #include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_data_path.h> +#include <utils/max_filter.h> #include <map> #include <set> @@ -31,25 +34,50 @@ namespace protocol { namespace rtc { -enum class PacketState : uint8_t { RECEIVED, LOST, UNKNOWN }; +// packet state +// RECEIVED: the packet was already received +// LOST: the packet is marked as lost but can be recovered +// DEFINITELY_LOST: the packet is lost and cannot be recovered +// TO_BE_RECEIVED: when a packet is received is sent to the FEC decoder. the fec +// decoder may decide to send the packet directly to the app. to avoid +// duplicated the packet is marked with this state +// SKIPPED: an interest that was not sent, only for FEC packets +// UNKNOWN: unknown state +enum class PacketState : uint8_t { + RECEIVED, + TO_BE_RECEIVED, + LOST, + DEFINITELY_LOST, + SKIPPED, + UNKNOWN +}; + +class RTCState : public std::enable_shared_from_this<RTCState> { + using PendingInterestsMap = std::map<uint32_t, uint64_t>; + + private: + const double MAX_CACHED_PACKETS = 8192; // XXX this value may be too small + // for high rate apps -class RTCState : std::enable_shared_from_this<RTCState> { public: using DiscoveredRttCallback = std::function<void()>; public: - RTCState(Indexer *indexer, - ProbeHandler::SendProbeCallback &&rtt_probes_callback, + RTCState(Indexer *indexer, ProbeHandler::SendProbeCallback &&probe_callback, DiscoveredRttCallback &&discovered_rtt_callback, asio::io_service &io_service); ~RTCState(); + // initialization + void initParams(); + // packet events void onSendNewInterest(const core::Name *interest_name); void onTimeout(uint32_t seq, bool lost); void onLossDetected(uint32_t seq); void onRetransmission(uint32_t seq); + void onPossibleLossWithNoRtx(uint32_t seq); void onDataPacketReceived(const core::ContentObject &content_object, bool compute_stats); void onFecPacketReceived(const core::ContentObject &content_object); @@ -57,8 +85,10 @@ class RTCState : std::enable_shared_from_this<RTCState> { bool compute_stats); void onPacketLost(uint32_t seq); void onPacketRecoveredRtx(uint32_t seq); - void onPacketRecoveredFec(uint32_t seq); + void onFecPacketRecoveredRtx(uint32_t seq); + void onPacketRecoveredFec(uint32_t seq, uint32_t size); bool onProbePacketReceived(const core::ContentObject &probe); + void onJumpForward(uint32_t next_seq); // protocol state void onNewRound(double round_len, bool in_sync); @@ -72,10 +102,21 @@ class RTCState : std::enable_shared_from_this<RTCState> { // delay metrics bool isRttDiscovered() const { return init_rtt_; } - uint64_t getRTT() const { + uint64_t getMinRTT() const { if (mainPathIsValid()) return main_path_->getMinRtt(); return 0; } + + uint64_t getAvgRTT() const { + if (mainPathIsValid()) return main_path_->getAvgRtt(); + return 0; + } + + uint64_t getMaxRTT() const { + if (mainPathIsValid()) return main_path_->getMaxRtt(); + return 0; + } + void resetRttStats() { if (mainPathIsValid()) main_path_->clearRtt(); } @@ -98,6 +139,7 @@ class RTCState : std::enable_shared_from_this<RTCState> { uint64_t getInterestSentTime(uint32_t seq) { auto it = pending_interests_.find(seq); if (it != pending_interests_.end()) return it->second; + return 0; } @@ -110,14 +152,15 @@ class RTCState : std::enable_shared_from_this<RTCState> { return pending_interests_.size(); } - PacketState isReceivedOrLost(uint32_t seq) { - auto it = received_or_lost_packets_.find(seq); - if (it != received_or_lost_packets_.end()) return it->second; + PacketState getPacketState(uint32_t seq) { + auto it = packet_cache_.find(seq); + if (it != packet_cache_.end()) return it->second; return PacketState::UNKNOWN; } // loss rate - double getLossRate() const { return loss_rate_; } + double getPerRoundLossRate() const { return loss_rate_; } + double getPerSecondLossRate() const { return per_sec_loss_rate_; } double getAvgLossRate() const { return avg_loss_rate_; } double getMaxLossRate() const { return max_loss_rate_; } double getLastRoundLossRate() const { return last_round_loss_rate_; } @@ -134,15 +177,22 @@ class RTCState : std::enable_shared_from_this<RTCState> { return highest_seq_received_in_order_; } + double getMaxLoss(); + // fec packets uint32_t getReceivedFecPackets() const { return received_fec_pkt_; } uint32_t getPendingFecPackets() const { return pending_fec_pkt_; } // generic stats uint32_t getReceivedBytesInRound() const { return received_bytes_; } + uint32_t getReceivedFecBytesInRound() const { return received_fec_bytes_; } + uint32_t getRecoveredFecBytesInRound() const { + return recovered_bytes_with_fec_; + } uint32_t getReceivedNacksInRound() const { return received_nacks_last_round_; } + uint32_t getReceivedDataInRound() const { return received_data_last_round_; } uint32_t getSentInterestInRound() const { return sent_interests_last_round_; } uint32_t getSentRtxInRound() const { return sent_rtx_last_round_; } @@ -150,6 +200,9 @@ class RTCState : std::enable_shared_from_this<RTCState> { double getAvailableBw() const { return 0.0; }; // TODO double getProducerRate() const { return production_rate_; } double getReceivedRate() const { return received_rate_; } + double getReceivedFecRate() const { return fec_received_rate_; } + double getRecoveredFecRate() const { return fec_recovered_rate_; } + double getAveragePacketSize() const { return avg_packet_size_; } // nacks @@ -162,22 +215,46 @@ class RTCState : std::enable_shared_from_this<RTCState> { // packets from cache double getPacketFromCacheRatio() const { return data_from_cache_rate_; } - std::map<uint32_t, uint64_t>::iterator getPendingInterestsMapBegin() { + PendingInterestsMap::iterator getPendingInterestsMapBegin() { return pending_interests_.begin(); } - std::map<uint32_t, uint64_t>::iterator getPendingInterestsMapEnd() { + PendingInterestsMap::iterator getPendingInterestsMapEnd() { return pending_interests_.end(); } + // quality + uint8_t getQualityScore() { + uint8_t qs = quality_score_.getQualityScore( + getMaxRTT(), std::round(getResidualLossRate() * 100)); + return qs; + } + + // We received a data pkt that will be set to RECEIVED, but first we have to + // go through FEC. We do not want to consider this pkt as recovered, thus we + // set it as TO_BE_RECEIVED. + void dataToBeReceived(uint32_t seq); + + // Extract RTC parameters from probes (init or RTT probes) and data packets. + static core::ParamsRTC getProbeParams(const core::ContentObject &probe); + static core::ParamsRTC getDataParams(const core::ContentObject &data); + private: - void initParams(); + void addToPacketCache(uint32_t seq, PacketState state) { + // this function adds or updates the current state + if (packet_cache_.size() >= MAX_CACHED_PACKETS) { + packet_cache_.erase(packet_cache_.begin()); + } + packet_cache_[seq] = state; + } + + void eraseFromPacketCache(uint32_t seq) { packet_cache_.erase(seq); } // update stats void updateState(); void updateReceivedBytes(const core::ContentObject &content_object); void updatePacketSize(const core::ContentObject &content_object); void updatePathStats(const core::ContentObject &content_object, bool is_nack); - void updateLossRate(); + void updateLossRate(bool in_sycn); void addRecvOrLost(uint32_t seq, PacketState state); @@ -211,22 +288,39 @@ class RTCState : std::enable_shared_from_this<RTCState> { double avg_loss_rate_; double max_loss_rate_; double last_round_loss_rate_; + utils::MaxFilter<double> loss_history_; + + // per second loss rate + uint32_t lost_per_sec_; + uint32_t total_expected_packets_; + double per_sec_loss_rate_; + + // conunters for residual losses + // residual losses are computed every second and are used + // as feedback to the upper levels (e.g application) + uint32_t expected_packets_; + uint32_t packets_sent_to_app_; + uint32_t rounds_from_last_compute_; double residual_loss_rate_; // bw counters uint32_t received_bytes_; + uint32_t received_fec_bytes_; + uint32_t recovered_bytes_with_fec_; double avg_packet_size_; - double production_rate_; // rate communicated by the producer using nacks - double received_rate_; // rate recevied by the consumer + double production_rate_; // rate communicated by the producer using nacks + double received_rate_; // rate recevied by the consumer (only data) + double fec_received_rate_; // fec rate recevied by the consumer + double fec_recovered_rate_; // rate recovered using fec - // nack counter + // nack counters // the bool takes tracks only about the valid nacks (no rtx) and it is used to // switch between the states. Instead received_nacks_last_round_ logs all the // nacks for statistics bool nack_on_last_round_; uint32_t received_nacks_last_round_; - // packets counter + // packets counters uint32_t received_packets_last_round_; uint32_t received_data_last_round_; uint32_t received_data_from_cache_; @@ -234,11 +328,11 @@ class RTCState : std::enable_shared_from_this<RTCState> { uint32_t sent_interests_last_round_; uint32_t sent_rtx_last_round_; - // fec counter + // fec counters uint32_t received_fec_pkt_; uint32_t pending_fec_pkt_; - // round conunters + // round counters uint32_t rounds_; uint32_t rounds_without_nacks_; uint32_t rounds_without_packets_; @@ -261,23 +355,27 @@ class RTCState : std::enable_shared_from_this<RTCState> { // packet received // cache where to store info about the last MAX_CACHED_PACKETS - std::map<uint32_t, PacketState> received_or_lost_packets_; + // these are packets that are received or lost or definitely lost and are not + // anymore in the pending intetest list + std::map<uint32_t, PacketState> packet_cache_; // pending interests - std::map<uint32_t, uint64_t> pending_interests_; + PendingInterestsMap pending_interests_; // indexer Indexer *indexer_; - // skipped interests + // used to keep track of the skipped interests uint32_t last_interest_sent_; - std::unordered_set<uint32_t> skipped_interests_; // probes - std::shared_ptr<ProbeHandler> rtt_probes_; + std::shared_ptr<ProbeHandler> probe_handler_; bool init_rtt_; std::unique_ptr<asio::steady_timer> init_rtt_timer_; + // quality score + RTCQualityScore quality_score_; + // callbacks DiscoveredRttCallback discovered_rtt_callback_; }; diff --git a/libtransport/src/protocols/rtc/rtc_verifier.cc b/libtransport/src/protocols/rtc/rtc_verifier.cc new file mode 100644 index 000000000..29968dd02 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.cc @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2017-2021 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 <core/facade.h> +#include <protocols/rtc/rtc_packet.h> +#include <protocols/rtc/rtc_verifier.h> + +namespace transport { +namespace protocol { +namespace rtc { + +RTCVerifier::RTCVerifier(std::shared_ptr<auth::Verifier> verifier, + uint32_t max_unverified_delay) + : verifier_(verifier), max_unverified_delay_(max_unverified_delay) {} + +void RTCVerifier::setState(std::shared_ptr<RTCState> rtc_state) { + rtc_state_ = rtc_state; +} + +void RTCVerifier::setVerifier(std::shared_ptr<auth::Verifier> verifier) { + verifier_ = verifier; +} + +void RTCVerifier::setMaxUnverifiedDelay(uint32_t max_unverified_delay) { + max_unverified_delay_ = max_unverified_delay; +} + +auth::VerificationPolicy RTCVerifier::verify( + core::ContentObject &content_object, bool is_fec) { + uint32_t suffix = content_object.getName().getSuffix(); + core::PayloadType payload_type = content_object.getPayloadType(); + + bool is_probe = ProbeHandler::getProbeType(suffix) != ProbeType::NOT_PROBE; + bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE; + bool is_manifest = !is_probe && !is_nack && !is_fec && + payload_type == core::PayloadType::MANIFEST; + bool is_data = !is_probe && !is_nack && !is_fec && + payload_type == core::PayloadType::DATA; + + if (is_probe) return verifyProbe(content_object); + if (is_nack) return verifyNack(content_object); + if (is_fec) return verifyFec(content_object); + if (is_data) return verifyData(content_object); + if (is_manifest) return verifyManifest(content_object); + + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; +} + +auth::VerificationPolicy RTCVerifier::verifyProbe( + core::ContentObject &content_object) { + switch (ProbeHandler::getProbeType(content_object.getName().getSuffix())) { + case ProbeType::INIT: { + auth::VerificationPolicy policy = verifyManifest(content_object); + if (policy != auth::VerificationPolicy::ACCEPT) { + return policy; + } + return processManifest(content_object); + } + case ProbeType::RTT: + return verifyNack(content_object); + default: + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback( + content_object.getName().getSuffix(), policy); + return policy; + } +} + +auth::VerificationPolicy RTCVerifier::verifyNack( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::verifyFec( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::verifyData( + core::ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + + if (_is_ah(content_object.getFormat())) { + return verifier_->verifyPackets(&content_object); + } + + unverified_bytes_[suffix] = + content_object.headerSize() + content_object.payloadSize(); + unverified_packets_[suffix] = + content_object.computeDigest(manifest_hash_algo_); + + // An alert is raised when too much packets remain unverified + if (getTotalUnverified() > max_unverified_bytes_) { + unverified_bytes_.clear(); + unverified_packets_.clear(); + + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; + } + + return auth::VerificationPolicy::ACCEPT; +} + +auth::VerificationPolicy RTCVerifier::verifyManifest( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::processManifest( + core::ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + + core::ContentObjectManifest manifest(content_object); + manifest.decode(); + + // Update last manifest + if (suffix > last_manifest_) { + last_manifest_ = suffix; + } + + // Extract parameters + manifest_hash_algo_ = manifest.getHashAlgorithm(); + core::ParamsRTC params = manifest.getParamsRTC(); + + if (params.prod_rate > 0) { + max_unverified_bytes_ = static_cast<uint64_t>( + (max_unverified_delay_ / 1000.0) * params.prod_rate); + } + + if (max_unverified_bytes_ == 0 || !rtc_state_) { + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; + } + + // Extract hashes + auth::Verifier::SuffixMap suffix_map = + core::ContentObjectManifest::getSuffixMap(&manifest); + + // Return early if the manifest is empty + if (suffix_map.empty()) { + return auth::VerificationPolicy::ACCEPT; + } + + // Remove lost packets from digest map + manifest_digests_.insert(suffix_map.begin(), suffix_map.end()); + for (auto it = manifest_digests_.begin(); it != manifest_digests_.end();) { + if (rtc_state_->getPacketState(it->first) == PacketState::DEFINITELY_LOST) { + unverified_packets_.erase(it->first); + unverified_bytes_.erase(it->first); + it = manifest_digests_.erase(it); + } else { + ++it; + } + } + + // Verify packets + auth::Verifier::PolicyMap policies = + verifier_->verifyHashes(unverified_packets_, manifest_digests_); + + for (const auto &policy : policies) { + switch (policy.second) { + case auth::VerificationPolicy::ACCEPT: { + manifest_digests_.erase(policy.first); + unverified_packets_.erase(policy.first); + unverified_bytes_.erase(policy.first); + break; + } + case auth::VerificationPolicy::UNKNOWN: + break; + case auth::VerificationPolicy::DROP: + case auth::VerificationPolicy::ABORT: + auth::VerificationPolicy p = policy.second; + verifier_->callVerificationFailedCallback(policy.first, p); + return p; + } + } + + return auth::VerificationPolicy::ACCEPT; +} + +void RTCVerifier::onDataRecoveredFec(uint32_t suffix) { + manifest_digests_.erase(suffix); +} + +void RTCVerifier::onJumpForward(uint32_t next_suffix) { + if (next_suffix <= last_manifest_ + 1) { + return; + } + + // When we jump forward in the suffix sequence, we remove packets that + // probably won't be verified. Those packets have a suffix in the range + // [last_manifest_ + 1, next_suffix[. + for (auto it = unverified_packets_.begin(); + it != unverified_packets_.end();) { + if (it->first > last_manifest_) { + unverified_bytes_.erase(it->first); + it = unverified_packets_.erase(it); + } else { + ++it; + } + } +} + +uint32_t RTCVerifier::getTotalUnverified() const { + uint32_t total = 0; + + for (auto bytes : unverified_bytes_) { + if (bytes.second > UINT32_MAX - total) { + total = UINT32_MAX; + break; + } + total += bytes.second; + } + + return total; +} + +uint32_t RTCVerifier::getMaxUnverified() const { return max_unverified_bytes_; } + +} // end namespace rtc +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_verifier.h b/libtransport/src/protocols/rtc/rtc_verifier.h new file mode 100644 index 000000000..596bd8536 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017-2021 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. + */ + +#pragma once + +#include <core/facade.h> +#include <hicn/transport/auth/verifier.h> +#include <hicn/transport/core/content_object.h> +#include <protocols/rtc/rtc_state.h> + +namespace transport { +namespace protocol { +namespace rtc { + +class RTCVerifier { + public: + RTCVerifier(std::shared_ptr<auth::Verifier> verifier, + uint32_t max_unverified_delay); + + virtual ~RTCVerifier() = default; + + void setState(std::shared_ptr<RTCState> rtc_state); + + void setVerifier(std::shared_ptr<auth::Verifier> verifier); + + void setMaxUnverifiedDelay(uint32_t max_unverified_delay); + + void onDataRecoveredFec(uint32_t suffix); + void onJumpForward(uint32_t next_suffix); + + uint32_t getTotalUnverified() const; + uint32_t getMaxUnverified() const; + + auth::VerificationPolicy verify(core::ContentObject &content_object, + bool is_fec = false); + auth::VerificationPolicy verifyProbe(core::ContentObject &content_object); + auth::VerificationPolicy verifyNack(core::ContentObject &content_object); + auth::VerificationPolicy verifyFec(core::ContentObject &content_object); + auth::VerificationPolicy verifyData(core::ContentObject &content_object); + auth::VerificationPolicy verifyManifest(core::ContentObject &content_object); + + auth::VerificationPolicy processManifest(core::ContentObject &content_object); + + protected: + // The RTC state. + std::shared_ptr<RTCState> rtc_state_; + // The verifier instance. + std::shared_ptr<auth::Verifier> verifier_; + // Hash algorithm used by manifests. + auth::CryptoHashType manifest_hash_algo_; + // The last manifest processed. + auth::Suffix last_manifest_; + // Hold digests extracted from all manifests received. + auth::Verifier::SuffixMap manifest_digests_; + // Hold hashes of all content objects received before they are verified. + auth::Verifier::SuffixMap unverified_packets_; + // Hold number of unverified bytes. + std::unordered_map<auth::Suffix, uint32_t> unverified_bytes_; + // Maximum delay (in ms) for an unverified byte to become verifed. + uint32_t max_unverified_delay_; + // Maximum number of unverified bytes before aborting the connection. + uint64_t max_unverified_bytes_; +}; + +} // end namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/transport_protocol.cc b/libtransport/src/protocols/transport_protocol.cc index d6954ac37..f1e49ec0b 100644 --- a/libtransport/src/protocols/transport_protocol.cc +++ b/libtransport/src/protocols/transport_protocol.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -25,7 +25,8 @@ using namespace interface; TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, Indexer *indexer, Reassembly *reassembly) - : socket_(icn_socket), + : Protocol(), + socket_(icn_socket), indexer_verifier_(indexer), reassembly_(reassembly), fec_decoder_(nullptr), @@ -36,90 +37,82 @@ TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, on_interest_satisfied_(VOID_HANDLER), on_content_object_input_(VOID_HANDLER), stats_summary_(VOID_HANDLER), + on_fwd_strategy_(VOID_HANDLER), + on_rec_strategy_(VOID_HANDLER), on_payload_(VOID_HANDLER), - fec_type_(fec::FECType::UNKNOWN), - is_running_(false) { + fec_type_(fec::FECType::UNKNOWN) { socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); - // Set this transport protocol as portal's consumer callback - portal_->setConsumerCallback(this); - indexer_verifier_->setReassembly(reassembly_.get()); reassembly->setIndexer(indexer_verifier_.get()); } +TransportProtocol::~TransportProtocol() {} + int TransportProtocol::start() { // If the protocol is already running, return otherwise set as running - if (is_running_) return -1; - - // Get all callbacks references - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION, - &on_interest_retransmission_); - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, - &on_interest_output_); - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED, - &on_interest_timeout_); - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED, - &on_interest_satisfied_); - socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT, - &on_content_object_input_); - socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, - &stats_summary_); - socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, - &on_payload_); - - socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); - - // Set it is the first time we schedule an interest - is_first_ = true; - - // Reset the protocol state machine - reset(); - - // Schedule next interests - scheduleNextInterests(); - - is_first_ = false; - - // Set the protocol as running - is_running_ = true; - - if (!is_async_) { - // Start Event loop - portal_->runEventsLoop(); - - // Not running anymore - is_running_ = false; + if (isRunning()) { + return -1; } - return 0; -} - -void TransportProtocol::stop() { - is_running_ = false; + // Start protocol on its own thread + portal_->getThread().add([this]() { + // Get all callbacks references + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION, + &on_interest_retransmission_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + &on_interest_output_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED, + &on_interest_timeout_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED, + &on_interest_satisfied_); + socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT, + &on_content_object_input_); + socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, + &stats_summary_); + socket_->getSocketOption(ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE, + &on_fwd_strategy_); + socket_->getSocketOption(ConsumerCallbacksOptions::REC_STRATEGY_CHANGE, + &on_rec_strategy_); + socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, + &on_payload_); + + socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); + + std::string fec_type_str = ""; + socket_->getSocketOption(GeneralTransportOptions::FEC_TYPE, fec_type_str); + if (fec_type_str != "") { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_type_str.c_str()); + } + + // Set it is the first time we schedule an interest + is_first_ = true; + + // Reset the protocol state machine + reset(); + + // Set this transport protocol as portal's consumer callback + portal_->registerTransportCallback(this); + + // Schedule next interests + scheduleNextInterests(); + + is_first_ = false; + + // Set the protocol as running + setRunning(); + }); - if (!is_async_) { - portal_->stopEventsLoop(); - } else { - portal_->clear(); - } + return 0; } void TransportProtocol::resume() { if (isRunning()) return; - is_running_ = true; + setRunning(); - scheduleNextInterests(); - - if (!is_async_) { - // Start Event loop - portal_->runEventsLoop(); - - // Not running anymore - is_running_ = false; - } + portal_->getThread().tryRunHandlerNow([this]() { scheduleNextInterests(); }); } void TransportProtocol::reset() { @@ -130,7 +123,7 @@ void TransportProtocol::reset() { } } -void TransportProtocol::onContentReassembled(std::error_code ec) { +void TransportProtocol::onContentReassembled(const std::error_code &ec) { stop(); if (!on_payload_) { @@ -153,7 +146,12 @@ void TransportProtocol::sendInterest( uint32_t len) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending interest for name " << interest_name; - auto interest = core::PacketManager<>::getInstance().getPacket<Interest>(); + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + + auto interest = + core::PacketManager<>::getInstance().getPacket<Interest>(format); interest->setName(interest_name); for (uint32_t i = 0; i < len; i++) { @@ -176,6 +174,14 @@ void TransportProtocol::sendInterest( portal_->sendInterest(std::move(interest)); } +void TransportProtocol::onError(const std::error_code &ec) { + // error from portal: stop socket + stop(); + + // signal error to application + on_payload_->readError(ec); +} + void TransportProtocol::onTimeout(Interest::Ptr &i, const Name &n) { if (TRANSPORT_EXPECT_FALSE(!isRunning())) { return; diff --git a/libtransport/src/protocols/transport_protocol.h b/libtransport/src/protocols/transport_protocol.h index 1008a238b..ad8cf0346 100644 --- a/libtransport/src/protocols/transport_protocol.h +++ b/libtransport/src/protocols/transport_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -19,10 +19,10 @@ #include <hicn/transport/interfaces/socket_consumer.h> #include <hicn/transport/interfaces/statistics.h> #include <hicn/transport/utils/object_pool.h> -#include <implementation/socket.h> #include <protocols/data_processing_events.h> #include <protocols/fec_base.h> #include <protocols/indexer.h> +#include <protocols/protocol.h> #include <protocols/reassembly.h> #include <array> @@ -38,8 +38,10 @@ class IndexVerificationManager; using ReadCallback = interface::ConsumerSocket::ReadCallback; -class TransportProtocol : public core::Portal::ConsumerCallback, - public ContentObjectProcessingEventCallback { +class TransportProtocol + : public ContentObjectProcessingEventCallback, + public Protocol, + public std::enable_shared_from_this<TransportProtocol> { static constexpr std::size_t interest_pool_size = 4096; friend class ManifestIndexManager; @@ -48,13 +50,11 @@ class TransportProtocol : public core::Portal::ConsumerCallback, TransportProtocol(implementation::ConsumerSocket *icn_socket, Indexer *indexer, Reassembly *reassembly); - virtual ~TransportProtocol() = default; - - TRANSPORT_ALWAYS_INLINE bool isRunning() { return is_running_; } + virtual ~TransportProtocol(); virtual int start(); - virtual void stop(); + using Protocol::stop; virtual void resume(); @@ -69,7 +69,7 @@ class TransportProtocol : public core::Portal::ConsumerCallback, virtual void scheduleNextInterests() = 0; // Events generated by the indexing - virtual void onContentReassembled(std::error_code ec); + virtual void onContentReassembled(const std::error_code &ec); virtual void onPacketDropped(Interest &interest, ContentObject &content_object, const std::error_code &ec) override = 0; @@ -90,7 +90,8 @@ class TransportProtocol : public core::Portal::ConsumerCallback, AllocatorHandler &&allocator_handler) { if (!fec_decoder_) { // Try to get FEC from environment - if (const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE")) { + const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE"); + if (fec_str && (fec_type_ == fec::FECType::UNKNOWN)) { LOG(INFO) << "Using FEC " << fec_str; fec_type_ = fec::FECUtils::fecTypeFromString(fec_str); } @@ -114,14 +115,13 @@ class TransportProtocol : public core::Portal::ConsumerCallback, // Consumer Callback void onContentObject(Interest &i, ContentObject &c) override; void onTimeout(Interest::Ptr &i, const Name &n) override; - void onError(std::error_code ec) override {} + void onError(const std::error_code &ec) override; protected: implementation::ConsumerSocket *socket_; std::unique_ptr<Indexer> indexer_verifier_; std::unique_ptr<Reassembly> reassembly_; std::unique_ptr<fec::ConsumerFEC> fec_decoder_; - std::shared_ptr<core::Portal> portal_; // True if it si the first time we schedule an interest std::atomic<bool> is_first_; interface::TransportStatistics *stats_; @@ -134,14 +134,13 @@ class TransportProtocol : public core::Portal::ConsumerCallback, interface::ConsumerContentObjectCallback *on_content_object_input_; interface::ConsumerContentObjectCallback *on_content_object_; interface::ConsumerTimerCallback *stats_summary_; + interface::StrategyCallback *on_fwd_strategy_; + interface::StrategyCallback *on_rec_strategy_; ReadCallback *on_payload_; bool is_async_; fec::FECType fec_type_; - - private: - std::atomic<bool> is_running_; }; } // end namespace protocol diff --git a/libtransport/src/test/CMakeLists.txt b/libtransport/src/test/CMakeLists.txt index 26fe7aee1..e7018ceed 100644 --- a/libtransport/src/test/CMakeLists.txt +++ b/libtransport/src/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -11,42 +11,68 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(BuildMacros) - +############################################################## +# Test sources +############################################################## list(APPEND TESTS_SRC main.cc + test_aggregated_header.cc test_auth.cc - test_consumer_producer_rtc.cc + # test_consumer_producer_rtc.cc test_core_manifest.cc - test_event_thread.cc + # test_event_thread.cc + test_fec_base_rs.cc test_fec_reedsolomon.cc + test_fixed_block_allocator.cc test_indexer.cc test_interest.cc test_packet.cc + test_packet_allocator.cc + test_quality_score.cc + test_sessions.cc + test_thread_pool.cc ) if (ENABLE_RELY) list(APPEND TESTS_SRC test_fec_rely_wrapper.cc + test_fec_base_rely.cc + ) +endif() + +if (UNIX AND NOT APPLE) + list(APPEND TESTS_SRC + test_memif_connector.cc ) endif() -build_executable(unit_tests + +############################################################## +# Link libraries +############################################################## +set(MEMIF_MODULE_LIBRARIES + ${LIBRARIES} + ${LIBTRANSPORT_SHARED} + ${GTEST_LIBRARIES} +) + + +############################################################## +# Build single unit test executable and add it to test list +############################################################## +build_executable(libtransport_tests NO_INSTALL SOURCES ${TESTS_SRC} LINK_LIBRARIES - ${LIBRARIES} - ${LIBTRANSPORT_STATIC} - ${GTEST_LIBRARIES} + ${MEMIF_MODULE_LIBRARIES} INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - ${LIBHICN_INCLUDE_DIRS} - ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + $<TARGET_PROPERTY:${LIBTRANSPORT_SHARED},INCLUDE_DIRECTORIES> ${GTEST_INCLUDE_DIRS} DEPENDS gtest ${LIBTRANSPORT_SHARED} COMPONENT ${LIBTRANSPORT_COMPONENT} - DEFINITIONS "${COMPILER_DEFINITIONS}" + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} LINK_FLAGS ${LINK_FLAGS} ) -add_test_internal(unit_tests) +add_test_internal(libtransport_tests) diff --git a/libtransport/src/test/main.cc b/libtransport/src/test/main.cc index a4d7ce1b3..591ed0d5b 100644 --- a/libtransport/src/test/main.cc +++ b/libtransport/src/test/main.cc @@ -16,7 +16,6 @@ #include <gtest/gtest.h> int main(int argc, char **argv) { - srand(time(0)); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
\ No newline at end of file diff --git a/libtransport/src/test/test_aggregated_header.cc b/libtransport/src/test/test_aggregated_header.cc new file mode 100644 index 000000000..0d88af5ab --- /dev/null +++ b/libtransport/src/test/test_aggregated_header.cc @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <protocols/rtc/rtc_packet.h> +#include <test/packet_samples.h> + +#include <climits> +#include <random> +#include <vector> + +namespace transport { + +namespace core { + +namespace { +// The fixture for testing class Foo. +class AggregatedPktHeaderTest : public ::testing::Test { + protected: + AggregatedPktHeaderTest() { + // You can do set-up work for each test here. + } + + virtual ~AggregatedPktHeaderTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } +}; + +} // namespace + +TEST_F(AggregatedPktHeaderTest, Add2Packets8bit) { + uint8_t buf[1500]; + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt1_len, 2); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + // print + // for (uint16_t i = 0; i < 40; i++){ + // std::cout << (int) i << " " << (int) buf[i] << std::endl; + //} + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add2Packets8bit255) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14}; // 255 + uint16_t pkt2_len = 255; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt2_len, 2); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add2Packets8bit256) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15}; // 256 + uint16_t pkt2_len = 256; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt2_len, 2); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add4Packets8bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + uint16_t pkt3_len = 10; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt1_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + *(ptr + i + pkt1_len + pkt2_len) = pkt3[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + *(ptr + i + pkt1_len + pkt2_len + pkt3_len) = pkt4[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add4Packets16bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; // 260 + uint16_t pkt3_len = 260; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt3_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + *(ptr + i + pkt1_len + pkt2_len) = pkt3[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + *(ptr + i + pkt1_len + pkt2_len + pkt3_len) = pkt4[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Defrag4Packets8bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + uint16_t pkt3_len = 10; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt1_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + + uint16_t offset = protocol::rtc::DATA_HEADER_SIZE + 8; // 8 = aggr hdr + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + buf[i + offset] = pkt1[i]; + } + offset += pkt1_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + buf[i + offset] = pkt2[i]; + } + offset += pkt2_len; + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + buf[i + offset] = pkt3[i]; + } + offset += pkt3_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + buf[i + offset] = pkt4[i]; + } + + protocol::rtc::AggrPktHeader hdr2(buf + protocol::rtc::DATA_HEADER_SIZE); + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + uint8_t packet_number = hdr2.getNumberOfPackets(); + EXPECT_EQ(packet_number, 4); + + hdr2.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr2.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr2.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr2.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Defrag4Packets16bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; // 260 + uint16_t pkt3_len = 260; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt3_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + + uint16_t offset = protocol::rtc::DATA_HEADER_SIZE + 12; // 12 = aggr hdr + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + buf[i + offset] = pkt1[i]; + } + offset += pkt1_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + buf[i + offset] = pkt2[i]; + } + offset += pkt2_len; + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + buf[i + offset] = pkt3[i]; + } + offset += pkt3_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + buf[i + offset] = pkt4[i]; + } + + protocol::rtc::AggrPktHeader hdr2(buf + protocol::rtc::DATA_HEADER_SIZE); + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + uint8_t packet_number = hdr2.getNumberOfPackets(); + EXPECT_EQ(packet_number, 4); + + hdr2.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr2.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr2.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr2.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_auth.cc b/libtransport/src/test/test_auth.cc index db1c3b52f..d7fd55433 100644 --- a/libtransport/src/test/test_auth.cc +++ b/libtransport/src/test/test_auth.cc @@ -15,10 +15,15 @@ #include <gtest/gtest.h> #include <hicn/transport/auth/crypto_hash.h> -#include <hicn/transport/auth/identity.h> #include <hicn/transport/auth/signer.h> #include <hicn/transport/auth/verifier.h> #include <hicn/transport/core/content_object.h> +#include <openssl/rand.h> + +using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>; +using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>; +using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>; +using DSA_ptr = std::unique_ptr<DSA, decltype(&::DSA_free)>; namespace transport { namespace auth { @@ -50,11 +55,23 @@ TEST_F(AuthTest, VoidVerifier) { } TEST_F(AuthTest, AsymmetricRSA) { - // Create the RSA signer from an Identity object - Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, - 30, "RSAVerifier"); - - std::shared_ptr<Signer> signer = identity.getSigner(); + // Create the RSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + RSA_ptr rsa(RSA_new(), ::RSA_free); + BN_ptr pub_exp(BN_new(), ::BN_free); + + BN_set_word(pub_exp.get(), RSA_F4); + if (1 != RSA_generate_key_ex(rsa.get(), 2048u, pub_exp.get(), NULL)) + throw errors::RuntimeException("can't generate the key"); + RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free); + RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free); + if (1 != EVP_PKEY_set1_RSA(pubKey.get(), rsa_pub.get())) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa_priv.get())) + throw errors::RuntimeException("can't generate the key"); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::RSA_SHA256, privateKey, pubKey); // Create a content object core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); @@ -68,61 +85,112 @@ TEST_F(AuthTest, AsymmetricRSA) { // Create the RSA verifier std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); + std::make_shared<AsymmetricVerifier>(pubKey); EXPECT_EQ(packet.getFormat(), HF_INET6_TCP_AH); EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); EXPECT_EQ(signer->getSuite(), CryptoSuite::RSA_SHA256); - EXPECT_EQ(signer->getSignatureSize(), 128u); + EXPECT_EQ(signer->getSignatureSize(), 256u); EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); } TEST_F(AuthTest, AsymmetricBufferRSA) { - // Create the RSA signer from an Identity object - Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, - 30, "RSAVerifier"); + // Create the RSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + RSA_ptr rsa(RSA_new(), ::RSA_free); + BN_ptr pub_exp(BN_new(), ::BN_free); + + BN_set_word(pub_exp.get(), RSA_F4); + if (1 != RSA_generate_key_ex(rsa.get(), 2048u, pub_exp.get(), NULL)) + throw errors::RuntimeException("can't generate the key"); + RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free); + RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free); + if (1 != EVP_PKEY_set1_RSA(pubKey.get(), rsa_pub.get())) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa_priv.get())) + throw errors::RuntimeException("can't generate the key"); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::RSA_SHA256, privateKey, pubKey); - std::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); } TEST_F(AuthTest, AsymmetricBufferDSA) { - // Create the DSA signer from an Identity object - Identity identity("test_dsa.p12", PASSPHRASE, CryptoSuite::DSA_SHA256, 1024u, - 30, "DSAVerifier"); + // Create the DSA keys + + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + + DSA_ptr dsa(DSA_new(), ::DSA_free); + unsigned char buf[32]; + if (RAND_bytes(buf, sizeof(buf)) != 1) { + throw errors::RuntimeException("can't generate the key"); + } + if (DSA_generate_parameters_ex(dsa.get(), 1024u, buf, sizeof(buf), NULL, NULL, + NULL) != 1) + throw errors::RuntimeException("can't generate the key"); + if (DSA_generate_key(dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_DSA(privateKey.get(), dsa.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<X509> cert(X509_new(), ::X509_free); + X509_set_pubkey(cert.get(), privateKey.get()); + std::shared_ptr<EVP_PKEY> pubKey(X509_get_pubkey(cert.get()), EVP_PKEY_free); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::DSA_SHA256, privateKey, pubKey); - std::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); } TEST_F(AuthTest, AsymmetricVerifierDSA) { - // Create the DSA signer from an Identity object - Identity identity("test_dsa.p12", PASSPHRASE, CryptoSuite::DSA_SHA256, 1024u, - 30, "DSAVerifier"); + // Create the DSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); - std::shared_ptr<Signer> signer = identity.getSigner(); + DSA_ptr dsa(DSA_new(), ::DSA_free); + unsigned char buf[32]; + if (RAND_bytes(buf, sizeof(buf)) != 1) { + throw errors::RuntimeException("can't generate the key"); + } + if (DSA_generate_parameters_ex(dsa.get(), 1024u, buf, sizeof(buf), NULL, NULL, + NULL) != 1) + throw errors::RuntimeException("can't generate the key"); + if (DSA_generate_key(dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_DSA(privateKey.get(), dsa.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<X509> cert(X509_new(), ::X509_free); + X509_set_pubkey(cert.get(), privateKey.get()); + std::shared_ptr<EVP_PKEY> pubKey(X509_get_pubkey(cert.get()), EVP_PKEY_free); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::DSA_SHA256, privateKey, pubKey); // Create a content object core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); @@ -134,7 +202,7 @@ TEST_F(AuthTest, AsymmetricVerifierDSA) { // EXPECT_EQ(signer->getSignatureSize(), 256u); signer->signPacket(&packet); std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); + std::make_shared<AsymmetricVerifier>(cert); EXPECT_EQ(packet.getFormat(), HF_INET6_TCP_AH); EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); @@ -143,33 +211,59 @@ TEST_F(AuthTest, AsymmetricVerifierDSA) { } TEST_F(AuthTest, AsymmetricBufferECDSA) { - // Create the ECDSA signer from an Identity object - Identity identity("test_ecdsa.p12", PASSPHRASE, CryptoSuite::ECDSA_SHA256, - 256u, 30, "ECDSAVerifier"); + // Create the ECDSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + EC_KEY_ptr ec_priv(EC_KEY_new_by_curve_name(NID_secp256k1), ::EC_KEY_free); + EC_KEY_ptr ec_pub(EC_KEY_new(), ::EC_KEY_free); + EC_KEY_set_asn1_flag(ec_priv.get(), OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_generate_key(ec_priv.get()) == 0) + throw errors::RuntimeException("can't generate the ecdsa key"); + if (1 != EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_priv.get())) + throw errors::RuntimeException("can't generate the key"); + EC_KEY_set_group(ec_pub.get(), EC_KEY_get0_group(ec_priv.get())); + EC_KEY_set_public_key(ec_pub.get(), EC_KEY_get0_public_key(ec_priv.get())); + if (1 != EVP_PKEY_set1_EC_KEY(pubKey.get(), ec_pub.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::ECDSA_SHA256, privateKey, pubKey); - std::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); -} +} // namespace auth TEST_F(AuthTest, AsymmetricVerifierECDSA) { - Identity identity("test_ecdsa.p12", PASSPHRASE, CryptoSuite::ECDSA_SHA256, - 256u, 30, "ECDSAVerifier"); - - std::shared_ptr<Signer> signer = identity.getSigner(); - std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); - // Create a content object + // Create the ECDSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + EC_KEY_ptr ec_priv(EC_KEY_new_by_curve_name(NID_secp256k1), ::EC_KEY_free); + EC_KEY_ptr ec_pub(EC_KEY_new(), ::EC_KEY_free); + EC_KEY_set_asn1_flag(ec_priv.get(), OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_generate_key(ec_priv.get()) == 0) + throw errors::RuntimeException("can't generate the ecdsa key"); + if (1 != EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_priv.get())) + throw errors::RuntimeException("can't generate the key"); + EC_KEY_set_group(ec_pub.get(), EC_KEY_get0_group(ec_priv.get())); + EC_KEY_set_public_key(ec_pub.get(), EC_KEY_get0_public_key(ec_priv.get())); + if (1 != EVP_PKEY_set1_EC_KEY(pubKey.get(), ec_pub.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::ECDSA_SHA256, privateKey, pubKey); + + std::shared_ptr<AsymmetricVerifier> verifier = + std::make_shared<AsymmetricVerifier>(pubKey); for (int i = 0; i < 100; i++) { core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); diff --git a/libtransport/src/test/test_consumer_producer_rtc.cc b/libtransport/src/test/test_consumer_producer_rtc.cc index 8541a9e1a..b11a6a388 100644 --- a/libtransport/src/test/test_consumer_producer_rtc.cc +++ b/libtransport/src/test/test_consumer_producer_rtc.cc @@ -41,7 +41,7 @@ class ConsumerProducerTest : public ::testing::Test, rtc_timer_(io_service_), stop_timer_(io_service_), consumer_(TransportProtocolAlgorithms::RTC, io_service_), - producer_(ProductionProtocolAlgorithms::RTC_PROD, io_service_), + producer_(ProductionProtocolAlgorithms::RTC_PROD, thread_), producer_prefix_(prefix), consumer_name_(name), packets_sent_(0), @@ -120,14 +120,13 @@ class ConsumerProducerTest : public ::testing::Test, size_t maxBufferSize() const override { return receive_buffer_size; } - void readError(const std::error_code ec) noexcept override { - FAIL() << "Error while reading from RTC socket"; + void readError(const std::error_code &ec) noexcept override { io_service_.stop(); + FAIL() << "Error while reading from RTC socket"; } void readSuccess(std::size_t total_size) noexcept override { packets_received_++; - std::cout << "Received something" << std::endl; } asio::io_service io_service_; diff --git a/libtransport/src/test/test_core_manifest.cc b/libtransport/src/test/test_core_manifest.cc index 93f4e87cb..23fd5e342 100644 --- a/libtransport/src/test/test_core_manifest.cc +++ b/libtransport/src/test/test_core_manifest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -17,6 +17,8 @@ #include <core/manifest_inline.h> #include <gtest/gtest.h> #include <hicn/transport/auth/crypto_hash.h> +#include <hicn/transport/auth/signer.h> +#include <hicn/transport/auth/verifier.h> #include <test/packet_samples.h> #include <climits> @@ -33,7 +35,7 @@ class ManifestTest : public ::testing::Test { protected: using ContentObjectManifest = ManifestInline<ContentObject, Fixed>; - ManifestTest() : name_("b001::123|321"), manifest1_(name_) { + ManifestTest() : name_("b001::123|321"), manifest1_(HF_INET6_TCP_AH, name_) { // You can do set-up work for each test here. } @@ -97,8 +99,8 @@ TEST_F(ManifestTest, MoveConstructor) { TEST_F(ManifestTest, SetLastManifest) { manifest1_.clear(); - manifest1_.setFinalManifest(true); - bool fcn = manifest1_.isFinalManifest(); + manifest1_.setIsLast(true); + bool fcn = manifest1_.getIsLast(); ASSERT_TRUE(fcn == true); } @@ -109,13 +111,13 @@ TEST_F(ManifestTest, SetManifestType) { ManifestType type1 = ManifestType::INLINE_MANIFEST; ManifestType type2 = ManifestType::FLIC_MANIFEST; - manifest1_.setManifestType(type1); - ManifestType type_returned1 = manifest1_.getManifestType(); + manifest1_.setType(type1); + ManifestType type_returned1 = manifest1_.getType(); manifest1_.clear(); - manifest1_.setManifestType(type2); - ManifestType type_returned2 = manifest1_.getManifestType(); + manifest1_.setType(type2); + ManifestType type_returned2 = manifest1_.getType(); ASSERT_EQ(type1, type_returned1); ASSERT_EQ(type2, type_returned2); @@ -146,17 +148,80 @@ TEST_F(ManifestTest, SetHashAlgorithm) { ASSERT_EQ(hash3, type_returned3); } -TEST_F(ManifestTest, SetNextSegmentCalculationStrategy) { +TEST_F(ManifestTest, setParamsBytestream) { manifest1_.clear(); - NextSegmentCalculationStrategy strategy1 = - NextSegmentCalculationStrategy::INCREMENTAL; + ParamsBytestream params{ + .final_segment = 1, + }; + + manifest1_.setParamsBytestream(params); + manifest1_.encode(); + + ContentObjectManifest manifest(manifest1_); + manifest.decode(); + + ASSERT_EQ(interface::ProductionProtocolAlgorithms::BYTE_STREAM, + manifest.getTransportType()); + ASSERT_EQ(params, manifest.getParamsBytestream()); +} + +TEST_F(ManifestTest, SetParamsRTC) { + manifest1_.clear(); + + ParamsRTC params{ + .timestamp = 1, + .prod_rate = 2, + .prod_seg = 3, + .support_fec = 1, + }; + + manifest1_.setParamsRTC(params); + manifest1_.encode(); - manifest1_.setNextSegmentCalculationStrategy(strategy1); - NextSegmentCalculationStrategy type_returned1 = - manifest1_.getNextSegmentCalculationStrategy(); + ContentObjectManifest manifest(manifest1_); + manifest.decode(); + + ASSERT_EQ(interface::ProductionProtocolAlgorithms::RTC_PROD, + manifest.getTransportType()); + ASSERT_EQ(params, manifest.getParamsRTC()); +} - ASSERT_EQ(strategy1, type_returned1); +TEST_F(ManifestTest, SignManifest) { + Name name("b001::", 0); + auto signer = std::make_shared<auth::SymmetricSigner>( + auth::CryptoSuite::HMAC_SHA256, "hunter2"); + auto verifier = std::make_shared<auth::SymmetricVerifier>("hunter2"); + std::shared_ptr<ContentObjectManifest> manifest; + + // Instantiate Manifest + manifest.reset(ContentObjectManifest::createManifest( + HF_INET6_TCP_AH, name, ManifestVersion::VERSION_1, + ManifestType::INLINE_MANIFEST, false, name, signer->getHashType(), + signer->getSignatureFieldSize())); + + // Add Manifest entry + auth::CryptoHash hash(signer->getHashType()); + hash.computeDigest(std::vector<uint8_t>{0x01, 0x02, 0x03, 0x04}); + manifest->addSuffixHash(1, hash); + + // Encode manifest + manifest->encode(); + + // Sign manifest + signer->signPacket(manifest.get()); + + // Check size + ASSERT_EQ(manifest->payloadSize(), manifest->estimateManifestSize()); + ASSERT_EQ(manifest->length(), + manifest->headerSize() + manifest->payloadSize()); + ASSERT_EQ(ContentObjectManifest::manifestHeaderSize( + interface::ProductionProtocolAlgorithms::UNKNOWN), + manifest->manifestHeaderSize()); + + // Verify manifest + auth::VerificationPolicy policy = verifier->verifyPackets(manifest.get()); + ASSERT_EQ(auth::VerificationPolicy::ACCEPT, policy); } TEST_F(ManifestTest, SetBaseName) { @@ -198,23 +263,8 @@ TEST_F(ManifestTest, SetSuffixList) { } manifest1_.setBaseName(base_name); - core::Name ret_name = manifest1_.getBaseName(); - // auto & hash_list = manifest1_.getSuffixHashList(); - - // bool cond; - // int i = 0; - - // for (auto & item : manifest1_.getSuffixList()) { - // auto hash = manifest1_.getHash(suffixes[i]); - // cond = auth::CryptoHash::compareBinaryDigest(hash, - // entries[i].second.getDigest<uint8_t>().data(), - // entries[i].second.getType()); - // ASSERT_TRUE(cond); - // i++; - // } - ASSERT_EQ(base_name, ret_name); delete[] entries; diff --git a/libtransport/src/test/test_event_thread.cc b/libtransport/src/test/test_event_thread.cc index 549ff9c1a..324250717 100644 --- a/libtransport/src/test/test_event_thread.cc +++ b/libtransport/src/test/test_event_thread.cc @@ -14,6 +14,7 @@ */ #include <gtest/gtest.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <hicn/transport/utils/event_thread.h> #include <cmath> @@ -48,7 +49,7 @@ class EventThreadTest : public ::testing::Test { utils::EventThread event_thread_; }; -double average(const unsigned long samples[], int size) { +inline double average(const unsigned long samples[], int size) { double sum = 0; for (int i = 0; i < size; i++) { @@ -58,7 +59,7 @@ double average(const unsigned long samples[], int size) { return sum / size; } -double stdDeviation(const unsigned long samples[], int size) { +inline double stdDeviation(const unsigned long samples[], int size) { double avg = average(samples, size); double var = 0; @@ -72,26 +73,23 @@ double stdDeviation(const unsigned long samples[], int size) { } // namespace TEST_F(EventThreadTest, DISABLED_SchedulingDelay) { - using namespace std::chrono; - const size_t size = 1000000; - std::vector<unsigned long> samples(size); - - for (unsigned int i = 0; i < size; i++) { - auto t0 = steady_clock::now(); - event_thread_.add([t0, &samples, i]() { - auto t1 = steady_clock::now(); - samples[i] = duration_cast<nanoseconds>(t1 - t0).count(); - }); - } + // using namespace std::chrono; + // const size_t size = 1000000; + // std::vector<unsigned long> samples(size); + + // for (unsigned int i = 0; i < size; i++) { + // event_thread_.add([t0, &samples, i]() { + // }); + // } - event_thread_.stop(); + // event_thread_.stop(); - auto avg = average(&samples[0], size); - auto sd = stdDeviation(&samples[0], size); - (void)sd; + // auto avg = average(&samples[0], size); + // auto sd = stdDeviation(&samples[0], size); + // (void)sd; - // Expect average to be less that 1 ms - EXPECT_LT(avg, 1000000); + // // Expect average to be less that 1 ms + // EXPECT_LT(avg, 1000000); } } // namespace utils diff --git a/libtransport/src/test/test_fec_base_rely.cc b/libtransport/src/test/test_fec_base_rely.cc new file mode 100644 index 000000000..41e1eae49 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rely.cc @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/global_object_pool.h> +#include <protocols/fec_base.h> +#include <protocols/fec_utils.h> +#include <protocols/rtc/rtc_consts.h> + +#include <algorithm> +#include <iostream> +#include <queue> +#include <random> + +namespace transport { +namespace protocol { + +class PacketFactory { + public: + PacketFactory(){}; + + ~PacketFactory(){}; + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, uint32_t payload_size, + uint32_t payload_filler) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + // create payload + auto buff = packet_manager.getMemBuf(); + buff->append(payload_size); + std::fill(buff->writableData(), buff->writableTail(), payload_filler); + + // create data packet + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(buff->data(), buff->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, fec::buffer payload) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(payload->data(), payload->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } +}; + +class Encoder { + public: + Encoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + if (fec_type_ == fec::FECType::UNKNOWN) + std::cout << "x" << fec_str << "x" << std::endl; + encoder_ = fec::FECUtils::getEncoder(fec_type_, 1); + encoder_->setFECCallback( + std::bind(&Encoder::onFecPackets, this, std::placeholders::_1)); + encoder_->setBufferCallback( + std::bind(&Encoder::getBuffer, this, std::placeholders::_1)); + }; + + ~Encoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + fec_packets_.push(packet.getBuffer()); + } + } + + fec::buffer getBuffer(std::size_t size) { + auto ret = core::PacketManager<>::getInstance() + .getPacket<transport::core::ContentObject>(HF_INET6_TCP, 0); + ret->updateLength(rtc::DATA_HEADER_SIZE + size); + ret->append(rtc::DATA_HEADER_SIZE + size); + ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE); + + return ret; + } + + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata) { + encoder_->onPacketProduced(content_object, offset, metadata); + } + + public: + std::queue<fec::buffer> fec_packets_; + + private: + std::unique_ptr<fec::ProducerFEC> encoder_; + fec::FECType fec_type_; +}; + +class Decoder { + public: + Decoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + decoder_ = fec::FECUtils::getDecoder(fec_type_, 1); + decoder_->setFECCallback( + std::bind(&Decoder::onFecPackets, this, std::placeholders::_1)); + decoder_->setBufferCallback(fec::FECBase::BufferRequested(0)); + }; + + ~Decoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + hicn_packet_dump(packet.getBuffer()->data(), + packet.getBuffer()->length()); + recovered_packets_.push(packet.getBuffer()); + } + } + + void onPacketReceived(core::ContentObject &content_object, uint32_t offset) { + decoder_->onDataPacket(content_object, offset); + } + + public: + std::queue<fec::buffer> recovered_packets_; + + private: + std::unique_ptr<fec::ConsumerFEC> decoder_; + fec::FECType fec_type_; +}; + +TEST(FECtestRely, RelyTestInOrder1) { + // use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + // we cannot use the original data 1 to check it we recovered the packet + // correclty because Rely modifies the packet so here we create a copy + auto data1_copy = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 1 is lost + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + + bool eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRely, RelyTestInOrder2) { + // use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + auto data2_copy = pf.createData(name, 2, 45, 2); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 2 is lost + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data 1 + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + + bool eq_len = (data2_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +#if 0 +TEST(FECtestRely, RelyTestOutOfOrder1) { + //use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + auto data1_copy = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + + std::cout << "dump packet 2" << std::endl; + hicn_packet_dump(data2->data(), data2->length()); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + std::cout << "dump packet 2" << std::endl; + hicn_packet_dump(data2->data(), data2->length()); + + std::cout << "dump packet 3" << std::endl; + hicn_packet_dump(data3->data(), data3->length()); + + // decoding ooo packets. data 1 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t) 2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + bool eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if(eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int) 0); +} +#endif + +TEST(FECtestRely, RelyTestOutOfOrder2) { + // use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + auto data2_copy = pf.createData(name, 2, 45, 2); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decoding ooo packets. data 2 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data 1 + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + bool eq_len = (data2_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRely, RelyTestLargerBlocks) { + // use Rely k = 4 N = 7 + std::string fec_str = "Rely_K4_N7"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + + auto data3 = pf.createData(name, 3, 12, 3); + auto data3_copy = pf.createData(name, 3, 12, 3); + + auto data4 = pf.createData(name, 4, 20, 4); + auto data4_copy = pf.createData(name, 4, 20, 4); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data4, data4->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data5 = pf.createData(name, 5, encoder.fec_packets_.front()); + encoder.fec_packets_.pop(); // pop 5 + encoder.fec_packets_.pop(); // pop 6 + auto data7 = pf.createData(name, 7, encoder.fec_packets_.front()); + + // decoding packets: lost data 3 and data 4 + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data7, data7->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data5, data5->headerSize() + rtc::DATA_HEADER_SIZE); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)4); + decoder.recovered_packets_.pop(); // pop data 1 + decoder.recovered_packets_.pop(); // pop data 2 + auto recovered3 = pf.createData(name, 3, decoder.recovered_packets_.front()); + decoder.recovered_packets_.pop(); + auto recovered4 = pf.createData(name, 4, decoder.recovered_packets_.front()); + + bool eq_len = (data3_copy->length() == recovered3->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data3_copy->data(), recovered3->data(), recovered3->length()); + EXPECT_EQ(ret, (int)0); + + eq_len = (data4_copy->length() == recovered4->length()); + EXPECT_TRUE(eq_len); + ret = -1; + if (eq_len) + ret = memcmp(data4_copy->data(), recovered4->data(), recovered4->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRely, RelyTestK1_N3) { + // use Rely k = 1 N = 3 + std::string fec_str = "Rely_K1_N3"; + Encoder encoder(fec_str); + Decoder decoder1(fec_str); // recv 2 + Decoder decoder2(fec_str); // recv 3 + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + auto data1_copy = pf.createData(name, 1, 50, 1); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packets + EXPECT_TRUE(encoder.fec_packets_.size() == 2); + auto data2 = pf.createData(name, 2, encoder.fec_packets_.front()); + encoder.fec_packets_.pop(); // pop 2 + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // test1: recv data 2 + decoder1.onPacketReceived(*data2, + data2->headerSize() + rtc::DATA_HEADER_SIZE); + EXPECT_EQ(decoder1.recovered_packets_.size(), (size_t)1); + + auto recovered = pf.createData(name, 1, decoder1.recovered_packets_.front()); + bool eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); + + // test2: recv data 3 + decoder2.onPacketReceived(*data3, + data3->headerSize() + rtc::DATA_HEADER_SIZE); + EXPECT_EQ(decoder2.recovered_packets_.size(), (size_t)1); + + recovered = pf.createData(name, 1, decoder2.recovered_packets_.front()); + eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + ret = -1; + if (eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_fec_base_rs.cc b/libtransport/src/test/test_fec_base_rs.cc new file mode 100644 index 000000000..7d7bcebc3 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rs.cc @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/global_object_pool.h> +#include <protocols/fec_base.h> +#include <protocols/fec_utils.h> +#include <protocols/rtc/rtc_consts.h> + +#include <algorithm> +#include <iostream> +#include <queue> +#include <random> + +namespace transport { +namespace protocol { + +class PacketFactory { + public: + PacketFactory(){}; + + ~PacketFactory(){}; + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, uint32_t payload_size, + uint32_t payload_filler) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + // create payload + auto buff = packet_manager.getMemBuf(); + buff->append(payload_size); + std::fill(buff->writableData(), buff->writableTail(), payload_filler); + + // create data packet + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(buff->data(), buff->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, fec::buffer payload) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(payload->data(), payload->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } +}; + +class Encoder { + public: + Encoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + encoder_ = fec::FECUtils::getEncoder(fec_type_, 1); + encoder_->setFECCallback( + std::bind(&Encoder::onFecPackets, this, std::placeholders::_1)); + encoder_->setBufferCallback( + std::bind(&Encoder::getBuffer, this, std::placeholders::_1)); + }; + + ~Encoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + fec_packets_.push(packet.getBuffer()); + } + } + + fec::buffer getBuffer(std::size_t size) { + auto ret = core::PacketManager<>::getInstance() + .getPacket<transport::core::ContentObject>(HF_INET6_TCP, 0); + ret->updateLength(rtc::DATA_HEADER_SIZE + size); + ret->append(rtc::DATA_HEADER_SIZE + size); + ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE); + + return ret; + } + + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata) { + encoder_->onPacketProduced(content_object, offset, metadata); + } + + public: + std::queue<fec::buffer> fec_packets_; + + private: + std::unique_ptr<fec::ProducerFEC> encoder_; + fec::FECType fec_type_; +}; + +class Decoder { + public: + Decoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + decoder_ = fec::FECUtils::getDecoder(fec_type_, 1); + decoder_->setFECCallback( + std::bind(&Decoder::onFecPackets, this, std::placeholders::_1)); + decoder_->setBufferCallback(fec::FECBase::BufferRequested(0)); + }; + + ~Decoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + recovered_packets_.push(packet.getBuffer()); + } + } + + void onPacketReceived(core::ContentObject &content_object, uint32_t offset) { + decoder_->onDataPacket(content_object, offset); + } + + public: + std::queue<fec::buffer> recovered_packets_; + + private: + std::unique_ptr<fec::ConsumerFEC> decoder_; + fec::FECType fec_type_; +}; + +TEST(FECtestRS, RSTestInOrder1) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 1 is lost + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + bool eq_len = (data1->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestInOrder2) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 2 is lost + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data packet 1 + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + + bool eq_len = (data2->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestOutOfOrder1) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decoding ooo packets. data 1 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + bool eq_len = (data1->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestOutOfOrder2) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decoding ooo packets. data 2 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data packet 1 + + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + bool eq_len = (data2->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestLargerBlocks) { + // use RS k = 4 N = 7 + std::string fec_str = "RS_K4_N7"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + auto data3 = pf.createData(name, 3, 12, 3); + const uint8_t *data3_ptr = data3->data(); + + auto data4 = pf.createData(name, 4, 20, 4); + const uint8_t *data4_ptr = data4->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data4, data4->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data5 = pf.createData(name, 5, encoder.fec_packets_.front()); + encoder.fec_packets_.pop(); // pop 5 + encoder.fec_packets_.pop(); // pop 6 + auto data7 = pf.createData(name, 7, encoder.fec_packets_.front()); + + // decoding packets: lost data 3 and data 4 + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data7, data7->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data5, data5->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + EXPECT_EQ((const uint8_t *)data3->data(), data3_ptr); + EXPECT_EQ((const uint8_t *)data4->data(), data4_ptr); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)4); + decoder.recovered_packets_.pop(); // pop data 1 + decoder.recovered_packets_.pop(); // pop data 2 + auto recovered3 = pf.createData(name, 3, decoder.recovered_packets_.front()); + decoder.recovered_packets_.pop(); // pop data 3 + auto recovered4 = pf.createData(name, 4, decoder.recovered_packets_.front()); + + bool eq_len = (data3->length() == recovered3->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data3->data(), recovered3->data(), recovered3->length()); + EXPECT_EQ(ret, (int)0); + + eq_len = (data4->length() == recovered4->length()); + EXPECT_TRUE(eq_len); + ret = -1; + if (eq_len) + ret = memcmp(data4->data(), recovered4->data(), recovered4->length()); + EXPECT_EQ(ret, (int)0); +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_fec_reedsolomon.cc b/libtransport/src/test/test_fec_reedsolomon.cc index c7e10d111..0973069b1 100644 --- a/libtransport/src/test/test_fec_reedsolomon.cc +++ b/libtransport/src/test/test_fec_reedsolomon.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <glog/logging.h> #include <gtest/gtest.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/global_object_pool.h> @@ -30,28 +31,35 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { fec::RSEncoder encoder(k, n, seq_offset); fec::RSDecoder decoder(k, n, seq_offset); - std::vector<fec::buffer> tx_block(k); - std::vector<fec::buffer> rx_block(k); + using BufferMetadata = std::pair<fec::buffer, uint32_t>; + + std::vector<BufferMetadata> tx_block(k); + std::vector<BufferMetadata> rx_block(k); int count = 0; int run = 0; + // Setup random engine + std::random_device + rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> dis(0, 99); + int iterations = 100; auto &packet_manager = core::PacketManager<>::getInstance(); - encoder.setFECCallback( - [&tx_block]( - std::vector<std::pair<uint32_t, fec::buffer>> &repair_packets) { - for (auto &p : repair_packets) { - // Append repair symbols to tx_block - tx_block.emplace_back(std::move(p).second); - } - }); + encoder.setFECCallback([&tx_block](fec::BufferArray &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + tx_block.emplace_back(p.getBuffer(), p.getMetadata()); + } + }); decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &source_packets) { + [&tx_block, &count, &k](fec::BufferArray &source_packets) { for (int i = 0; i < k; i++) { // Compare decoded source packets with original transmitted packets. - if (*tx_block[i] != *source_packets[i].second) { + if (*tx_block[i].first != *source_packets[i].getBuffer() || + tx_block[i].second != source_packets[i].getMetadata()) { count++; } } @@ -60,7 +68,7 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { do { // Discard eventual packet appended in previous callback call tx_block.erase(tx_block.begin() + k, tx_block.end()); - auto _seq_offet = seq_offset; + uint32_t _seq_offset = seq_offset; // Initialization. Feed encoder with first k source packets for (int i = 0; i < k; i++) { @@ -69,45 +77,46 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { // Let's append a bit less than size, so that the FEC class will take care // of filling the rest with zeros - auto cur_size = size - (rand() % 100); + auto cur_size = size - dis(gen); // Set payload, saving 2 bytes at the beginning of the buffer for encoding // the length packet->append(cur_size); - packet->trimStart(2); - std::generate(packet->writableData(), packet->writableTail(), rand); std::fill(packet->writableData(), packet->writableTail(), i + 1); // Set first byte of payload to seq_offset, to reorder at receiver side - packet->writableData()[0] = uint8_t(_seq_offet++); + uint32_t *pkt_head = (uint32_t *)packet->writableData(); + *pkt_head = _seq_offset++; + + // Set a metadata integer + uint32_t metadata = dis(gen); // Store packet in tx buffer and clear rx buffer - tx_block[i] = std::move(packet); + tx_block[i] = std::make_pair(std::move(packet), metadata); } // Create the repair packets for (auto &tx : tx_block) { - encoder.consume(tx, tx->writableBuffer()[0]); + encoder.consume(tx.first, tx.first->writableBuffer()[0], 0, tx.second); } // Simulate transmission on lossy channel - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); std::vector<bool> losses(n, false); for (int i = 0; i < n - k; i++) losses[i] = true; int rxi = 0; - std::shuffle(losses.begin(), losses.end(), - std::default_random_engine(seed)); + std::shuffle(losses.begin(), losses.end(), gen); for (int i = 0; i < n && rxi < k; i++) if (losses[i] == false) { rx_block[rxi++] = tx_block[i]; if (i < k) { // Source packet - decoder.consumeSource(rx_block[rxi - 1], - rx_block[rxi - 1]->data()[0]); + uint32_t index = *((uint32_t *)rx_block[rxi - 1].first->data()); + decoder.consumeSource(rx_block[rxi - 1].first, index, 0, + rx_block[rxi - 1].second); } else { // Repair packet - decoder.consumeRepair(rx_block[rxi - 1]); + decoder.consumeRepair(rx_block[rxi - 1].first); } } @@ -126,6 +135,12 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { fec::RSEncoder encoder(k, n); fec::RSDecoder decoder(k, n); + // Setup random engine + std::random_device + rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> dis(0, 99); + auto &packet_manager = core::PacketManager<>::getInstance(); std::vector<std::pair<fec::buffer, uint32_t>> tx_block; @@ -136,33 +151,39 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { // Receiver will receive packet for n_sourceblocks in a random order. int total_packets = n * n_sourceblocks; int tx_packets = k * n_sourceblocks; - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); - encoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &repair_packets) { - for (auto &p : repair_packets) { - // Append repair symbols to tx_block - tx_block.emplace_back(std::move(p.second), ++i); - } + encoder.setFECCallback([&tx_block, &rx_block, &i, &n, &k, + &encoder](fec::BufferArray &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + ++i; + tx_block.emplace_back(std::move(p.getBuffer()), i); + } - EXPECT_EQ(tx_block.size(), size_t(n)); + EXPECT_EQ(tx_block.size(), size_t(n)); - // Select k packets to send, including at least one symbol. We start - // from the end for this reason. - for (int j = n - 1; j > n - k - 1; j--) { - rx_block.emplace_back(std::move(tx_block[j])); - } + // Select k packets to send, including at least one symbol. We start + // from the end for this reason. + for (int j = n - 1; j > n - k - 1; j--) { + rx_block.emplace_back(std::move(tx_block[j])); + } - // Clear tx block for next source block - tx_block.clear(); - encoder.clear(); - }); + // Clear tx block for next source block + tx_block.clear(); + encoder.clear(); + }); // The decode callback must be called exactly n_sourceblocks times - decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &source_packets) { - count++; - }); + decoder.setFECCallback([&count](fec::BufferArray &source_packets) { + // Check buffers + for (auto &packet : source_packets) { + auto packet_index = ((uint32_t *)packet.getBuffer()->writableData())[0]; + EXPECT_EQ(packet_index, packet.getIndex()) + << "Packet index: " << packet_index + << " -- FEC Index: " << packet.getIndex(); + } + count++; + }); // Produce n * n_sourceblocks // - ( k ) * n_sourceblocks source packets @@ -173,7 +194,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { // Let's append a bit less than size, so that the FEC class will take care // of filling the rest with zeros - auto cur_size = size - (rand() % 100); + auto cur_size = size - dis(gen); // Set payload, saving 2 bytes at the beginning of the buffer for encoding // the length @@ -182,7 +203,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { std::fill(packet->writableData(), packet->writableTail(), i + 1); // Set first byte of payload to i, to reorder at receiver side - packet->writableData()[0] = uint8_t(i); + ((uint32_t *)packet->writableData())[0] = uint32_t(i); // Store packet in tx buffer tx_block.emplace_back(packet, i); @@ -195,8 +216,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { EXPECT_EQ(size_t(tx_packets), size_t(rx_block.size())); // Lets shuffle the rx_block before starting feeding the decoder. - std::shuffle(rx_block.begin(), rx_block.end(), - std::default_random_engine(seed)); + std::shuffle(rx_block.begin(), rx_block.end(), gen); for (auto &p : rx_block) { int index = p.second % n; @@ -231,7 +251,7 @@ foreach_rs_fec_type #undef _ TEST(ReedSolomonMultiBlockTest, RSMB10) { - int blocks = 10; + int blocks = 1; ReedSolomonMultiBlockTest(blocks); } diff --git a/libtransport/src/test/test_fec_rely_wrapper.cc b/libtransport/src/test/test_fec_rely_wrapper.cc index c5b73f8d2..764e4dd2d 100644 --- a/libtransport/src/test/test_fec_rely_wrapper.cc +++ b/libtransport/src/test/test_fec_rely_wrapper.cc @@ -13,18 +13,23 @@ * limitations under the License. */ +#include <glog/logging.h> #include <gtest/gtest.h> #include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/utils/log.h> #include <protocols/fec/rely.h> #include <queue> +#include <random> namespace transport { namespace protocol { -std::string printMissing( - const std::map<uint32_t, core::ContentObject::Ptr> &missing) { +using SavedPacketMap = + std::map<uint32_t, std::pair<core::ContentObject::Ptr, uint32_t>>; + +std::string printMissing(const SavedPacketMap &missing) { std::stringstream stream; for (auto &[seq, packet] : missing) { @@ -48,14 +53,15 @@ std::string printMissing( * @param loss_rate The loss rate */ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, - int64_t timeout, uint32_t max_iterations, + int64_t /* timeout */, uint32_t max_iterations, int loss_rate) { // Create 1 encoder and 1 decoder fec::RelyEncoder _encoder(k, n); fec::RelyDecoder _decoder(k, n); // Seed the pseudo-random with known value to always get same loss pattern - srand(k * n); + std::mt19937 gen(k * + n); // Standard mersenne_twister_engine seeded with rd(); // We will interact with rely encoder/decoder using the interface fec::ProducerFEC &encoder = _encoder; @@ -68,73 +74,78 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, auto &packet_manager = core::PacketManager<>::getInstance(); // Store packets to verify them in the decoder callback - std::map<uint32_t, core::ContentObject::Ptr> saved_packets; + SavedPacketMap saved_packets; // Save repair packets here in encoder callback std::queue<fec::buffer> pending_repair_packets; // Set callback called by encoder when a buffer is required. - encoder.setBufferCallback([](std::size_t size) -> fec::buffer { + encoder.setBufferCallback([](std::size_t size) { auto ret = - core::PacketManager<>::getInstance().getPacket<core::ContentObject>(); + core::PacketManager<>::getInstance().getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); ret->updateLength(size); ret->append(size); ret->trimStart(ret->headerSize()); - assert(ret->length() >= size); + DCHECK(ret->length() >= size); return ret; }); // Set callback to be called by encoder when repair packets are ready - encoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &packets) { - // We must get n - k symbols - EXPECT_EQ(packets.size(), n - k); - // TRANSPORT_LOGD("Got %zu symbols", packets.size()); - - // Save symbols in pending_repair_packets queue and increment iterations - for (auto &packet : packets) { - ++iterations; - pending_repair_packets.push(packet.second); - } - }); + encoder.setFECCallback([&iterations, &pending_repair_packets, &n, + &k](fec::BufferArray &packets) { + // We must get n - k symbols + EXPECT_EQ(packets.size(), n - k); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Got " << packets.size() << " symbols"; + + // Save symbols in pending_repair_packets queue and increment iterations + for (auto &packet : packets) { + ++iterations; + pending_repair_packets.push(packet.getBuffer()); + } + }); // Set callback to be called when decoder recover a packet - decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &packets) { - for (auto &packet : packets) { - // TRANSPORT_LOGD("Recovering packet %u", packet.first); + decoder.setFECCallback([&saved_packets](fec::BufferArray &packets) { + for (auto &packet : packets) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Recovering packet " << packet.getIndex(); + + // Ensure recovered packet is in packets actually produced by encoder + auto original = saved_packets.find(packet.getIndex()); + ASSERT_TRUE(original != saved_packets.end()); + auto &original_packet = *original->second.first; - // Ensure recovered packet is in packets actually produced by encoder - auto original = saved_packets.find(packet.first); - ASSERT_TRUE(original != saved_packets.end()); - auto &original_packet = *original->second; + // Remove additional headers at the beginning of the packet. This may + // change in the future. + original_packet.trimStart(60 /* Ip + TCP */ + 32 /* Rely header */ + + 4 /* Packet size */); - // Remove additional headers at the beginning of the packet. This may - // change in the future. - original_packet.trimStart(60 /* Ip + TCP */ + 28 /* Rely header */ + - 4 /* Packet size */); + // Recovered packet should be equal to the original one + EXPECT_TRUE(original_packet == *packet.getBuffer()); - // Recovered packet should be equal to the original one - EXPECT_TRUE(original_packet == *packet.second); + // Also metadata should correspond + EXPECT_TRUE(original->second.second == packet.getMetadata()); - // Restore removed headers - original_packet.prepend(60 + 28 + 4); + // Restore removed headers + original_packet.prepend(60 + 32 + 4); - // Erase packet from saved packet list - saved_packets.erase(original); - } - }); + // Erase packet from saved packet list + saved_packets.erase(original); + } + }); // Send max_iterations packets from encoder to decoder + std::uniform_int_distribution<> dis(0, 1299); while (iterations < max_iterations) { // Create a payload, the size is between 50 and 1350 bytes. - auto payload_size = 50 + (rand() % 1300); + auto payload_size = 50 + (dis(gen)); uint8_t payload[max_packet_size]; - std::generate(payload, payload + payload_size, rand); + std::generate(payload, payload + payload_size, gen); // Get a packet from global pool and set name - auto buffer = packet_manager.getPacket<core::ContentObject>(); + auto buffer = packet_manager.getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); buffer->setName(core::Name("b001::abcd", iterations)); // Get offset @@ -145,20 +156,23 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // its own header). buffer->appendPayload(payload, payload_size); + // Set an u32 metadata to pass altogether with the buffer + uint32_t metadata = dis(gen); + // Save packet in the saving_packets list - // TRANSPORT_LOGD("Saving packet with index %lu", iterations); - saved_packets.emplace(iterations, buffer); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Saving packet with index " << iterations; + saved_packets.emplace(iterations, std::make_pair(buffer, metadata)); // Feed buffer into the encoder. This will eventually trigger a call to the // FEC callback as soon as k packets are fed into the endocer. - encoder.onPacketProduced(*buffer, offset); + encoder.onPacketProduced(*buffer, offset, metadata); // Check returned packet. We calculate the difference in size and we compare // only the part of the returned packet corresponding to the original // payload. Rely should only add a header and should not modify the actual // payload content. If it does it, this check will fail. auto diff = buffer->length() - payload_size - offset; - // TRANSPORT_LOGD("Difference is %zu", diff); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Difference is " << diff; auto cmp = std::memcmp(buffer->data() + offset + diff, payload, payload_size); EXPECT_FALSE(cmp); @@ -170,29 +184,33 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // using future packets that are not created in the test. For this reason, // we ensure the test ends without losses. #define DROP_CONDITION(loss_rate, max_iterations) \ - (rand() % 100) >= loss_rate || iterations >= max_iterations * 0.9 + (dis(gen)) >= loss_rate || iterations >= max_iterations * 0.9 // Handle the source packet to the decoder, id drop condition returns true if (DROP_CONDITION(loss_rate, max_iterations)) { // Pass packet to decoder - // TRANSPORT_LOGD("Passing packet %u to decoder", - // buffer->getName().getSuffix()); - decoder.onDataPacket(*buffer, offset); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << buffer->getName().getSuffix() + << " to decoder"; + decoder.onDataPacket(*buffer, offset, metadata); } else { - // TRANSPORT_LOGD("Packet %u, dropped", buffer->getName().getSuffix()); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet " << buffer->getName().getSuffix() << " dropped"; } - // Check if previous call to encoder.consumer() generated repair packets, + // Check if previous call to encoder.consume() generated repair packets, // and if yes, feed them to the decoder. while (pending_repair_packets.size()) { // Also repair packets can be lost if (DROP_CONDITION(loss_rate, max_iterations)) { auto &packet = pending_repair_packets.front(); - // TRANSPORT_LOGD("Passing packet %u to decoder", iterations); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << iterations << " to decoder"; core::ContentObject &co = (core::ContentObject &)(*packet); decoder.onDataPacket(co, 0); } else { - // TRANSPORT_LOGD("Packet (repair) %u dropped", iterations); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet (repair) " << iterations << " dropped"; } // Remove packet from the queue @@ -206,9 +224,6 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // 0.001 residual losses EXPECT_LE(saved_packets.size(), iterations * 0.001) << printMissing(saved_packets); - - // Reset seed - srand(time(0)); } /** diff --git a/libtransport/src/test/test_fixed_block_allocator.cc b/libtransport/src/test/test_fixed_block_allocator.cc new file mode 100644 index 000000000..33e048031 --- /dev/null +++ b/libtransport/src/test/test_fixed_block_allocator.cc @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021 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 <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/fixed_block_allocator.h> + +namespace utils { + +class FixedBlockAllocatorTest : public ::testing::Test { + protected: + static inline const std::size_t default_size = 2048; + static inline const std::size_t default_n_buffer = 1024; + + // Get fixed block allocator_ of 1024 buffers of size 2048 bytes + FixedBlockAllocatorTest() + : allocator_( + ::utils::FixedBlockAllocator<default_size, + default_n_buffer>::getInstance()) { + // You can do set-up work for each test here. + } + + virtual ~FixedBlockAllocatorTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + allocator_.reset(); + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + allocator_.reset(); + } + + static bool pointerIsAligned(const void *pointer, size_t byte_count) { + // Sanity check + EXPECT_THAT(reinterpret_cast<std::uintptr_t>(pointer) & + (alignof(std::max_align_t) - 1), + testing::Eq(std::uintptr_t(0))); + + return uintptr_t(pointer) % byte_count == 0; + } + + ::utils::FixedBlockAllocator<default_size, default_n_buffer> &allocator_; +}; + +TEST_F(FixedBlockAllocatorTest, DefaultChecks) { + EXPECT_EQ(allocator_.blockSize(), default_size); + EXPECT_EQ(allocator_.blockCount(), default_n_buffer); + EXPECT_EQ(allocator_.allocations(), 0UL); + EXPECT_EQ(allocator_.deallocations(), 0UL); + EXPECT_EQ(allocator_.blocksInUse(), 0UL); + + // Allocate one single block of memory + auto block = allocator_.allocateBlock(); + + ASSERT_THAT(block, testing::NotNull()); + + // Check statistics + EXPECT_EQ(allocator_.allocations(), 1UL); + EXPECT_EQ(allocator_.deallocations(), 0UL); + EXPECT_EQ(allocator_.blocksInUse(), 1UL); + + // Deallocate it + allocator_.deallocateBlock(block); + + // check statistics + EXPECT_EQ(allocator_.allocations(), 1UL); + EXPECT_EQ(allocator_.deallocations(), 1UL); + EXPECT_EQ(allocator_.blocksInUse(), 0UL); + + // Test reset + allocator_.reset(); + + EXPECT_EQ(allocator_.blockSize(), default_size); + EXPECT_EQ(allocator_.blockCount(), default_n_buffer); + EXPECT_EQ(allocator_.allocations(), 0UL); + EXPECT_EQ(allocator_.deallocations(), 0UL); + EXPECT_EQ(allocator_.blocksInUse(), 0UL); +} + +TEST_F(FixedBlockAllocatorTest, CheckMemoryIsReused) { + // Get one block. As it is the first one, it will be retrieved from the pool + auto block = allocator_.allocateBlock(); + + // Make sure block is valid + ASSERT_THAT(block, testing::NotNull()); + + // Release block + allocator_.deallocateBlock(block); + + // Get same memory block again + auto block2 = allocator_.allocateBlock(); + + // Make sure memory is reused + ASSERT_EQ(block, block2); + + // Get a third block + auto block3 = allocator_.allocateBlock(); + + // Make sure is different memory + ASSERT_NE(block2, block3); + + // Deallocate both and check we get back the laso one + allocator_.deallocateBlock(block2); + allocator_.deallocateBlock(block3); + + auto block4 = allocator_.allocateBlock(); + ASSERT_EQ(block3, block4); +} + +TEST_F(FixedBlockAllocatorTest, CheckMemoryIsContiguous) { + // Get one block. As it is the first one, it will be retrieved from the pool + auto block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + + // Make sure block is valid + ASSERT_THAT(block, testing::NotNull()); + + // Get another block + auto block2 = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + + // Make sure block is valid + ASSERT_THAT(block2, testing::NotNull()); + + // Check the 2 blocks come from contiguous memory + ASSERT_THAT(std::size_t(block2 - block), testing::Eq(default_size)); +} + +TEST_F(FixedBlockAllocatorTest, CheckPoolExpansion) { + // Get all the blocks we setup when constructing the allocator + std::array<uint8_t *, default_n_buffer> blocks; + blocks[0] = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + for (std::size_t i = 1; i < default_n_buffer; i++) { + blocks[i] = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + ASSERT_THAT(std::size_t(blocks[i] - blocks[i - 1]), + testing::Eq(default_size)); + } + + ASSERT_THAT(allocator_.blockCount(), testing::Eq(default_n_buffer)); + + // We should have finished all the blocks belonging to first pool. Let's get + // one additional block + auto new_block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + + // Make sure the block count doubled its size + ASSERT_THAT(allocator_.blockCount(), testing::Eq(2 * default_n_buffer)); + + // Check the new block is not contiguous with respect last block in blocks + ASSERT_THAT(std::size_t(new_block - blocks[default_n_buffer - 1]), + testing::Ne(default_size)); +} + +TEST_F(FixedBlockAllocatorTest, CheckMemoryIsAligned) { + for (std::size_t i = 0; i < default_n_buffer; i++) { + auto block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + ASSERT_THAT(pointerIsAligned(block, alignof(std::max_align_t)), + testing::IsTrue); + } +} + +TEST_F(FixedBlockAllocatorTest, Multithreading) { + // Create 4 threads + utils::EventThread threads[4]; + ::utils::FixedBlockAllocator<default_size, default_n_buffer> + *allocator_addresses[4] = {nullptr, nullptr, nullptr, nullptr}; + int i = 0; + for (auto &t : threads) { + t.add([&allocator_addresses, i]() { + auto &allocator = + ::utils::FixedBlockAllocator<default_size, + default_n_buffer>::getInstance(); + allocator_addresses[i] = &allocator; + }); + i++; + } + + // Stop threads + for (auto &t : threads) { + t.stop(); + } + + // Check the instance of allocator was different for each thread + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + ASSERT_NE(allocator_addresses[i], allocator_addresses[j]); + } + } +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/test/test_interest.cc b/libtransport/src/test/test_interest.cc index 8853563b0..d9c535881 100644 --- a/libtransport/src/test/test_interest.cc +++ b/libtransport/src/test/test_interest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -30,7 +30,7 @@ namespace { // The fixture for testing class Foo. class InterestTest : public ::testing::Test { protected: - InterestTest() : name_("b001::123|321"), interest_() { + InterestTest() : name_("b001::123|321"), interest_(HF_INET6_TCP) { // You can do set-up work for each test here. } @@ -108,7 +108,7 @@ TEST_F(InterestTest, ConstructorWithName) { Name n("b001::1|123"); try { - Interest interest(n); + Interest interest(HF_INET6_TCP, n); } catch (...) { FAIL() << "ERROR: Unexpected exception thrown"; } @@ -176,27 +176,27 @@ TEST_F(InterestTest, SetGetLocator) { auto l = interest.getLocator(); ip_address_t address; - ip_address_pton("b006::ab:cdab:cdef", &address); - auto ret = !std::memcmp(&l, &address, sizeof(address)); + inet_pton(AF_INET6, "b006::ab:cdab:cdef", &address); + auto ret = !ip_address_cmp(&l, &address, AF_INET6); EXPECT_TRUE(ret); // Set different locator - ip_address_pton("2001::1234::4321::abcd::", &address); + inet_pton(AF_INET6, "2001::1234::4321::abcd::", &address); // Set it on interest interest.setLocator(address); // Check it was set l = interest.getLocator(); - ret = !std::memcmp(&l, &address, sizeof(address)); + ret = !ip_address_cmp(&l, &address, AF_INET6); EXPECT_TRUE(ret); } TEST_F(InterestTest, SetGetLifetime) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); const constexpr uint32_t lifetime = 10000; // Set lifetime @@ -211,7 +211,7 @@ TEST_F(InterestTest, SetGetLifetime) { TEST_F(InterestTest, HasManifest) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); // Let's expect anexception here try { @@ -232,7 +232,7 @@ TEST_F(InterestTest, HasManifest) { TEST_F(InterestTest, AppendSuffixesEncodeAndIterate) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); // Appenad some suffixes, with some duplicates interest.appendSuffix(1); diff --git a/libtransport/src/test/test_memif_connector.cc b/libtransport/src/test/test_memif_connector.cc new file mode 100644 index 000000000..562a12c88 --- /dev/null +++ b/libtransport/src/test/test_memif_connector.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 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 <core/memif_connector.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/utils/chrono_typedefs.h> + +namespace transport { +namespace core { + +namespace { + +using namespace std::placeholders; + +/** + * Master memif connector + */ +template <int Master> +class Memif { + static inline std::size_t counter = 256; + static inline std::size_t total_packets = counter * 4096; + static inline std::size_t packet_size = 64; + + public: + Memif(asio::io_service &io_service) + : io_service_(io_service), + memif_connector_(std::make_shared<MemifConnector>( + std::bind(&Memif::onPacketReceived, this, _1, _2, _3), + std::bind(&Memif::onPacketSent, this, _1, _2), + std::bind(&Memif::onClose, this, _1), + std::bind(&Memif::onReconnect, this, _1, _2), io_service_, + Master ? "test_master" : "test_slave")), + recv_counter_(0), + sent_counter_(0) { + memif_connector_->connect(0 /* Memif ID */, Master /* Is Master */, + "@hicntransport/test/memif"); + } + + void setStart() { t0_ = utils::SteadyTime::now(); } + + void startTest() { + if constexpr (!Master) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + // Send in busrt of 256 packet per time + for (std::size_t i = 0; i < counter; i++) { + auto packet = packet_manager.getMemBuf(); + packet->append(packet_size); + memif_connector_->send(packet); + sent_counter_++; + } + + if (sent_counter_ < total_packets) { + asio::post(io_service_, std::bind(&Memif::startTest, this)); + } + } else { + setStart(); + } + } + + auto getRecvCounter() { return recv_counter_; } + auto getSentCounter() { return sent_counter_; } + + private: + void onPacketReceived(Connector *c, + const std::vector<utils::MemBuf::Ptr> &buffers, + const std::error_code &ec) { + if constexpr (Master) { + recv_counter_ += buffers.size(); + if (recv_counter_ == total_packets) { + auto t1 = utils::SteadyTime::now(); + auto delta = utils::SteadyTime::getDurationS(t0_, t1); + auto rate = recv_counter_ / delta.count(); + LOG(INFO) << "rate: " << rate << " packets/s"; + io_service_.stop(); + } + } else { + FAIL() << "Slave should not receive packets"; + } + } + void onPacketSent(Connector *c, const std::error_code &ec) {} + void onClose(Connector *c) {} + void onReconnect(Connector *c, const std::error_code &ec) {} + + private: + asio::io_service &io_service_; + std::shared_ptr<MemifConnector> memif_connector_; + std::size_t recv_counter_; + std::size_t sent_counter_; + utils::SteadyTime::TimePoint t0_; +}; + +using MemifMaster = Memif<1>; +using MemifSlave = Memif<0>; + +} // namespace + +class MemifTest : public ::testing::Test { + protected: + MemifTest() : io_service_(), master_(io_service_), slave_(io_service_) { + // You can do set-up work for each test here. + } + + void run() { + asio::post(io_service_, std::bind(&MemifSlave::startTest, &slave_)); + master_.startTest(); + io_service_.run(); + + EXPECT_THAT(master_.getRecvCounter(), + ::testing::Eq(slave_.getSentCounter())); + } + + virtual ~MemifTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + protected: + asio::io_service io_service_; + MemifMaster master_; + MemifSlave slave_; +}; + +TEST_F(MemifTest, Test) { run(); } +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_packet.cc b/libtransport/src/test/test_packet.cc index 76ad352d6..ca20cdfb7 100644 --- a/libtransport/src/test/test_packet.cc +++ b/libtransport/src/test/test_packet.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #include <gtest/gtest.h> #include <hicn/transport/core/packet.h> #include <hicn/transport/errors/not_implemented_exception.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <test/packet_samples.h> #include <climits> @@ -33,7 +34,7 @@ namespace core { class PacketForTest : public Packet { public: template <typename... Args> - PacketForTest(Args &&... args) : Packet(std::forward<Args>(args)...) {} + PacketForTest(Args &&...args) : Packet(std::forward<Args>(args)...) {} virtual ~PacketForTest() {} @@ -302,7 +303,7 @@ TEST_F(PacketTest, ConstructorWithNew) { auto &_packet = raw_packets_[HF_INET6_TCP]; auto packet_ptr = new PacketForTest(Packet::WRAP_BUFFER, &_packet[0], _packet.size(), _packet.size()); - (void)packet_ptr; + delete packet_ptr; } TEST_F(PacketTest, ConstructorWithRawBufferInet6Tcp) { @@ -682,9 +683,7 @@ TEST_F(PacketTest, SetGetTestSignatureTimestamp) { // Let's try to set the signature timestamp in a packet without AH header. We // expect an exception. using namespace std::chrono; - uint64_t now = - duration_cast<milliseconds>(system_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); try { packet.setSignatureTimestamp(now); diff --git a/libtransport/src/test/test_packet_allocator.cc b/libtransport/src/test/test_packet_allocator.cc new file mode 100644 index 000000000..b63ddde8d --- /dev/null +++ b/libtransport/src/test/test_packet_allocator.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 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 <glog/logging.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/interest.h> +#define ALLOCATION_CHECKS +#include <hicn/transport/core/global_object_pool.h> +#undef ALLOCATION_CHECKS +#include <hicn/transport/utils/event_thread.h> + +namespace transport { +namespace core { + +class PacketAllocatorTest : public ::testing::Test { + protected: + static inline const std::size_t default_size = 2048; + static inline const std::size_t default_n_buffer = 1024; + + // Get fixed block allocator_ of 1024 buffers of size 2048 bytes + PacketAllocatorTest() : allocator_(PacketManager<>::getInstance()) { + // You can do set-up work for each test here. + } + + virtual ~PacketAllocatorTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() {} + + virtual void TearDown() {} + + static bool pointerIsAligned(const void *pointer, size_t byte_count) { + return uintptr_t(pointer) % byte_count == 0; + } + + template <typename T, typename... Args> + void allocationTest(Args &&...args) { + // Create packet + auto packet = allocator_.getPacket<T>(std::forward<Args>(args)...); + + // Check boundaries + LOG(INFO) << "packet size: " << sizeof(*packet) + sizeof(packet) + << std::endl; + EXPECT_LE(sizeof(*packet) + sizeof(packet) + sizeof(std::max_align_t), + sizeof(PacketManager<>::PacketStorage::packet_and_shared_ptr)); + } + + PacketManager<> &allocator_; +}; + +TEST_F(PacketAllocatorTest, ContentObjectAllocation) { + allocationTest<core::ContentObject>(HF_INET_TCP); +} + +TEST_F(PacketAllocatorTest, InterestAllocation) { + allocationTest<core::Interest>(HF_INET_TCP); +} + +// TEST_F(PacketAllocatorTest, MemBufAllocation) { +// allocationTest<::utils::MemBuf>(); +// } + +TEST_F(PacketAllocatorTest, CheckAllocationIsCorrect) { + // Create packet + auto packet = allocator_.getPacket<core::ContentObject>(HF_INET_TCP); + + // Address of actual buffer + uint8_t *buffer_address = packet->writableData(); + + // Address of packet + uint8_t *packet_address = reinterpret_cast<uint8_t *>(packet.get()); + + uint8_t *start_address = + buffer_address - + sizeof(PacketManager<>::PacketStorage::packet_and_shared_ptr); + + // Check memory was allocated on correct positions + EXPECT_TRUE(pointerIsAligned(start_address, alignof(std::max_align_t))); + EXPECT_TRUE(packet_address > start_address && + packet_address < buffer_address); + EXPECT_TRUE(pointerIsAligned(buffer_address, alignof(std::max_align_t))); + EXPECT_THAT(std::size_t(buffer_address - start_address), + testing::Eq(sizeof( + PacketManager<>::PacketStorage::packet_and_shared_ptr))); +} + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/test/test_quality_score.cc b/libtransport/src/test/test_quality_score.cc new file mode 100644 index 000000000..9513c94a6 --- /dev/null +++ b/libtransport/src/test/test_quality_score.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> +#include <hicn/transport/utils/rtc_quality_score.h> + +#include <climits> +#include <random> +#include <vector> + +namespace transport { + +namespace protocol { + +namespace rtc { + +TEST(QualityScoreTest, testQS) { + RTCQualityScore qs; + uint8_t score; + + // 0 losses + score = qs.getQualityScore(0, 0); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(98, 0); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(188, 0); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(398, 0); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(400, 0); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(598, 0); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(600, 0); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(700, 0); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(50000, 0); + EXPECT_EQ(score, (uint8_t)1); + + // 0 delay + score = qs.getQualityScore(0, 2); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(0, 9); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(0, 29); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(0, 30); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(0, 39); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(0, 40); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(0, 50); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(0, 5000); + EXPECT_EQ(score, (uint8_t)1); + + // loss < 10 + score = qs.getQualityScore(0, 3); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(98, 9); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(100, 9); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(398, 5); + EXPECT_EQ(score, (uint8_t)2); + + score = qs.getQualityScore(400, 5); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(4000, 5); + EXPECT_EQ(score, (uint8_t)1); + + // loss < 20 + score = qs.getQualityScore(0, 10); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(30, 10); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(198, 15); + EXPECT_EQ(score, (uint8_t)2); + + score = qs.getQualityScore(200, 19); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(300, 10); + EXPECT_EQ(score, (uint8_t)1); + + // loss < 30 + + score = qs.getQualityScore(0, 29); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(10, 29); + EXPECT_EQ(score, (uint8_t)2); + + score = qs.getQualityScore(0, 100); + EXPECT_EQ(score, (uint8_t)1); +} + +} // namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_sessions.cc b/libtransport/src/test/test_sessions.cc new file mode 100644 index 000000000..d2e8c27bb --- /dev/null +++ b/libtransport/src/test/test_sessions.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 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 <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/interfaces/global_conf_interface.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/interfaces/socket_producer.h> + +namespace transport { +namespace interface { + +class SessionsTest : public ::testing::Test { + protected: + static inline const std::size_t default_size = 2048; + static inline const std::size_t default_n_buffer = 1024; + + // Get fixed block allocator_ of 1024 buffers of size 2048 bytes + SessionsTest() { + // You can do set-up work for each test here. + // Set io_module to local forwarder with no external connections + global_config::IoModuleConfiguration config; + config.name = "forwarder_module"; + config.set(); + } + + virtual ~SessionsTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + std::vector<ConsumerSocket> consumers_; + std::vector<ProducerSocket> producers_; +}; + +TEST_F(SessionsTest, SessionAllocations) { + // Create 1000 consumer sockets and 1000 producer sockets + int cprotocol = TransportProtocolAlgorithms::RAAQM; + int pprotocol = ProductionProtocolAlgorithms::BYTE_STREAM; + int offset = 0; + + for (int i = 0; i < 1000; i++) { + auto &c = consumers_.emplace_back(cprotocol + (offset % 3)); + auto &p = producers_.emplace_back(pprotocol + (offset % 2)); + c.connect(); + p.connect(); + offset++; + } +} + +} // namespace interface +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/test/test_thread_pool.cc b/libtransport/src/test/test_thread_pool.cc new file mode 100644 index 000000000..1b6b4cc81 --- /dev/null +++ b/libtransport/src/test/test_thread_pool.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 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 <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/utils/thread_pool.h> + +namespace utils { + +class ThreadPoolTest : public ::testing::Test { + protected: + ThreadPoolTest() : thread_pool_() { + // You can do set-up work for each test here. + } + + virtual ~ThreadPoolTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + ::utils::ThreadPool thread_pool_; +}; + +TEST_F(ThreadPoolTest, DefaultConstructor) { + // EXPECT_EQ(thread_pool_.GetNumThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumIdleThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumBusyThreads(), 0); +} + +TEST_F(ThreadPoolTest, GetNThreads) { + auto n_threads = thread_pool_.getNThreads(); + EXPECT_GT(n_threads, std::size_t(0)); + EXPECT_EQ(n_threads, std::thread::hardware_concurrency()); + + ::utils::ThreadPool pool(64); + n_threads = pool.getNThreads(); + EXPECT_GT(n_threads, std::size_t(0)); + EXPECT_NE(n_threads, std::thread::hardware_concurrency()); + EXPECT_EQ(n_threads, std::size_t(64)); + + // EXPECT_EQ(thread_pool_.GetNumThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumIdleThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumBusyThreads(), 0); +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/transport.config b/libtransport/src/transport.config index 89db204d5..d5c2918a6 100644 --- a/libtransport/src/transport.config +++ b/libtransport/src/transport.config @@ -17,7 +17,7 @@ forwarder = { remote_port = 33436; } }; - + listeners = { l0 = { local_address = "127.0.0.1"; diff --git a/libtransport/src/utils/CMakeLists.txt b/libtransport/src/utils/CMakeLists.txt index a85ab16bf..5bb76303a 100644 --- a/libtransport/src/utils/CMakeLists.txt +++ b/libtransport/src/utils/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 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: @@ -23,6 +23,7 @@ list(APPEND SOURCE_FILES list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h + ${CMAKE_CURRENT_SOURCE_DIR}/max_filter.h ${CMAKE_CURRENT_SOURCE_DIR}/stream_buffer.h ${CMAKE_CURRENT_SOURCE_DIR}/suffix_strategy.h ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h @@ -34,10 +35,6 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.h ${CMAKE_CURRENT_SOURCE_DIR}/fd_deadline_timer.h ) - - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.cc - ) endif() if(NOT WIN32) diff --git a/libtransport/src/utils/content_store.cc b/libtransport/src/utils/content_store.cc index 8ae7fd4d4..d9d4147f7 100644 --- a/libtransport/src/utils/content_store.cc +++ b/libtransport/src/utils/content_store.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -17,6 +17,7 @@ #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <hicn/transport/core/name.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <utils/content_store.h> namespace utils { @@ -56,7 +57,7 @@ void ContentStore::insert( fifo_list_.push_front(std::cref(content_object->getName())); auto pos = fifo_list_.begin(); content_store_hash_table_[content_object->getName()] = ContentStoreEntry( - ObjectTimeEntry(content_object, std::chrono::steady_clock::now()), pos); + ObjectTimeEntry(content_object, utils::SteadyTime::now()), pos); } std::shared_ptr<ContentObject> ContentStore::find(const Name &name) { @@ -67,8 +68,8 @@ std::shared_ptr<ContentObject> ContentStore::find(const Name &name) { if (it != content_store_hash_table_.end()) { auto content_lifetime = it->second.first.first->getLifetime(); auto time_passed_since_creation = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now() - it->second.first.second) + utils::SteadyTime::getDurationMs(it->second.first.second, + utils::SteadyTime::now()) .count(); if (time_passed_since_creation > content_lifetime) { diff --git a/libtransport/src/utils/content_store.h b/libtransport/src/utils/content_store.h index abe5e7f6c..ecd62cc2c 100644 --- a/libtransport/src/utils/content_store.h +++ b/libtransport/src/utils/content_store.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -35,8 +35,7 @@ using Name = transport::core::Name; using ContentObject = transport::core::ContentObject; using Interest = transport::core::Interest; -typedef std::pair<std::shared_ptr<ContentObject>, - std::chrono::steady_clock::time_point> +typedef std::pair<std::shared_ptr<ContentObject>, utils::SteadyTime::TimePoint> ObjectTimeEntry; typedef std::pair<ObjectTimeEntry, std::list<std::reference_wrapper<const Name>>::iterator> diff --git a/libtransport/src/utils/daemonizator.cc b/libtransport/src/utils/daemonizator.cc index 6cb7d16ba..4c17aa96b 100644 --- a/libtransport/src/utils/daemonizator.cc +++ b/libtransport/src/utils/daemonizator.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/deadline_timer.h b/libtransport/src/utils/deadline_timer.h index 5187754f0..6c105ed3d 100644 --- a/libtransport/src/utils/deadline_timer.h +++ b/libtransport/src/utils/deadline_timer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -22,61 +22,6 @@ #include <cstring> #include <utility> -namespace std { -namespace chrono { -namespace detail { - -template <typename From, typename To> -struct posix_duration_cast; - -// chrono -> timespec caster -template <typename Rep, typename Period> -struct posix_duration_cast<std::chrono::duration<Rep, Period>, - struct timespec> { - static struct timespec cast(std::chrono::duration<Rep, Period> const &d) { - struct timespec tv; - - std::chrono::seconds const sec = - std::chrono::duration_cast<std::chrono::seconds>(d); - - tv.tv_sec = sec.count(); - tv.tv_nsec = - std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count(); - - return tv; - } -}; - -// timespec -> chrono caster -template <typename Rep, typename Period> -struct posix_duration_cast<struct timespec, - std::chrono::duration<Rep, Period>> { - static std::chrono::duration<Rep, Period> cast(struct timespec const &tv) { - return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>( - std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec)); - } -}; - -} // namespace detail - -// chrono -> timespec -template <typename T, typename Rep, typename Period> -auto duration_cast(std::chrono::duration<Rep, Period> const &d) -> - typename std::enable_if<std::is_same<T, struct timespec>::value, - struct timespec>::type { - return detail::posix_duration_cast<std::chrono::duration<Rep, Period>, - timespec>::cast(d); -} - -// timespec -> chrono -template <typename Duration> -Duration duration_cast(struct timespec const &tv) { - return detail::posix_duration_cast<struct timespec, Duration>::cast(tv); -} - -} // namespace chrono -} // namespace std - namespace utils { template <typename Implementation> diff --git a/libtransport/src/utils/epoll_event_reactor.cc b/libtransport/src/utils/epoll_event_reactor.cc deleted file mode 100644 index 457727bbe..000000000 --- a/libtransport/src/utils/epoll_event_reactor.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2017-2019 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 <glog/logging.h> -#include <hicn/transport/utils/branch_prediction.h> -#include <signal.h> -#include <unistd.h> -#include <utils/epoll_event_reactor.h> -#include <utils/fd_deadline_timer.h> - -#include <iostream> - -namespace utils { - -EpollEventReactor::EpollEventReactor() - : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {} - -EpollEventReactor::~EpollEventReactor() { close(epoll_fd_); } - -int EpollEventReactor::addFileDescriptor(int fd, uint32_t events) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - LOG(ERROR) << "invalid fd " << fd; - return -1; - } - - struct epoll_event evt; - std::memset(&evt, 0, sizeof(evt)); - evt.events = events; - evt.data.fd = fd; - - if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) < - 0)) { - LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; - return -1; - } - - return 0; -} - -int EpollEventReactor::modFileDescriptor(int fd, uint32_t events) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - LOG(ERROR) << "invalid fd " << fd; - return -1; - } - - struct epoll_event evt; - memset(&evt, 0, sizeof(evt)); - evt.events = events; - evt.data.fd = fd; - - if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) < - 0)) { - LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; - return -1; - } - - return 0; -} - -std::size_t EpollEventReactor::mapSize() { return event_callback_map_.size(); } - -int EpollEventReactor::delFileDescriptor(int fd) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - LOG(ERROR) << "invalid fd " << fd; - return -1; - } - - struct epoll_event evt; - memset(&evt, 0, sizeof(evt)); - - if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) < - 0)) { - LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; - return -1; - } - - utils::SpinLock::Acquire locked(event_callback_map_lock_); - event_callback_map_.erase(fd); - - return 0; -} - -void EpollEventReactor::runEventLoop(int timeout) { - Event evt[128]; - int en = 0; - EventCallbackMap::iterator it; - EventCallback callback; - - // evt.events = EPOLLIN | EPOLLOUT; - sigset_t sigset; - sigemptyset(&sigset); - - while (run_event_loop_) { - memset(&evt, 0, sizeof(evt)); - en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset); - - if (TRANSPORT_EXPECT_FALSE(en < 0)) { - LOG(ERROR) << "epoll_pwait: " << strerror(errno); - if (errno == EINTR) { - continue; - } else { - return; - } - } - - for (int i = 0; i < en; i++) { - if (evt[i].data.fd > 0) { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - it = event_callback_map_.find(evt[i].data.fd); - } - - if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { - LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; - } else { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - callback = event_callback_map_[evt[i].data.fd]; - } - - callback(evt[i]); - - // In the callback the epoll event reactor could have been stopped, - // then we need to check whether the event loop is still running. - if (TRANSPORT_EXPECT_FALSE(!run_event_loop_)) { - return; - } - } - } else { - LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; - } - } - } -} - -void EpollEventReactor::runOneEvent() { - Event evt; - int en = 0; - EventCallbackMap::iterator it; - EventCallback callback; - - // evt.events = EPOLLIN | EPOLLOUT; - sigset_t sigset; - sigemptyset(&sigset); - - memset(&evt, 0, sizeof(evt)); - - en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset); - - if (TRANSPORT_EXPECT_FALSE(en < 0)) { - LOG(ERROR) << "epoll_pwait: " << strerror(errno); - return; - } - - if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - it = event_callback_map_.find(evt.data.fd); - } - - if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { - LOG(ERROR) << "unexpected event. fd " << evt.data.fd; - } else { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - callback = event_callback_map_[evt.data.fd]; - } - - callback(evt); - } - } else { - LOG(ERROR) << "unexpected event. fd " << evt.data.fd; - } -} - -void EpollEventReactor::stop() { run_event_loop_ = false; } - -} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/utils/epoll_event_reactor.h b/libtransport/src/utils/epoll_event_reactor.h index 9ebfca937..8e7681c20 100644 --- a/libtransport/src/utils/epoll_event_reactor.h +++ b/libtransport/src/utils/epoll_event_reactor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,6 +15,7 @@ #pragma once +#include <glog/logging.h> #include <hicn/transport/utils/spinlock.h> #include <sys/epoll.h> #include <utils/event_reactor.h> @@ -35,9 +36,10 @@ typedef std::unordered_map<int, EventCallback> EventCallbackMap; class EpollEventReactor : public EventReactor { public: - explicit EpollEventReactor(); + explicit EpollEventReactor() + : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {} - ~EpollEventReactor(); + ~EpollEventReactor() { close(epoll_fd_); } template <typename EventHandler> int addFileDescriptor(int fd, uint32_t events, EventHandler &&callback) { @@ -56,20 +58,163 @@ class EpollEventReactor : public EventReactor { return ret; } - int delFileDescriptor(int fd); + int delFileDescriptor(int fd) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + LOG(ERROR) << "invalid fd " << fd; + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) < + 0)) { + return -1; + } + + utils::SpinLock::Acquire locked(event_callback_map_lock_); + event_callback_map_.erase(fd); + + return 0; + } + + int modFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + LOG(ERROR) << "invalid fd " << fd; + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) < + 0)) { + LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; + return -1; + } + + return 0; + } + + void runEventLoop(int timeout = -1) override { + Event evt[128]; + int en = 0; + EventCallbackMap::iterator it; + EventCallback callback; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + while (run_event_loop_) { + memset(&evt, 0, sizeof(evt)); + en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset); + + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + LOG(ERROR) << "epoll_pwait: " << strerror(errno); + if (errno == EINTR) { + continue; + } else { + return; + } + } + + for (int i = 0; i < en; i++) { + if (evt[i].data.fd > 0) { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + it = event_callback_map_.find(evt[i].data.fd); + } + + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; + } else { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + callback = event_callback_map_[evt[i].data.fd]; + } + + callback(evt[i]); + + // In the callback the epoll event reactor could have been stopped, + // then we need to check whether the event loop is still running. + if (TRANSPORT_EXPECT_FALSE(!run_event_loop_)) { + return; + } + } + } else { + LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; + } + } + } + } + + void runOneEvent() override { + Event evt; + int en = 0; + EventCallbackMap::iterator it; + EventCallback callback; - int modFileDescriptor(int fd, uint32_t events); + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); - void runEventLoop(int timeout = -1) override; + memset(&evt, 0, sizeof(evt)); - void runOneEvent() override; + en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset); - void stop() override; + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + LOG(ERROR) << "epoll_pwait: " << strerror(errno); + return; + } + + if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + it = event_callback_map_.find(evt.data.fd); + } - std::size_t mapSize(); + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + LOG(ERROR) << "unexpected event. fd " << evt.data.fd; + } else { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + callback = event_callback_map_[evt.data.fd]; + } + + callback(evt); + } + } else { + LOG(ERROR) << "unexpected event. fd " << evt.data.fd; + } + } + + void stop() override { run_event_loop_ = false; } + + std::size_t mapSize() { return event_callback_map_.size(); } private: - int addFileDescriptor(int fd, uint32_t events); + int addFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + LOG(ERROR) << "invalid fd " << fd; + return -1; + } + + struct epoll_event evt; + std::memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) < + 0)) { + LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; + return -1; + } + + return 0; + } int epoll_fd_; std::atomic_bool run_event_loop_; diff --git a/libtransport/src/utils/event_reactor.h b/libtransport/src/utils/event_reactor.h index 4f8b58296..09af6f84c 100644 --- a/libtransport/src/utils/event_reactor.h +++ b/libtransport/src/utils/event_reactor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/fd_deadline_timer.h b/libtransport/src/utils/fd_deadline_timer.h index 22c13a763..e15cd4d2a 100644 --- a/libtransport/src/utils/fd_deadline_timer.h +++ b/libtransport/src/utils/fd_deadline_timer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <sys/timerfd.h> #include <unistd.h> #include <utils/deadline_timer.h> @@ -37,6 +38,11 @@ class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> { } } + FdDeadlineTimer(const FdDeadlineTimer &other) + : reactor_(other.reactor_), + timer_fd_(other.timer_fd_), + flags_(other.flags_) {} + ~FdDeadlineTimer() { close(timer_fd_); } template <typename WaitHandler> diff --git a/libtransport/src/utils/log.cc b/libtransport/src/utils/log.cc index 755d5bafa..44acf4595 100644 --- a/libtransport/src/utils/log.cc +++ b/libtransport/src/utils/log.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -36,7 +36,7 @@ foreach_log_level static constexpr char log_config_section[] = "log"; #define LOG_NAME \ "Libhicntransport-" HICNTRANSPORT_VERSION_MAJOR \ - "." HICNTRANSPORT_VERSION_MINOR "." HICNTRANSPORT_VERSION_REVISION + "." HICNTRANSPORT_VERSION_MINOR "." HICNTRANSPORT_VERSION_PATCH static constexpr char log_name[] = LOG_NAME; #define foreach_log_config \ diff --git a/libtransport/src/utils/max_filter.h b/libtransport/src/utils/max_filter.h new file mode 100644 index 000000000..7a2c6aace --- /dev/null +++ b/libtransport/src/utils/max_filter.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 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.sudo make instamake install + */ + +#pragma once + +#include <deque> +#include <iostream> +#include <set> +#include <type_traits> +#include <vector> + +namespace utils { + +template <typename T> +class MaxFilter { + public: + MaxFilter(std::size_t size) : size_(size) {} + + std::size_t size() { return by_arrival_.size(); } + + template <typename R> + void pushBack(R&& value) { + if (by_arrival_.size() >= size_) { + by_order_.erase(by_arrival_.back()); + by_arrival_.pop_back(); + } + + by_arrival_.push_front(by_order_.insert(std::forward<R>(value))); + } + + void clear() { + by_arrival_.clear(); + by_order_.clear(); + } + + const T& begin() { return *by_order_.crbegin(); } + + const T& rBegin() { return *by_order_.rbegin(); } + + private: + std::multiset<T> by_order_; + std::deque<typename std::multiset<T>::const_iterator> by_arrival_; + std::size_t size_; +}; + +} // namespace utils diff --git a/libtransport/src/utils/membuf.cc b/libtransport/src/utils/membuf.cc index 73c45cf6d..952116bb7 100644 --- a/libtransport/src/utils/membuf.cc +++ b/libtransport/src/utils/membuf.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Copyright 2013-present Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include <hicn/transport/portability/win_portability.h> #endif +#include <glog/logging.h> #include <hicn/transport/utils/membuf.h> #include <cassert> @@ -209,7 +210,7 @@ MemBuf::MemBuf(CopyBufferOp /* op */, const void* buf, std::size_t size, : MemBuf(CREATE, headroom + size + min_tailroom) { advance(headroom); if (size > 0) { - assert(buf != nullptr); + DCHECK(buf != nullptr); memcpy(writableData(), buf, size); append(size); } @@ -369,8 +370,8 @@ MemBuf::MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, length_(length), capacity_(capacity), flags_and_shared_info_(flagsAndSharedInfo) { - assert(data >= buf); - assert(data + length <= buf + capacity); + DCHECK(data >= buf); + DCHECK(data + length <= buf + capacity); } MemBuf::~MemBuf() { @@ -559,7 +560,7 @@ void MemBuf::unshareOneSlow() { // minimum capacity we also maintain at least the same amount of tailroom. std::size_t headlen = headroom(); if (length_ > 0) { - assert(data_ != nullptr); + DCHECK(data_ != nullptr); memcpy(buf + headlen, data_, length_); } @@ -576,7 +577,7 @@ void MemBuf::unshareOneSlow() { void MemBuf::unshareChained() { // unshareChained() should only be called if we are part of a chain of // multiple MemBufs. The caller should have already verified this. - assert(isChained()); + DCHECK(isChained()); MemBuf* current = this; while (true) { @@ -606,7 +607,7 @@ void MemBuf::markExternallyShared() { } void MemBuf::makeManagedChained() { - assert(isChained()); + DCHECK(isChained()); MemBuf* current = this; while (true) { @@ -677,15 +678,15 @@ void MemBuf::coalesceAndReallocate(size_t new_headroom, size_t new_length, size_t remaining = new_length; do { if (current->length_ > 0) { - assert(current->length_ <= remaining); - assert(current->data_ != nullptr); + DCHECK(current->length_ <= remaining); + DCHECK(current->data_ != nullptr); remaining -= current->length_; memcpy(p, current->data_, current->length_); p += current->length_; } current = current->next_; } while (current != end); - assert(remaining == 0); + DCHECK(remaining == 0); // Point at the new buffer decrementRefcount(); @@ -799,7 +800,7 @@ void MemBuf::reserveSlow(std::size_t min_headroom, std::size_t min_tailroom) { new_allocated_capacity = goodExtBufferSize(new_capacity); new_buffer = static_cast<uint8_t*>(malloc(new_allocated_capacity)); if (length_ > 0) { - assert(data_ != nullptr); + DCHECK(data_ != nullptr); memcpy(new_buffer + min_headroom, data_, length_); } if (sharedInfo()) { diff --git a/libtransport/src/utils/memory_pool_allocator.h b/libtransport/src/utils/memory_pool_allocator.h index a960b91bb..c2b34e7aa 100644 --- a/libtransport/src/utils/memory_pool_allocator.h +++ b/libtransport/src/utils/memory_pool_allocator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/min_filter.h b/libtransport/src/utils/min_filter.h index 092555ce0..4c7ae81f1 100644 --- a/libtransport/src/utils/min_filter.h +++ b/libtransport/src/utils/min_filter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/stream_buffer.h b/libtransport/src/utils/stream_buffer.h index adfb696f2..dde769a55 100644 --- a/libtransport/src/utils/stream_buffer.h +++ b/libtransport/src/utils/stream_buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/string_tokenizer.cc b/libtransport/src/utils/string_tokenizer.cc index a280a3c43..cb0a4a3ad 100644 --- a/libtransport/src/utils/string_tokenizer.cc +++ b/libtransport/src/utils/string_tokenizer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/suffix_strategy.h b/libtransport/src/utils/suffix_strategy.h index ee016308e..96eaed662 100644 --- a/libtransport/src/utils/suffix_strategy.h +++ b/libtransport/src/utils/suffix_strategy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: @@ -15,72 +15,88 @@ #pragma once -#include <core/manifest_format.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/errors/runtime_exception.h> namespace utils { -using transport::core::NextSegmentCalculationStrategy; +/** + * INCREMENTAL: Manifests will be received inline with the data with no specific + * assumption regarding the manifest capacity. Consumers can send interests + * using a +1 heuristic. + * + * MANIFEST_CAPACITY_BASED: manifests with capacity N have a suffix multiple of + * N+1: 0, N+1, 2(N+1) etc. Contents have a suffix incremented by 1 except when + * it conflicts with a manifest: 1, 2, ..., N, N+2, N+3, ..., 2N+1, 2N+3 + */ +enum class NextSuffixStrategy : uint8_t { + INCREMENTAL = 1, +}; class SuffixStrategy { public: - static constexpr uint32_t INVALID_SUFFIX = - std::numeric_limits<uint32_t>::max(); + static constexpr uint32_t MAX_SUFFIX = std::numeric_limits<uint32_t>::max(); + static constexpr uint8_t MAX_MANIFEST_CAPACITY = + std::numeric_limits<uint8_t>::max(); - SuffixStrategy(NextSegmentCalculationStrategy strategy) + SuffixStrategy(NextSuffixStrategy strategy, uint32_t offset = 0, + uint32_t manifest_capacity = MAX_MANIFEST_CAPACITY) : suffix_stragegy_(strategy), + next_suffix_(offset), + manifest_capacity_(manifest_capacity), total_count_(0), - final_suffix_(INVALID_SUFFIX) {} + final_suffix_(MAX_SUFFIX) {} virtual ~SuffixStrategy() = default; - virtual uint32_t checkNextSuffix() = 0; - + virtual uint32_t checkNextSuffix() const = 0; virtual uint32_t getNextSuffix() = 0; - virtual uint32_t getFinalSuffix() { return final_suffix_; } - - virtual void setFinalSuffix(std::uint32_t final_suffix) { - if (final_suffix != INVALID_SUFFIX) { - final_suffix_ = final_suffix; - } - } - - virtual uint32_t checkNextManifestSuffix() = 0; - + virtual uint32_t checkNextManifestSuffix() const = 0; virtual uint32_t getNextManifestSuffix() = 0; - virtual uint32_t checkNextContentSuffix() = 0; - + virtual uint32_t checkNextContentSuffix() const = 0; virtual uint32_t getNextContentSuffix() = 0; - virtual void reset(uint32_t offset = 0) = 0; + virtual void reset(uint32_t offset = 0) { + next_suffix_ = offset; + total_count_ = 0; + } - virtual uint32_t getManifestCapacity() = 0; + virtual uint32_t getManifestCapacity() const { return manifest_capacity_; }; - virtual void setManifestCapacity(uint32_t capacity) = 0; + virtual void setManifestCapacity(uint8_t capacity) { + manifest_capacity_ = capacity; + } - virtual uint32_t getTotalCount() { return total_count_; }; + virtual uint32_t getFinalSuffix() const { return final_suffix_; } - NextSegmentCalculationStrategy getSuffixStrategy() { - return suffix_stragegy_; + virtual void setFinalSuffix(std::uint32_t final_suffix) { + if (final_suffix != MAX_SUFFIX) { + final_suffix_ = final_suffix; + } } - protected: - inline void incrementTotalCount() { total_count_++; }; + NextSuffixStrategy getSuffixStrategy() const { return suffix_stragegy_; } + + virtual uint32_t getTotalCount() const { return total_count_; } protected: - NextSegmentCalculationStrategy suffix_stragegy_; + NextSuffixStrategy suffix_stragegy_; + std::uint32_t next_suffix_; + std::uint8_t manifest_capacity_; std::uint32_t total_count_; std::uint32_t final_suffix_; + + inline void incrementTotalCount() { total_count_++; }; }; class IncrementalSuffixStrategy : public SuffixStrategy { public: IncrementalSuffixStrategy(std::uint32_t start_offset) - : SuffixStrategy(NextSegmentCalculationStrategy::INCREMENTAL), - next_suffix_(start_offset) {} + : SuffixStrategy(NextSuffixStrategy::INCREMENTAL, start_offset) {} - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() override { + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() const override { return next_suffix_; } @@ -89,7 +105,8 @@ class IncrementalSuffixStrategy : public SuffixStrategy { return next_suffix_++; } - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextContentSuffix() override { + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextContentSuffix() + const override { return checkNextSuffix(); } @@ -97,7 +114,8 @@ class IncrementalSuffixStrategy : public SuffixStrategy { return getNextSuffix(); } - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() override { + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() + const override { return checkNextSuffix(); } @@ -105,90 +123,20 @@ class IncrementalSuffixStrategy : public SuffixStrategy { return getNextSuffix(); } - uint32_t getManifestCapacity() override { - throw errors::RuntimeException( - "No manifest capacity in IncrementalSuffixStrategy."); - } - - void setManifestCapacity(uint32_t capacity) override { - throw errors::RuntimeException( - "No manifest capacity in IncrementalSuffixStrategy."); - } - void reset(std::uint32_t offset = 0) override { next_suffix_ = offset; } - - protected: - std::uint32_t next_suffix_; -}; - -class CapacityBasedSuffixStrategy : public SuffixStrategy { - public: - CapacityBasedSuffixStrategy(std::uint32_t start_offset, - std::uint32_t manifest_capacity) - : SuffixStrategy(NextSegmentCalculationStrategy::INCREMENTAL), - next_suffix_(start_offset), - segments_in_manifest_(manifest_capacity), - current_manifest_iteration_(0) {} - - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() override { - return next_suffix_; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextSuffix() override { - incrementTotalCount(); - return next_suffix_++; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextContentSuffix() override { - return next_suffix_ % segments_in_manifest_ == 0 ? next_suffix_ - : (next_suffix_ + 1); - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextContentSuffix() override { - incrementTotalCount(); - return next_suffix_ % segments_in_manifest_ == 0 ? next_suffix_++ - : ++next_suffix_; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() override { - return (current_manifest_iteration_ + 1) * (segments_in_manifest_ + 1); - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override { - incrementTotalCount(); - return (current_manifest_iteration_++) * (segments_in_manifest_ + 1); - } - - TRANSPORT_ALWAYS_INLINE uint32_t getManifestCapacity() override { - return segments_in_manifest_; - } - - TRANSPORT_ALWAYS_INLINE void setManifestCapacity(uint32_t capacity) override { - segments_in_manifest_ = capacity; - } - - void reset(std::uint32_t offset = 0) override { next_suffix_ = offset; } - - protected: - std::uint32_t next_suffix_; - std::uint32_t segments_in_manifest_; - std::uint32_t current_manifest_iteration_; }; class SuffixStrategyFactory { public: static std::unique_ptr<SuffixStrategy> getSuffixStrategy( - NextSegmentCalculationStrategy strategy, uint32_t start_offset, - uint32_t manifest_capacity = 0) { + NextSuffixStrategy strategy, uint32_t start_offset = 0, + uint32_t manifest_capacity = SuffixStrategy::MAX_MANIFEST_CAPACITY) { switch (strategy) { - case NextSegmentCalculationStrategy::INCREMENTAL: + case NextSuffixStrategy::INCREMENTAL: return std::make_unique<IncrementalSuffixStrategy>(start_offset); - case NextSegmentCalculationStrategy::MANIFEST_CAPACITY_BASED: - return std::make_unique<CapacityBasedSuffixStrategy>(start_offset, - manifest_capacity); default: throw errors::RuntimeException( - "No valid NextSegmentCalculationStrategy specified."); + "No valid NextSuffixStrategy specified."); } } }; diff --git a/libtransport/src/utils/test.h b/libtransport/src/utils/test.h index e3dd619ac..b91c8cb1f 100644 --- a/libtransport/src/utils/test.h +++ b/libtransport/src/utils/test.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/src/utils/uri.cc b/libtransport/src/utils/uri.cc index 33eb8b45b..12b11641c 100644 --- a/libtransport/src/utils/uri.cc +++ b/libtransport/src/utils/uri.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 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: diff --git a/libtransport/third-party/CMakeLists.txt b/libtransport/third-party/CMakeLists.txt index 46a4c0f23..ad7b14ead 100644 --- a/libtransport/third-party/CMakeLists.txt +++ b/libtransport/third-party/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 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: @@ -11,8 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(FetchContent) - set (THIRD_PARTY_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}) option(ENABLE_RELY "Enable download/build of rely library" OFF) @@ -20,21 +18,34 @@ option(ENABLE_RELY "Enable download/build of rely library" OFF) if (ENABLE_RELY) ################################## # Download librely - - if(DEFINED ENV{BITBUCKET_USERNAME} AND DEFINED ENV{BITBUCKET_PASSWORD}) - set(GIT_REPO https://$ENV{BITBUCKET_USERNAME}:$ENV{BITBUCKET_PASSWORD}@bitbucket-eng-gpk1.cisco.com/bitbucket/scm/icn/rely.git) + + if (INTERNAL_ENVIRONMENT) + include(SetRelyGitRepo) + SetRelyGitRepo() else() - set(GIT_REPO ssh://git@bitbucket-eng-gpk1.cisco.com:7999/icn/rely.git) + if(DEFINED ENV{GITHUB_USERNAME} AND DEFINED ENV{GITHUB_PASSWORD}) + set(GIT_REPO https://$ENV{GITHUB_USERNAME}:$ENV{GITHUB_PASSWORD}@github.com/steinwurf/rely.git) + else() + set(GIT_REPO ssh://github.com/steinwurf/rely.git) + endif() endif() FetchContent_Declare( rely GIT_REPOSITORY ${GIT_REPO} GIT_TAG release/latest + GIT_SHALLOW + GIT_PROGRESS + EXCLUDE_FROM_ALL ) set(ENABLE_PIC ON) - FetchContent_MakeAvailable(rely) + FetchContent_GetProperties(rely) + if(NOT rely_POPULATED) + pr("Download and build" "rely") + FetchContent_Populate(rely) + add_subdirectory(${rely_SOURCE_DIR} ${rely_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() list(APPEND THIRD_PARTY_INCLUDE_DIRS ${rely_BINARY_DIR} @@ -44,7 +55,7 @@ if (ENABLE_RELY) # Get rely libraries get_property(steinwurf_object_libraries GLOBAL PROPERTY steinwurf::object_libraries) - + foreach(rely_library ${steinwurf_object_libraries}) list(APPEND THIRD_PARTY_OBJECT_LIBRARIES $<TARGET_OBJECTS:${rely_library}> @@ -60,9 +71,19 @@ FetchContent_Declare( glog URL https://github.com/google/glog/archive/refs/tags/v0.5.0.zip PATCH_COMMAND patch -p1 CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/glog.patch + EXCLUDE_FROM_ALL ) -FetchContent_MakeAvailable(glog) +FetchContent_GetProperties(glog) +if(NOT glog_POPULATED) + pr("Download and build" "glog") + FetchContent_Populate(glog) + option(WITH_GFLAGS OFF) + option(WITH_GTEST OFF) + option(WITH_UNWIND OFF) + option(BUILD_TESTING OFF) + add_subdirectory(${glog_SOURCE_DIR} ${glog_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() list(APPEND THIRD_PARTY_INCLUDE_DIRS ${glog_BINARY_DIR} @@ -77,6 +98,42 @@ list(APPEND THIRD_PARTY_DEPENDENCIES glog ) +if(UNIX AND (NOT APPLE) AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES Android)) + ############################################################## + # Get memif version + ############################################################## + list(GET LIBMEMIF_DEFAULT_VERSION 0 MEMIF_VERSION) + + FetchContent_Declare( + memif + GIT_REPOSITORY https://github.com/FDio/vpp + GIT_TAG v${MEMIF_VERSION} + GIT_SHALLOW + GIT_PROGRESS + PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/memif.patch + EXCLUDE_FROM_ALL + ) + + FetchContent_GetProperties(memif) + if(NOT memif_POPULATED) + FetchContent_Populate(memif) + add_subdirectory(${memif_SOURCE_DIR}/extras/libmemif ${memif_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + list(APPEND THIRD_PARTY_INCLUDE_DIRS + ${memif_BINARY_DIR} + ${memif_SOURCE_DIR}/extras/libmemif/src + ) + + list(APPEND THIRD_PARTY_OBJECT_LIBRARIES + $<TARGET_OBJECTS:memif> + ) + + list(APPEND THIRD_PARTY_DEPENDENCIES + memif + ) +endif() + set (THIRD_PARTY_LIBRARIES ${THIRD_PARTY_LIBRARIES} PARENT_SCOPE) set (THIRD_PARTY_OBJECT_LIBRARIES ${THIRD_PARTY_OBJECT_LIBRARIES} PARENT_SCOPE) set (THIRD_PARTY_INCLUDE_DIRS ${THIRD_PARTY_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/libtransport/third-party/CMakeLists.txt.orig b/libtransport/third-party/CMakeLists.txt.orig deleted file mode 100644 index 84db8cb4c..000000000 --- a/libtransport/third-party/CMakeLists.txt.orig +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2021 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(FetchContent) - -set (THIRD_PARTY_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}) - -option(ENABLE_RELY "Enable download/build of rely library" OFF) - -if (ENABLE_RELY) - ################################## - # Download librely - - if(DEFINED ENV{BITBUCKET_USERNAME} AND DEFINED ENV{BITBUCKET_PASSWORD}) - set(GIT_REPO https://$ENV{BITBUCKET_USERNAME}:$ENV{BITBUCKET_PASSWORD}@bitbucket-eng-gpk1.cisco.com/bitbucket/scm/icn/rely.git) - else() - set(GIT_REPO ssh://git@bitbucket-eng-gpk1.cisco.com:7999/icn/rely.git) - endif() - - FetchContent_Declare( - rely - GIT_REPOSITORY ${GIT_REPO} - GIT_TAG release/latest - FETCHCONTENT_QUIET - ) - - set(ENABLE_PIC ON) - FetchContent_MakeAvailable(rely) - - list(APPEND THIRD_PARTY_INCLUDE_DIRS - ${rely_BINARY_DIR} - ${rely_SOURCE_DIR}/src - ) - - # Get rely libraries - get_property(steinwurf_object_libraries GLOBAL - PROPERTY steinwurf::object_libraries) - - foreach(rely_library ${steinwurf_object_libraries}) - list(APPEND THIRD_PARTY_OBJECT_LIBRARIES - $<TARGET_OBJECTS:${rely_library}> - ) - endforeach() - - list(APPEND THIRD_PARTY_DEPENDENCIES - rely - ) -endif() - -FetchContent_Declare( - glog - URL https://github.com/google/glog/archive/refs/tags/v0.5.0.zip - PATCH_COMMAND patch -p1 CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/glog.patch - FETCHCONTENT_QUIET -) - -FetchContent_MakeAvailable(glog) - -list(APPEND THIRD_PARTY_INCLUDE_DIRS - ${glog_BINARY_DIR} - ${glog_SOURCE_DIR}/src -) - -list(APPEND THIRD_PARTY_OBJECT_LIBRARIES - $<TARGET_OBJECTS:glog> -) - -list(APPEND THIRD_PARTY_DEPENDENCIES - glog -) - -set (THIRD_PARTY_LIBRARIES ${THIRD_PARTY_LIBRARIES} PARENT_SCOPE) -set (THIRD_PARTY_OBJECT_LIBRARIES ${THIRD_PARTY_OBJECT_LIBRARIES} PARENT_SCOPE) -set (THIRD_PARTY_INCLUDE_DIRS ${THIRD_PARTY_INCLUDE_DIRS} PARENT_SCOPE) -set (THIRD_PARTY_DEPENDENCIES ${THIRD_PARTY_DEPENDENCIES} PARENT_SCOPE) diff --git a/libtransport/third-party/CMakeLists.txt.rej b/libtransport/third-party/CMakeLists.txt.rej deleted file mode 100644 index 4ebaf9061..000000000 --- a/libtransport/third-party/CMakeLists.txt.rej +++ /dev/null @@ -1,21 +0,0 @@ ---- CMakeLists.txt -+++ CMakeLists.txt -@@ -41,9 +41,6 @@ if (NOT WITH_THREADS) - set (CMAKE_DISABLE_FIND_PACKAGE_Threads ON) - endif (NOT WITH_THREADS) - --set (CMAKE_C_VISIBILITY_PRESET hidden) --set (CMAKE_CXX_VISIBILITY_PRESET hidden) --set (CMAKE_VISIBILITY_INLINES_HIDDEN 1) - list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) - - include (CheckCXXSourceCompiles) -@@ -570,7 +567,7 @@ if (_glog_CMake_MODULES) - ) - endif (_glog_CMake_MODULES) - --add_library (glog -+add_library (glog OBJECT - ${GLOG_SRCS} - ${_glog_BINARY_CMake_MODULES} - ) diff --git a/libtransport/third-party/glog.patch b/libtransport/third-party/glog.patch index 9d6e46df0..4f4d440eb 100644 --- a/libtransport/third-party/glog.patch +++ b/libtransport/third-party/glog.patch @@ -20,4 +20,4 @@ index 62ebbcc..7d92fa5 100644 +add_library (glog OBJECT ${GLOG_SRCS} ${_glog_BINARY_CMake_MODULES} - ) + )
\ No newline at end of file diff --git a/libtransport/third-party/memif.patch b/libtransport/third-party/memif.patch new file mode 100644 index 000000000..0a64513e3 --- /dev/null +++ b/libtransport/third-party/memif.patch @@ -0,0 +1,43 @@ +diff --git a/extras/libmemif/CMakeLists.txt b/extras/libmemif/CMakeLists.txt +index b6b658c2d..28bb8c135 100644 +--- a/extras/libmemif/CMakeLists.txt ++++ b/extras/libmemif/CMakeLists.txt +@@ -24,7 +24,7 @@ if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") + endif () + +-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -DMEMIF_DBG -DICMP_DBG") ++set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -DICMP_DBG") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib) + set(CMAKE_INSTALL_MESSAGE NEVER) +diff --git a/extras/libmemif/src/CMakeLists.txt b/extras/libmemif/src/CMakeLists.txt +index ddb8a52f8..b52566e5c 100644 +--- a/extras/libmemif/src/CMakeLists.txt ++++ b/extras/libmemif/src/CMakeLists.txt +@@ -32,22 +32,7 @@ list(APPEND MEMIF_SOURCES + + include_directories(${HEADERS_DIR}) + +-add_library(memif SHARED ${MEMIF_SOURCES}) +-target_link_libraries(memif ${CMAKE_THREAD_LIBS_INIT}) ++add_library(memif OBJECT ${MEMIF_SOURCES}) ++set_property(TARGET memif PROPERTY POSITION_INDEPENDENT_CODE ON) + +-find_library(LIB_BSD bsd) +-if(LIB_BSD) +- add_compile_definitions(HAS_LIB_BSD) +- target_link_libraries(memif ${LIB_BSD}) +-endif() +- +-foreach(file ${MEMIF_HEADERS}) +- get_filename_component(dir ${file} DIRECTORY) +- install( +- FILES ${file} +- DESTINATION include/${lib}/${dir} +- COMPONENT libmemif-dev +- ) +-endforeach() +- +-install(TARGETS memif DESTINATION lib COMPONENT libmemif) ++target_link_libraries(memif ${CMAKE_THREAD_LIBS_INIT}) |