diff options
Diffstat (limited to 'libtransport/src')
251 files changed, 26748 insertions, 13254 deletions
diff --git a/libtransport/src/CMakeLists.txt b/libtransport/src/CMakeLists.txt index 33497e0f4..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,80 +11,185 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - -include(GNUInstallDirs) - -set(ASIO_STANDALONE 1) - +############################################################## +# Source files +############################################################## add_subdirectory(core) add_subdirectory(interfaces) add_subdirectory(protocols) -add_subdirectory(security) +add_subdirectory(auth) 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 lib${LIBTRANSPORT}-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} ) -set (COMPILER_DEFINITIONS "-DTRANSPORT_LOG_DEF_LEVEL=TRANSPORT_LOG_${TRANSPORT_LOG_LEVEL}") -list(INSERT LIBTRANSPORT_INTERNAL_INCLUDE_DIRS 0 - ${CMAKE_CURRENT_SOURCE_DIR}/ - ${CMAKE_CURRENT_BINARY_DIR}/ +############################################################## +# 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 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}> ) -set(LIBTRANSPORT_INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - "" CACHE INTERNAL - "" FORCE + +############################################################## +# Dependencies +############################################################## +list(APPEND DEPENDENCIES + ${THIRD_PARTY_DEPENDENCIES} +) + +############################################################## +# 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") + list(APPEND COMPILER_OPTIONS + PRIVATE "-/wd4200 -D_WIN32_WINDOWS=0x0400" + ) if (CMAKE_BUILD_TYPE EQUAL "RELEASE") - set(CMAKE_SHARED_LINKER_FLAGS "/NODEFAULTLIB:\"MSVCRTD\"" ) + list(APPEND COMPILER_OPTIONS + PRIVATE "/NODEFAULTLIB:\"MSVCRTD\"" + ) endif () endif () -if (${CMAKE_SYSTEM_NAME} STREQUAL "Android") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -isystem -lm") + +if (${CMAKE_SYSTEM_NAME} MATCHES "Android") + list(APPEND COMPILER_OPTIONS + PRIVATE "-stdlib=libc++" + PRIVATE "-isystem" + PRIVATE "-lm" + ) endif() -if (DISABLE_SHARED_LIBRARIES) - build_library(${LIBTRANSPORT} - STATIC - SOURCES ${SOURCE_FILES} ${HEADER_FILES} - INSTALL_HEADERS ${LIBHICNTRANSPORT_TO_INSTALL_HEADER_FILES} - LINK_LIBRARIES ${LIBRARIES} - DEPENDS ${DEPENDENCIES} - COMPONENT lib${LIBTRANSPORT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn/transport - DEFINITIONS ${COMPILER_DEFINITIONS} + +############################################################## +# 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 () - build_library(${LIBTRANSPORT} - STATIC SHARED - SOURCES ${SOURCE_FILES} ${HEADER_FILES} - INSTALL_HEADERS ${LIBHICNTRANSPORT_TO_INSTALL_HEADER_FILES} - LINK_LIBRARIES ${LIBRARIES} - DEPENDS ${DEPENDENCIES} - COMPONENT lib${LIBTRANSPORT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - INSTALL_ROOT_DIR hicn/transport - DEFINITIONS ${COMPILER_DEFINITIONS} + install( + FILES "transport.config" + DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/hicn + COMPONENT ${LIBTRANSPORT_COMPONENT} ) -endif () +endif() + + +############################################################## +# IO Modules +############################################################## +add_subdirectory(io_modules) + +############################################################## +# Build type +############################################################## +set (BUILD_TYPES "STATIC") + +if (NOT DISABLE_SHARED_LIBRARIES) + list(APPEND BUILD_TYPES + "SHARED" + ) +endif() + + +############################################################## +# Build library +############################################################## +build_library(${LIBTRANSPORT} + ${BUILD_TYPES} + SOURCES ${SOURCE_FILES} ${HEADER_FILES} + INSTALL_HEADERS ${LIBHICNTRANSPORT_TO_INSTALL_HEADER_FILES} + LINK_LIBRARIES ${LIBRARIES} + OBJECT_LIBRARIES ${THIRD_PARTY_OBJECT_LIBRARIES} + DEPENDS ${DEPENDENCIES} + COMPONENT ${LIBTRANSPORT_COMPONENT} + INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + 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/security/CMakeLists.txt b/libtransport/src/auth/CMakeLists.txt index 0e7b5832b..8d3286338 100644 --- a/libtransport/src/security/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: @@ -11,12 +11,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - 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 ) set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) diff --git a/libtransport/src/auth/crypto_hash.cc b/libtransport/src/auth/crypto_hash.cc new file mode 100644 index 000000000..cf781d08e --- /dev/null +++ b/libtransport/src/auth/crypto_hash.cc @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#include <glog/logging.h> +#include <hicn/transport/auth/crypto_hash.h> +#include <hicn/transport/core/global_object_pool.h> + +namespace transport { +namespace auth { + +CryptoHash::CryptoHash() : CryptoHash(CryptoHashType::UNKNOWN) {} + +CryptoHash::CryptoHash(const CryptoHash &other) + : digest_type_(other.digest_type_), + digest_(other.digest_), + digest_size_(other.digest_size_) {} + +CryptoHash::CryptoHash(CryptoHash &&other) noexcept + : digest_type_(std::move(other.digest_type_)), + digest_(std::move(other.digest_)), + digest_size_(other.digest_size_) {} + +CryptoHash::CryptoHash(CryptoHashType hash_type) + : digest_(core::PacketManager<>::getInstance().getMemBuf()) { + setType(hash_type); +} + +CryptoHash::CryptoHash(const uint8_t *hash, size_t size, + CryptoHashType hash_type) + : digest_type_(hash_type), digest_size_(size) { + digest_ = core::PacketManager<>::getInstance().getMemBuf(); + digest_->append(size); + memcpy(digest_->writableData(), hash, size); +} + +CryptoHash::CryptoHash(const std::vector<uint8_t> &hash, + CryptoHashType hash_type) + : CryptoHash(hash.data(), hash.size(), hash_type) {} + +CryptoHash &CryptoHash::operator=(const CryptoHash &other) { + if (this != &other) { + digest_type_ = other.digest_type_; + digest_ = other.digest_; + digest_size_ = other.digest_size_; + } + return *this; +} + +bool CryptoHash::operator==(const CryptoHash &other) const { + return (digest_type_ == other.digest_type_ && *digest_ == *other.digest_); +} + +void CryptoHash::computeDigest(const uint8_t *buffer, size_t len) { + const EVP_MD *hash_md = CryptoHash::getMD(digest_type_); + if (hash_md == nullptr) { + throw errors::RuntimeException("Unknown hash type"); + } + + if (EVP_Digest(buffer, len, digest_->writableData(), + reinterpret_cast<unsigned int *>(&digest_size_), hash_md, + nullptr) != 1) { + throw errors::RuntimeException("Digest computation failed."); + }; +} + +void CryptoHash::computeDigest(const std::vector<uint8_t> &buffer) { + computeDigest(buffer.data(), buffer.size()); +} + +void CryptoHash::computeDigest(const utils::MemBuf *buffer) { + if (buffer->isChained()) { + throw errors::RuntimeException( + "Digest of chained membuf is not supported."); + } + + computeDigest(buffer->data(), buffer->length()); +} + +const utils::MemBuf::Ptr &CryptoHash::getDigest() const { return digest_; } + +std::string CryptoHash::getStringDigest() const { + std::stringstream string_digest; + + string_digest << std::hex << std::setfill('0'); + + for (size_t i = 0; i < digest_size_; ++i) { + string_digest << std::hex << std::setw(2) + << static_cast<int>(digest_->data()[i]); + } + + return string_digest.str(); +} + +CryptoHashType CryptoHash::getType() const { return digest_type_; } + +size_t CryptoHash::getSize() const { return digest_size_; } + +void CryptoHash::setType(CryptoHashType hash_type) { + digest_type_ = hash_type; + digest_size_ = CryptoHash::getSize(hash_type); + DCHECK(digest_size_ <= digest_->tailroom()); + digest_->setLength(digest_size_); +} + +void CryptoHash::display() { + switch (digest_type_) { + case CryptoHashType::SHA256: + LOG(INFO) << "SHA256: " << getStringDigest(); + break; + case CryptoHashType::SHA512: + LOG(INFO) << "SHA512: " << getStringDigest(); + break; + case CryptoHashType::BLAKE2S256: + LOG(INFO) << "BLAKE2s256: " << getStringDigest(); + break; + case CryptoHashType::BLAKE2B512: + LOG(INFO) << "BLAKE2b512: " << getStringDigest(); + break; + default: + LOG(INFO) << "UNKNOWN: " << getStringDigest(); + break; + } +} + +void CryptoHash::reset() { + digest_type_ = CryptoHashType::UNKNOWN; + digest_size_ = 0; + digest_->setLength(0); +} + +const EVP_MD *CryptoHash::getMD(CryptoHashType hash_type) { + switch (hash_type) { + case CryptoHashType::SHA256: + return EVP_sha256(); + case CryptoHashType::SHA512: + return EVP_sha512(); + case CryptoHashType::BLAKE2S256: + return EVP_blake2s256(); + case CryptoHashType::BLAKE2B512: + return EVP_blake2b512(); + default: + return nullptr; + } +} + +size_t CryptoHash::getSize(CryptoHashType hash_type) { + const EVP_MD *hash_md = CryptoHash::getMD(hash_type); + return hash_md == nullptr ? 0 : EVP_MD_size(hash_md); +} + +bool CryptoHash::compareDigest(const uint8_t *digest1, const uint8_t *digest2, + CryptoHashType hash_type) { + const EVP_MD *hash_md = CryptoHash::getMD(hash_type); + return hash_md == nullptr + ? false + : !static_cast<bool>( + memcmp(digest1, digest2, CryptoHash::getSize(hash_type))); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/crypto_suite.cc b/libtransport/src/auth/crypto_suite.cc new file mode 100644 index 000000000..e7b097ac4 --- /dev/null +++ b/libtransport/src/auth/crypto_suite.cc @@ -0,0 +1,131 @@ +/* + * 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/auth/crypto_suite.h> + +namespace transport { +namespace auth { + +CryptoSuite getSuite(int nid) { + switch (nid) { + case NID_ED25519: + return CryptoSuite::ED25519; + case NID_ED448: + return CryptoSuite::ED448; + case NID_ecdsa_with_SHA256: + return CryptoSuite::ECDSA_SHA256; + case NID_ecdsa_with_SHA512: + return CryptoSuite::ECDSA_SHA512; + case NID_sha256WithRSAEncryption: + return CryptoSuite::RSA_SHA256; + case NID_sha512WithRSAEncryption: + return CryptoSuite::RSA_SHA512; + case NID_hmacWithSHA256: + return CryptoSuite::HMAC_SHA256; + case NID_hmacWithSHA512: + return CryptoSuite::HMAC_SHA512; + case NID_dsa_with_SHA256: + return CryptoSuite::DSA_SHA256; + case NID_dsa_with_SHA512: + return CryptoSuite::DSA_SHA512; + default: + return CryptoSuite::UNKNOWN; + } +} + +std::string getStringSuite(CryptoSuite suite) { + switch (suite) { + case CryptoSuite::ED25519: + return "ED25519"; + case CryptoSuite::ED448: + return "ED448"; + 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"; + } +} + +CryptoHashType getHashType(CryptoSuite suite) { + switch (suite) { + case CryptoSuite::DSA_BLAKE2B512: + case CryptoSuite::ECDSA_BLAKE2B512: + case CryptoSuite::HMAC_BLAKE2B512: + case CryptoSuite::RSA_BLAKE2B512: + return CryptoHashType::BLAKE2B512; + case CryptoSuite::DSA_BLAKE2S256: + case CryptoSuite::ECDSA_BLAKE2S256: + case CryptoSuite::HMAC_BLAKE2S256: + case CryptoSuite::RSA_BLAKE2S256: + return CryptoHashType::BLAKE2S256; + case CryptoSuite::DSA_SHA256: + case CryptoSuite::ECDSA_SHA256: + case CryptoSuite::ED25519: + case CryptoSuite::ED448: + case CryptoSuite::HMAC_SHA256: + case CryptoSuite::RSA_SHA256: + return CryptoHashType::SHA256; + case CryptoSuite::DSA_SHA512: + case CryptoSuite::ECDSA_SHA512: + case CryptoSuite::HMAC_SHA512: + case CryptoSuite::RSA_SHA512: + return CryptoHashType::SHA512; + default: + return CryptoHashType::UNKNOWN; + } +} + +const EVP_MD *getMD(CryptoSuite suite) { + switch (suite) { + case CryptoSuite::ED25519: + case CryptoSuite::ED448: + return nullptr; + default: + return CryptoHash::getMD(getHashType(suite)); + } +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/signer.cc b/libtransport/src/auth/signer.cc new file mode 100644 index 000000000..5fcb242be --- /dev/null +++ b/libtransport/src/auth/signer.cc @@ -0,0 +1,270 @@ +/* + * 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. + */ + +#include <glog/logging.h> +#include <hicn/transport/auth/signer.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/utils/chrono_typedefs.h> + +#include "hicn/transport/core/global_object_pool.h" + +namespace transport { +namespace auth { + +// --------------------------------------------------------- +// Base Signer +// --------------------------------------------------------- +Signer::Signer() + : suite_(CryptoSuite::UNKNOWN), + signature_(core::PacketManager<>::getInstance().getMemBuf()), + signature_len_(0), + key_(nullptr) {} + +Signer::~Signer() {} + +void Signer::signPacket(PacketPtr packet) { + DCHECK(key_ != nullptr); + if (!packet->hasAH()) { + throw errors::MalformedAHPacketException(); + } + + // Set signature size + size_t signature_field_len = getSignatureFieldSize(); + packet->setSignatureFieldSize(signature_field_len); + packet->updateLength(); // update IP payload length + + // Copy IP+TCP / ICMP header before zeroing them + u8 header_copy[HICN_HDRLEN_MAX]; + size_t header_len; + packet->saveHeader(header_copy, &header_len); + + // Copy bitmap from interest manifest + uint32_t request_bitmap[BITMAP_SIZE] = {0}; + if (packet->isInterest()) { + core::Interest *interest = dynamic_cast<core::Interest *>(packet); + if (interest->hasManifest()) + memcpy(request_bitmap, interest->getRequestBitmap(), + BITMAP_SIZE * sizeof(uint32_t)); + } + + // Fill in the hICN AH header + auto now = utils::SteadyTime::nowMs().count(); + packet->setSignatureTimestamp(now); + packet->setValidationAlgorithm(suite_); + + // Set key ID + const utils::MemBuf::Ptr &key_id = key_id_.getDigest(); + packet->setKeyId({key_id->writableData(), key_id->length()}); + + // Reset fields to compute the packet hash + packet->resetForHash(); + + // Compute the signature and put it in the packet + signBuffer(packet); + packet->setSignature(signature_); + packet->setSignatureSize(signature_len_); + + // Restore header + packet->loadHeader(header_copy, header_len); + + // Restore bitmap in interest manifest + if (packet->isInterest()) { + core::Interest *interest = dynamic_cast<core::Interest *>(packet); + interest->setRequestBitmap(request_bitmap); + } +} + +void Signer::signBuffer(const uint8_t *buffer, std::size_t len) { + const EVP_MD *hash_md = getMD(suite_); + + std::shared_ptr<EVP_MD_CTX> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (md_ctx == nullptr) { + throw errors::RuntimeException("Signature context allocation failed"); + } + + if (EVP_DigestSignInit(md_ctx.get(), nullptr, hash_md, nullptr, key_.get()) != + 1) { + throw errors::RuntimeException("Signature initialization failed"); + } + + if (EVP_DigestSign(md_ctx.get(), nullptr, &signature_len_, buffer, len) != + 1) { + throw errors::RuntimeException("Signature length computation failed"); + }; + + DCHECK(signature_len_ <= signature_->tailroom()); + signature_->setLength(signature_len_); + + if (EVP_DigestSign(md_ctx.get(), signature_->writableData(), &signature_len_, + buffer, len) != 1) { + throw errors::RuntimeException("Signature computation failed"); + }; + + DCHECK(signature_len_ <= signature_->tailroom()); + signature_->setLength(signature_len_); +} + +void Signer::signBuffer(const std::vector<uint8_t> &buffer) { + signBuffer(buffer.data(), buffer.size()); +} + +void Signer::signBuffer(const utils::MemBuf *buffer) { + if (buffer->isChained()) { + throw errors::RuntimeException( + "Signature of chained membuf is not supported."); + } + + signBuffer(buffer->data(), buffer->length()); +} + +const utils::MemBuf::Ptr &Signer::getSignature() const { return signature_; } + +std::string Signer::getStringSignature() const { + std::stringstream string_sig; + string_sig << std::hex << std::setfill('0'); + + for (size_t i = 0; i < signature_len_; ++i) { + string_sig << std::hex << std::setw(2) + << static_cast<int>(signature_->data()[i]); + } + + return string_sig.str(); +} + +size_t Signer::getSignatureSize() const { return signature_len_; } + +size_t Signer::getSignatureFieldSize() const { + if (signature_len_ % 4 == 0) { + return signature_len_; + } + + return (signature_len_ + 4) - (signature_len_ % 4); +} + +CryptoSuite Signer::getSuite() const { return suite_; } + +CryptoHashType Signer::getHashType() const { + return ::transport::auth::getHashType(suite_); +} + +void Signer::display() { + LOG(INFO) << getStringSuite(suite_) << ": " << getStringSignature(); +} + +// --------------------------------------------------------- +// Void Signer +// --------------------------------------------------------- +void VoidSigner::signPacket(PacketPtr packet) {} + +void VoidSigner::signBuffer(const uint8_t *buffer, std::size_t len) {} + +void VoidSigner::signBuffer(const std::vector<uint8_t> &buffer) {} + +void VoidSigner::signBuffer(const utils::MemBuf *buffer) {} + +// --------------------------------------------------------- +// Asymmetric Signer +// --------------------------------------------------------- +AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, + std::shared_ptr<EVP_PKEY> key, + std::shared_ptr<EVP_PKEY> pub_key) + : Signer() { + setKey(suite, key, pub_key); +} + +AsymmetricSigner::AsymmetricSigner(std::string keystore_path, + std::string password) + : Signer() { + 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; + + signature_len_ = EVP_PKEY_size(key_.get()); + DCHECK(signature_len_ <= signature_->tailroom()); + signature_->setLength(signature_len_); + + // Key ID is not supported yet. + uint8_t id[8] = {0}; + key_id_ = CryptoHash(getHashType()); + key_id_.computeDigest(id, 8); +} + +size_t AsymmetricSigner::getSignatureFieldSize() const { + size_t field_size = EVP_PKEY_size(key_.get()); + + if (field_size % 4 == 0) { + return field_size; + } + + return (field_size + 4) - (field_size % 4); +} + +// --------------------------------------------------------- +// Symmetric Signer +// --------------------------------------------------------- +SymmetricSigner::SymmetricSigner(CryptoSuite suite, + const std::string &passphrase) + : Signer() { + suite_ = suite; + 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); + + const EVP_MD *hash_md = getMD(suite_); + if (hash_md == nullptr) { + throw errors::RuntimeException("Unknown hash type"); + } + + signature_len_ = EVP_MD_size(hash_md); + DCHECK(signature_len_ <= signature_->tailroom()); + signature_->setLength(signature_len_); + + // Key ID is not supported yet. + uint8_t id[8] = {0}; + key_id_ = CryptoHash(getHashType()); + key_id_.computeDigest(id, 8); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/auth/verifier.cc b/libtransport/src/auth/verifier.cc new file mode 100644 index 000000000..0919aec7d --- /dev/null +++ b/libtransport/src/auth/verifier.cc @@ -0,0 +1,370 @@ +/* + * 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. + */ + +#include <hicn/transport/auth/verifier.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/core/interest.h> +#include <protocols/errors.h> + +#include "glog/logging.h" + +namespace transport { +namespace auth { + +const std::vector<VerificationPolicy> Verifier::DEFAULT_FAILED_POLICIES = { + VerificationPolicy::DROP, + VerificationPolicy::ABORT, +}; + +// --------------------------------------------------------- +// Base Verifier +// --------------------------------------------------------- +Verifier::Verifier() + : verification_failed_cb_(interface::VOID_HANDLER), + failed_policies_(DEFAULT_FAILED_POLICIES) {} + +Verifier::~Verifier() {} + +bool Verifier::verifyPacket(PacketPtr packet) { + if (!packet->hasAH()) { + throw errors::MalformedAHPacketException(); + } + + // Get crypto suite + CryptoSuite suite = packet->getValidationAlgorithm(); + + // Copy IP+TCP / ICMP header before zeroing them + u8 header_copy[HICN_HDRLEN_MAX]; + size_t header_len; + packet->saveHeader(header_copy, &header_len); + + // Copy bitmap from interest manifest + uint32_t request_bitmap[BITMAP_SIZE] = {0}; + if (packet->isInterest()) { + core::Interest *interest = dynamic_cast<core::Interest *>(packet); + memcpy(request_bitmap, interest->getRequestBitmap(), + BITMAP_SIZE * sizeof(uint32_t)); + } + + // Retrieve packet signature + utils::MemBuf::Ptr signature_raw = packet->getSignature(); + std::size_t signature_len = packet->getSignatureSize(); + DCHECK(signature_len <= signature_raw->tailroom()); + signature_raw->setLength(signature_len); + + // Reset fields that are not used to compute signature + packet->resetForHash(); + + // Check signatures + bool valid_packet = + verifyBuffer(static_cast<utils::MemBuf *>(packet), signature_raw, suite); + + // Restore header + packet->loadHeader(header_copy, header_len); + packet->setSignature(signature_raw); + packet->setSignatureSize(signature_raw->length()); + + // Restore bitmap in interest manifest + if (packet->isInterest()) { + core::Interest *interest = dynamic_cast<core::Interest *>(packet); + interest->setRequestBitmap(request_bitmap); + } + + return valid_packet; +} + +Verifier::PolicyMap Verifier::verifyPackets( + const std::vector<PacketPtr> &packets) { + PolicyMap policies; + + for (const auto &packet : packets) { + Suffix suffix = packet->getName().getSuffix(); + VerificationPolicy policy = VerificationPolicy::ABORT; + + if (verifyPacket(packet)) { + policy = VerificationPolicy::ACCEPT; + } + + callVerificationFailedCallback(suffix, policy); + policies[suffix] = policy; + } + + return policies; +} + +Verifier::PolicyMap Verifier::verifyHashes(const SuffixMap &packet_map, + const SuffixMap &suffix_map) { + PolicyMap policies; + + for (const auto &packet_hash : packet_map) { + VerificationPolicy policy = VerificationPolicy::UNKNOWN; + auto manifest_hash = suffix_map.find(packet_hash.first); + + if (manifest_hash != suffix_map.end()) { + policy = VerificationPolicy::ABORT; + + if (packet_hash.second == manifest_hash->second) { + policy = VerificationPolicy::ACCEPT; + } + } + + callVerificationFailedCallback(packet_hash.first, policy); + policies[packet_hash.first] = policy; + } + + return policies; +} + +Verifier::PolicyMap Verifier::verifyPackets( + const std::vector<PacketPtr> &packets, const SuffixMap &suffix_map) { + PolicyMap policies; + + for (const auto &packet : packets) { + Suffix suffix = packet->getName().getSuffix(); + VerificationPolicy policy = VerificationPolicy::UNKNOWN; + auto manifest_hash = suffix_map.find(suffix); + + if (manifest_hash != suffix_map.end()) { + policy = VerificationPolicy::ABORT; + CryptoHashType hash_type = manifest_hash->second.getType(); + CryptoHash packet_hash = packet->computeDigest(hash_type); + + if (packet_hash == manifest_hash->second) { + policy = VerificationPolicy::ACCEPT; + } + } + + callVerificationFailedCallback(suffix, policy); + policies[suffix] = policy; + } + + return policies; +} + +void Verifier::setVerificationFailedCallback( + VerificationFailedCallback verfication_failed_cb, + const std::vector<VerificationPolicy> &failed_policies) { + verification_failed_cb_ = verfication_failed_cb; + failed_policies_ = failed_policies; +} + +void Verifier::getVerificationFailedCallback( + VerificationFailedCallback **verfication_failed_cb) { + *verfication_failed_cb = &verification_failed_cb_; +} + +void Verifier::callVerificationFailedCallback(Suffix suffix, + VerificationPolicy &policy) { + if (verification_failed_cb_ == interface::VOID_HANDLER) { + return; + } + + if (find(failed_policies_.begin(), failed_policies_.end(), policy) != + failed_policies_.end()) { + policy = verification_failed_cb_(suffix, policy); + } +} + +// --------------------------------------------------------- +// Void Verifier +// --------------------------------------------------------- +bool VoidVerifier::verifyPacket(PacketPtr packet) { return true; } + +bool VoidVerifier::verifyBuffer(const uint8_t *buffer, std::size_t len, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + return true; +} + +bool VoidVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + return true; +} + +bool VoidVerifier::verifyBuffer(const utils::MemBuf *buffer, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + return true; +} + +Verifier::PolicyMap VoidVerifier::verifyPackets( + const std::vector<PacketPtr> &packets) { + PolicyMap policies; + + for (const auto &packet : packets) { + auth::Suffix suffix = packet->getName().getSuffix(); + VerificationPolicy policy = VerificationPolicy::ACCEPT; + callVerificationFailedCallback(suffix, policy); + policies[suffix] = policy; + } + + return policies; +} + +Verifier::PolicyMap VoidVerifier::verifyPackets( + const std::vector<PacketPtr> &packets, const SuffixMap &suffix_map) { + return verifyPackets(packets); +} + +// --------------------------------------------------------- +// Asymmetric Verifier +// --------------------------------------------------------- +AsymmetricVerifier::AsymmetricVerifier(std::shared_ptr<EVP_PKEY> key) { + setKey(key); +} + +AsymmetricVerifier::AsymmetricVerifier(const std::string &cert_path) { + useCertificate(cert_path); +} + +AsymmetricVerifier::AsymmetricVerifier(std::shared_ptr<X509> cert) { + useCertificate(cert); +} + +void AsymmetricVerifier::setKey(std::shared_ptr<EVP_PKEY> key) { key_ = key; }; + +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"); + } + + 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(std::shared_ptr<X509> cert) { + key_ = + std::shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), ::EVP_PKEY_free); +} + +bool AsymmetricVerifier::verifyBuffer(const uint8_t *buffer, std::size_t len, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + const EVP_MD *hash_md = getMD(suite); + + std::shared_ptr<EVP_MD_CTX> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (md_ctx == nullptr) { + throw errors::RuntimeException("Signature context allocation failed"); + } + + if (EVP_DigestVerifyInit(md_ctx.get(), nullptr, hash_md, nullptr, + key_.get()) != 1) { + throw errors::RuntimeException("Signature initialization failed"); + } + + return EVP_DigestVerify(md_ctx.get(), signature->data(), signature->length(), + buffer, len) == 1; +}; + +bool AsymmetricVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + return verifyBuffer(buffer.data(), buffer.size(), signature, suite); +} + +bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + if (buffer->isChained()) { + throw errors::RuntimeException( + "Signature of chained membuf is not supported."); + } + + return verifyBuffer(buffer->data(), buffer->length(), signature, suite); +} + +// --------------------------------------------------------- +// Symmetric Verifier +// --------------------------------------------------------- +SymmetricVerifier::SymmetricVerifier(const std::string &passphrase) { + setPassphrase(passphrase); +} + +// Create and set a symmetric key from a passphrase. +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 uint8_t *buffer, std::size_t len, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + const EVP_MD *hash_md = getMD(suite); + if (hash_md == nullptr) { + throw errors::RuntimeException("Unknown hash type"); + } + + const utils::MemBuf::Ptr &signature_bis = + core::PacketManager<>::getInstance().getMemBuf(); + size_t signature_bis_len; + + std::shared_ptr<EVP_MD_CTX> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); + if (md_ctx == nullptr) { + throw errors::RuntimeException("Signature context allocation failed"); + } + + if (EVP_DigestSignInit(md_ctx.get(), nullptr, hash_md, nullptr, key_.get()) != + 1) { + throw errors::RuntimeException("Signature initialization failed"); + } + + if (EVP_DigestSign(md_ctx.get(), nullptr, &signature_bis_len, buffer, len) != + 1) { + throw errors::RuntimeException("Signature length computation failed"); + }; + + DCHECK(signature_bis_len <= signature_bis->tailroom()); + signature_bis->append(signature_bis_len); + + if (EVP_DigestSign(md_ctx.get(), signature_bis->writableData(), + &signature_bis_len, buffer, len) != 1) { + throw errors::RuntimeException("Signature computation failed"); + }; + + DCHECK(signature_bis_len <= signature_bis->tailroom()); + signature_bis->setLength(signature_bis_len); + + return signature->length() == signature_bis_len && + *signature == *signature_bis; +} + +bool SymmetricVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + return verifyBuffer(buffer.data(), buffer.size(), signature, suite); +} + +bool SymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, + const utils::MemBuf::Ptr &signature, + CryptoSuite suite) { + if (buffer->isChained()) { + throw errors::RuntimeException( + "Signature of chained membuf is not supported."); + } + + return verifyBuffer(buffer->data(), buffer->length(), signature, suite); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/config.h.in b/libtransport/src/config.h.in index 4e9a0f262..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,16 +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 -#ifndef SECURE_HICNTRANSPORT -#cmakedefine SECURE_HICNTRANSPORT -#endif - #define RAAQM_CONFIG_PATH "@raaqm_config_path@" #cmakedefine __vpp__ diff --git a/libtransport/src/core/CMakeLists.txt b/libtransport/src/core/CMakeLists.txt index 5c8ab9270..777772a04 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: @@ -11,65 +11,50 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/facade.h ${CMAKE_CURRENT_SOURCE_DIR}/manifest.h - ${CMAKE_CURRENT_SOURCE_DIR}/manifest_inline.h ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.h ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format.h ${CMAKE_CURRENT_SOURCE_DIR}/pending_interest.h ${CMAKE_CURRENT_SOURCE_DIR}/portal.h - ${CMAKE_CURRENT_SOURCE_DIR}/connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/tcp_socket_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/errors.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_id_counter.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 + ${CMAKE_CURRENT_SOURCE_DIR}/global_module_manager.h ) list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/content_object.cc ${CMAKE_CURRENT_SOURCE_DIR}/interest.cc + ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc ${CMAKE_CURRENT_SOURCE_DIR}/packet.cc ${CMAKE_CURRENT_SOURCE_DIR}/name.cc ${CMAKE_CURRENT_SOURCE_DIR}/prefix.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tcp_socket_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.cc ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.cc - ${CMAKE_CURRENT_SOURCE_DIR}/connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/portal.cc + ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.cc + ${CMAKE_CURRENT_SOURCE_DIR}/io_module.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_listener.cc + ${CMAKE_CURRENT_SOURCE_DIR}/constructor.cc ) -if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - if (BUILD_WITH_VPP OR BUILD_HICNPLUGIN) - list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.h - ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.h - ) - +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES Android) + if (UNIX AND NOT APPLE) list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.cc ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.c - ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.c ) - endif() - list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.h - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.h - ) - - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.cc - ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.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 +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/core/connector.cc b/libtransport/src/core/connector.cc deleted file mode 100644 index 63919537d..000000000 --- a/libtransport/src/core/connector.cc +++ /dev/null @@ -1,51 +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/connector.h> - -namespace transport { - -namespace core { - -std::once_flag Connector::init_flag_; - -Connector::Connector(PacketReceivedCallback &&receive_callback, - OnReconnect &&reconnect_callback) - : packet_pool_(), - receive_callback_(std::move(receive_callback)), - on_reconnect_callback_(std::move(reconnect_callback)), - state_(ConnectorState::CLOSED) { - init(); -} - -void Connector::init() { increasePoolSize(); } - -void Connector::increasePoolSize(std::size_t size) { - // Allocate space for receiving packets - const auto capacity = packet_size * size; - uint8_t *buffer = static_cast<uint8_t *>(malloc(capacity)); - std::unique_ptr<utils::MemBuf> buffer0 = - utils::MemBuf::takeOwnership(buffer, capacity, 0, nullptr, nullptr, true); - - for (std::size_t i = 1; i < size; i++) { - auto b = buffer0->cloneOne(); - b->advance(i * packet_size); - packet_pool_.add(b.release()); - } -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/core/connector.h b/libtransport/src/core/connector.h deleted file mode 100644 index f2bbe5dcd..000000000 --- a/libtransport/src/core/connector.h +++ /dev/null @@ -1,109 +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/core/packet.h> -#include <hicn/transport/utils/membuf.h> -#include <hicn/transport/utils/object_pool.h> -#include <hicn/transport/utils/ring_buffer.h> - -#include <deque> -#include <functional> - -namespace transport { - -namespace core { - -enum class ConnectorType : uint8_t { - SOCKET_CONNECTOR, - RAW_SOCKET_CONNECTOR, - VPP_CONNECTOR, -}; - -class Connector { - protected: - enum class ConnectorState { - CLOSED, - CONNECTING, - CONNECTED, - }; - - public: - static constexpr std::size_t packet_size = 2048; - static constexpr std::size_t queue_size = 4096; - static constexpr std::size_t packet_pool_size = 4096; - - using PacketRing = utils::CircularFifo<Packet::MemBufPtr, queue_size>; - using PacketQueue = std::deque<Packet::MemBufPtr>; - using PacketReceivedCallback = std::function<void(Packet::MemBufPtr &&)>; - using OnReconnect = std::function<void()>; - using PacketSentCallback = std::function<void()>; - - Connector(PacketReceivedCallback &&receive_callback, - OnReconnect &&reconnect_callback); - - virtual ~Connector(){}; - - virtual void send(const Packet::MemBufPtr &packet) = 0; - - virtual void send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent = 0) = 0; - - virtual void close() = 0; - - virtual ConnectorState state() { return state_; }; - - virtual bool isConnected() { return state_ == ConnectorState::CONNECTED; } - - protected: - void increasePoolSize(std::size_t size = packet_pool_size); - - TRANSPORT_ALWAYS_INLINE utils::ObjectPool<utils::MemBuf>::Ptr getPacket() { - auto result = packet_pool_.get(); - - while (TRANSPORT_EXPECT_FALSE(!result.first)) { - // Add packets to the pool - increasePoolSize(); - result = packet_pool_.get(); - } - - if (result.second->isChained()) { - result.second->separateChain(result.second->next(), - result.second->prev()); - } - - result.second->trimEnd(result.second->length()); - return std::move(result.second); - } - - private: - void init(); - - protected: - static std::once_flag init_flag_; - utils::ObjectPool<utils::MemBuf> packet_pool_; - PacketQueue output_buffer_; - - // Connector events - PacketReceivedCallback receive_callback_; - OnReconnect on_reconnect_callback_; - - // Connector state - ConnectorState state_; -}; -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/core/constructor.cc b/libtransport/src/core/constructor.cc new file mode 100644 index 000000000..0c7f0dfa8 --- /dev/null +++ b/libtransport/src/core/constructor.cc @@ -0,0 +1,36 @@ +/* + * 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/global_configuration.h> +#include <core/global_module_manager.h> +#include <core/global_workers.h> +#include <hicn/transport/core/global_object_pool.h> + +namespace transport { +namespace core { + +void __attribute__((constructor)) libtransportInit() { + // First the global module manager is initialized + GlobalModuleManager::getInstance(); + // Then the packet allocator is initialized + PacketManager<>::getInstance(); + // Then the global configuration is initialized + GlobalConfiguration::getInstance(); + // Then the global workers are initialized + GlobalWorkers::getInstance(); +} + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/content_object.cc b/libtransport/src/core/content_object.cc index f5cccf404..7ed6c57ab 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-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: @@ -15,6 +15,7 @@ #include <hicn/transport/core/content_object.h> #include <hicn/transport/errors/errors.h> +#include <hicn/transport/portability/endianess.h> #include <hicn/transport/utils/branch_prediction.h> extern "C" { @@ -32,65 +33,55 @@ namespace transport { namespace core { -ContentObject::ContentObject(const Name &name, Packet::Format format) - : Packet(format) { - if (TRANSPORT_EXPECT_FALSE( - hicn_data_set_name(format, packet_start_, &name.name_) < 0)) { +ContentObject::ContentObject(const Name &name, Packet::Format format, + std::size_t additional_header_size) + : Packet(HICN_PACKET_TYPE_DATA, format, additional_header_size) { + if (TRANSPORT_EXPECT_FALSE(hicn_data_set_name(&pkbuf_, &name.name_) < 0)) { throw errors::RuntimeException("Error filling the packet name."); } - if (TRANSPORT_EXPECT_FALSE(hicn_data_get_name(format_, packet_start_, - name_.getStructReference()) < - 0)) { + if (TRANSPORT_EXPECT_FALSE( + hicn_data_get_name(&pkbuf_, &name_.getStructReference()) < 0)) { throw errors::MalformedPacketException(); } } +ContentObject::ContentObject(hicn_packet_format_t format, + std::size_t additional_header_size) + : ContentObject( #ifdef __ANDROID__ -ContentObject::ContentObject(hicn_format_t format) - : ContentObject(Name("0::0|0"), format) {} + Name("0::0|0"), #else -ContentObject::ContentObject(hicn_format_t format) - : ContentObject(Packet::base_name, format) {} + Packet::base_name, #endif + format, additional_header_size) { +} -ContentObject::ContentObject(const Name &name, hicn_format_t format, +ContentObject::ContentObject(const Name &name, hicn_packet_format_t format, + std::size_t additional_header_size, const uint8_t *payload, std::size_t size) - : ContentObject(name, format) { + : ContentObject(name, format, additional_header_size) { appendPayload(payload, size); } -ContentObject::ContentObject(const uint8_t *buffer, std::size_t size) - : Packet(buffer, size) { - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::RuntimeException("Error getting name from content object."); - } +ContentObject::ContentObject(ContentObject &&other) : Packet(std::move(other)) { + name_ = std::move(other.name_); } -ContentObject::ContentObject(MemBufPtr &&buffer) : Packet(std::move(buffer)) { - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::RuntimeException("Error getting name from content object."); - } +ContentObject::ContentObject(const ContentObject &other) : Packet(other) { + name_ = other.name_; } -ContentObject::ContentObject(ContentObject &&other) : Packet(std::move(other)) { - name_ = std::move(other.name_); - - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::MalformedPacketException(); - } +ContentObject &ContentObject::operator=(const ContentObject &other) { + return (ContentObject &)Packet::operator=(other); } ContentObject::~ContentObject() {} const Name &ContentObject::getName() const { if (!name_) { - if (hicn_data_get_name(format_, packet_start_, - (hicn_name_t *)name_.getConstStructReference()) < - 0) { + if (hicn_data_get_name( + &pkbuf_, (hicn_name_t *)&name_.getConstStructReference()) < 0) { throw errors::MalformedPacketException(); } } @@ -101,33 +92,18 @@ const Name &ContentObject::getName() const { Name &ContentObject::getWritableName() { return const_cast<Name &>(getName()); } void ContentObject::setName(const Name &name) { - if (hicn_data_set_name(format_, packet_start_, - name.getConstStructReference()) < 0) { + if (hicn_data_set_name(&pkbuf_, &name.getConstStructReference()) < 0) { throw errors::RuntimeException("Error setting content object name."); } - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { - throw errors::MalformedPacketException(); - } -} - -void ContentObject::setName(Name &&name) { - if (hicn_data_set_name(format_, packet_start_, name.getStructReference()) < - 0) { - throw errors::RuntimeException( - "Error getting the payload length from content object."); - } - - if (hicn_data_get_name(format_, packet_start_, name_.getStructReference()) < - 0) { + if (hicn_data_get_name(&pkbuf_, &name_.getStructReference()) < 0) { throw errors::MalformedPacketException(); } } -uint32_t ContentObject::getPathLabel() const { - uint32_t path_label; - if (hicn_data_get_path_label(packet_start_, &path_label) < 0) { +hicn_path_label_t ContentObject::getPathLabel() const { + hicn_path_label_t path_label; + if (hicn_data_get_path_label(&pkbuf_, &path_label) < 0) { throw errors::RuntimeException( "Error retrieving the path label from content object"); } @@ -135,9 +111,8 @@ uint32_t ContentObject::getPathLabel() const { return path_label; } -ContentObject &ContentObject::setPathLabel(uint32_t path_label) { - if (hicn_data_set_path_label((hicn_header_t *)packet_start_, path_label) < - 0) { +ContentObject &ContentObject::setPathLabel(hicn_path_label_t path_label) { + if (hicn_data_set_path_label(&pkbuf_, path_label) < 0) { throw errors::RuntimeException( "Error setting the path label from content object"); } @@ -145,18 +120,18 @@ ContentObject &ContentObject::setPathLabel(uint32_t path_label) { return *this; } -void ContentObject::setLocator(const ip_address_t &ip_address) { - if (hicn_data_set_locator(format_, packet_start_, &ip_address) < 0) { +void ContentObject::setLocator(const hicn_ip_address_t &ip_address) { + if (hicn_data_set_locator(&pkbuf_, &ip_address) < 0) { throw errors::RuntimeException("Error setting content object locator"); } return; } -ip_address_t ContentObject::getLocator() const { - ip_address_t ip; +hicn_ip_address_t ContentObject::getLocator() const { + hicn_ip_address_t ip; - if (hicn_data_get_locator(format_, packet_start_, &ip) < 0) { + if (hicn_data_get_locator(&pkbuf_, &ip) < 0) { throw errors::RuntimeException("Error getting content object locator."); } @@ -164,7 +139,7 @@ ip_address_t ContentObject::getLocator() const { } void ContentObject::setLifetime(uint32_t lifetime) { - if (hicn_data_set_expiry_time(packet_start_, lifetime) < 0) { + if (hicn_data_set_expiry_time(&pkbuf_, lifetime) < 0) { throw errors::MalformedPacketException(); } } @@ -172,7 +147,7 @@ void ContentObject::setLifetime(uint32_t lifetime) { uint32_t ContentObject::getLifetime() const { uint32_t lifetime = 0; - if (hicn_data_get_expiry_time(packet_start_, &lifetime) < 0) { + if (hicn_data_get_expiry_time(&pkbuf_, &lifetime) < 0) { throw errors::MalformedPacketException(); } @@ -180,13 +155,29 @@ uint32_t ContentObject::getLifetime() const { } void ContentObject::resetForHash() { - if (hicn_data_reset_for_hash( - format_, reinterpret_cast<hicn_header_t *>(packet_start_)) < 0) { + if (hicn_data_reset_for_hash(&pkbuf_) < 0) { throw errors::RuntimeException( "Error resetting content object fields for hash computation."); } } +bool ContentObject::isLast() const { + int is_last = 0; + if (hicn_data_is_last(&pkbuf_, &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(&pkbuf_) < 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 new file mode 100644 index 000000000..68fd7bf38 --- /dev/null +++ b/libtransport/src/core/errors.cc @@ -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. + */ + +#include <core/errors.h> + +namespace transport { +namespace core { + +const std::error_category& core_category() { + static core_category_impl instance; + + return instance; +} + +const char* core_category_impl::name() const throw() { + return "transport::protocol::error"; +} + +std::string core_category_impl::message(int ev) const { + switch (static_cast<core_error>(ev)) { + case core_error::success: { + return "Success"; + } + case core_error::configuration_parse_failed: { + return "Error parsing configuration."; + } + 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"; + } + } +} + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/errors.h b/libtransport/src/core/errors.h new file mode 100644 index 000000000..4532e6dc5 --- /dev/null +++ b/libtransport/src/core/errors.h @@ -0,0 +1,90 @@ +/* + * 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 <string> +#include <system_error> + +namespace transport { +namespace core { + +/** + * @brief Get the default server error category. + * @return The default server error category instance. + * + * @warning The first call to this function is thread-safe only starting with + * C++11. + */ +const std::error_category& core_category(); + +/** + * The list of errors. + */ +enum class core_error { + success = 0, + configuration_parse_failed, + configuration_not_applied, + send_failed, + send_buffer_allocation_failed, + receive_failed +}; + +/** + * @brief Create an error_code instance for the given error. + * @param error The error. + * @return The error_code instance. + */ +inline std::error_code make_error_code(core_error error) { + return std::error_code(static_cast<int>(error), core_category()); +} + +/** + * @brief Create an error_condition instance for the given error. + * @param error The error. + * @return The error_condition instance. + */ +inline std::error_condition make_error_condition(core_error error) { + return std::error_condition(static_cast<int>(error), core_category()); +} + +/** + * @brief A server error category. + */ +class core_category_impl : public std::error_category { + public: + /** + * @brief Get the name of the category. + * @return The name of the category. + */ + virtual const char* name() const throw(); + + /** + * @brief Get the error message for a given error. + * @param ev The error numeric value. + * @return The message associated to the error. + */ + virtual std::string message(int ev) const; +}; +} // namespace core +} // namespace transport + +namespace std { +// namespace system { +template <> +struct is_error_code_enum<::transport::core::core_error> + : public std::true_type {}; +// } // namespace system +} // namespace std
\ No newline at end of file diff --git a/libtransport/src/core/facade.h b/libtransport/src/core/facade.h index 04f643f63..77c1d16d2 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: @@ -15,38 +15,16 @@ #pragma once -#include <core/forwarder_interface.h> -#include <core/hicn_forwarder_interface.h> +#include <core/manifest.h> #include <core/manifest_format_fixed.h> -#include <core/manifest_inline.h> #include <core/portal.h> -#ifdef __linux__ -#ifndef __ANDROID__ -#include <core/raw_socket_interface.h> -#ifdef __vpp__ -#include <core/vpp_forwarder_interface.h> -#endif -#endif -#endif - namespace transport { namespace core { -using HicnForwarderPortal = Portal<HicnForwarderInterface>; - -#ifdef __linux__ -#ifndef __ANDROID__ -using RawSocketPortal = Portal<RawSocketInterface>; -#endif -#ifdef __vpp__ -using VPPForwarderPortal = Portal<VPPForwarderInterface>; -#endif -#endif - -using ContentObjectManifest = core::ManifestInline<ContentObject, Fixed>; -using InterestManifest = core::ManifestInline<Interest, Fixed>; +using ContentObjectManifest = core::Manifest<Fixed>; +using InterestManifest = core::Manifest<Fixed>; } // namespace core diff --git a/libtransport/src/core/forwarder_interface.h b/libtransport/src/core/forwarder_interface.h deleted file mode 100644 index a94414d38..000000000 --- a/libtransport/src/core/forwarder_interface.h +++ /dev/null @@ -1,149 +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/udp_socket_connector.h> -#include <hicn/transport/core/prefix.h> -#include <hicn/transport/portability/portability.h> -#include <hicn/transport/utils/chrono_typedefs.h> - -#include <deque> - -namespace transport { - -namespace core { - -typedef struct { - uint64_t rx_packets; - uint64_t tx_packets; - uint64_t rx_bytes; - uint64_t tx_bytes; - uint64_t rx_errors; - uint64_t tx_errors; -} Counters; - -template <typename Implementation, typename ConnectorType> -class ForwarderInterface { - static_assert(std::is_base_of<Connector, ConnectorType>::value, - "T must inherit from connector!"); - - static constexpr uint32_t standard_cs_reserved = 5000; - - protected: - ForwarderInterface(ConnectorType &c) - : connector_(c), - inet_address_({}), - inet6_address_({}), - mtu_(1500), - output_interface_(""), - content_store_reserved_(standard_cs_reserved) { - inet_address_.v4.as_u32 = htonl(0x7f00001); - inet6_address_.v6.as_u8[15] = 0x01; - } - - public: - virtual ~ForwarderInterface() {} - - TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { - static_cast<Implementation &>(*this).connect(is_consumer); - } - - TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { - static_cast<Implementation &>(*this).registerRoute(); - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getMtu() { - return static_cast<Implementation &>(*this).getMtu(); - } - - TRANSPORT_ALWAYS_INLINE static bool isControlMessage(const uint8_t *message) { - return Implementation::isControlMessageImpl(message); - } - - template <typename R> - TRANSPORT_ALWAYS_INLINE void processControlMessageReply(R &&packet_buffer) { - return static_cast<Implementation &>(*this).processControlMessageReplyImpl( - std::forward<R &&>(packet_buffer)); - } - - TRANSPORT_ALWAYS_INLINE void closeConnection() { - return static_cast<Implementation &>(*this).closeConnection(); - } - - template < - typename R, - typename = std::enable_if_t< - std::is_base_of<Packet, typename std::remove_reference_t<R>>::value, - R>> - TRANSPORT_ALWAYS_INLINE void send(R &&packet) { - counters_.tx_packets++; - counters_.tx_bytes += packet.payloadSize() + packet.headerSize(); - - if (_is_ipv4(packet.getFormat())) { - packet.setLocator(inet_address_); - } else { - packet.setLocator(inet6_address_); - } - -#ifndef __vpp__ - /* In the case of VPP we try to offload checksum computation to hardware */ - packet.setChecksum(); -#endif - connector_.send(packet.acquireMemBufReference()); - } - - TRANSPORT_ALWAYS_INLINE void send(const uint8_t *packet, std::size_t len) { - counters_.tx_packets++; - counters_.tx_bytes += len; - - // Perfect forwarding - connector_.send(packet, len); - } - - TRANSPORT_ALWAYS_INLINE void shutdown() { connector_.close(); } - - TRANSPORT_ALWAYS_INLINE Connector &getConnector() { return connector_; } - - TRANSPORT_ALWAYS_INLINE void setContentStoreSize(uint32_t cs_size) { - content_store_reserved_ = cs_size; - } - - TRANSPORT_ALWAYS_INLINE uint32_t getContentStoreSize() const { - return content_store_reserved_; - } - - TRANSPORT_ALWAYS_INLINE void setOutputInterface( - const std::string &interface) { - output_interface_ = interface; - } - - TRANSPORT_ALWAYS_INLINE std::string &getOutputInterface() { - return output_interface_; - } - - protected: - ConnectorType &connector_; - ip_address_t inet_address_; - ip_address_t inet6_address_; - uint16_t mtu_; - std::string output_interface_; - uint32_t content_store_reserved_; - Counters counters_; -}; - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/core/global_configuration.cc b/libtransport/src/core/global_configuration.cc new file mode 100644 index 000000000..f53e1f0e2 --- /dev/null +++ b/libtransport/src/core/global_configuration.cc @@ -0,0 +1,171 @@ +/* + * 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/global_configuration.h> +#include <glog/logging.h> +#include <hicn/transport/core/connector.h> + +#include <libconfig.h++> +#include <map> + +namespace transport { +namespace core { + +GlobalConfiguration::GlobalConfiguration() {} + +bool GlobalConfiguration::parseTransportConfig(const std::string& path) { + using namespace libconfig; + Config cfg; + + try { + cfg.readFile(path.c_str()); + } catch (const FileIOException& fioex) { + LOG(ERROR) << "I/O error while reading file."; + return false; + } catch (const ParseException& pex) { + LOG(ERROR) << "Parse error at " << pex.getFile() << ":" << pex.getLine() + << " - " << pex.getError(); + return false; + } + + Setting& root = cfg.getRoot(); + + /** + * Iterate over sections. Best thing to do here would be to have other + * components of the program registering a callback here, to parse their + * section of the configuration file. + */ + for (auto section = root.begin(); section != root.end(); section++) { + std::string name = section->getName(); + std::error_code ec; + VLOG(2) << "Parsing Section: " << name; + + auto it = configuration_parsers_.find(name); + if (it != configuration_parsers_.end() && !it->second.first) { + VLOG(2) << "Found valid configuration parser"; + it->second.second(*section, ec); + it->second.first = true; + } + } + + return true; +} + +void GlobalConfiguration::parseConfiguration(const std::string& path) { + // Check if an environment variable with the configuration path exists. Conf + // variable comes first. + std::unique_lock<std::mutex> lck(cp_mtx_); + if (const char* env_c = std::getenv(GlobalConfiguration::conf_file)) { + conf_file_path_ = env_c; + parseTransportConfig(env_c); + } else if (!path.empty()) { + conf_file_path_ = path; + parseTransportConfig(conf_file_path_); + } else { + LOG(ERROR) + << "Called parseConfiguration but no configuration file was provided."; + } +} + +void GlobalConfiguration::registerConfigurationSetter( + const std::string& key, const SetCallback& set_callback) { + std::unique_lock<std::mutex> lck(cp_mtx_); + if (configuration_setters_.find(key) != configuration_setters_.end()) { + LOG(WARNING) << "Trying to register configuration setter " << key + << " twice. Ignoring second " + "registration attempt."; + } else { + configuration_setters_.emplace(key, set_callback); + } +} + +void GlobalConfiguration::registerConfigurationGetter( + const std::string& key, const GetCallback& get_callback) { + std::unique_lock<std::mutex> lck(cp_mtx_); + if (configuration_getters_.find(key) != configuration_getters_.end()) { + LOG(WARNING) << "Trying to register configuration setter " << key + << " twice. Ignoring second " + "registration attempt."; + } else { + configuration_getters_.emplace(key, get_callback); + } +} + +void GlobalConfiguration::registerConfigurationParser( + const std::string& key, const ParserCallback& parser) { + std::unique_lock<std::mutex> lck(cp_mtx_); + if (configuration_parsers_.find(key) != configuration_parsers_.end()) { + LOG(WARNING) << "Trying to register configuration setter " << key + << " twice. Ignoring second " + "registration attempt."; + } else { + configuration_parsers_.emplace(key, std::make_pair(false, parser)); + + // Trigger a parsing of the configuration. + if (!conf_file_path_.empty()) { + parseTransportConfig(conf_file_path_); + } + } +} + +void GlobalConfiguration::unregisterConfigurationParser( + const std::string& key) { + std::unique_lock<std::mutex> lck(cp_mtx_); + auto it = configuration_parsers_.find(key); + if (it != configuration_parsers_.end()) { + configuration_parsers_.erase(it); + } +} + +void GlobalConfiguration::unregisterConfigurationSetter( + const std::string& key) { + std::unique_lock<std::mutex> lck(cp_mtx_); + auto it = configuration_setters_.find(key); + if (it != configuration_setters_.end()) { + configuration_setters_.erase(it); + } +} + +void GlobalConfiguration::unregisterConfigurationGetter( + const std::string& key) { + std::unique_lock<std::mutex> lck(cp_mtx_); + auto it = configuration_getters_.find(key); + if (it != configuration_getters_.end()) { + configuration_getters_.erase(it); + } +} + +void GlobalConfiguration::getConfiguration( + interface::global_config::ConfigurationObject& configuration_object, + std::error_code& ec) { + auto it = configuration_getters_.find(configuration_object.getKey()); + + if (it != configuration_getters_.end()) { + it->second(configuration_object, ec); + } +} + +void GlobalConfiguration::setConfiguration( + const interface::global_config::ConfigurationObject& configuration_object, + std::error_code& ec) { + auto it = configuration_setters_.find(configuration_object.getKey()); + + if (it != configuration_setters_.end()) { + it->second(configuration_object, ec); + } +} + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/global_configuration.h b/libtransport/src/core/global_configuration.h new file mode 100644 index 000000000..dcc8d94e3 --- /dev/null +++ b/libtransport/src/core/global_configuration.h @@ -0,0 +1,102 @@ +/* + * 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/global_conf_interface.h> +#include <hicn/transport/utils/singleton.h> + +#include <functional> +#include <map> +#include <memory> +#include <mutex> +#include <system_error> + +namespace libconfig { +class Setting; +} + +namespace transport { +namespace core { + +/** + * Class holding workflow for global configuration. + * This class does not contains the actual configuration, which is rather stored + * inside the modules to be configured. This class contains the handlers to call + * for getting/setting the configurations and to parse the corresponding + * sections of the configuration file. Each class register 3 callbacks: one to + * parse conf section and 2 to set/get the configuration through programming + * interface. + */ +class GlobalConfiguration : public utils::Singleton<GlobalConfiguration> { + static const constexpr char *conf_file = "TRANSPORT_CONFIG"; + friend class utils::Singleton<GlobalConfiguration>; + + public: + /** + * This callback will be called by GlobalConfiguration in + * + */ + using ParserCallback = std::function<void(const libconfig::Setting &config, + std::error_code &ec)>; + using GetCallback = + std::function<void(interface::global_config::ConfigurationObject &object, + std::error_code &ec)>; + + using SetCallback = std::function<void( + const interface::global_config::ConfigurationObject &object, + std::error_code &ec)>; + + ~GlobalConfiguration() = default; + + public: + void parseConfiguration(const std::string &path); + + void registerConfigurationParser(const std::string &key, + const ParserCallback &parser); + + void registerConfigurationSetter(const std::string &key, + const SetCallback &set_callback); + void registerConfigurationGetter(const std::string &key, + const GetCallback &get_callback); + + void unregisterConfigurationParser(const std::string &key); + + void unregisterConfigurationSetter(const std::string &key); + + void unregisterConfigurationGetter(const std::string &key); + + void getConfiguration( + interface::global_config::ConfigurationObject &configuration_object, + std::error_code &ec); + void setConfiguration( + const interface::global_config::ConfigurationObject &configuration_object, + std::error_code &ec); + + private: + GlobalConfiguration(); + std::string conf_file_path_; + bool parseTransportConfig(const std::string &path); + + private: + std::mutex cp_mtx_; + using ParserPair = std::pair<bool, ParserCallback>; + std::map<std::string, ParserPair> configuration_parsers_; + std::map<std::string, GetCallback> configuration_getters_; + std::map<std::string, SetCallback> configuration_setters_; +}; + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.h b/libtransport/src/core/global_id_counter.h index 3ea84095b..0a67b76d5 100644 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.h +++ b/libtransport/src/core/global_id_counter.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,22 +15,25 @@ #pragma once -#include <hicn/transport/interfaces/socket_producer.h> +#include <hicn/transport/utils/singleton.h> -namespace transport { +#include <atomic> +#include <mutex> -namespace implementation { -class TLSRTCProducerSocket; -} +namespace transport { -namespace interface { +namespace core { -class TLSRTCProducerSocket : public ProducerSocket { +template <typename T = uint64_t> +class GlobalCounter : public utils::Singleton<GlobalCounter<T>> { public: - TLSRTCProducerSocket(implementation::TLSRTCProducerSocket *implementation); + friend class utils::Singleton<GlobalCounter>; + T getNext() { return counter_++; } - ~TLSRTCProducerSocket(); + private: + GlobalCounter() : counter_(0) {} + std::atomic<T> counter_; }; -} // namespace interface -} // end namespace transport +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/global_module_manager.h b/libtransport/src/core/global_module_manager.h new file mode 100644 index 000000000..c9d272cdb --- /dev/null +++ b/libtransport/src/core/global_module_manager.h @@ -0,0 +1,94 @@ +/* + * 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 + +#include <glog/logging.h> +#include <hicn/transport/utils/singleton.h> + +#ifndef _WIN32 +#include <dlfcn.h> +#endif + +#include <atomic> +#include <iostream> +#include <mutex> +#include <unordered_map> + +namespace transport { +namespace core { + +class GlobalModuleManager : public utils::Singleton<GlobalModuleManager> { + public: + friend class utils::Singleton<GlobalModuleManager>; + + ~GlobalModuleManager() { + for (const auto &[key, value] : modules_) { + unload(value); + } + } + + void *loadModule(const std::string &module_name) { + void *handle = nullptr; + const char *error = nullptr; + + // Lock + std::unique_lock lck(mtx_); + + auto it = modules_.find(module_name); + if (it != modules_.end()) { + return it->second; + } + + // open module + handle = dlopen(module_name.c_str(), RTLD_NOW); + if (!handle) { + if ((error = dlerror()) != nullptr) { + LOG(ERROR) << error; + } + return nullptr; + } + + auto ret = modules_.try_emplace(module_name, handle); + DCHECK(ret.second); + + return handle; + } + + void unload(void *handle) { + // destroy object and close module + dlclose(handle); + } + + bool unloadModule(const std::string &module_name) { + // Lock + std::unique_lock lck(mtx_); + auto it = modules_.find(module_name); + if (it != modules_.end()) { + unload(it->second); + return true; + } + + return false; + } + + private: + GlobalModuleManager() = default; + std::mutex mtx_; + std::unordered_map<std::string, void *> modules_; +}; + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/global_workers.h b/libtransport/src/core/global_workers.h new file mode 100644 index 000000000..c5d794ef2 --- /dev/null +++ b/libtransport/src/core/global_workers.h @@ -0,0 +1,45 @@ +/* + * 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()); + } + + auto& getWorkers() { return thread_pool_.getWorkers(); } + + 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/hicn_forwarder_interface.cc b/libtransport/src/core/hicn_forwarder_interface.cc deleted file mode 100644 index 5a0faa360..000000000 --- a/libtransport/src/core/hicn_forwarder_interface.cc +++ /dev/null @@ -1,135 +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 <core/hicn_forwarder_interface.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 { - -HicnForwarderInterface::HicnForwarderInterface(UdpSocketConnector &connector) - : ForwarderInterface<HicnForwarderInterface, UdpSocketConnector>( - connector) {} - -HicnForwarderInterface::~HicnForwarderInterface() {} - -void HicnForwarderInterface::connect(bool is_consumer) { connector_.connect(); } - -void HicnForwarderInterface::registerRoute(Prefix &prefix) { - auto command = createCommandRoute(prefix.toSockaddr(), - (uint8_t)prefix.getPrefixLength()); - send((uint8_t *)&command, sizeof(RouteToSelfCommand)); -} - -void HicnForwarderInterface::closeConnection() { - auto command = createCommandDeleteConnection(); - send((uint8_t *)&command, sizeof(DeleteSelfConnectionCommand)); - connector_.close(); -} - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/core/hicn_forwarder_interface.h b/libtransport/src/core/hicn_forwarder_interface.h deleted file mode 100644 index c4138c6c2..000000000 --- a/libtransport/src/core/hicn_forwarder_interface.h +++ /dev/null @@ -1,86 +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. - */ - -#pragma once - -#include <hicn/transport/core/prefix.h> - -#include <core/forwarder_interface.h> -#include <core/udp_socket_connector.h> - -#include <deque> - -namespace transport { - -namespace core { - -class HicnForwarderInterface - : public ForwarderInterface<HicnForwarderInterface, UdpSocketConnector> { - static constexpr uint8_t ack_code = 0xc2; - static constexpr uint8_t nack_code = 0xc3; - - public: - union addressLight { - uint32_t ipv4; - struct in6_addr ipv6; - }; - - struct route_to_self_command { - uint8_t messageType; - uint8_t commandID; - uint16_t length; - uint32_t seqNum; - char symbolicOrConnid[16]; - union addressLight address; - uint16_t cost; - uint8_t addressType; - uint8_t len; - }; - - using route_to_self_command = struct route_to_self_command; - using ConnectorType = UdpSocketConnector; - - HicnForwarderInterface(UdpSocketConnector &connector); - - ~HicnForwarderInterface(); - - 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 message[0] == ack_code || message[0] == nack_code; - } - - TRANSPORT_ALWAYS_INLINE void processControlMessageReplyImpl( - Packet::MemBufPtr &&packet_buffer) { - if (packet_buffer->data()[0] == nack_code) { - throw errors::RuntimeException( - "Received Nack message from hicn light forwarder."); - } - } - - void closeConnection(); - - private: - static constexpr std::uint16_t interface_mtu = 1500; -}; - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/core/interest.cc b/libtransport/src/core/interest.cc index 9ee662615..c3eb7c379 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-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: @@ -21,7 +21,9 @@ extern "C" { #ifndef _WIN32 TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") #endif +#include <hicn/base.h> #include <hicn/hicn.h> +#include <hicn/interest_manifest.h> } #include <cstring> @@ -31,36 +33,32 @@ namespace transport { namespace core { -Interest::Interest(const Name &interest_name, Packet::Format format) - : Packet(format) { - if (hicn_interest_set_name(format_, packet_start_, - interest_name.getConstStructReference()) < 0) { +Interest::Interest(const Name &interest_name, Packet::Format format, + std::size_t additional_header_size) + : Packet(HICN_PACKET_TYPE_INTEREST, format, additional_header_size) { + if (hicn_interest_set_name(&pkbuf_, + &interest_name.getConstStructReference()) < 0) { throw errors::MalformedPacketException(); } - if (hicn_interest_get_name(format_, packet_start_, - name_.getStructReference()) < 0) { + if (hicn_interest_get_name(&pkbuf_, &name_.getStructReference()) < 0) { throw errors::MalformedPacketException(); } } +Interest::Interest(hicn_packet_format_t format, + std::size_t additional_header_size) + : Interest( #ifdef __ANDROID__ -Interest::Interest(hicn_format_t format) : Interest(Name("0::0|0"), format) {} + Name("0::0|0"), #else -Interest::Interest(hicn_format_t format) : Interest(base_name, format) {} + base_name, #endif - -Interest::Interest(const uint8_t *buffer, std::size_t size) - : Packet(buffer, size) { - if (hicn_interest_get_name(format_, packet_start_, - name_.getStructReference()) < 0) { - throw errors::MalformedPacketException(); - } + format, additional_header_size) { } -Interest::Interest(MemBufPtr &&buffer) : Packet(std::move(buffer)) { - if (hicn_interest_get_name(format_, packet_start_, - name_.getStructReference()) < 0) { +Interest::Interest(MemBuf &&buffer) : Packet(std::move(buffer)) { + if (hicn_interest_get_name(&pkbuf_, &name_.getStructReference()) < 0) { throw errors::MalformedPacketException(); } } @@ -70,15 +68,21 @@ Interest::Interest(Interest &&other_interest) name_ = std::move(other_interest.name_); } -Interest::~Interest() {} +Interest::Interest(const Interest &other_interest) : Packet(other_interest) { + name_ = other_interest.name_; +} + +Interest &Interest::operator=(const Interest &other) { + return (Interest &)Packet::operator=(other); +} + +Interest::~Interest() = default; const Name &Interest::getName() const { - if (!name_) { - if (hicn_interest_get_name(format_, packet_start_, - (hicn_name_t *)name_.getConstStructReference()) < - 0) { - throw errors::MalformedPacketException(); - } + if (!name_ && + hicn_interest_get_name( + &pkbuf_, (hicn_name_t *)&name_.getConstStructReference()) < 0) { + throw errors::MalformedPacketException(); } return name_; @@ -87,41 +91,27 @@ const Name &Interest::getName() const { Name &Interest::getWritableName() { return const_cast<Name &>(getName()); } void Interest::setName(const Name &name) { - if (hicn_interest_set_name(format_, packet_start_, - name.getConstStructReference()) < 0) { - throw errors::RuntimeException("Error setting interest name."); - } - - if (hicn_interest_get_name(format_, packet_start_, - name_.getStructReference()) < 0) { - throw errors::MalformedPacketException(); - } -} - -void Interest::setName(Name &&name) { - if (hicn_interest_set_name(format_, packet_start_, - name.getStructReference()) < 0) { + if (hicn_interest_set_name(&pkbuf_, &name.getConstStructReference()) < 0) { throw errors::RuntimeException("Error setting interest name."); } - if (hicn_interest_get_name(format_, packet_start_, - name_.getStructReference()) < 0) { + if (hicn_interest_get_name(&pkbuf_, &name_.getStructReference()) < 0) { throw errors::MalformedPacketException(); } } -void Interest::setLocator(const ip_address_t &ip_address) { - if (hicn_interest_set_locator(format_, packet_start_, &ip_address) < 0) { +void Interest::setLocator(const hicn_ip_address_t &ip_address) { + if (hicn_interest_set_locator(&pkbuf_, &ip_address) < 0) { throw errors::RuntimeException("Error setting interest locator."); } return; } -ip_address_t Interest::getLocator() const { - ip_address_t ip; +hicn_ip_address_t Interest::getLocator() const { + hicn_ip_address_t ip; - if (hicn_interest_get_locator(format_, packet_start_, &ip) < 0) { + if (hicn_interest_get_locator(&pkbuf_, &ip) < 0) { throw errors::RuntimeException("Error getting interest locator."); } @@ -129,7 +119,7 @@ ip_address_t Interest::getLocator() const { } void Interest::setLifetime(uint32_t lifetime) { - if (hicn_interest_set_lifetime(packet_start_, lifetime) < 0) { + if (hicn_interest_set_lifetime(&pkbuf_, lifetime) < 0) { throw errors::MalformedPacketException(); } } @@ -137,7 +127,7 @@ void Interest::setLifetime(uint32_t lifetime) { uint32_t Interest::getLifetime() const { uint32_t lifetime = 0; - if (hicn_interest_get_lifetime(packet_start_, &lifetime) < 0) { + if (hicn_interest_get_lifetime(&pkbuf_, &lifetime) < 0) { throw errors::MalformedPacketException(); } @@ -145,11 +135,124 @@ uint32_t Interest::getLifetime() const { } void Interest::resetForHash() { - if (hicn_interest_reset_for_hash( - format_, reinterpret_cast<hicn_header_t *>(packet_start_)) < 0) { + if (hicn_interest_reset_for_hash(&pkbuf_) < 0) { throw errors::RuntimeException( "Error resetting interest fields for hash computation."); } + + // Reset request bitmap in manifest + if (hasManifest()) { + auto int_manifest_header = + (interest_manifest_header_t *)(writableData() + headerSize()); + memset(int_manifest_header->request_bitmap, 0, BITMAP_SIZE * sizeof(u32)); + } +} + +bool Interest::hasManifest() const { + return (getPayloadType() == PayloadType::MANIFEST); +} + +void Interest::appendSuffix(std::uint32_t suffix) { + if (TRANSPORT_EXPECT_FALSE(suffix_set_.empty())) { + setPayloadType(PayloadType::MANIFEST); + } + + suffix_set_.emplace(suffix); +} + +void Interest::encodeSuffixes() { + if (!hasManifest()) { + return; + } + + // We assume interest does not hold signature for the moment. + auto int_manifest_header = + (interest_manifest_header_t *)(writableData() + headerSize()); + + interest_manifest_init(int_manifest_header, name_.getSuffix()); + for (auto it = suffix_set_.begin(); it != suffix_set_.end(); it++) { + interest_manifest_add_suffix(int_manifest_header, *it); + } + + std::size_t additional_length = + sizeof(interest_manifest_header_t) + + int_manifest_header->n_suffixes * sizeof(uint32_t); + + append(additional_length); + updateLength(); +} + +void Interest::serializeSuffixes() { + if (!hasManifest()) { + return; + } + + // We assume interest does not hold signature for the moment. + auto int_manifest_header = + (interest_manifest_header_t *)(writableData() + headerSize()); + // Serialize interest manifest + interest_manifest_serialize(int_manifest_header); +} + +void Interest::deserializeSuffixes() { + if (!hasManifest()) { + return; + } + + // We assume interest does not hold signature for the moment. + auto int_manifest_header = + (interest_manifest_header_t *)(writableData() + headerSize()); + // Serialize interest manifest + interest_manifest_deserialize(int_manifest_header); +} + +uint32_t *Interest::firstSuffix() { + if (!hasManifest()) { + return nullptr; + } + + auto ret = (interest_manifest_header_t *)(writableData() + headerSize()); + ret += 1; + + return (uint32_t *)ret; +} + +uint32_t Interest::numberOfSuffixes() { + if (!hasManifest()) { + return 0; + } + + auto header = (interest_manifest_header_t *)(writableData() + headerSize()); + + return header->n_suffixes; +} + +hicn_uword *Interest::getRequestBitmap() { + if (!hasManifest()) return nullptr; + + auto header = (interest_manifest_header_t *)(writableData() + headerSize()); + return header->request_bitmap; +} + +interest_manifest_header_t *Interest::getIntManifestHeader() { + if (!hasManifest()) return nullptr; + + auto header = (interest_manifest_header_t *)(writableData() + headerSize()); + return header; +}; + +void Interest::setRequestBitmap(const uint32_t *request_bitmap) { + if (!hasManifest()) return; + + auto header = (interest_manifest_header_t *)(writableData() + headerSize()); + memcpy(header->request_bitmap, request_bitmap, + BITMAP_SIZE * sizeof(uint32_t)); +} + +bool Interest::isValid() { + if (!hasManifest()) return true; + auto header = (interest_manifest_header_t *)(writableData() + headerSize()); + return interest_manifest_is_valid(header, payloadSize()); } } // end namespace core diff --git a/libtransport/src/core/io_module.cc b/libtransport/src/core/io_module.cc new file mode 100644 index 000000000..0fdb735c4 --- /dev/null +++ b/libtransport/src/core/io_module.cc @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef _WIN32 +#include <dlfcn.h> +#endif +#include <core/global_module_manager.h> +#include <glog/logging.h> +#include <hicn/transport/core/io_module.h> + +#include <iostream> + +#ifdef ANDROID +#include <io_modules/hicn-light/hicn_forwarder_module.h> +#elif _WIN32 +#include <hicn/util/windows/windows_utils.h> +#endif + +#include <deque> + +namespace transport { +namespace core { + +IoModule::~IoModule() {} + +IoModule *IoModule::load(const char *module_name) { +#ifdef ANDROID + return new HicnForwarderModule(); +#else + IoModule *iomodule = nullptr; + IoModule *(*creator)(void) = nullptr; + const char *error = nullptr; + + auto handle = GlobalModuleManager::getInstance().loadModule(module_name); + + // get factory method + creator = (IoModule * (*)(void)) dlsym(handle, "create_module"); + if (!creator) { + if ((error = dlerror()) != nullptr) { + LOG(ERROR) << error << ": " << module_name; + } + + return nullptr; + } + + // create object and return it + iomodule = (*creator)(); + + return iomodule; +#endif +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/core/local_connector.h b/libtransport/src/core/local_connector.h new file mode 100644 index 000000000..bf64b71d6 --- /dev/null +++ b/libtransport/src/core/local_connector.h @@ -0,0 +1,88 @@ +/* + * 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/errors.h> +#include <hicn/transport/core/asio_wrapper.h> +#include <hicn/transport/core/connector.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <hicn/transport/utils/shared_ptr_utils.h> +#include <io_modules/forwarder/errors.h> + +namespace transport { +namespace core { + +class LocalConnector : public Connector { + public: + template <typename ReceiveCallback, typename SentCallback, typename OnClose, + typename OnReconnect> + LocalConnector(asio::io_service &io_service, + ReceiveCallback &&receive_callback, SentCallback &&packet_sent, + OnClose &&close_callback, OnReconnect &&on_reconnect) + : Connector(receive_callback, packet_sent, close_callback, on_reconnect), + io_service_(io_service), + io_service_work_(io_service_.get()) {} + + ~LocalConnector() override = default; + + auto shared_from_this() { return utils::shared_from(this); } + + void send(Packet &packet) override { send(packet.shared_from_this()); } + + void send(const utils::MemBuf::Ptr &buffer) override { + throw errors::NotImplementedException(); + } + + void receive(const std::vector<utils::MemBuf::Ptr> &buffers) override { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending packet to local socket."; + std::weak_ptr<LocalConnector> self = shared_from_this(); + io_service_.get().post([self, _buffers{std::move(buffers)}]() mutable { + if (auto ptr = self.lock()) { + ptr->receive_callback_(ptr.get(), _buffers, + make_error_code(core_error::success)); + } + }); + } + + void reconnect() override { + state_ = State::CONNECTED; + std::weak_ptr<LocalConnector> self = shared_from_this(); + io_service_.get().post([self]() { + if (auto ptr = self.lock()) { + ptr->on_reconnect_callback_(ptr.get(), + make_error_code(core_error::success)); + } + }); + } + + void close() override { + std::weak_ptr<LocalConnector> self = shared_from_this(); + io_service_.get().post([self]() mutable { + if (auto ptr = self.lock()) { + ptr->on_close_callback_(ptr.get()); + } + }); + } + + private: + std::reference_wrapper<asio::io_service> io_service_; + asio::io_service::work io_service_work_; + std::string name_; +}; + +} // namespace core +} // namespace transport diff --git a/libtransport/src/core/manifest.cc b/libtransport/src/core/manifest.cc deleted file mode 100644 index 3f890f3d0..000000000 --- a/libtransport/src/core/manifest.cc +++ /dev/null @@ -1,33 +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/core/manifest.h> - -namespace transport { - -namespace core { - -std::string ManifestEncoding::manifest_type = std::string("manifest_type"); - -std::map<ManifestType, std::string> ManifestEncoding::manifest_types = { - {FINAL_CHUNK_NUMBER, "FinalChunkNumber"}, {NAME_LIST, "NameList"}}; - -std::string ManifestEncoding::final_chunk_number = - std::string("final_chunk_number"); -std::string ManifestEncoding::content_name = std::string("content_name"); - -} // end namespace core - -} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/manifest.h b/libtransport/src/core/manifest.h index eadfed752..40832bb6b 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: @@ -15,136 +15,74 @@ #pragma once -#include <hicn/transport/core/content_object.h> -#include <hicn/transport/core/name.h> - #include <core/manifest_format.h> - -#include <set> +#include <glog/logging.h> +#include <hicn/transport/auth/verifier.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/core/packet.h> namespace transport { - namespace core { -using typename core::Name; -using typename core::Packet; -using typename core::PayloadType; - -template <typename Base, typename FormatTraits, typename ManifestImpl> -class Manifest : public Base { - static_assert(std::is_base_of<Packet, Base>::value, - "Base must inherit from packet!"); - +template <typename FormatTraits> +class Manifest : public FormatTraits::Encoder, public FormatTraits::Decoder { public: + using Ptr = std::shared_ptr<Manifest>; + using Encoder = typename FormatTraits::Encoder; using Decoder = typename FormatTraits::Decoder; - Manifest(std::size_t signature_size = 0) - : Base(HF_INET6_TCP_AH), - encoder_(*this, signature_size), - decoder_(*this) { - Base::setPayloadType(PayloadType::MANIFEST); - } - - Manifest(const core::Name &name, std::size_t signature_size = 0) - : Base(name, HF_INET6_TCP_AH), - encoder_(*this, signature_size), - decoder_(*this) { - Base::setPayloadType(PayloadType::MANIFEST); - } + using Hash = typename FormatTraits::Hash; + using HashType = typename FormatTraits::HashType; + using Suffix = typename FormatTraits::Suffix; + using SuffixList = typename FormatTraits::SuffixList; + using HashEntry = std::pair<auth::CryptoHashType, std::vector<uint8_t>>; - template <typename T> - Manifest(T &&base) - : Base(std::forward<T &&>(base)), encoder_(*this), decoder_(*this) { - Base::setPayloadType(PayloadType::MANIFEST); + Manifest(Packet::Ptr packet, bool clear = false) + : Encoder(packet, clear), Decoder(packet), packet_(packet) { + packet->setPayloadType(PayloadType::MANIFEST); } virtual ~Manifest() = default; - std::size_t estimateManifestSize(std::size_t additional_entries = 0) { - return static_cast<ManifestImpl &>(*this).estimateManifestSizeImpl( - additional_entries); - } - - /* - * After the call to encode, users MUST call clear before adding data - * to the manifest. - */ - Manifest &encode() { return static_cast<ManifestImpl &>(*this).encodeImpl(); } - - Manifest &decode() { - Manifest::decoder_.decode(); + Packet::Ptr getPacket() const { return packet_; } - manifest_type_ = decoder_.getManifestType(); - hash_algorithm_ = decoder_.getHashAlgorithm(); - is_last_ = decoder_.getIsFinalManifest(); - - return static_cast<ManifestImpl &>(*this).decodeImpl(); + void setHeaders(ManifestType type, uint8_t max_capacity, HashType hash_algo, + bool is_last, const Name &base_name) { + Encoder::setType(type); + Encoder::setMaxCapacity(max_capacity); + Encoder::setHashAlgorithm(hash_algo); + Encoder::setIsLast(is_last); + Encoder::setBaseName(base_name); } - static std::size_t getManifestHeaderSize() { - return Encoder::getManifestHeaderSize(); - } + auth::Verifier::SuffixMap getSuffixMap() const { + auth::Verifier::SuffixMap suffix_map; - static std::size_t getManifestEntrySize() { - return Encoder::getManifestEntrySize(); - } + HashType hash_algo = Decoder::getHashAlgorithm(); + SuffixList suffix_list = Decoder::getEntries(); - Manifest &setManifestType(ManifestType type) { - manifest_type_ = type; - encoder_.setManifestType(manifest_type_); - return *this; - } + for (auto it = suffix_list.begin(); it != suffix_list.end(); ++it) { + Hash hash(it->second, Hash::getSize(hash_algo), hash_algo); + suffix_map[it->first] = hash; + } - Manifest &setHashAlgorithm(utils::CryptoHashType hash_algorithm) { - hash_algorithm_ = hash_algorithm; - encoder_.setHashAlgorithm(hash_algorithm_); - return *this; + return suffix_map; } - utils::CryptoHashType getHashAlgorithm() { return hash_algorithm_; } - - ManifestType getManifestType() const { return manifest_type_; } - - bool isFinalManifest() 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); - return *this; - } - - uint32_t getFinalBlockNumber() const { - return decoder_.getFinalBlockNumber(); - } - - ManifestVersion getVersion() const { return decoder_.getVersion(); } - - Manifest &setFinalManifest(bool is_final_manifest) { - encoder_.setIsFinalManifest(is_final_manifest); - is_last_ = is_final_manifest; - return *this; - } - - Manifest &clear() { - encoder_.clear(); - decoder_.clear(); - return *this; - } + static Manifest::Ptr createContentManifest(Packet::Format format, + const core::Name &manifest_name, + std::size_t signature_size) { + ContentObject::Ptr content_object = + core::PacketManager<>::getInstance().getPacket<ContentObject>( + format, signature_size); + content_object->setName(manifest_name); + return std::make_shared<Manifest>(content_object, true); + }; protected: - ManifestType manifest_type_; - utils::CryptoHashType hash_algorithm_; - bool is_last_; - - Encoder encoder_; - Decoder decoder_; + Packet::Ptr packet_; }; } // end namespace core - } // end namespace transport diff --git a/libtransport/src/core/manifest_format.h b/libtransport/src/core/manifest_format.h index 36d23f99b..89412316a 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: @@ -15,56 +15,52 @@ #pragma once +#include <hicn/transport/auth/crypto_hash.h> #include <hicn/transport/core/name.h> -#include <hicn/transport/security/crypto_hasher.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <protocols/fec_utils.h> #include <cinttypes> #include <type_traits> #include <unordered_map> 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, -}; - enum class ManifestType : uint8_t { INLINE_MANIFEST = 1, FINAL_CHUNK_NUMBER = 2, 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; + protocol::fec::FECType fec_type; + + bool operator==(const ParamsRTC &other) const { + return (timestamp == other.timestamp && prod_rate == other.prod_rate && + prod_seg == other.prod_seg && fec_type == other.fec_type); + } +}; + +struct ParamsBytestream { + std::uint32_t final_segment; + + bool operator==(const ParamsBytestream &other) const { + return (final_segment == other.final_segment); + } }; template <typename T> struct format_traits { using Encoder = typename T::Encoder; using Decoder = typename T::Decoder; + using Hash = typename T::Hash; using HashType = typename T::HashType; - using HashList = typename T::HashList; + using Suffix = typename T::Suffix; + using SuffixList = typename T::SuffixList; }; class Packet; @@ -82,22 +78,25 @@ class ManifestEncoder { return static_cast<Implementation &>(*this).clearImpl(); } - ManifestEncoder &setManifestType(ManifestType type) { - return static_cast<Implementation &>(*this).setManifestTypeImpl(type); + bool isEncoded() const { + return static_cast<const Implementation &>(*this).isEncodedImpl(); } - ManifestEncoder &setHashAlgorithm(utils::CryptoHashType hash) { - return static_cast<Implementation &>(*this).setHashAlgorithmImpl(hash); + ManifestEncoder &setType(ManifestType type) { + return static_cast<Implementation &>(*this).setTypeImpl(type); } - ManifestEncoder &setFinalChunkNumber(uint32_t final_chunk) { - return static_cast<Implementation &>(*this).setFinalChunkImpl(final_chunk); + ManifestEncoder &setMaxCapacity(uint8_t max_capacity) { + return static_cast<Implementation &>(*this).setMaxCapacityImpl( + max_capacity); } - ManifestEncoder &setNextSegmentCalculationStrategy( - NextSegmentCalculationStrategy strategy) { - return static_cast<Implementation &>(*this) - .setNextSegmentCalculationStrategyImpl(strategy); + ManifestEncoder &setHashAlgorithm(auth::CryptoHashType hash) { + return static_cast<Implementation &>(*this).setHashAlgorithmImpl(hash); + } + + ManifestEncoder &setIsLast(bool is_last) { + return static_cast<Implementation &>(*this).setIsLastImpl(is_last); } template < @@ -108,40 +107,36 @@ class ManifestEncoder { return static_cast<Implementation &>(*this).setBaseNameImpl(name); } - template <typename Hash> - ManifestEncoder &addSuffixAndHash(uint32_t suffix, Hash &&hash) { - return static_cast<Implementation &>(*this).addSuffixAndHashImpl( - suffix, std::forward<Hash &&>(hash)); - } - - ManifestEncoder &setIsFinalManifest(bool is_last) { - return static_cast<Implementation &>(*this).setIsFinalManifestImpl(is_last); + ManifestEncoder &setParamsBytestream(const ParamsBytestream ¶ms) { + return static_cast<Implementation &>(*this).setParamsBytestreamImpl(params); } - ManifestEncoder &setVersion(ManifestVersion version) { - return static_cast<Implementation &>(*this).setVersionImpl(version); + ManifestEncoder &setParamsRTC(const ParamsRTC ¶ms) { + return static_cast<Implementation &>(*this).setParamsRTCImpl(params); } - std::size_t estimateSerializedLength(std::size_t number_of_entries) { - return static_cast<Implementation &>(*this).estimateSerializedLengthImpl( - number_of_entries); + template <typename Hash> + ManifestEncoder &addEntry(uint32_t suffix, Hash &&hash) { + return static_cast<Implementation &>(*this).addEntryImpl( + suffix, std::forward<Hash>(hash)); } - ManifestEncoder &update() { - return static_cast<Implementation &>(*this).updateImpl(); + ManifestEncoder &removeEntry(uint32_t suffix) { + return static_cast<Implementation &>(*this).removeEntryImpl(suffix); } - ManifestEncoder &setFinalBlockNumber(std::uint32_t final_block_number) { - return static_cast<Implementation &>(*this).setFinalBlockNumberImpl( - final_block_number); + std::size_t manifestHeaderSize() const { + return static_cast<const Implementation &>(*this).manifestHeaderSizeImpl(); } - static std::size_t getManifestHeaderSize() { - return Implementation::getManifestHeaderSizeImpl(); + std::size_t manifestPayloadSize(size_t additional_entries = 0) const { + return static_cast<const Implementation &>(*this).manifestPayloadSizeImpl( + additional_entries); } - static std::size_t getManifestEntrySize() { - return Implementation::getManifestEntrySizeImpl(); + std::size_t manifestSize(size_t additional_entries = 0) const { + return static_cast<const Implementation &>(*this).manifestSizeImpl( + additional_entries); } }; @@ -150,55 +145,68 @@ class ManifestDecoder { public: virtual ~ManifestDecoder() = default; + ManifestDecoder &decode() { + return static_cast<Implementation &>(*this).decodeImpl(); + } + ManifestDecoder &clear() { return static_cast<Implementation &>(*this).clearImpl(); } - void decode() { static_cast<Implementation &>(*this).decodeImpl(); } + bool isDecoded() const { + return static_cast<const Implementation &>(*this).isDecodedImpl(); + } - ManifestType getManifestType() const { - return static_cast<const Implementation &>(*this).getManifestTypeImpl(); + ManifestType getType() const { + return static_cast<const Implementation &>(*this).getTypeImpl(); } - utils::CryptoHashType getHashAlgorithm() const { - return static_cast<const Implementation &>(*this).getHashAlgorithmImpl(); + interface::ProductionProtocolAlgorithms getTransportType() const { + return static_cast<const Implementation &>(*this).getTransportTypeImpl(); + } + + uint8_t getMaxCapacity() const { + return static_cast<const Implementation &>(*this).getMaxCapacityImpl(); } - uint32_t getFinalChunkNumber() const { - return static_cast<const Implementation &>(*this).getFinalChunkImpl(); + auth::CryptoHashType getHashAlgorithm() const { + return static_cast<const Implementation &>(*this).getHashAlgorithmImpl(); } - NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() const { - return static_cast<const Implementation &>(*this) - .getNextSegmentCalculationStrategyImpl(); + bool getIsLast() const { + return static_cast<const Implementation &>(*this).getIsLastImpl(); } core::Name getBaseName() const { return static_cast<const Implementation &>(*this).getBaseNameImpl(); } - auto getSuffixHashList() { - return static_cast<Implementation &>(*this).getSuffixHashListImpl(); + ParamsBytestream getParamsBytestream() const { + return static_cast<const Implementation &>(*this).getParamsBytestreamImpl(); } - bool getIsFinalManifest() const { - return static_cast<const Implementation &>(*this).getIsFinalManifestImpl(); + ParamsRTC getParamsRTC() const { + return static_cast<const Implementation &>(*this).getParamsRTCImpl(); } - ManifestVersion getVersion() const { - return static_cast<const Implementation &>(*this).getVersionImpl(); + auto getEntries() const { + return static_cast<const Implementation &>(*this).getEntriesImpl(); } - std::size_t estimateSerializedLength(std::size_t number_of_entries) const { - return static_cast<const Implementation &>(*this) - .estimateSerializedLengthImpl(number_of_entries); + std::size_t manifestHeaderSize() const { + return static_cast<const Implementation &>(*this).manifestHeaderSizeImpl(); } - uint32_t getFinalBlockNumber() const { - return static_cast<const Implementation &>(*this).getFinalBlockNumberImpl(); + std::size_t manifestPayloadSize(size_t additional_entries = 0) const { + return static_cast<const Implementation &>(*this).manifestPayloadSizeImpl( + additional_entries); + } + + std::size_t manifestSize(size_t additional_entries = 0) const { + return static_cast<const Implementation &>(*this).manifestSizeImpl( + additional_entries); } }; } // namespace core - } // namespace transport diff --git a/libtransport/src/core/manifest_format_fixed.cc b/libtransport/src/core/manifest_format_fixed.cc index ca80c38b1..bda666c0c 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-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,215 +13,336 @@ * limitations under the License. */ +#include <core/manifest_format_fixed.h> #include <hicn/transport/core/packet.h> #include <hicn/transport/utils/literals.h> -#include <core/manifest_format_fixed.h> - namespace transport { - namespace core { -// TODO use preallocated pool of membufs -FixedManifestEncoder::FixedManifestEncoder(Packet &packet, - std::size_t signature_size) +// --------------------------------------------------------- +// FixedManifest +// --------------------------------------------------------- +size_t FixedManifest::manifestHeaderSize( + 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; + } + + return MANIFEST_META_SIZE + MANIFEST_ENTRY_META_SIZE + params_size; +} + +size_t FixedManifest::manifestPayloadSize(size_t nb_entries) { + return nb_entries * MANIFEST_ENTRY_SIZE; +} + +// --------------------------------------------------------- +// FixedManifestEncoder +// --------------------------------------------------------- +FixedManifestEncoder::FixedManifestEncoder(Packet::Ptr packet, bool clear) : packet_(packet), - max_size_(Packet::default_mtu - packet_.headerSize() - signature_size), - manifest_( - utils::MemBuf::create(Packet::default_mtu - packet_.headerSize())), - manifest_header_( - reinterpret_cast<ManifestHeader *>(manifest_->writableData())), - manifest_entries_(reinterpret_cast<ManifestEntry *>( - manifest_->writableData() + sizeof(ManifestHeader))), - current_entry_(0), - signature_size_(signature_size) { - *manifest_header_ = {0}; + transport_type_(interface::ProductionProtocolAlgorithms::UNKNOWN), + encoded_(false) { + manifest_meta_ = reinterpret_cast<ManifestMeta *>(packet_->writableData() + + packet_->headerSize()); + manifest_entry_meta_ = + reinterpret_cast<ManifestEntryMeta *>(manifest_meta_ + 1); + + if (clear) { + *manifest_meta_ = {0}; + *manifest_entry_meta_ = {0}; + } } FixedManifestEncoder::~FixedManifestEncoder() {} FixedManifestEncoder &FixedManifestEncoder::encodeImpl() { - manifest_->append(sizeof(ManifestHeader) + - manifest_header_->number_of_entries * - sizeof(ManifestEntry)); - packet_.appendPayload(std::move(manifest_)); + if (encoded_) { + return *this; + } + + // Copy manifest header + manifest_meta_->transport_type = static_cast<uint8_t>(transport_type_); + manifest_entry_meta_->nb_entries = manifest_entries_.size(); + + packet_->append(manifestHeaderSizeImpl()); + + packet_->updateLength(); + auto params = reinterpret_cast<uint8_t *>(manifest_entry_meta_ + 1); + + switch (transport_type_) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: { + auto bytestream = reinterpret_cast<const uint8_t *>(¶ms_bytestream_); + std::memcpy(params, bytestream, MANIFEST_PARAMS_BYTESTREAM_SIZE); + break; + } + case interface::ProductionProtocolAlgorithms::RTC_PROD: { + auto rtc = reinterpret_cast<const uint8_t *>(¶ms_rtc_); + std::memcpy(params, rtc, MANIFEST_PARAMS_RTC_SIZE); + break; + } + default: + break; + } + + // Copy manifest entries + auto payload = reinterpret_cast<const uint8_t *>(manifest_entries_.data()); + packet_->appendPayload(payload, manifestPayloadSizeImpl()); + + packet_->updateLength(); + if (TRANSPORT_EXPECT_FALSE(packet_->payloadSize() < manifestSizeImpl())) { + throw errors::RuntimeException("Error encoding the manifest"); + } + + encoded_ = true; return *this; } FixedManifestEncoder &FixedManifestEncoder::clearImpl() { - manifest_ = utils::MemBuf::create(Packet::default_mtu - packet_.headerSize() - - signature_size_); + if (encoded_) { + packet_->trimEnd(manifestSizeImpl()); + } + + transport_type_ = interface::ProductionProtocolAlgorithms::UNKNOWN; + encoded_ = false; + *manifest_meta_ = {0}; + *manifest_entry_meta_ = {0}; + params_bytestream_ = {0}; + params_rtc_ = {0}; + manifest_entries_.clear(); + return *this; } -FixedManifestEncoder &FixedManifestEncoder::setHashAlgorithmImpl( - utils::CryptoHashType algorithm) { - manifest_header_->hash_algorithm = static_cast<uint8_t>(algorithm); +bool FixedManifestEncoder::isEncodedImpl() const { return encoded_; } + +FixedManifestEncoder &FixedManifestEncoder::setTypeImpl( + ManifestType manifest_type) { + manifest_meta_->type = static_cast<uint8_t>(manifest_type); return *this; } -FixedManifestEncoder &FixedManifestEncoder::setManifestTypeImpl( - ManifestType manifest_type) { - manifest_header_->manifest_type = static_cast<uint8_t>(manifest_type); +FixedManifestEncoder &FixedManifestEncoder::setMaxCapacityImpl( + uint8_t max_capacity) { + manifest_meta_->max_capacity = max_capacity; + 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 utils::CryptoHash &hash) { - auto _hash = hash.getDigest<std::uint8_t>(); - addSuffixHashBytes(suffix, _hash.data(), _hash.length()); +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, + .fec_type = static_cast<uint32_t>(params.fec_type), + }; + return *this; +} - manifest_header_->number_of_entries++; - current_entry_++; +FixedManifestEncoder &FixedManifestEncoder::addEntryImpl( + uint32_t suffix, const auth::CryptoHash &hash) { + ManifestEntry last_entry = { + .suffix = portability::host_to_net(suffix), + .hash = {0}, + }; - if (TRANSPORT_EXPECT_FALSE(estimateSerializedLengthImpl() > max_size_)) { - throw errors::RuntimeException("Manifest size exceeded the packet MTU!"); - } -} + auto last_hash = reinterpret_cast<uint8_t *>(last_entry.hash); + std::memcpy(last_hash, hash.getDigest()->data(), hash.getSize()); -FixedManifestEncoder &FixedManifestEncoder::setIsFinalManifestImpl( - bool is_last) { - manifest_header_->flags.is_last = static_cast<uint8_t>(is_last); + manifest_entries_.push_back(last_entry); return *this; } -FixedManifestEncoder &FixedManifestEncoder::setVersionImpl( - ManifestVersion version) { - manifest_header_->version = static_cast<uint8_t>(version); +FixedManifestEncoder &FixedManifestEncoder::removeEntryImpl(uint32_t suffix) { + for (auto it = manifest_entries_.begin(); it != manifest_entries_.end();) { + if (it->suffix == suffix) + it = manifest_entries_.erase(it); + else + ++it; + } return *this; } -std::size_t FixedManifestEncoder::estimateSerializedLengthImpl( - std::size_t additional_entries) { - return sizeof(ManifestHeader) + - (manifest_header_->number_of_entries + additional_entries) * - sizeof(ManifestEntry); -} - -FixedManifestEncoder &FixedManifestEncoder::updateImpl() { - max_size_ = Packet::default_mtu - packet_.headerSize() - signature_size_; - return *this; +size_t FixedManifestEncoder::manifestHeaderSizeImpl() const { + return FixedManifest::manifestHeaderSize(transport_type_); } -FixedManifestEncoder &FixedManifestEncoder::setFinalBlockNumberImpl( - std::uint32_t final_block_number) { - manifest_header_->final_block_number = htonl(final_block_number); - return *this; +size_t FixedManifestEncoder::manifestPayloadSizeImpl( + size_t additional_entries) const { + return FixedManifest::manifestPayloadSize(manifest_entries_.size() + + additional_entries); } -std::size_t FixedManifestEncoder::getManifestHeaderSizeImpl() { - return sizeof(ManifestHeader); +size_t FixedManifestEncoder::manifestSizeImpl(size_t additional_entries) const { + return manifestHeaderSizeImpl() + manifestPayloadSizeImpl(additional_entries); } -std::size_t FixedManifestEncoder::getManifestEntrySizeImpl() { - return sizeof(ManifestEntry); +// --------------------------------------------------------- +// FixedManifestDecoder +// --------------------------------------------------------- +FixedManifestDecoder::FixedManifestDecoder(Packet::Ptr packet) + : packet_(packet), decoded_(false) { + manifest_meta_ = + reinterpret_cast<ManifestMeta *>(packet_->getPayload()->writableData()); + manifest_entry_meta_ = + reinterpret_cast<ManifestEntryMeta *>(manifest_meta_ + 1); } -FixedManifestDecoder::FixedManifestDecoder(Packet &packet) - : packet_(packet), - manifest_header_(reinterpret_cast<ManifestHeader *>( - packet_.getPayload()->writableData())), - manifest_entries_(reinterpret_cast<ManifestEntry *>( - packet_.getPayload()->writableData() + sizeof(ManifestHeader))) {} - FixedManifestDecoder::~FixedManifestDecoder() {} -void FixedManifestDecoder::decodeImpl() { - std::size_t packet_size = packet_.payloadSize(); +FixedManifestDecoder &FixedManifestDecoder::decodeImpl() { + if (decoded_) { + return *this; + } - if (packet_size < sizeof(ManifestHeader) || - packet_size < estimateSerializedLengthImpl()) { + if (packet_->payloadSize() < manifestSizeImpl()) { throw errors::RuntimeException( - "The packet does not match expected manifest size."); + "The packet payload size does not match expected manifest size"); } -} -FixedManifestDecoder &FixedManifestDecoder::clearImpl() { return *this; } + switch (getTransportTypeImpl()) { + 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; + } -ManifestType FixedManifestDecoder::getManifestTypeImpl() const { - return static_cast<ManifestType>(manifest_header_->manifest_type); + decoded_ = true; + return *this; } -utils::CryptoHashType FixedManifestDecoder::getHashAlgorithmImpl() const { - return static_cast<utils::CryptoHashType>(manifest_header_->hash_algorithm); +FixedManifestDecoder &FixedManifestDecoder::clearImpl() { + decoded_ = false; + return *this; } -NextSegmentCalculationStrategy -FixedManifestDecoder::getNextSegmentCalculationStrategyImpl() const { - return static_cast<NextSegmentCalculationStrategy>( - manifest_header_->next_segment_strategy); +bool FixedManifestDecoder::isDecodedImpl() const { return decoded_; } + +ManifestType FixedManifestDecoder::getTypeImpl() const { + return static_cast<ManifestType>(manifest_meta_->type); } -typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { - typename Fixed::SuffixList hash_list; +interface::ProductionProtocolAlgorithms +FixedManifestDecoder::getTransportTypeImpl() const { + return static_cast<interface::ProductionProtocolAlgorithms>( + manifest_meta_->transport_type); +} - for (int i = 0; i < manifest_header_->number_of_entries; i++) { - hash_list.insert(hash_list.end(), - std::make_pair(ntohl(manifest_entries_[i].suffix), - reinterpret_cast<uint8_t *>( - &manifest_entries_[i].hash[0]))); - } +uint8_t FixedManifestDecoder::getMaxCapacityImpl() const { + return manifest_meta_->max_capacity; +} - return hash_list; +auth::CryptoHashType FixedManifestDecoder::getHashAlgorithmImpl() const { + return static_cast<auth::CryptoHashType>(manifest_meta_->hash_algorithm); +} + +bool FixedManifestDecoder::getIsLastImpl() const { + return static_cast<bool>(manifest_meta_->is_last); } 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)); + 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_header_->prefix)); + return core::Name( + AF_INET, reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix)); } } -bool FixedManifestDecoder::getIsFinalManifestImpl() const { - return static_cast<bool>(manifest_header_->flags.is_last); +ParamsBytestream FixedManifestDecoder::getParamsBytestreamImpl() const { + return ParamsBytestream{ + .final_segment = params_bytestream_->final_segment, + }; } -ManifestVersion FixedManifestDecoder::getVersionImpl() const { - return static_cast<ManifestVersion>(manifest_header_->version); +ParamsRTC FixedManifestDecoder::getParamsRTCImpl() const { + return ParamsRTC{ + .timestamp = params_rtc_->timestamp, + .prod_rate = params_rtc_->prod_rate, + .prod_seg = params_rtc_->prod_seg, + .fec_type = static_cast<protocol::fec::FECType>(params_rtc_->fec_type), + }; } -std::size_t FixedManifestDecoder::estimateSerializedLengthImpl( - std::size_t additional_entries) const { - return sizeof(ManifestHeader) + - (additional_entries + manifest_header_->number_of_entries) * - sizeof(ManifestEntry); +typename Fixed::SuffixList FixedManifestDecoder::getEntriesImpl() const { + typename Fixed::SuffixList hash_list; + + for (int i = 0; i < manifest_entry_meta_->nb_entries; i++) { + hash_list.insert( + hash_list.end(), + std::make_pair( + portability::net_to_host(manifest_entries_[i].suffix), + reinterpret_cast<uint8_t *>(&manifest_entries_[i].hash[0]))); + } + + return hash_list; } -uint32_t FixedManifestDecoder::getFinalBlockNumberImpl() const { - return ntohl(manifest_header_->final_block_number); +size_t FixedManifestDecoder::manifestHeaderSizeImpl() const { + interface::ProductionProtocolAlgorithms type = getTransportTypeImpl(); + return FixedManifest::manifestHeaderSize(type); } -} // end namespace core +size_t FixedManifestDecoder::manifestPayloadSizeImpl( + size_t additional_entries) const { + size_t nb_entries = manifest_entry_meta_->nb_entries + additional_entries; + return FixedManifest::manifestPayloadSize(nb_entries); +} +size_t FixedManifestDecoder::manifestSizeImpl(size_t additional_entries) const { + return manifestHeaderSizeImpl() + manifestPayloadSizeImpl(additional_entries); +} + +} // end namespace core } // end namespace transport diff --git a/libtransport/src/core/manifest_format_fixed.h b/libtransport/src/core/manifest_format_fixed.h index 1d7cd7d32..7ab371974 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: @@ -15,9 +15,8 @@ #pragma once -#include <hicn/transport/core/packet.h> - #include <core/manifest_format.h> +#include <hicn/transport/core/packet.h> #include <string> @@ -25,26 +24,72 @@ 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | TTYpe | Max Capacity | Hash Algo |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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | FEC Type | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// 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; @@ -53,117 +98,149 @@ class Packet; struct Fixed { using Encoder = FixedManifestEncoder; using Decoder = FixedManifestDecoder; - using HashType = utils::CryptoHash; - using SuffixList = std::list<std::pair<std::uint32_t, std::uint8_t *>>; + using Hash = auth::CryptoHash; + using HashType = auth::CryptoHashType; + using Suffix = uint32_t; + using SuffixList = std::list<std::pair<uint32_t, uint8_t *>>; +}; + +const size_t MANIFEST_META_SIZE = 4; +struct __attribute__((__packed__)) ManifestMeta { + std::uint8_t type : 4; + std::uint8_t transport_type : 4; + std::uint8_t max_capacity; + std::uint8_t hash_algorithm; + std::uint8_t is_last; }; +static_assert(sizeof(ManifestMeta) == MANIFEST_META_SIZE); -struct Flags { - std::uint8_t ipv6 : 1; - std::uint8_t is_last : 1; - std::uint8_t unused : 6; +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 ManifestEntry { +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 fec_type; +}; +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); -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]; +class FixedManifest { + public: + static size_t manifestHeaderSize( + interface::ProductionProtocolAlgorithms transport_type); + static size_t manifestPayloadSize(size_t nb_entries); }; -static const constexpr std::uint8_t manifest_version = 1; - class FixedManifestEncoder : public ManifestEncoder<FixedManifestEncoder> { public: - FixedManifestEncoder(Packet &packet, std::size_t signature_size = 0); + FixedManifestEncoder(Packet::Ptr packet, bool clear = false); ~FixedManifestEncoder(); FixedManifestEncoder &encodeImpl(); - FixedManifestEncoder &clearImpl(); + bool isEncodedImpl() const; - FixedManifestEncoder &setManifestTypeImpl(ManifestType manifest_type); - - FixedManifestEncoder &setHashAlgorithmImpl(utils::CryptoHashType algorithm); - - FixedManifestEncoder &setNextSegmentCalculationStrategyImpl( - NextSegmentCalculationStrategy strategy); + // ManifestMeta + FixedManifestEncoder &setTypeImpl(ManifestType manifest_type); + FixedManifestEncoder &setMaxCapacityImpl(uint8_t max_capacity); + FixedManifestEncoder &setHashAlgorithmImpl(Fixed::HashType algorithm); + FixedManifestEncoder &setIsLastImpl(bool is_last); + // ManifestEntryMeta FixedManifestEncoder &setBaseNameImpl(const core::Name &base_name); - FixedManifestEncoder &addSuffixAndHashImpl(uint32_t suffix, - const utils::CryptoHash &hash); + // TransportParams + FixedManifestEncoder &setParamsBytestreamImpl(const ParamsBytestream ¶ms); + FixedManifestEncoder &setParamsRTCImpl(const ParamsRTC ¶ms); - FixedManifestEncoder &setIsFinalManifestImpl(bool is_last); + // ManifestEntry + FixedManifestEncoder &addEntryImpl(uint32_t suffix, const Fixed::Hash &hash); + FixedManifestEncoder &removeEntryImpl(uint32_t suffix); - 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(); + size_t manifestHeaderSizeImpl() const; + size_t manifestPayloadSizeImpl(size_t additional_entries = 0) const; + size_t manifestSizeImpl(size_t additional_entries = 0) const; private: - void addSuffixHashBytes(uint32_t suffix, const uint8_t *hash, - std::size_t length); - - Packet &packet_; - std::size_t max_size_; - std::unique_ptr<utils::MemBuf> manifest_; - ManifestHeader *manifest_header_; - ManifestEntry *manifest_entries_; - std::size_t current_entry_; - std::size_t signature_size_; + Packet::Ptr packet_; + 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> { public: - FixedManifestDecoder(Packet &packet); + FixedManifestDecoder(Packet::Ptr packet); ~FixedManifestDecoder(); - void decodeImpl(); - + FixedManifestDecoder &decodeImpl(); FixedManifestDecoder &clearImpl(); + bool isDecodedImpl() const; - ManifestType getManifestTypeImpl() const; - - utils::CryptoHashType getHashAlgorithmImpl() const; - - NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; - - typename Fixed::SuffixList getSuffixHashListImpl(); + // ManifestMeta + ManifestType getTypeImpl() const; + interface::ProductionProtocolAlgorithms getTransportTypeImpl() const; + uint8_t getMaxCapacityImpl() const; + Fixed::HashType getHashAlgorithmImpl() const; + bool getIsLastImpl() const; + // ManifestEntryMeta core::Name getBaseNameImpl() const; - bool getIsFinalManifestImpl() const; + // TransportParams + ParamsBytestream getParamsBytestreamImpl() const; + ParamsRTC getParamsRTCImpl() const; - std::size_t estimateSerializedLengthImpl( - std::size_t additional_entries = 0) const; + // ManifestEntry + typename Fixed::SuffixList getEntriesImpl() const; - ManifestVersion getVersionImpl() const; - - uint32_t getFinalBlockNumberImpl() const; + size_t manifestHeaderSizeImpl() const; + size_t manifestPayloadSizeImpl(size_t additional_entries = 0) const; + size_t manifestSizeImpl(size_t additional_entries = 0) const; private: - Packet &packet_; - ManifestHeader *manifest_header_; + Packet::Ptr packet_; + 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 deleted file mode 100644 index dedf82b45..000000000 --- a/libtransport/src/core/manifest_inline.h +++ /dev/null @@ -1,115 +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/manifest.h> -#include <core/manifest_format.h> -#include <hicn/transport/portability/portability.h> - -#include <set> - -namespace transport { - -namespace core { - -template <typename Base, typename FormatTraits> -class ManifestInline - : public Manifest<Base, FormatTraits, ManifestInline<Base, FormatTraits>> { - using ManifestBase = - Manifest<Base, FormatTraits, ManifestInline<Base, FormatTraits>>; - using HashType = typename FormatTraits::HashType; - using SuffixList = typename FormatTraits::SuffixList; - - public: - ManifestInline() : ManifestBase() {} - - ManifestInline(const core::Name &name, std::size_t signature_size = 0) - : ManifestBase(name, signature_size) {} - - template <typename T> - ManifestInline(T &&base) : ManifestBase(std::forward<T &&>(base)) {} - - static TRANSPORT_ALWAYS_INLINE ManifestInline *createManifest( - const core::Name &manifest_name, ManifestVersion version, - ManifestType type, utils::CryptoHashType algorithm, bool is_last, - const Name &base_name, NextSegmentCalculationStrategy strategy, - std::size_t signature_size) { - auto manifest = new ManifestInline(manifest_name, signature_size); - manifest->setVersion(version); - manifest->setManifestType(type); - manifest->setHashAlgorithm(algorithm); - manifest->setFinalManifest(is_last); - manifest->setBaseName(base_name); - manifest->setNextSegmentCalculationStrategy(strategy); - - return manifest; - } - - ManifestInline &encodeImpl() { - ManifestBase::encoder_.encode(); - return *this; - } - - ManifestInline &decodeImpl() { - base_name_ = ManifestBase::decoder_.getBaseName(); - next_segment_strategy_ = - ManifestBase::decoder_.getNextSegmentCalculationStrategy(); - suffix_hash_map_ = ManifestBase::decoder_.getSuffixHashList(); - - return *this; - } - - std::size_t estimateManifestSizeImpl(std::size_t additional_entries = 0) { - return ManifestBase::encoder_.estimateSerializedLength(additional_entries); - } - - ManifestInline &setBaseName(const Name &name) { - base_name_ = name; - ManifestBase::encoder_.setBaseName(base_name_); - return *this; - } - - const Name &getBaseName() { return base_name_; } - - ManifestInline &addSuffixHash(uint32_t suffix, const HashType &hash) { - ManifestBase::encoder_.addSuffixAndHash(suffix, hash); - return *this; - } - - // 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_; - } - - private: - core::Name base_name_; - NextSegmentCalculationStrategy next_segment_strategy_; - SuffixList suffix_hash_map_; -}; - -} // end namespace core - -} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/memif_connector.cc b/libtransport/src/core/memif_connector.cc index 087e8cba8..a224beb11 100644 --- a/libtransport/src/core/memif_connector.cc +++ b/libtransport/src/core/memif_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: @@ -13,18 +13,16 @@ * 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> - -#ifdef __vpp__ - #include <sys/epoll.h> #include <cstdlib> -extern "C" { -#include <memif/libmemif.h> -}; +/* sstrncpy */ +#include <hicn/util/sstrncpy.h> #define CANCEL_TIMER 1 @@ -32,190 +30,174 @@ 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, - OnReconnect &&on_reconnect_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(on_reconnect_callback)), - memif_worker_(nullptr), + : 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_(std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), - disconnect_timer_( - std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), + send_timer_(event_reactor_), + disconnect_timer_(event_reactor_), io_service_(io_service), - packet_counter_(0), - memif_connection_(std::make_unique<memif_connection_t>()), + 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_("") { - 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)) { - TRANSPORT_LOGE("memif_init: %s", memif_strerror(err)); + socket_filename_(""), + buffer_size_(kbuf_size), + log2_ring_size_(klog2_ring_size), + max_memif_bufs_(1 << klog2_ring_size) {} + +MemifConnector::~MemifConnector() { + try { + close(); + } catch (errors::RuntimeException &e) { + // do nothing } } -void MemifConnector::connect(uint32_t memif_id, long memif_mode) { - state_ = ConnectorState::CONNECTING; +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_ = "/run/vpp/memif.sock"; - - createMemif(memif_id, memif_mode, nullptr); + 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); +} - work_ = std::make_unique<asio::io_service::work>(io_service_); +int MemifConnector::createMemif(uint32_t index, uint8_t is_master) { + int err = MEMIF_ERR_SUCCESS; - while (state_ != ConnectorState::CONNECTED) { - MemifConnector::main_event_reactor_.runOneEvent(); - } + 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)); - int err; + // Setup memif socket first - /* 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)) { - TRANSPORT_LOGE("memif_get_queue_efd: %s", memif_strerror(err)); - return; + 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); } - // 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); - }); + 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); + } - memif_worker_ = std::make_unique<std::thread>( - std::bind(&MemifConnector::threadMain, this)); -} + socket_args.on_control_fd_update = controlFdUpdate; + socket_args.alloc = nullptr; + socket_args.realloc = nullptr; + socket_args.free = nullptr; -int MemifConnector::createMemif(uint32_t index, uint8_t mode, char *s) { - memif_connection_t *c = memif_connection_.get(); + err = memif_create_socket(&args.socket, &socket_args, this); - /* setting memif connection arguments */ - memif_conn_args_t args; - memset(&args, 0, sizeof(args)); + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } - args.is_master = mode; - args.log2_ring_size = MEMIF_LOG2_RING_SIZE; - args.buffer_size = MEMIF_BUF_SIZE; + // 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; - strncpy((char *)args.interface_name, IF_NAME, strlen(IF_NAME) + 1); + strcpy_s((char *)args.interface_name, sizeof(args.interface_name), IF_NAME); args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; - - int err; - - err = memif_create_socket(&args.socket, socket_filename_.c_str(), nullptr); + 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)); } - 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; + memif_connection_.index = (uint16_t)index; + memif_connection_.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); + 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() { - memif_connection_t *c = memif_connection_.get(); - - if (c->rx_bufs) { - free(c->rx_bufs); + if (memif_connection_.rx_bufs) { + free(memif_connection_.rx_bufs); } - c->rx_bufs = nullptr; - c->rx_buf_num = 0; + memif_connection_.rx_bufs = nullptr; + memif_connection_.rx_buf_num = 0; - if (c->tx_bufs) { - free(c->tx_bufs); + if (memif_connection_.tx_bufs) { + free(memif_connection_.tx_bufs); } - c->tx_bufs = nullptr; - c->tx_buf_num = 0; + memif_connection_.tx_bufs = nullptr; + memif_connection_.tx_buf_num = 0; int err; /* disconenct then delete memif connection */ - err = memif_delete(&c->conn); + err = memif_delete(&memif_connection_.conn); if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - TRANSPORT_LOGE("memif_delete: %s", memif_strerror(err)); + LOG(ERROR) << "memif_delete: " << memif_strerror(err); } - if (TRANSPORT_EXPECT_FALSE(c->conn != nullptr)) { - TRANSPORT_LOGE("memif delete fail"); + if (TRANSPORT_EXPECT_FALSE(memif_connection_.conn != nullptr)) { + LOG(ERROR) << "memif delete fail"; } + state_ = State::CLOSED; + return 0; } -int MemifConnector::controlFdUpdate(int fd, uint8_t events, void *private_ctx) { +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) { - return MemifConnector::main_event_reactor_.delFileDescriptor(fd); + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: DEL fd " << fd; + return self->event_reactor_.delFileDescriptor(fd); } - uint32_t evt = 0; + 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; @@ -225,13 +207,10 @@ int MemifConnector::controlFdUpdate(int fd, uint8_t events, void *private_ctx) { 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; + 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) { @@ -246,83 +225,86 @@ int MemifConnector::controlFdUpdate(int fd, uint8_t events, void *private_ctx) { event |= MEMIF_FD_EVENT_ERROR; } - memif_err = memif_control_fd_handler(evt.data.fd, event); + memif_err = memif_control_fd_handler(fde.private_ctx, + memif_fd_event_type_t(event)); if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { - TRANSPORT_LOGE("memif_control_fd_handler: %s", - memif_strerror(memif_err)); + 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(); +uint16_t MemifConnector::bufferAlloc(long n, uint16_t qid, + std::error_code &ec) { int err; - uint16_t r; + uint16_t r = 0; /* 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); + err = memif_buffer_alloc(memif_connection_.conn, qid, + memif_connection_.tx_bufs, n, &r, buffer_size_); if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - TRANSPORT_LOGE("memif_buffer_alloc: %s", memif_strerror(err)); - return -1; + ec = make_error_code(core_error::send_buffer_allocation_failed); } - c->tx_buf_num += r; + memif_connection_.tx_buf_num += r; return r; } -int MemifConnector::txBurst(uint16_t qid) { - memif_connection_t *c = memif_connection_.get(); - int err; - uint16_t 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(c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); + 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)) { - TRANSPORT_LOGE("memif_tx_burst: %s", memif_strerror(err)); + ec = make_error_code(core_error::send_failed); } - // err = memif_refill_queue(c->conn, qid, r, 0); + memif_connection_.tx_buf_num -= tx; + return tx; +} - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - TRANSPORT_LOGE("memif_tx_burst: %s", memif_strerror(err)); - c->tx_buf_num -= r; - return -1; +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)); } - - c->tx_buf_num -= r; - return 0; } void MemifConnector::sendCallback(const std::error_code &ec) { timer_set_ = false; - if (TRANSPORT_EXPECT_TRUE(!ec && state_ == ConnectorState::CONNECTED)) { + if (TRANSPORT_EXPECT_TRUE(!ec && state_ == State::CONNECTED)) { doSend(); } } -void MemifConnector::processInputBuffer(std::uint16_t total_packets) { - Packet::MemBufPtr ptr; - - for (; total_packets > 0; total_packets--) { - if (input_buffer_.pop(ptr)) { - receive_callback_(std::move(ptr)); - } - } -} - /* 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_ = ConnectorState::CONNECTED; + 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; } @@ -330,54 +312,51 @@ int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { identify connection (multiple connections WIP) */ int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { MemifConnector *connector = (MemifConnector *)private_ctx; - connector->state_ = ConnectorState::CLOSED; + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Memif " << connector->app_name_ << " disconnected"; return 0; } -void MemifConnector::threadMain() { event_reactor_.runEventLoop(1000); } +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; - memif_connection_t *c = connector->memif_connection_.get(); + 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 total_packets = 0; - uint16_t rx; + uint16_t rx = 0; do { - err = memif_rx_burst(conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); + 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)) { - TRANSPORT_LOGE("memif_rx_burst: %s", memif_strerror(err)); + ec = make_error_code(core_error::receive_failed); + LOG(ERROR) << "memif_rx_burst: " << memif_strerror(err); goto error; } - c->rx_buf_num += rx; + c.rx_buf_num += rx; if (TRANSPORT_EXPECT_FALSE(connector->io_service_.stopped())) { - TRANSPORT_LOGE("socket stopped: ignoring %u packets", rx); + 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 packet = connector->getPacket(); - packet_length = (c->rx_bufs + i)->len; - std::memcpy(packet->writableData(), - reinterpret_cast<const uint8_t *>((c->rx_bufs + i)->data), - packet_length); - packet->append(packet_length); - - if (!connector->input_buffer_.push(std::move(packet))) { - TRANSPORT_LOGE("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) - } + 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 */ @@ -386,114 +365,139 @@ int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, err = memif_refill_queue(conn, qid, rx, 0); if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - TRANSPORT_LOGE("memif_buffer_free: %s", memif_strerror(err)); + LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); } - c->rx_buf_num -= rx; - total_packets += rx; + c.rx_buf_num -= rx; } while (ret_val == MEMIF_ERR_NOBUF); - connector->io_service_.post( - std::bind(&MemifConnector::processInputBuffer, connector, total_packets)); + 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); + err = memif_refill_queue(c.conn, qid, rx, 0); if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - TRANSPORT_LOGE("memif_buffer_free: %s", memif_strerror(err)); + LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); } - c->rx_buf_num -= rx; + 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_ != ConnectorState::CLOSED) { - disconnect_timer_->expiresFromNow(std::chrono::microseconds(50)); - disconnect_timer_->asyncWait([this](const std::error_code &ec) { + if (state_ != State::CLOSED) { + disconnect_timer_.expiresFromNow(std::chrono::microseconds(50)); + disconnect_timer_.asyncWait([this](const std::error_code &ec) { deleteMemif(); event_reactor_.stop(); - work_.reset(); }); + } - if (memif_worker_ && memif_worker_->joinable()) { - memif_worker_->join(); - } + if (memif_worker_.joinable()) { + memif_worker_.join(); } } -void MemifConnector::send(const Packet::MemBufPtr &packet) { +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(packet); + output_buffer_.push_back(buffer); } #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)); - } + scheduleSend(50); #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(); + 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; + } } - do { - max = size < MAX_MEMIF_BUFS ? size : MAX_MEMIF_BUFS; - n = bufferAlloc(max, memif_connection_->tx_qid); - - if (TRANSPORT_EXPECT_FALSE(n < 0)) { - TRANSPORT_LOGE("Error allocating buffers."); - return -1; - } + // Continue trying to send buffers in output_buffer_ + size = output_buffer_.size(); + max = size < max_burst ? size : max_burst; - for (uint16_t i = 0; i < n; i++) { - utils::SpinLock::Acquire locked(write_msgs_lock_); + ret = bufferAlloc(max, memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool() && ret == 0)) { + delay = 200; + goto done; + } - 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); + // 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(); + } - memif_connection_->tx_bufs[i].len = uint32_t(offset); + // 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; + } - output_buffer_.pop_front(); - } +done: + memif_refill_queue(memif_connection_.conn, memif_connection_.tx_qid, ret, 0); - txBurst(memif_connection_->tx_qid); + // If there are still packets to send, schedule another send + if (memif_connection_.tx_buf_num > 0 || !output_buffer_.empty()) { + scheduleSend(delay); + } - utils::SpinLock::Acquire locked(write_msgs_lock_); - size = output_buffer_.size(); - } while (size > 0); + // 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; } -void MemifConnector::send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent) { - throw errors::NotImplementedException(); -} - } // end namespace core } // end namespace transport - -#endif // __vpp__ diff --git a/libtransport/src/core/memif_connector.h b/libtransport/src/core/memif_connector.h index 8a0e9efad..d36be4616 100644 --- a/libtransport/src/core/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: @@ -15,20 +15,23 @@ #pragma once -#include <core/connector.h> #include <hicn/transport/config.h> +#include <hicn/transport/core/connector.h> #include <hicn/transport/portability/portability.h> #include <hicn/transport/utils/ring_buffer.h> //#include <hicn/transport/core/hicn_vapi.h> +#include <hicn/transport/core/asio_wrapper.h> #include <utils/epoll_event_reactor.h> #include <utils/fd_deadline_timer.h> -#include <asio.hpp> #include <deque> +#include <future> #include <mutex> #include <thread> -#ifdef __vpp__ +extern "C" { +#include <libmemif.h> +}; #define _Static_assert static_assert @@ -36,34 +39,55 @@ 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 { - typedef void *memif_conn_handle_t; + 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, - OnReconnect &&on_reconnect_callback, + PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, + OnReconnectCallback &&on_reconnect, asio::io_service &io_service, std::string app_name = "Libtransport"); ~MemifConnector() override; - void send(const Packet::MemBufPtr &packet) override; + void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent = 0) 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,28 +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_; - std::unique_ptr<asio::io_service::work> work_; - uint32_t packet_counter_; - std::unique_ptr<memif_connection_t> memif_connection_; + asio::executor_work_guard<asio::io_context::executor_type> work_; + Details memif_connection_; uint16_t tx_buf_counter_; PacketRing input_buffer_; @@ -122,12 +144,11 @@ 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 } // end namespace transport - -#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/core/name.cc b/libtransport/src/core/name.cc index 811e93b87..4f8ba7873 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-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,44 +13,43 @@ * limitations under the License. */ +#include <core/manifest_format.h> +#include <hicn/name.h> #include <hicn/transport/core/name.h> #include <hicn/transport/errors/errors.h> #include <hicn/transport/errors/tokenizer_exception.h> #include <hicn/transport/utils/hash.h> #include <hicn/transport/utils/string_tokenizer.h> -#include <core/manifest_format.h> - namespace transport { namespace core { -Name::Name() { name_ = {}; } +Name::Name() { std::memset(&name_, 0, sizeof(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(); } @@ -60,7 +59,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; @@ -81,9 +79,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; @@ -98,7 +100,12 @@ bool Name::operator!=(const Name &name) const { } Name::operator bool() const { - return bool(hicn_name_empty((hicn_name_t *)&name_)); + auto ret = isValid(); + return ret; +} + +bool Name::isValid() const { + return bool(!hicn_name_empty((hicn_name_t *)&name_)); } bool Name::equals(const Name &name, bool consider_segment) const { @@ -118,16 +125,20 @@ std::string Name::toString() const { } uint32_t Name::getHash32(bool consider_suffix) const { - uint32_t hash; - if (hicn_name_hash(&name_, &hash, consider_suffix) < 0) { + uint32_t hash = _hicn_name_get_hash(&name_, consider_suffix); + if (hash < 0) { throw errors::RuntimeException("Error computing the hash of the name!"); } 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; @@ -138,49 +149,32 @@ uint32_t Name::getSuffix() const { return ret; } -Name &Name::setSuffix(uint32_t seq_number) { - if (hicn_name_set_seq_number(&name_, seq_number) < 0) { - throw errors::RuntimeException( - "Impossible to set the sequence number to the name."); +Name &Name::setSuffix(hicn_name_suffix_t suffix) { + if (hicn_name_set_suffix(&name_, suffix) < 0) { + throw errors::RuntimeException("Impossible to set name suffix."); } 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; +hicn_ip_prefix_t Name::toIpAddress() const { + hicn_ip_prefix_t ret; std::memset(&ret, 0, sizeof(ret)); - if (hicn_name_to_ip_prefix(&name_, &ret) < 0) { + if (hicn_name_to_hicn_ip_prefix(&name_, &ret) < 0) { throw errors::InvalidIpAddressException(); } return ret; } +std::string Name::getPrefix() const { + char prefix[MAXSZ_HICN_NAME]; + hicn_name_no_suffix_snprintf(prefix, MAXSZ_HICN_NAME, &name_); + + return std::string(prefix); +} + int Name::getAddressFamily() const { int ret = 0; @@ -191,8 +185,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 6815868f0..bcd0b8498 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-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,15 +13,18 @@ * limitations under the License. */ +#include <glog/logging.h> +#include <hicn/transport/auth/crypto_hash.h> +#include <hicn/transport/core/global_object_pool.h> #include <hicn/transport/core/packet.h> #include <hicn/transport/errors/malformed_packet_exception.h> #include <hicn/transport/utils/hash.h> -#include <hicn/transport/utils/log.h> extern "C" { #ifndef _WIN32 TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") #endif +#include <hicn/base.h> #include <hicn/error.h> } @@ -31,521 +34,527 @@ namespace core { const core::Name Packet::base_name("0::0|0"); -Packet::Packet(Format format) - : packet_(utils::MemBuf::create(getHeaderSizeFromFormat(format, 256)) - .release()), - packet_start_(reinterpret_cast<hicn_header_t *>(packet_->writableData())), - header_head_(packet_.get()), - payload_head_(nullptr), - format_(format) { - if (hicn_packet_init_header(format, packet_start_) < 0) { - throw errors::RuntimeException("Unexpected error initializing the packet."); - } - - packet_->append(getHeaderSizeFromFormat(format_)); +Packet::Packet(Type type, Format format, std::size_t additional_header_size) + : utils::MemBuf(utils::MemBuf(CREATE, 2048)), + payload_type_(PayloadType::UNSPECIFIED) { + /* + * We define the format and the storage area of the packet buffer we + * manipulate + */ + setFormat(format); + setBuffer(); + initializeType(type); // type requires packet format + initialize(additional_header_size); +} + +Packet::Packet(CopyBufferOp, const uint8_t *buffer, std::size_t size) + : utils::MemBuf(COPY_BUFFER, buffer, size), + payload_type_(PayloadType::UNSPECIFIED) { + setBuffer(); + analyze(); +} + +Packet::Packet(WrapBufferOp, uint8_t *buffer, std::size_t length, + std::size_t size) + : utils::MemBuf(WRAP_BUFFER, buffer, length, size), + payload_type_(PayloadType::UNSPECIFIED) { + setBuffer(); + analyze(); +} + +Packet::Packet(CreateOp, Type type, uint8_t *buffer, std::size_t length, + std::size_t size, Format format, + std::size_t additional_header_size) + : utils::MemBuf(WRAP_BUFFER, buffer, length, size), + payload_type_(PayloadType::UNSPECIFIED) { + clear(); + setType(type); + setFormat(format); + setBuffer(); + initialize(additional_header_size); +} + +Packet::Packet(MemBuf &&buffer) + : utils::MemBuf(std::move(buffer)), + payload_type_(PayloadType::UNSPECIFIED) { + setBuffer(); + analyze(); } -Packet::Packet(MemBufPtr &&buffer) - : packet_(std::move(buffer)), - packet_start_(reinterpret_cast<hicn_header_t *>(packet_->writableData())), - header_head_(packet_.get()), - payload_head_(nullptr), - format_(getFormatFromBuffer(packet_->writableData())) {} - -Packet::Packet(const uint8_t *buffer, std::size_t size) - : Packet(MemBufPtr(utils::MemBuf::copyBuffer(buffer, size).release())) {} +/* + * In the two following constructors, we inherit the pkbuf and only need to + * recompute the pointer fields, aka the buffer. + */ Packet::Packet(Packet &&other) - : packet_(std::move(other.packet_)), - packet_start_(other.packet_start_), - header_head_(other.header_head_), - payload_head_(other.payload_head_), - format_(other.format_) { - other.packet_start_ = nullptr; - other.header_head_ = nullptr; - other.payload_head_ = nullptr; - other.format_ = HF_UNSPEC; + : utils::MemBuf(std::move(other)), + pkbuf_(other.pkbuf_), + payload_type_(PayloadType::UNSPECIFIED) { + hicn_packet_reset(&other.pkbuf_); +} + +Packet::Packet(const Packet &other) + : utils::MemBuf(other), + pkbuf_(other.pkbuf_), + payload_type_(PayloadType::UNSPECIFIED) { + setBuffer(); } Packet::~Packet() {} -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(); +Packet &Packet::operator=(const Packet &other) { + if (this != &other) { + *this = other; + setBuffer(); } - return header_length; + + return *this; } -bool Packet::isInterest(const uint8_t *buffer) { - bool is_interest = false; +std::shared_ptr<utils::MemBuf> Packet::acquireMemBufReference() { + return std::static_pointer_cast<utils::MemBuf>(shared_from_this()); +} - if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece((const hicn_header_t *)buffer, - &is_interest) < 0)) { - throw errors::RuntimeException( - "Impossible to retrieve ece flag from packet"); - } +Packet::Format Packet::getFormat() const { + return hicn_packet_get_format(&pkbuf_); +} - return !is_interest; +void Packet::setFormat(Packet::Format format) { + hicn_packet_set_format(&pkbuf_, format); } -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(); +void Packet::initialize(std::size_t additional_header_size) { + if (hicn_packet_init_header(&pkbuf_, additional_header_size) < 0) { + throw errors::RuntimeException("Unexpected error initializing the packet."); } - return payload_length; + auto header_size = getHeaderSizeFromFormat(getFormat()); + DCHECK(header_size <= tailroom()); + append(header_size); + DCHECK(additional_header_size <= tailroom()); + append(additional_header_size); } -std::size_t Packet::payloadSize() const { - return getPayloadSizeFromBuffer(format_, - reinterpret_cast<uint8_t *>(packet_start_)); +void Packet::analyze() { + if (hicn_packet_analyze(&pkbuf_) < 0) + throw errors::MalformedPacketException(); } -std::size_t Packet::headerSize() const { - return getHeaderSizeFromBuffer(format_, - reinterpret_cast<uint8_t *>(packet_start_)); +Packet::Type Packet::getType() const { return hicn_packet_get_type(&pkbuf_); } + +void Packet::initializeType(Packet::Type type) { + hicn_packet_initialize_type(&pkbuf_, type); } -Packet &Packet::appendPayload(std::unique_ptr<utils::MemBuf> &&payload) { - separateHeaderPayload(); +void Packet::setType(Packet::Type type) { hicn_packet_set_type(&pkbuf_, type); } - if (!payload_head_) { - payload_head_ = payload.get(); +void Packet::setBuffer() { + hicn_packet_set_buffer(&pkbuf_, writableData(), + this->capacity() - this->headroom(), this->length()); +} + +PayloadType Packet::getPayloadType() const { + if (payload_type_ == PayloadType::UNSPECIFIED) { + hicn_payload_type_t ret; + + if (hicn_packet_get_payload_type(&pkbuf_, &ret) < 0) { + throw errors::RuntimeException("Impossible to retrieve payload type."); + } + + payload_type_ = (PayloadType)ret; } - header_head_->prependChain(std::move(payload)); - updateLength(); + return payload_type_; +} + +Packet &Packet::setPayloadType(PayloadType payload_type) { + if (hicn_packet_set_payload_type(&pkbuf_, hicn_payload_type_t(payload_type)) < + 0) { + throw errors::RuntimeException("Error setting payload type of the packet."); + } + + payload_type_ = payload_type; return *this; } -Packet &Packet::appendPayload(const uint8_t *buffer, std::size_t length) { - return appendPayload(utils::MemBuf::copyBuffer(buffer, length)); +std::unique_ptr<utils::MemBuf> Packet::getPayload() const { + auto ret = clone(); + ret->trimStart(headerSize()); + return ret; } -Packet &Packet::appendHeader(std::unique_ptr<utils::MemBuf> &&header) { - separateHeaderPayload(); +Packet &Packet::appendPayload(std::unique_ptr<utils::MemBuf> &&payload) { + prependChain(std::move(payload)); + updateLength(); + return *this; +} + +Packet &Packet::appendPayload(const uint8_t *buffer, std::size_t length) { + prependPayload(&buffer, &length); - if (!payload_head_) { - header_head_->prependChain(std::move(header)); - } else { - payload_head_->prependChain(std::move(header)); + if (length) { + appendPayload(utils::MemBuf::copyBuffer(buffer, length)); } updateLength(); return *this; } -Packet &Packet::appendHeader(const uint8_t *buffer, std::size_t length) { - return appendHeader(utils::MemBuf::copyBuffer(buffer, length)); +std::size_t Packet::headerSize() const { + std::size_t len; + hicn_packet_get_header_len(&pkbuf_, &len); + return len; } -std::unique_ptr<utils::MemBuf> Packet::getPayload() const { - const_cast<Packet *>(this)->separateHeaderPayload(); +std::size_t Packet::payloadSize() const { + std::size_t len; + hicn_packet_get_payload_len(&pkbuf_, &len); + return len; +} + +auth::CryptoHash Packet::computeDigest(auth::CryptoHashType algorithm) const { + auth::CryptoHash hash; + hash.setType(algorithm); + + // Copy IP+TCP/ICMP header before zeroing them + u8 header_copy[HICN_HDRLEN_MAX]; + size_t header_len; + hicn_packet_save_header(&pkbuf_, header_copy, &header_len, + /* copy_ah */ false); + const_cast<Packet *>(this)->resetForHash(); - // Hopefully the payload is contiguous - if (TRANSPORT_EXPECT_FALSE(payload_head_ && - payload_head_->next() != header_head_)) { - payload_head_->gather(payloadSize()); + hash.computeDigest(this); + hicn_packet_load_header(&pkbuf_, header_copy, header_len); + + return hash; +} + +void Packet::reset() { + clear(); + hicn_packet_reset(&pkbuf_); + setBuffer(); + payload_type_ = PayloadType::UNSPECIFIED; + name_.clear(); + + if (isChained()) { + separateChain(next(), prev()); } +} - return payload_head_->cloneOne(); +bool Packet::isInterest() { + return hicn_packet_get_type(&pkbuf_) == HICN_PACKET_TYPE_INTEREST; } Packet &Packet::updateLength(std::size_t length) { std::size_t total_length = length; - for (utils::MemBuf *current = payload_head_; - current && current != header_head_; current = current->next()) { + const utils::MemBuf *current = this; + do { total_length += current->length(); + current = current->next(); + } while (current != this); + + if (hicn_packet_set_len(&pkbuf_, total_length) < 0) { + throw errors::RuntimeException("Error setting the packet length."); } - if (hicn_packet_set_payload_length(format_, packet_start_, total_length) < - 0) { - throw errors::RuntimeException("Error setting the packet payload."); + total_length -= headerSize(); + + if (hicn_packet_set_payload_length(&pkbuf_, total_length) < 0) { + throw errors::RuntimeException("Error setting the packet payload length."); } return *this; } -PayloadType Packet::getPayloadType() const { - hicn_payload_type_t ret = HPT_UNSPEC; - - if (hicn_packet_get_payload_type(packet_start_, &ret) < 0) { - throw errors::RuntimeException("Impossible to retrieve payload type."); - } +void Packet::dump() const { + LOG(INFO) << "HEADER -- Length: " << headerSize(); + LOG(INFO) << "PAYLOAD -- Length: " << payloadSize(); - return PayloadType(ret); + const utils::MemBuf *current = this; + do { + LOG(INFO) << "MemBuf Length: " << current->length(); + dump((uint8_t *)current->data(), current->length()); + current = current->next(); + } while (current != this); } -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."); - } +void Packet::setChecksum() { + size_t header_len = 0; + if (hicn_packet_get_header_len(&pkbuf_, &header_len) < 0) + throw errors::RuntimeException( + "Error setting getting packet header length."); - return *this; -} + uint16_t partial_csum = csum(data() + header_len, length() - header_len, 0); -Packet::Format Packet::getFormat() const { - if (format_ == HF_UNSPEC) { - if (hicn_packet_get_format(packet_start_, &format_) < 0) { - throw errors::MalformedPacketException(); - } + for (utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); } - return format_; + if (hicn_packet_compute_header_checksum(&pkbuf_, partial_csum) < 0) { + throw errors::MalformedPacketException(); + } } -const std::shared_ptr<utils::MemBuf> Packet::acquireMemBufReference() const { - return packet_; -} +bool Packet::checkIntegrity() const { + size_t header_len = 0; + if (hicn_packet_get_header_len(&pkbuf_, &header_len) < 0) + throw errors::RuntimeException( + "Error setting getting packet header length."); -void Packet::dump() const { - const_cast<Packet *>(this)->separateHeaderPayload(); + uint16_t partial_csum = csum(data() + header_len, length() - header_len, 0); - std::cout << "HEADER -- Length: " << headerSize() << std::endl; - hicn_packet_dump((uint8_t *)header_head_->data(), headerSize()); + for (const utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); + } - std::cout << std::endl << "PAYLOAD -- Length: " << payloadSize() << std::endl; - for (utils::MemBuf *current = payload_head_; - current && current != header_head_; current = current->next()) { - std::cout << "MemBuf Length: " << current->length() << std::endl; - hicn_packet_dump((uint8_t *)current->data(), current->length()); + if (hicn_packet_check_integrity_no_payload(&pkbuf_, partial_csum) < 0) { + return false; } + + return true; } -void Packet::setSignatureSize(std::size_t size_bytes) { - int ret = hicn_packet_set_signature_size(format_, packet_start_, size_bytes); +bool Packet::hasAH() const { return HICN_PACKET_FORMAT_IS_AH(getFormat()); } - if (ret < 0) { +utils::MemBuf::Ptr Packet::getSignature() const { + if (!hasAH()) { throw errors::RuntimeException("Packet without Authentication Header."); } - packet_->append(size_bytes); - updateLength(); -} - -uint8_t *Packet::getSignature() const { uint8_t *signature; - int ret = hicn_packet_get_signature(format_, packet_start_, &signature); + int ret = hicn_packet_get_signature(&pkbuf_, &signature); if (ret < 0) { - throw errors::RuntimeException("Packet without Authentication Header."); + throw errors::RuntimeException("Error getting signature."); } - return signature; + utils::MemBuf::Ptr membuf = PacketManager<>::getInstance().getMemBuf(); + membuf->append(getSignatureFieldSize()); + memcpy(membuf->writableData(), signature, getSignatureFieldSize()); + + return membuf; } -void Packet::setSignatureTimestamp(const uint64_t ×tamp) { - int ret = - hicn_packet_set_signature_timestamp(format_, packet_start_, timestamp); +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(&pkbuf_, &field_size); if (ret < 0) { - throw errors::RuntimeException("Error setting the signature timestamp."); + throw errors::RuntimeException("Error reading signature field size"); } + return field_size; } -uint64_t Packet::getSignatureTimestamp() const { - uint64_t return_value; - int ret = hicn_packet_get_signature_timestamp(format_, packet_start_, - &return_value); +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(&pkbuf_, &padding); if (ret < 0) { - throw errors::RuntimeException("Error getting the signature timestamp."); + throw errors::RuntimeException("Error reading signature padding"); + } + + size_t size = getSignatureFieldSize() - padding; + if (size < 0) { + throw errors::RuntimeException("Error reading signature size"); } - return return_value; + return size; } -void Packet::setValidationAlgorithm( - const utils::CryptoSuite &validation_algorithm) { - int ret = hicn_packet_set_validation_algorithm(format_, packet_start_, - uint8_t(validation_algorithm)); +uint64_t Packet::getSignatureTimestamp() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + uint64_t timestamp; + int ret = hicn_packet_get_signature_timestamp(&pkbuf_, ×tamp); if (ret < 0) { - throw errors::RuntimeException("Error setting the validation algorithm."); + throw errors::RuntimeException("Error getting the signature timestamp."); } + return timestamp; } -utils::CryptoSuite Packet::getValidationAlgorithm() const { - uint8_t return_value; - int ret = hicn_packet_get_validation_algorithm(format_, packet_start_, - &return_value); +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(&pkbuf_, &key_id.first, &key_id.second); if (ret < 0) { throw errors::RuntimeException("Error getting the validation algorithm."); } - - return utils::CryptoSuite(return_value); + return key_id; } -void Packet::setKeyId(const utils::KeyId &key_id) { - 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::CryptoSuite Packet::getValidationAlgorithm() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } -} - -utils::KeyId Packet::getKeyId() const { - utils::KeyId return_value; - int ret = hicn_packet_get_key_id(format_, packet_start_, &return_value.first, - &return_value.second); + uint8_t return_value; + int ret = hicn_packet_get_validation_algorithm(&pkbuf_, &return_value); if (ret < 0) { throw errors::RuntimeException("Error getting the validation algorithm."); } - - return return_value; -} - -utils::CryptoHash Packet::computeDigest(utils::CryptoHashType algorithm) const { - utils::CryptoHasher hasher(static_cast<utils::CryptoHashType>(algorithm)); - hasher.init(); - - // 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(); - - auto current = header_head_; - do { - hasher.updateBytes(current->data(), current->length()); - current = current->next(); - } while (current != header_head_); - - hicn_packet_copy_header(format_, &header_copy, packet_start_, false); - - return hasher.finalize(); + return auth::CryptoSuite(return_value); } -bool Packet::checkIntegrity() const { - if (hicn_packet_check_integrity(format_, packet_start_) < 0) { - return false; +void Packet::setSignature(const utils::MemBuf::Ptr &signature) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } - return true; -} - -Packet &Packet::setSyn() { - if (hicn_packet_set_syn(packet_start_) < 0) { - throw errors::RuntimeException("Error setting syn bit in the packet."); + uint8_t *signature_field; + int ret = hicn_packet_get_signature(&pkbuf_, &signature_field); + if (ret < 0) { + throw errors::RuntimeException("Error getting signature."); } - - return *this; + memcpy(signature_field, signature->data(), signature->length()); } -Packet &Packet::resetSyn() { - if (hicn_packet_reset_syn(packet_start_) < 0) { - throw errors::RuntimeException("Error resetting syn bit in the packet."); +void Packet::setSignatureFieldSize(std::size_t size) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } - return *this; -} - -bool Packet::testSyn() const { - bool res = false; - if (hicn_packet_test_syn(packet_start_, &res) < 0) { - throw errors::RuntimeException("Error testing syn bit in the packet."); + int ret = hicn_packet_set_signature_size(&pkbuf_, size); + if (ret < 0) { + throw errors::RuntimeException("Error setting signature size."); } - - return res; } -Packet &Packet::setAck() { - if (hicn_packet_set_ack(packet_start_) < 0) { - throw errors::RuntimeException("Error setting ack bit in the packet."); +void Packet::setSignatureSize(std::size_t size) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } - return *this; -} - -Packet &Packet::resetAck() { - if (hicn_packet_reset_ack(packet_start_) < 0) { - throw errors::RuntimeException("Error resetting ack bit in the packet."); + size_t padding = getSignatureFieldSize() - size; + if (padding < 0) { + throw errors::RuntimeException("Error setting signature padding."); } - return *this; -} - -bool Packet::testAck() const { - bool res = false; - if (hicn_packet_test_ack(packet_start_, &res) < 0) { - throw errors::RuntimeException("Error testing ack bit in the packet."); + int ret = hicn_packet_set_signature_padding(&pkbuf_, padding); + if (ret < 0) { + throw errors::RuntimeException("Error setting signature padding."); } - - return res; } -Packet &Packet::setRst() { - if (hicn_packet_set_rst(packet_start_) < 0) { - throw errors::RuntimeException("Error setting rst bit in the packet."); +void Packet::setSignatureTimestamp(const uint64_t ×tamp) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } - return *this; -} - -Packet &Packet::resetRst() { - if (hicn_packet_reset_rst(packet_start_) < 0) { - throw errors::RuntimeException("Error resetting rst bit in the packet."); + int ret = hicn_packet_set_signature_timestamp(&pkbuf_, timestamp); + if (ret < 0) { + throw errors::RuntimeException("Error setting the signature timestamp."); } - - return *this; } -bool Packet::testRst() const { - bool res = false; - if (hicn_packet_test_rst(packet_start_, &res) < 0) { - throw errors::RuntimeException("Error testing rst bit in the packet."); +void Packet::setKeyId(const auth::KeyId &key_id) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } - return res; -} - -Packet &Packet::setFin() { - if (hicn_packet_set_fin(packet_start_) < 0) { - throw errors::RuntimeException("Error setting fin bit in the packet."); + int ret = hicn_packet_set_key_id(&pkbuf_, key_id.first, key_id.second); + if (ret < 0) { + throw errors::RuntimeException("Error setting the key id."); } - - return *this; } -Packet &Packet::resetFin() { - if (hicn_packet_reset_fin(packet_start_) < 0) { - throw errors::RuntimeException("Error resetting fin bit in the packet."); +void Packet::setValidationAlgorithm( + const auth::CryptoSuite &validation_algorithm) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); } - return *this; -} - -bool Packet::testFin() const { - bool res = false; - if (hicn_packet_test_fin(packet_start_, &res) < 0) { - throw errors::RuntimeException("Error testing fin bit in the packet."); + int ret = hicn_packet_set_validation_algorithm(&pkbuf_, + uint8_t(validation_algorithm)); + if (ret < 0) { + throw errors::RuntimeException("Error setting the validation algorithm."); } +} - return res; +Packet::Format Packet::toAHFormat(const Format &format) { + return hicn_get_ah_format(format); } -Packet &Packet::resetFlags() { - resetSyn(); - resetAck(); - resetRst(); - resetFin(); +Packet::Format Packet::getFormatFromBuffer(const uint8_t *buffer, + std::size_t length) { + hicn_packet_buffer_t pkbuf; + /* un-const to be able to use pkbuf API */ + hicn_packet_set_buffer(&pkbuf, (uint8_t *)buffer, length, length); + if (hicn_packet_analyze(&pkbuf) < 0) throw errors::MalformedPacketException(); - return *this; + return hicn_packet_get_format(&pkbuf); } -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; +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 = HICN_PACKET_FORMAT_IS_AH(format); + return is_ah * (header_length + signature_size) + (!is_ah) * header_length; } -Packet &Packet::setSrcPort(uint16_t srcPort) { - if (hicn_packet_set_src_port(packet_start_, srcPort) < 0) { - throw errors::RuntimeException("Error setting source port in the packet."); - } +std::size_t Packet::getHeaderSizeFromBuffer(const uint8_t *buffer, + std::size_t length) { + size_t header_length; - return *this; -} + hicn_packet_buffer_t pkbuf; + /* un-const to be able to use pkbuf API */ + hicn_packet_set_buffer(&pkbuf, (uint8_t *)buffer, length, length); + if (hicn_packet_analyze(&pkbuf) < 0) throw errors::MalformedPacketException(); -Packet &Packet::setDstPort(uint16_t dstPort) { - if (hicn_packet_set_dst_port(packet_start_, dstPort) < 0) { - throw errors::RuntimeException( - "Error setting destination port in the packet."); - } + int rc = hicn_packet_get_header_len(&pkbuf, &header_length); + if (TRANSPORT_EXPECT_FALSE(rc < 0)) throw errors::MalformedPacketException(); - return *this; + return header_length; } -uint16_t Packet::getSrcPort() const { - uint16_t port = 0; - - if (hicn_packet_get_src_port(packet_start_, &port) < 0) { - throw errors::RuntimeException("Error reading source port in the packet."); - } - - return port; -} +std::size_t Packet::getPayloadSizeFromBuffer(const uint8_t *buffer, + std::size_t length) { + std::size_t payload_length; -uint16_t Packet::getDstPort() const { - uint16_t port = 0; + hicn_packet_buffer_t pkbuf; + /* un-const to be able to use pkbuf API */ + hicn_packet_set_buffer(&pkbuf, (uint8_t *)buffer, length, length); + if (hicn_packet_analyze(&pkbuf) < 0) throw errors::MalformedPacketException(); - if (hicn_packet_get_dst_port(packet_start_, &port) < 0) { - throw errors::RuntimeException( - "Error reading destination port in the packet."); - } + int rc = hicn_packet_get_payload_len(&pkbuf, &payload_length); + if (TRANSPORT_EXPECT_FALSE(rc < 0)) throw errors::MalformedPacketException(); - return port; + return payload_length; } -Packet &Packet::setTTL(uint8_t hops) { - if (hicn_packet_set_hoplimit(packet_start_, hops) < 0) { - throw errors::RuntimeException("Error setting TTL."); - } - - return *this; +void Packet::dump(uint8_t *buffer, std::size_t length) { + hicn_packet_dump(buffer, length); } -uint8_t Packet::getTTL() const { - uint8_t hops = 0; - if (hicn_packet_get_hoplimit(packet_start_, &hops) < 0) { - throw errors::RuntimeException("Error reading TTL."); - } - - return hops; +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; } -void Packet::separateHeaderPayload() { - if (payload_head_) { - return; - } - - int signature_size = 0; - if (_is_ah(format_)) { - signature_size = (uint32_t)getSignatureSize(); - } - - auto header_size = getHeaderSizeFromFormat(format_, signature_size); - auto payload_length = packet_->length() - header_size; - - packet_->trimEnd(packet_->length()); - - auto payload = packet_->cloneOne(); - payload_head_ = payload.get(); - payload_head_->advance(header_size); - payload_head_->append(payload_length); - packet_->prependChain(std::move(payload)); - packet_->append(header_size); +void Packet::saveHeader(u8 *header, size_t *header_len) { + hicn_packet_save_header(&pkbuf_, header, header_len, /* copy_ah */ false); } -void Packet::resetPayload() { - if (packet_->isChained()) { - packet_->separateChain(packet_->next(), packet_->prev()); - payload_head_ = nullptr; - updateLength(); - } +void Packet::loadHeader(u8 *header, size_t header_len) { + hicn_packet_load_header(&pkbuf_, header, header_len); } } // end namespace core 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 aeff78ea2..f49348bac 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: @@ -16,16 +16,14 @@ #pragma once #include <hicn/transport/config.h> +#include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <hicn/transport/core/name.h> #include <hicn/transport/interfaces/portal.h> #include <hicn/transport/portability/portability.h> - #include <utils/deadline_timer.h> -#include <asio/steady_timer.hpp> - namespace transport { namespace core { @@ -34,83 +32,70 @@ class HicnForwarderInterface; class VPPForwarderInterface; class RawSocketInterface; -template <typename ForwarderInt> class Portal; using OnContentObjectCallback = interface::Portal::OnContentObjectCallback; using OnInterestTimeoutCallback = interface::Portal::OnInterestTimeoutCallback; class PendingInterest { - friend class Portal<HicnForwarderInterface>; - friend class Portal<VPPForwarderInterface>; - friend class Portal<RawSocketInterface>; + friend class Portal; public: using Ptr = utils::ObjectPool<PendingInterest>::Ptr; - PendingInterest() - : interest_(nullptr, nullptr), - timer_(), - 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)), - on_content_object_callback_(), - on_interest_timeout_callback_() {} - - PendingInterest(Interest::Ptr &&interest, + + PendingInterest(asio::io_service &io_service, const Interest::Ptr &interest) + : interest_(interest), timer_(io_service) {} + + 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)) {} ~PendingInterest() = default; template <typename Handler> - TRANSPORT_ALWAYS_INLINE void startCountdown(Handler &&cb) { - timer_->expires_from_now( - std::chrono::milliseconds(interest_->getLifetime())); - timer_->async_wait(std::forward<Handler &&>(cb)); + void startCountdown(uint32_t lifetime, Handler &&cb) { + timer_.expires_from_now(std::chrono::milliseconds(lifetime)); + timer_.async_wait(std::forward<Handler>(cb)); } - TRANSPORT_ALWAYS_INLINE void cancelTimer() { timer_->cancel(); } - - TRANSPORT_ALWAYS_INLINE Interest::Ptr &&getInterest() { - return std::move(interest_); + void cancelTimer() { + try { + timer_.cancel(); + } catch (asio::system_error &e) { + // do nothing + } } - TRANSPORT_ALWAYS_INLINE void setInterest(Interest::Ptr &&interest) { - interest_ = std::move(interest); - } + Interest::Ptr &&getInterest() { return std::move(interest_); } + + const Interest::Ptr &getInterestReference() const { return interest_; } + + void setInterest(const Interest::Ptr &interest) { interest_ = interest; } - TRANSPORT_ALWAYS_INLINE const OnContentObjectCallback &getOnDataCallback() - const { + const OnContentObjectCallback &getOnDataCallback() const { return on_content_object_callback_; } - TRANSPORT_ALWAYS_INLINE void setOnContentObjectCallback( - OnContentObjectCallback &&on_content_object) { - PendingInterest::on_content_object_callback_ = on_content_object; + void setOnContentObjectCallback(OnContentObjectCallback &&on_content_object) { + PendingInterest::on_content_object_callback_ = std::move(on_content_object); } - TRANSPORT_ALWAYS_INLINE const OnInterestTimeoutCallback & - getOnTimeoutCallback() const { + const OnInterestTimeoutCallback &getOnTimeoutCallback() const { return on_interest_timeout_callback_; } - TRANSPORT_ALWAYS_INLINE void setOnTimeoutCallback( - OnInterestTimeoutCallback &&on_interest_timeout) { - PendingInterest::on_interest_timeout_callback_ = on_interest_timeout; + void setOnTimeoutCallback(OnInterestTimeoutCallback &&on_interest_timeout) { + PendingInterest::on_interest_timeout_callback_ = + std::move(on_interest_timeout); } 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 new file mode 100644 index 000000000..72b6a6f37 --- /dev/null +++ b/libtransport/src/core/portal.cc @@ -0,0 +1,152 @@ +/* + * 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/global_configuration.h> +#include <core/portal.h> +#include <hicn/transport/interfaces/global_conf_interface.h> +#include <hicn/transport/portability/platform.h> +#include <hicn/transport/utils/file.h> + +#include <libconfig.h++> + +using namespace transport::interface::global_config; + +namespace transport { +namespace core { + +#ifdef ANDROID +static const constexpr char default_module[] = ""; +#elif defined(MACINTOSH) +static const constexpr char default_module[] = "hicnlight_module.dylib"; +#elif defined(LINUX) +static const constexpr char default_module[] = "hicnlight_module.so"; +#elif defined(WINDOWS) +static const constexpr char default_module[] = "hicnlight_module.lib"; +#endif + +IoModuleConfiguration Portal::conf_; +std::string Portal::io_module_path_ = defaultIoModule(); + +std::string Portal::defaultIoModule() { + using namespace std::placeholders; + GlobalConfiguration::getInstance().registerConfigurationParser( + IoModuleConfiguration::section, + std::bind(&Portal::parseIoModuleConfiguration, _1, _2)); + GlobalConfiguration::getInstance().registerConfigurationGetter( + IoModuleConfiguration::section, + std::bind(&Portal::getModuleConfiguration, _1, _2)); + GlobalConfiguration::getInstance().registerConfigurationSetter( + IoModuleConfiguration::section, + std::bind(&Portal::setModuleConfiguration, _1, _2)); + + // return default + conf_.name = default_module; + return default_module; +} + +void Portal::getModuleConfiguration(ConfigurationObject& object, + std::error_code& ec) { + DCHECK(object.getKey() == IoModuleConfiguration::section); + + auto conf = dynamic_cast<const IoModuleConfiguration&>(object); + conf = conf_; + ec = std::error_code(); +} + +std::string getIoModulePath(const std::string& name, + const std::vector<std::string>& paths, + std::error_code& ec) { +#ifdef LINUX + std::string extension = ".so"; +#elif defined(MACINTOSH) + std::string extension = ".dylib"; +#elif defined(WINDOWS) + std::string extension = ".lib"; +#else +#error "Platform not supported."; +#endif + + std::string complete_path = name; + + if (name.empty()) { + ec = make_error_code(core_error::configuration_parse_failed); + return ""; + } + + complete_path += extension; + + for (auto& p : paths) { + if (p.at(0) != '/') { + LOG(WARNING) << "Path " << p << " is not an absolute path. Ignoring it."; + continue; + } + + if (utils::File::exists(p + "/" + complete_path)) { + complete_path = p + "/" + complete_path; + break; + } + } + + return complete_path; +} + +void Portal::setModuleConfiguration(const ConfigurationObject& object, + std::error_code& ec) { + DCHECK(object.getKey() == IoModuleConfiguration::section); + + const IoModuleConfiguration& conf = + dynamic_cast<const IoModuleConfiguration&>(object); + auto path = getIoModulePath(conf.name, conf.search_path, ec); + if (!ec) { + conf_ = conf; + io_module_path_ = path; + } +} + +void Portal::parseIoModuleConfiguration(const libconfig::Setting& io_config, + std::error_code& ec) { + using namespace libconfig; + // path property: the list of paths where to look for the module. + std::vector<std::string> paths; + std::string name; + + if (io_config.exists("path")) { + // get path where looking for modules + const Setting& path_list = io_config.lookup("path"); + auto count = path_list.getLength(); + + for (int i = 0; i < count; i++) { + paths.emplace_back(path_list[i].c_str()); + } + } + + if (io_config.exists("name")) { + io_config.lookupValue("name", name); + } else { + ec = make_error_code(core_error::configuration_parse_failed); + return; + } + + auto path = getIoModulePath(name, paths, ec); + if (!ec) { + conf_.name = name; + conf_.search_path = paths; + io_module_path_ = path; + } +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/core/portal.h b/libtransport/src/core/portal.h index 364a36577..c4ba096b9 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-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: @@ -15,55 +15,53 @@ #pragma once -#include <core/forwarder_interface.h> +#include <core/global_workers.h> #include <core/pending_interest.h> -#include <core/udp_socket_connector.h> +#include <glog/logging.h> #include <hicn/transport/config.h> +#include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> +#include <hicn/transport/core/io_module.h> #include <hicn/transport/core/name.h> #include <hicn/transport/core/prefix.h> #include <hicn/transport/errors/errors.h> +#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 <hicn/transport/utils/log.h> -#ifdef __vpp__ -#include <core/memif_connector.h> -#endif - -#include <asio.hpp> -#include <asio/steady_timer.hpp> #include <future> #include <memory> #include <queue> #include <unordered_map> +namespace libconfig { +class Setting; +} + namespace transport { namespace core { namespace portal_details { -static constexpr uint32_t pool_size = 2048; +static constexpr uint32_t pit_size = 1024; class HandlerMemory { #ifdef __vpp__ - static constexpr std::size_t memory_size = 1024 * 1024; - public: HandlerMemory() {} HandlerMemory(const HandlerMemory &) = delete; HandlerMemory &operator=(const HandlerMemory &) = delete; - TRANSPORT_ALWAYS_INLINE void *allocate(std::size_t size) { - return utils::FixedBlockAllocator<128, 4096>::getInstance() - ->allocateBlock(); + void *allocate(std::size_t /* size */) { + return utils::FixedBlockAllocator<128, 8192>::getInstance().allocateBlock(); } - TRANSPORT_ALWAYS_INLINE void deallocate(void *pointer) { - utils::FixedBlockAllocator<128, 4096>::getInstance()->deallocateBlock( + void deallocate(void *pointer) { + utils::FixedBlockAllocator<128, 8192>::getInstance().deallocateBlock( pointer); } #else @@ -73,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 }; @@ -96,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); } @@ -139,7 +131,7 @@ class CustomAllocatorHandler { } template <typename... Args> - void operator()(Args &&... args) { + void operator()(Args &&...args) { handler_(std::forward<Args>(args)...); } @@ -155,86 +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(); - increaseInterestPool(); - increaseContentObjectPool(); - } - - TRANSPORT_ALWAYS_INLINE void increasePendingInterestPool() { - // Create pool of pending interests to reuse - for (uint32_t i = 0; i < pool_size; i++) { - pending_interests_pool_.add(new PendingInterest( - Interest::Ptr(nullptr), - std::make_unique<asio::steady_timer>(io_service_))); - } - } - - TRANSPORT_ALWAYS_INLINE void increaseInterestPool() { - // Create pool of interests to reuse - for (uint32_t i = 0; i < pool_size; i++) { - interest_pool_.add(new Interest()); - } - } - - TRANSPORT_ALWAYS_INLINE void increaseContentObjectPool() { - // Create pool of content object to reuse - for (uint32_t i = 0; i < pool_size; i++) { - content_object_pool_.add(new ContentObject()); - } - } - - 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); - } - - TRANSPORT_ALWAYS_INLINE ContentObject::Ptr getContentObject() { - auto res = content_object_pool_.get(); - while (TRANSPORT_EXPECT_FALSE(!res.first)) { - increaseContentObjectPool(); - res = content_object_pool_.get(); - } - - return std::move(res.second); - } - - TRANSPORT_ALWAYS_INLINE Interest::Ptr getInterest() { - auto res = interest_pool_.get(); - while (TRANSPORT_EXPECT_FALSE(!res.first)) { - increaseInterestPool(); - res = interest_pool_.get(); - } - - return std::move(res.second); - } - - private: - utils::ObjectPool<PendingInterest> pending_interests_pool_; - utils::ObjectPool<ContentObject> content_object_pool_; - utils::ObjectPool<Interest> interest_pool_; - asio::io_service &io_service_; -}; - } // namespace portal_details -using PendingInterestHashTable = - std::unordered_map<uint32_t, PendingInterest::Ptr>; +class PortalConfiguration; -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 @@ -250,72 +172,118 @@ using interface::BindConfig; * The portal class is not thread safe, appropriate locking is required by the * users of this class. */ -template <typename ForwarderInt> -class Portal { - static_assert( - std::is_base_of<ForwarderInterface<ForwarderInt, - typename ForwarderInt::ConnectorType>, - ForwarderInt>::value, - "ForwarderInt must inherit from ForwarderInterface!"); + +class Portal : public ::utils::NonCopyable, + public std::enable_shared_from_this<Portal> { + private: + Portal() : Portal(GlobalWorkers::getInstance().getWorker()) {} + + Portal(::utils::EventThread &worker) + : io_module_(nullptr), + worker_(worker), + app_name_("libtransport_application"), + transport_callback_(nullptr), + is_consumer_(false) {} public: - using ConsumerCallback = interface::Portal::ConsumerCallback; - using ProducerCallback = interface::Portal::ProducerCallback; + using TransportCallback = interface::Portal::TransportCallback; + friend class PortalConfiguration; - Portal() : Portal(internal_io_service_) {} + static std::shared_ptr<Portal> createShared() { + return std::shared_ptr<Portal>(new Portal()); + } - Portal(asio::io_service &io_service) - : io_service_(io_service), - packet_pool_(io_service), - app_name_("libtransport_application"), - consumer_callback_(nullptr), - producer_callback_(nullptr), - connector_(std::bind(&Portal::processIncomingMessages, this, - std::placeholders::_1), - std::bind(&Portal::setLocalRoutes, this), io_service_, - app_name_), - forwarder_interface_(connector_) {} + 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) { - forwarder_interface_.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) { - pending_interest_hash_table_.reserve(portal_details::pool_size); - forwarder_interface_.connect(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]([[maybe_unused]] Connector *c) { /* Nothing to do here */ }, + [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; + } + }); } /** @@ -328,9 +296,10 @@ class Portal { * * @param name - The interest name. */ - TRANSPORT_ALWAYS_INLINE bool interestIsPending(const Name &name) { - auto it = - pending_interest_hash_table_.find(name.getHash32() + name.getSuffix()); + 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; } @@ -339,49 +308,117 @@ class Portal { } /** + * @brief Add interest to PIT + * + */ + void addInterestToPIT( + const Interest::Ptr &interest, uint32_t lifetime, + OnContentObjectCallback &&on_content_object_callback = UNSET_CALLBACK, + OnInterestTimeoutCallback &&on_interest_timeout_callback = + UNSET_CALLBACK) { + uint32_t initial_hash = interest->getName().getHash32(false); + auto hash = initial_hash + interest->getName().getSuffix(); + uint32_t seq = interest->getName().getSuffix(); + const uint32_t *suffix = interest->firstSuffix() != nullptr + ? interest->firstSuffix() + 1 + : nullptr; + auto n_suffixes = + interest->numberOfSuffixes() > 0 ? interest->numberOfSuffixes() - 1 : 0; + uint32_t counter = 0; + // Set timers + do { + auto pend_int = pending_interest_hash_table_.try_emplace( + hash, worker_.getIoService(), interest); + PendingInterest &pending_interest = pend_int.first->second; + if (!pend_int.second) { + // element was already in map + pend_int.first->second.cancelTimer(); + pending_interest.setInterest(interest); + } + + pending_interest.setOnContentObjectCallback( + std::move(on_content_object_callback)); + pending_interest.setOnTimeoutCallback( + std::move(on_interest_timeout_callback)); + + if (is_consumer_) { + auto self = weak_from_this(); + pending_interest.startCountdown( + lifetime, portal_details::makeCustomAllocatorHandler( + async_callback_memory_, + [self, hash, seq](const std::error_code &ec) { + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + return; + } + + if (auto ptr = self.lock()) { + ptr->timerHandler(hash, seq); + } + })); + } + + if (suffix) { + hash = initial_hash + *suffix; + seq = *suffix; + suffix++; + } + } while (counter++ < n_suffixes); + } + + void matchContentObjectInPIT(ContentObject &content_object) { + uint32_t hash = getHash(content_object.getName()); + auto it = pending_interest_hash_table_.find(hash); + if (it != pending_interest_hash_table_.end()) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Found pending interest."; + + PendingInterest &pend_interest = it->second; + pend_interest.cancelTimer(); + auto _int = pend_interest.getInterest(); + auto callback = pend_interest.getOnDataCallback(); + pending_interest_hash_table_.erase(it); + + if (is_consumer_) { + // Send object is for the app + if (callback != UNSET_CALLBACK) { + callback(*_int, content_object); + } else if (transport_callback_) { + transport_callback_->onContentObject(*_int, content_object); + } + } else { + // Send content object to the network + io_module_->send(content_object); + } + } else if (is_consumer_) { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "No interest pending for received content object."; + } + } + + /** * Send an interest through to the local forwarder. * * @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( - Interest::Ptr &&interest, + void sendInterest( + Interest::Ptr &interest, uint32_t lifetime, OnContentObjectCallback &&on_content_object_callback = UNSET_CALLBACK, OnInterestTimeoutCallback &&on_interest_timeout_callback = UNSET_CALLBACK) { - uint32_t hash = - interest->getName().getHash32() + interest->getName().getSuffix(); - // Send it - forwarder_interface_.send(*interest); - - auto pending_interest = packet_pool_.getPendingInterest(); - pending_interest->setInterest(std::move(interest)); - pending_interest->setOnContentObjectCallback( - std::move(on_content_object_callback)); - pending_interest->setOnTimeoutCallback( - std::move(on_interest_timeout_callback)); - pending_interest->startCountdown(portal_details::makeCustomAllocatorHandler( - async_callback_memory_, std::bind(&Portal<ForwarderInt>::timerHandler, - this, std::placeholders::_1, hash))); + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); - 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); - } + addInterestToPIT(interest, lifetime, std::move(on_content_object_callback), + std::move(on_interest_timeout_callback)); + interest->serializeSuffixes(); + io_module_->send(*interest); } /** @@ -390,178 +427,196 @@ 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) { - 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(); - - if (ptr->getOnTimeoutCallback() != UNSET_CALLBACK) { - ptr->on_interest_timeout_callback_(std::move(_int)); - } else if (consumer_callback_) { - consumer_callback_->onTimeout(std::move(_int)); - } + 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. + * Send a data packet to the local forwarder. * - * @param config - The configuration for the local forwarder binding. + * @param content_object - The data packet. */ - TRANSPORT_ALWAYS_INLINE void bind(const BindConfig &config) { - forwarder_interface_.setContentStoreSize(config.csReserved()); - served_namespaces_.push_back(config.prefix()); - - setLocalRoutes(); + void sendContentObject(ContentObject &content_object) { + DCHECK(io_module_); + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + matchContentObjectInPIT(content_object); } /** - * Start the event loop. This function blocks here and calls the callback set - * by the application upon interest/data received or timeout. + * Disconnect the transport from the local forwarder. */ - TRANSPORT_ALWAYS_INLINE void runEventsLoop() { - if (io_service_.stopped()) { - io_service_.reset(); // ensure that run()/poll() will do some work + void killConnection() { + if (TRANSPORT_EXPECT_TRUE(io_module_ != nullptr)) { + io_module_->closeConnection(); } - - io_service_.run(); } /** - * Run one event and return. + * Clear the pending interest hash table. */ - TRANSPORT_ALWAYS_INLINE void runOneEvent() { - if (io_service_.stopped()) { - io_service_.reset(); // ensure that run()/poll() will do some work - } - - io_service_.run_one(); + void clear() { + worker_.tryRunHandlerNow([self{shared_from_this()}]() { self->doClear(); }); } /** - * 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. + * Get a reference to the io_service object. */ - TRANSPORT_ALWAYS_INLINE void sendContentObject( - ContentObject &content_object) { - forwarder_interface_.send(content_object); - } + utils::EventThread &getThread() { return worker_; } /** - * 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. + * Register a route to the local forwarder. */ - TRANSPORT_ALWAYS_INLINE void stopEventsLoop() { - if (!io_service_.stopped()) { - io_service_.dispatch([this]() { - clear(); - io_service_.stop(); - }); - } + 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); + } + } + }); } /** - * Disconnect the transport from the local forwarder. + * Send a MAP-Me update to traverse NATs. */ - TRANSPORT_ALWAYS_INLINE void killConnection() { - forwarder_interface_.closeConnection(); + TRANSPORT_ALWAYS_INLINE void sendMapme() { + if (io_module_->isConnected()) { + io_module_->sendMapme(); + } } /** - * Clear the pending interest hash table. + * set forwarding strategy */ - TRANSPORT_ALWAYS_INLINE void clear() { - if (!io_service_.stopped()) { - io_service_.dispatch(std::bind(&Portal::doClear, this)); - } else { - doClear(); + TRANSPORT_ALWAYS_INLINE void setForwardingStrategy(Prefix &prefix, + std::string &strategy) { + if (io_module_->isConnected()) { + io_module_->setForwardingStrategy(prefix, strategy); } } /** - * Get a reference to the io_service object. + * Check if the transport is connected to a forwarder or not */ - TRANSPORT_ALWAYS_INLINE asio::io_service &getIoService() { - return io_service_; + 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: /** - * Register a route to the local forwarder. + * Compute name hash */ - TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { - served_namespaces_.push_back(prefix); - if (connector_.isConnected()) { - forwarder_interface_.registerRoute(prefix); - } + uint32_t getHash(const Name &name) { + return name.getHash32(false) + name.getSuffix(); } - private: /** * Clear the pending interest hash table. */ - TRANSPORT_ALWAYS_INLINE void doClear() { + 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(); + pend_interest.second.cancelTimer(); } pending_interest_hash_table_.clear(); } + void dumpPIT() { + std::vector<Name> sorted_elements; + for (const auto &[key, value] : pending_interest_hash_table_) { + sorted_elements.push_back(value.getInterestReference()->getName()); + } + + std::sort(sorted_elements.begin(), sorted_elements.end(), + [](const Name &a, const Name &b) { + return a.getSuffix() < b.getSuffix(); + }); + + for (auto &elt : sorted_elements) { + LOG(INFO) << elt; + } + } + /** - * 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( - Packet::MemBufPtr &&packet_buffer) { - 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( - ForwarderInt::isControlMessage(packet_buffer->data()))) { - processControlMessage(std::move(packet_buffer)); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + // Error receiving from underlying infra. + if (transport_callback_) { + transport_callback_->onError(ec); + } + return; } - Packet::Format format = Packet::getFormatFromBuffer(packet_buffer->data()); + for (auto &buffer_ptr : buffers) { + auto &buffer = *buffer_ptr; - if (TRANSPORT_EXPECT_TRUE(_is_tcp(format))) { - if (!Packet::isInterest(packet_buffer->data())) { - auto content_object = packet_pool_.getContentObject(); - content_object->replace(std::move(packet_buffer)); - processContentObject(std::move(content_object)); - } else { - auto interest = packet_pool_.getInterest(); - interest->replace(std::move(packet_buffer)); - processInterest(std::move(interest)); + if (TRANSPORT_EXPECT_FALSE(io_module_->isControlMessage(buffer))) { + processControlMessage(buffer); + return; + } + + // The buffer is a base class for an interest or a content object + Packet &packet_buffer = static_cast<Packet &>(buffer); + + switch (packet_buffer.getType()) { + case HICN_PACKET_TYPE_INTEREST: + if (!is_consumer_) { + processInterest(static_cast<Interest &>(packet_buffer)); + } else { + LOG(ERROR) << "Received an Interest packet with name " + << packet_buffer.getName() + << " in a consumer transport. Ignoring it."; + } + break; + case HICN_PACKET_TYPE_DATA: + if (is_consumer_) { + processContentObject(static_cast<ContentObject &>(packet_buffer)); + } else { + LOG(ERROR) << "Received a Data packet with name " + << packet_buffer.getName() + << " in a producer transport. Ignoring it."; + } + break; + default: + LOG(ERROR) << "Received not supported packet. Ignoring it."; + break; } - } else { - TRANSPORT_LOGE("Received not supported packet. Ignoring it."); } } @@ -570,18 +625,25 @@ 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 (connector_.isConnected()) { - forwarder_interface_.registerRoute(prefix); - } + io_module_->registerRoute(prefix); } } - TRANSPORT_ALWAYS_INLINE void processInterest(Interest::Ptr &&interest) { + void processInterest(Interest &interest) { // Interest for a producer - if (TRANSPORT_EXPECT_TRUE(producer_callback_ != nullptr)) { - producer_callback_->onInterest(std::move(interest)); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "processInterest " << interest.getName(); + + // Save interest in PIT + interest.deserializeSuffixes(); + addInterestToPIT(interest.shared_from_this(), interest.getLifetime()); + if (TRANSPORT_EXPECT_TRUE(transport_callback_ != nullptr)) { + transport_callback_->onInterest(interest); } } @@ -593,55 +655,47 @@ class Portal { * * @param content_object - The data packet */ - TRANSPORT_ALWAYS_INLINE void processContentObject( - ContentObject::Ptr &&content_object) { - uint32_t hash = content_object->getName().getHash32() + - content_object->getName().getSuffix(); - - auto it = pending_interest_hash_table_.find(hash); - if (it != pending_interest_hash_table_.end()) { - PendingInterest::Ptr interest_ptr = std::move(it->second); - 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_(std::move(_int), - std::move(content_object)); - } else if (consumer_callback_) { - consumer_callback_->onContentObject(std::move(_int), - std::move(content_object)); - } - } + void processContentObject(ContentObject &content_object) { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "processContentObject " << content_object.getName(); + matchContentObjectInPIT(content_object); } /** - * 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( - Packet::MemBufPtr &&packet_buffer) { - forwarder_interface_.processControlMessageReply(std::move(packet_buffer)); + void processControlMessage(utils::MemBuf &packet_buffer) { + io_module_->processControlMessageReply(packet_buffer); } private: portal_details::HandlerMemory async_callback_memory_; + std::unique_ptr<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_; - typename ForwarderInt::ConnectorType connector_; - ForwarderInt forwarder_interface_; + bool is_consumer_; + + private: + static std::string defaultIoModule(); + static void parseIoModuleConfiguration(const libconfig::Setting &io_config, + std::error_code &ec); + static void getModuleConfiguration( + interface::global_config::ConfigurationObject &conf, std::error_code &ec); + static void setModuleConfiguration( + const interface::global_config::ConfigurationObject &conf, + std::error_code &ec); + static interface::global_config::IoModuleConfiguration conf_; + static std::string io_module_path_; }; } // namespace core diff --git a/libtransport/src/core/prefix.cc b/libtransport/src/core/prefix.cc index eea4aeb8b..8c5b153bc 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-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,8 +13,10 @@ * limitations under the License. */ +#include <glog/logging.h> #include <hicn/transport/core/prefix.h> #include <hicn/transport/errors/errors.h> +#include <hicn/transport/portability/endianess.h> #include <hicn/transport/utils/string_tokenizer.h> #ifndef _WIN32 @@ -25,21 +27,17 @@ extern "C" { #include <hicn/transport/portability/win_portability.h> #endif +#include <openssl/rand.h> + #include <cstring> #include <memory> #include <random> -#include <openssl/rand.h> - namespace transport { namespace core { -Prefix::Prefix() { std::memset(&ip_prefix_, 0, sizeof(ip_prefix_t)); } - -Prefix::Prefix(const char *prefix) : Prefix(std::string(prefix)) {} - -Prefix::Prefix(std::string &&prefix) : Prefix(prefix) {} +Prefix::Prefix() { std::memset(&hicn_ip_prefix_, 0, sizeof(hicn_ip_prefix_t)); } Prefix::Prefix(const std::string &prefix) { utils::StringTokenizer st(prefix, "/"); @@ -56,7 +54,7 @@ Prefix::Prefix(const std::string &prefix) { buildPrefix(ip_address, uint16_t(atoi(prefix_length.c_str())), family); } -Prefix::Prefix(std::string &prefix, uint16_t prefix_length) { +Prefix::Prefix(const std::string &prefix, uint16_t prefix_length) { int family = get_addr_family(prefix.c_str()); buildPrefix(prefix, prefix_length, family); } @@ -68,24 +66,28 @@ Prefix::Prefix(const core::Name &content_name, uint16_t prefix_length) { throw errors::InvalidIpAddressException(); } - ip_prefix_ = content_name.toIpAddress(); - ip_prefix_.len = prefix_length; - ip_prefix_.family = family; + hicn_ip_prefix_ = content_name.toIpAddress(); + hicn_ip_prefix_.len = (u8)prefix_length; + hicn_ip_prefix_.family = family; } -void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, +void Prefix::buildPrefix(const std::string &prefix, uint16_t prefix_length, int family) { if (!checkPrefixLengthAndAddressFamily(prefix_length, family)) { throw errors::InvalidIpAddressException(); } + std::memset(&hicn_ip_prefix_, 0, sizeof(hicn_ip_prefix_t)); + int ret; switch (family) { case AF_INET: - ret = inet_pton(AF_INET, prefix.c_str(), ip_prefix_.address.v4.buffer); + ret = + inet_pton(AF_INET, prefix.c_str(), hicn_ip_prefix_.address.v4.buffer); break; case AF_INET6: - ret = inet_pton(AF_INET6, prefix.c_str(), ip_prefix_.address.v6.buffer); + ret = inet_pton(AF_INET6, prefix.c_str(), + hicn_ip_prefix_.address.v6.buffer); break; default: throw errors::InvalidIpAddressException(); @@ -95,14 +97,22 @@ void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, throw errors::InvalidIpAddressException(); } - ip_prefix_.len = prefix_length; - ip_prefix_.family = family; + hicn_ip_prefix_.len = (u8)prefix_length; + hicn_ip_prefix_.family = family; } -std::unique_ptr<Sockaddr> Prefix::toSockaddr() { +bool Prefix::operator<(const Prefix &other) const { + return hicn_ip_prefix_cmp(&hicn_ip_prefix_, &other.hicn_ip_prefix_) < 0; +} + +bool Prefix::operator==(const Prefix &other) const { + return hicn_ip_prefix_cmp(&hicn_ip_prefix_, &other.hicn_ip_prefix_) == 0; +} + +std::unique_ptr<Sockaddr> Prefix::toSockaddr() const { Sockaddr *ret = nullptr; - switch (ip_prefix_.family) { + switch (hicn_ip_prefix_.family) { case AF_INET6: ret = (Sockaddr *)new Sockaddr6; break; @@ -113,72 +123,79 @@ std::unique_ptr<Sockaddr> Prefix::toSockaddr() { throw errors::InvalidIpAddressException(); } - if (ip_prefix_to_sockaddr(&ip_prefix_, ret) < 0) { + if (hicn_ip_prefix_to_sockaddr(&hicn_ip_prefix_, ret) < 0) { throw errors::InvalidIpAddressException(); } return std::unique_ptr<Sockaddr>(ret); } -uint16_t Prefix::getPrefixLength() { return ip_prefix_.len; } +uint16_t Prefix::getPrefixLength() const { return hicn_ip_prefix_.len; } Prefix &Prefix::setPrefixLength(uint16_t prefix_length) { - ip_prefix_.len = prefix_length; - return *this; -} - -int Prefix::getAddressFamily() { return ip_prefix_.family; } + if (!checkPrefixLengthAndAddressFamily(prefix_length, + hicn_ip_prefix_.family)) { + throw errors::InvalidIpAddressException(); + } -Prefix &Prefix::setAddressFamily(int address_family) { - ip_prefix_.family = address_family; + hicn_ip_prefix_.len = (u8)prefix_length; return *this; } +int Prefix::getAddressFamily() const { return hicn_ip_prefix_.family; } + std::string Prefix::getNetwork() const { - if (!checkPrefixLengthAndAddressFamily(ip_prefix_.len, ip_prefix_.family)) { + if (!checkPrefixLengthAndAddressFamily(hicn_ip_prefix_.len, + hicn_ip_prefix_.family)) { throw errors::InvalidIpAddressException(); } - std::size_t size = - ip_prefix_.family == 4 + AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; - - std::string network(size, 0); + char buffer[INET6_ADDRSTRLEN]; - if (ip_prefix_ntop_short(&ip_prefix_, (char *)network.c_str(), size) < 0) { + if (hicn_ip_prefix_ntop_short(&hicn_ip_prefix_, buffer, INET6_ADDRSTRLEN) < + 0) { throw errors::RuntimeException( "Impossible to retrieve network from ip address."); } - return network; + return buffer; } -int Prefix::contains(const ip_address_t &content_name) const { - int res = - ip_address_cmp(&content_name, &(ip_prefix_.address), ip_prefix_.family); - - if (ip_prefix_.len != (ip_prefix_.family == AF_INET6 ? IPV6_ADDR_LEN_BITS - : IPV4_ADDR_LEN_BITS)) { - const u8 *ip_prefix_buffer = - ip_address_get_buffer(&(ip_prefix_.address), ip_prefix_.family); - const u8 *content_name_buffer = - ip_address_get_buffer(&content_name, ip_prefix_.family); - uint8_t mask = 0xFF >> (ip_prefix_.len % 8); - mask = ~mask; - - res += (ip_prefix_buffer[ip_prefix_.len] & mask) == - (content_name_buffer[ip_prefix_.len] & mask); +bool Prefix::contains(const hicn_ip_address_t &content_name) const { + uint64_t mask[2] = {0, 0}; + auto content_name_copy = content_name; + auto network_copy = hicn_ip_prefix_.address; + + auto prefix_length = getPrefixLength(); + if (hicn_ip_prefix_.family == AF_INET) { + prefix_length += 3 * IPV4_ADDR_LEN_BITS; } - return res; -} + if (prefix_length == 0) { + mask[0] = mask[1] = 0; + } else if (prefix_length <= 64) { + mask[0] = portability::host_to_net((uint64_t)(~0) << (64 - prefix_length)); + mask[1] = 0; + } else if (prefix_length == 128) { + mask[0] = mask[1] = 0xffffffffffffffff; + } else { + prefix_length -= 64; + mask[0] = 0xffffffffffffffff; + mask[1] = portability::host_to_net((uint64_t)(~0) << (64 - prefix_length)); + } -int Prefix::contains(const core::Name &content_name) const { - return contains(content_name.toIpAddress().address); + // Apply mask + content_name_copy.v6.as_u64[0] &= mask[0]; + content_name_copy.v6.as_u64[1] &= mask[1]; + + network_copy.v6.as_u64[0] &= mask[0]; + network_copy.v6.as_u64[1] &= mask[1]; + + return hicn_ip_address_cmp(&network_copy, &content_name_copy) == 0; } -Name Prefix::getName() const { - std::string s(getNetwork()); - return Name(s); +bool Prefix::contains(const core::Name &content_name) const { + return contains(content_name.toIpAddress().address); } /* @@ -187,23 +204,25 @@ Name Prefix::getName() const { */ Name Prefix::getName(const core::Name &mask, const core::Name &components, const core::Name &content_name) const { - if (ip_prefix_.family != mask.getAddressFamily() || - ip_prefix_.family != components.getAddressFamily() || - ip_prefix_.family != content_name.getAddressFamily()) + if (hicn_ip_prefix_.family != mask.getAddressFamily() || + hicn_ip_prefix_.family != components.getAddressFamily() || + hicn_ip_prefix_.family != content_name.getAddressFamily()) throw errors::RuntimeException( - "Prefix, mask, components and content name are not of the same address " - "family"); - - ip_address_t mask_ip = mask.toIpAddress().address; - ip_address_t component_ip = components.toIpAddress().address; - ip_address_t name_ip = content_name.toIpAddress().address; - const u8 *mask_ip_buffer = ip_address_get_buffer(&mask_ip, ip_prefix_.family); + "Prefix, mask, components and content name are not of the same" + "address family"); + + hicn_ip_address_t mask_ip = mask.toIpAddress().address; + hicn_ip_address_t component_ip = components.toIpAddress().address; + hicn_ip_address_t name_ip = content_name.toIpAddress().address; + const u8 *mask_ip_buffer = + hicn_ip_address_get_buffer(&mask_ip, hicn_ip_prefix_.family); const u8 *component_ip_buffer = - ip_address_get_buffer(&component_ip, ip_prefix_.family); - u8 *name_ip_buffer = - const_cast<u8 *>(ip_address_get_buffer(&name_ip, ip_prefix_.family)); + hicn_ip_address_get_buffer(&component_ip, hicn_ip_prefix_.family); + u8 *name_ip_buffer = const_cast<u8 *>( + hicn_ip_address_get_buffer(&name_ip, hicn_ip_prefix_.family)); - int addr_len = ip_prefix_.family == AF_INET6 ? IPV6_ADDR_LEN : IPV4_ADDR_LEN; + int addr_len = + hicn_ip_prefix_.family == AF_INET6 ? IPV6_ADDR_LEN : IPV4_ADDR_LEN; for (int i = 0; i < addr_len; i++) { if (mask_ip_buffer[i]) { @@ -211,108 +230,98 @@ 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); -} - -Name Prefix::getRandomName() const { - ip_address_t name_ip = ip_prefix_.address; - u8 *name_ip_buffer = - const_cast<u8 *>(ip_address_get_buffer(&name_ip, ip_prefix_.family)); - - int addr_len = - (ip_prefix_.family == AF_INET6 ? IPV6_ADDR_LEN * 8 : IPV4_ADDR_LEN * 8) - - ip_prefix_.len; - - size_t size = (size_t)ceil((float)addr_len / 8.0); - uint8_t *buffer = (uint8_t *) malloc(sizeof(uint8_t) * size); - - RAND_bytes(buffer, size); - - int j = 0; - for (uint8_t i = (uint8_t)ceil((float)ip_prefix_.len / 8.0); - i < (ip_prefix_.family == AF_INET6 ? IPV6_ADDR_LEN : IPV4_ADDR_LEN); - i++) { - name_ip_buffer[i] = buffer[j]; - j++; - } - free(buffer); - - return Name(ip_prefix_.family, (uint8_t *)&name_ip); + return Name(hicn_ip_prefix_.family, (uint8_t *)&name_ip); } /* * Map a name in a different name prefix to this name prefix */ Name Prefix::mapName(const core::Name &content_name) const { - if (ip_prefix_.family != content_name.getAddressFamily()) + if (hicn_ip_prefix_.family != content_name.getAddressFamily()) throw errors::RuntimeException( "Prefix content name are not of the same address " "family"); - ip_address_t name_ip = content_name.toIpAddress().address; - const u8 *ip_prefix_buffer = - ip_address_get_buffer(&(ip_prefix_.address), ip_prefix_.family); - u8 *name_ip_buffer = - const_cast<u8 *>(ip_address_get_buffer(&name_ip, ip_prefix_.family)); - - memcpy(name_ip_buffer, ip_prefix_buffer, ip_prefix_.len / 8); - - if (ip_prefix_.len != (ip_prefix_.family == AF_INET6 ? IPV6_ADDR_LEN_BITS - : IPV4_ADDR_LEN_BITS)) { - uint8_t mask = 0xFF >> (ip_prefix_.len % 8); - name_ip_buffer[ip_prefix_.len / 8 + 1] = - (name_ip_buffer[ip_prefix_.len / 8 + 1] & mask) | - (ip_prefix_buffer[ip_prefix_.len / 8 + 1] & ~mask); + hicn_ip_address_t name_ip = content_name.toIpAddress().address; + const u8 *hicn_ip_prefix_buffer = hicn_ip_address_get_buffer( + &(hicn_ip_prefix_.address), hicn_ip_prefix_.family); + u8 *name_ip_buffer = const_cast<u8 *>( + hicn_ip_address_get_buffer(&name_ip, hicn_ip_prefix_.family)); + + memcpy(name_ip_buffer, hicn_ip_prefix_buffer, hicn_ip_prefix_.len / 8); + + if (hicn_ip_prefix_.len != (hicn_ip_prefix_.family == AF_INET6 + ? IPV6_ADDR_LEN_BITS + : IPV4_ADDR_LEN_BITS)) { + uint8_t mask = 0xFF >> (hicn_ip_prefix_.len % 8); + name_ip_buffer[hicn_ip_prefix_.len / 8 + 1] = + (name_ip_buffer[hicn_ip_prefix_.len / 8 + 1] & mask) | + (hicn_ip_prefix_buffer[hicn_ip_prefix_.len / 8 + 1] & ~mask); } - return Name(ip_prefix_.family, (uint8_t *)&name_ip); + return Name(hicn_ip_prefix_.family, (uint8_t *)&name_ip); } -Prefix &Prefix::setNetwork(std::string &network) { - if (!inet_pton(AF_INET6, network.c_str(), ip_prefix_.address.v6.buffer)) { +Prefix &Prefix::setNetwork(const std::string &network) { + if (hicn_ip_address_pton(network.c_str(), &hicn_ip_prefix_.address) < 0) { throw errors::RuntimeException("The network name is not valid."); } return *this; } +Name Prefix::makeName() const { return makeNameWithIndex(0); } + 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( - 0, std::numeric_limits<uint32_t>::max()); - uint64_t random_number = idis(eng); - - uint32_t hash_size_bits = IPV6_ADDR_LEN_BITS - ip_prefix_.len; - uint64_t ip_address[2]; - memcpy(ip_address, ip_prefix_.address.v6.buffer, sizeof(uint64_t)); - memcpy(ip_address + 1, ip_prefix_.address.v6.buffer + 8, sizeof(uint64_t)); - std::string network(IPV6_ADDR_LEN * 3, 0); - - // Let's do the magic ;) - int shift_size = hash_size_bits > sizeof(random_number) * 8 - ? sizeof(random_number) * 8 - : hash_size_bits; - - ip_address[1] >>= shift_size; - ip_address[1] <<= shift_size; - - ip_address[1] |= random_number >> (sizeof(uint64_t) * 8 - shift_size); - - if (!inet_ntop(ip_prefix_.family, ip_address, (char *)network.c_str(), - IPV6_ADDR_LEN * 3)) { - throw errors::RuntimeException( - "Impossible to retrieve network from ip address."); - } + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution<uint32_t> idis( + 0, std::numeric_limits<uint32_t>::max()); + uint64_t random_number = idis(eng); + + return makeNameWithIndex(random_number); +} + +Name Prefix::makeNameWithIndex(std::uint64_t index) const { + uint16_t prefix_length = getPrefixLength(); - return Name(network); + Name ret; + + // Adjust prefix length depending on the address family + if (getAddressFamily() == AF_INET) { + // Sanity check + DCHECK(prefix_length <= 32); + // Convert prefix length to ip46_address_t prefix length + prefix_length += IPV4_ADDR_LEN_BITS * 3; + } + + std::memcpy(ret.getStructReference().prefix.v6.as_u8, + hicn_ip_prefix_.address.v6.as_u8, sizeof(hicn_ip_address_t)); + + // Convert index in network byte order + index = portability::host_to_net(index); + + // Apply mask + uint64_t mask; + if (prefix_length == 0) { + mask = 0; + } else if (prefix_length <= 64) { + mask = 0; + } else if (prefix_length == 128) { + mask = 0xffffffffffffffff; + } else { + prefix_length -= 64; + mask = portability::host_to_net((uint64_t)(~0) << (64 - prefix_length)); } - return Name(); + ret.getStructReference().prefix.v6.as_u64[1] &= mask; + // Eventually truncate index if too big + index &= ~mask; + + // Apply index + ret.getStructReference().prefix.v6.as_u64[1] |= index; + + // Done + return ret; } bool Prefix::checkPrefixLengthAndAddressFamily(uint16_t prefix_length, @@ -332,7 +341,9 @@ bool Prefix::checkPrefixLengthAndAddressFamily(uint16_t prefix_length, return true; } -ip_prefix_t &Prefix::toIpPrefixStruct() { return ip_prefix_; } +const hicn_ip_prefix_t &Prefix::toIpPrefixStruct() const { + return hicn_ip_prefix_; +} } // namespace core diff --git a/libtransport/src/core/raw_socket_connector.cc b/libtransport/src/core/raw_socket_connector.cc deleted file mode 100644 index 4d780959b..000000000 --- a/libtransport/src/core/raw_socket_connector.cc +++ /dev/null @@ -1,203 +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/utils/conversions.h> -#include <hicn/transport/utils/log.h> - -#include <core/raw_socket_connector.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 { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); - } - }); -} - -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 { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); - } - 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/core/raw_socket_connector.h b/libtransport/src/core/raw_socket_connector.h deleted file mode 100644 index 1d4e9cb39..000000000 --- a/libtransport/src/core/raw_socket_connector.h +++ /dev/null @@ -1,81 +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/name.h> - -#include <core/connector.h> - -#include <linux/if_packet.h> -#include <net/ethernet.h> -#include <sys/socket.h> -#include <asio.hpp> -#include <asio/steady_timer.hpp> -#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/core/raw_socket_interface.cc b/libtransport/src/core/raw_socket_interface.cc deleted file mode 100644 index 7ee2a844d..000000000 --- a/libtransport/src/core/raw_socket_interface.cc +++ /dev/null @@ -1,57 +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/utils/linux.h> - -#include <core/raw_socket_interface.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/core/raw_socket_interface.h b/libtransport/src/core/raw_socket_interface.h deleted file mode 100644 index c06d14637..000000000 --- a/libtransport/src/core/raw_socket_interface.h +++ /dev/null @@ -1,62 +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/core/prefix.h> - -#include <core/forwarder_interface.h> -#include <core/raw_socket_connector.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/core/tcp_socket_connector.cc b/libtransport/src/core/tcp_socket_connector.cc index 20b3d6ce6..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: @@ -18,8 +18,8 @@ #include <hicn/transport/portability/win_portability.h> #endif +#include <glog/logging.h> #include <hicn/transport/errors/errors.h> -#include <hicn/transport/utils/log.h> #include <hicn/transport/utils/object_pool.h> #include <thread> @@ -33,6 +33,8 @@ namespace { class NetworkMessage { public: static constexpr std::size_t fixed_header_length = 10; + static constexpr std::uint8_t ccnx_flag = 102; + static constexpr std::size_t ccnx_packet_length = 44; static std::size_t decodeHeader(const uint8_t *packet) { // General checks @@ -40,11 +42,12 @@ class NetworkMessage { uint8_t first_byte = packet[0]; uint8_t ip_format = (packet[0] & 0xf0) >> 4; - if (TRANSPORT_EXPECT_FALSE(first_byte == 102)) { + if (TRANSPORT_EXPECT_FALSE(first_byte == ccnx_flag)) { // Get packet length - return 44; + return ccnx_packet_length; } else if (TRANSPORT_EXPECT_TRUE(ip_format == 6 || ip_format == 4)) { - Packet::Format format = Packet::getFormatFromBuffer(packet); + Packet::Format format = + Packet::getFormatFromBuffer(packet, fixed_header_length); return Packet::getHeaderSizeFromBuffer(format, packet) + Packet::getPayloadSizeFromBuffer(format, packet); } @@ -82,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)); @@ -148,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()) { @@ -159,7 +164,7 @@ void TcpSocketConnector::doWrite() { // The connection has been closed by the application. return; } else { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + LOG(ERROR) << ec.value() << ":" << ec.message(); tryReconnect(); } }); @@ -169,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_)); @@ -179,7 +184,7 @@ void TcpSocketConnector::doReadBody(std::size_t body_length) { // The connection has been closed by the application. return; } else { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + LOG(ERROR) << ec.value() << " " << ec.message(); tryReconnect(); } }); @@ -192,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; @@ -200,23 +205,23 @@ void TcpSocketConnector::doReadHeader() { 0) { doReadBody(body_length - length); } else { - TRANSPORT_LOGE("Decoding error. Ignoring packet."); + LOG(ERROR) << "Decoding error. Ignoring packet."; } } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { // The connection has been closed by the application. return; } else { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + LOG(ERROR) << ec.value() << " " << ec.message(); tryReconnect(); } }); } void TcpSocketConnector::tryReconnect() { - if (state_ == ConnectorState::CONNECTED) { - TRANSPORT_LOGE("Connection lost. Trying to reconnect...\n"); - state_ = ConnectorState::CONNECTING; + 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()) { @@ -232,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; @@ -247,7 +252,7 @@ void TcpSocketConnector::doConnect() { if (is_reconnection_) { is_reconnection_ = false; - TRANSPORT_LOGI("Connection recovered!\n"); + LOG(INFO) << "Connection recovered!"; on_reconnect_callback_(); } } else { @@ -271,7 +276,7 @@ void TcpSocketConnector::handleDeadline(const std::error_code &ec) { if (!ec) { io_service_.post([this]() { socket_.close(); - TRANSPORT_LOGE("Error connecting. Is the forwarder running?\n"); + LOG(ERROR) << "Error connecting. Is the forwarder running?"; io_service_.stop(); }); } diff --git a/libtransport/src/core/tcp_socket_connector.h b/libtransport/src/core/tcp_socket_connector.h index c57123e9f..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: @@ -15,14 +15,12 @@ #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 <hicn/transport/utils/branch_prediction.h> -#include <core/connector.h> - -#include <asio.hpp> -#include <asio/steady_timer.hpp> #include <deque> namespace transport { diff --git a/libtransport/src/core/udp_connector.cc b/libtransport/src/core/udp_connector.cc new file mode 100644 index 000000000..5f620b1a8 --- /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, buffer]() { + bool write_in_progress = !self->output_buffer_.empty(); + self->output_buffer_.push_back(std::move(buffer)); + 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(std::move(array), [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) { // NOSONAR + 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(); + } + }); + } else { + sent_callback_(this, make_error_code(core_error::success)); + } +} + +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) { // NOSONAR + // 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/core/udp_connector.h b/libtransport/src/core/udp_connector.h new file mode 100644 index 000000000..002f4ca9f --- /dev/null +++ b/libtransport/src/core/udp_connector.h @@ -0,0 +1,145 @@ +/* + * 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 <iostream> +#include <memory> + +namespace transport { +namespace core { + +class UdpTunnelListener; + +class UdpTunnelConnector : public Connector { + friend class UdpTunnelListener; + + public: + template <typename ReceiveCallback, typename SentCallback, typename OnClose, + typename OnReconnect> + UdpTunnelConnector(asio::io_service &io_service, + ReceiveCallback &&receive_callback, + SentCallback &&packet_sent, OnClose &&on_close_callback, + OnReconnect &&on_reconnect) + : Connector(receive_callback, packet_sent, on_close_callback, + on_reconnect), + io_service_(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_{}, + tx_msgs_{}, + rx_iovecs_{}, + rx_msgs_{}, + current_position_(0), +#else + read_msg_(nullptr, 0), +#endif + data_available_(false) { + } + + template <typename ReceiveCallback, typename SentCallback, typename OnClose, + typename OnReconnect, typename EndpointType> + UdpTunnelConnector(std::shared_ptr<asio::ip::udp::socket> &socket, + std::shared_ptr<asio::io_service::strand> &strand, + ReceiveCallback &&receive_callback, + SentCallback &&packet_sent, OnClose &&on_close_callback, + OnReconnect &&on_reconnect, EndpointType &&remote_endpoint) + : Connector(receive_callback, packet_sent, on_close_callback, + on_reconnect), +#if ((ASIO_VERSION / 100 % 1000) < 12) + io_service_(socket->get_io_service()), +#else + io_service_((asio::io_context &)(socket->get_executor().context())), +#endif + socket_(socket), + resolver_(io_service_), + remote_endpoint_send_(std::forward<EndpointType>(remote_endpoint)), + timer_(io_service_), +#ifdef LINUX + send_timer_(io_service_), + tx_iovecs_{}, + tx_msgs_{}, + rx_iovecs_{}, + rx_msgs_{}, + current_position_(0), +#else + read_msg_(nullptr, 0), +#endif + data_available_(false) { + if (socket_->is_open()) { + state_ = State::CONNECTED; + remote_endpoint_ = Endpoint(remote_endpoint_send_); + local_endpoint_ = socket_->local_endpoint(); + } + } + + ~UdpTunnelConnector() override; + + void send(Packet &packet) override; + + void send(const utils::MemBuf::Ptr &buffer) override; + + void close() override; + + void connect(const std::string &hostname, std::uint16_t port, + const std::string &bind_address = "", + std::uint16_t bind_port = 0); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + void retryConnection(); + void doConnect(std::shared_ptr<UdpTunnelConnector> &self); + void doRecvPacket(); + + 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(const std::error_code &ec); + void writeHandler(); +#endif + + void setConnected() { state_ = State::CONNECTED; } + + void doSendPacket(const std::shared_ptr<UdpTunnelConnector> &self); + void doClose(); + + private: + asio::io_service &io_service_; + std::shared_ptr<asio::ip::udp::socket> socket_; + asio::ip::udp::resolver resolver_; + asio::ip::udp::resolver::iterator endpoint_iterator_; + asio::ip::udp::endpoint remote_endpoint_send_; + asio::ip::udp::endpoint remote_endpoint_recv_; + + asio::steady_timer timer_; + +#ifdef LINUX + asio::steady_timer send_timer_; + struct iovec tx_iovecs_[max_burst][8]; + struct mmsghdr tx_msgs_[max_burst]; + struct iovec rx_iovecs_[max_burst][8]; + struct mmsghdr rx_msgs_[max_burst]; + std::uint8_t current_position_; +#else + std::pair<uint8_t *, std::size_t> read_msg_; +#endif + + bool data_available_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/core/udp_listener.cc b/libtransport/src/core/udp_listener.cc new file mode 100644 index 000000000..caa97e0ee --- /dev/null +++ b/libtransport/src/core/udp_listener.cc @@ -0,0 +1,182 @@ +/* + * 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/portability/endianess.h> +#include <hicn/transport/utils/hash.h> + +#ifndef LINUX +namespace std { +size_t hash<asio::ip::udp::endpoint>::operator()( + const asio::ip::udp::endpoint &endpoint) const { + auto hash_ip = endpoint.address().is_v4() + ? endpoint.address().to_v4().to_ulong() + : utils::hash::fnv32_buf( + endpoint.address().to_v6().to_bytes().data(), 16); + uint16_t port = endpoint.port(); + return utils::hash::fnv32_buf(&port, 2, (unsigned int)hash_ip); +} +} // namespace std +#endif + +namespace transport { +namespace core { + +UdpTunnelListener::~UdpTunnelListener() {} + +void UdpTunnelListener::close() { + strand_->post([this]() { + if (socket_->is_open()) { + socket_->close(); + } + }); +} + +#ifdef LINUX +void UdpTunnelListener::readHandler(const std::error_code &ec) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; + + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (current_position_ == 0) { + for (int i = 0; i < Connector::max_burst; i++) { + auto read_buffer = Connector::getRawBuffer(); + iovecs_[i][0].iov_base = read_buffer.first; + iovecs_[i][0].iov_len = read_buffer.second; + msgs_[i].msg_hdr.msg_iov = iovecs_[i]; + msgs_[i].msg_hdr.msg_iovlen = 1; + msgs_[i].msg_hdr.msg_name = &remote_endpoints_[i]; + msgs_[i].msg_hdr.msg_namelen = sizeof(remote_endpoints_[i]); + } + } + + int res = recvmmsg(socket_->native_handle(), msgs_ + current_position_, + Connector::max_burst - current_position_, MSG_DONTWAIT, + nullptr); + if (res < 0) { + LOG(ERROR) << "Error in recvmmsg."; + return; + } + + for (int i = 0; i < res; i++) { + auto packet = Connector::getPacketFromBuffer( + reinterpret_cast<uint8_t *>( + msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), + msgs_[current_position_].msg_len); + auto connector_id = + utils::hash::fnv64_buf(msgs_[current_position_].msg_hdr.msg_name, + msgs_[current_position_].msg_hdr.msg_namelen); + + auto connector = connectors_.find(connector_id); + if (connector == connectors_.end()) { + // Create new connector corresponding to new client + + /* + * Get the remote endpoint for this particular message + */ + using namespace asio::ip; + if (local_endpoint_.address().is_v4()) { + auto addr = reinterpret_cast<struct sockaddr_in *>( + &remote_endpoints_[current_position_]); + address_v4::bytes_type address_bytes; + std::copy_n(reinterpret_cast<uint8_t *>(&addr->sin_addr), + address_bytes.size(), address_bytes.begin()); + address_v4 address(address_bytes); + remote_endpoint_ = + udp::endpoint(address, portability::net_to_host(addr->sin_port)); + } else { + auto addr = reinterpret_cast<struct sockaddr_in6 *>( + &remote_endpoints_[current_position_]); + address_v6::bytes_type address_bytes; + std::copy_n(reinterpret_cast<uint8_t *>(&addr->sin6_addr), + address_bytes.size(), address_bytes.begin()); + address_v6 address(address_bytes); + remote_endpoint_ = + udp::endpoint(address, portability::net_to_host(addr->sin6_port)); + } + + /** + * Create new connector sharing the same socket of this listener. + */ + auto ret = connectors_.emplace( + connector_id, + std::make_shared<UdpTunnelConnector>( + socket_, strand_, receive_callback_, + [](Connector *, const std::error_code &) {}, [](Connector *) {}, + [](Connector *, const std::error_code &) {}, + std::move(remote_endpoint_))); + connector = ret.first; + connector->second->setConnectorId(connector_id); + } + + /** + * Use connector callback to process incoming message. + */ + UdpTunnelConnector *c = + dynamic_cast<UdpTunnelConnector *>(connector->second.get()); + c->doRecvPacket(packet); + + ++current_position_; + } + + doRecvPacket(); + } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { + LOG(ERROR) << "The connection has been closed by the application."; + return; + } else { + LOG(ERROR) << ec.value() << " " << ec.message(); + } +} +#endif + +void UdpTunnelListener::doRecvPacket() { +#ifdef LINUX +#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(&UdpTunnelListener::readHandler, this, std::placeholders::_1)); +#else + read_msg_ = Connector::getRawBuffer(); + socket_->async_receive_from( + asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_, + [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 = + std::hash<asio::ip::udp::endpoint>{}(remote_endpoint_); + auto connector = connectors_.find(connector_id); + if (connector == connectors_.end()) { + // Create new connector corresponding to new client + auto ret = connectors_.emplace( + connector_id, std::make_shared<UdpTunnelConnector>( + socket_, strand_, receive_callback_, + [](Connector *, const std::error_code &) {}, + [](Connector *) {}, + [](Connector *, const std::error_code &) {}, + std::move(remote_endpoint_))); + connector = ret.first; + connector->second->setConnectorId(connector_id); + } + + UdpTunnelConnector *c = + dynamic_cast<UdpTunnelConnector *>(connector->second.get()); + c->doRecvPacket(packet); + doRecvPacket(); + } else if (ec.value() == + static_cast<int>(std::errc::operation_canceled)) { + LOG(ERROR) << "The connection has been closed by the application."; + return; + } else { + LOG(ERROR) << ec.value() << " " << ec.message(); + } + }); +#endif +} +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/udp_listener.h b/libtransport/src/core/udp_listener.h new file mode 100644 index 000000000..d8095a262 --- /dev/null +++ b/libtransport/src/core/udp_listener.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#pragma once + +#include <hicn/transport/core/asio_wrapper.h> +#include <hicn/transport/core/connector.h> +#include <hicn/transport/portability/platform.h> + +#include <unordered_map> + +namespace std { +template <> +struct hash<asio::ip::udp::endpoint> { + size_t operator()(const asio::ip::udp::endpoint &endpoint) const; +}; +} // namespace std + +namespace transport { +namespace core { + +class UdpTunnelListener + : public std::enable_shared_from_this<UdpTunnelListener> { + using PacketReceivedCallback = Connector::PacketReceivedCallback; + using EndpointId = std::pair<uint32_t, uint16_t>; + + static constexpr uint16_t default_port = 5004; + + public: + using Ptr = std::shared_ptr<UdpTunnelListener>; + + template <typename ReceiveCallback> + UdpTunnelListener(asio::io_service &io_service, + ReceiveCallback &&receive_callback, + asio::ip::udp::endpoint endpoint = asio::ip::udp::endpoint( + asio::ip::udp::v4(), default_port)) + : io_service_(io_service), + strand_(std::make_shared<asio::io_service::strand>(io_service_)), + socket_(std::make_shared<asio::ip::udp::socket>(io_service_, + endpoint.protocol())), + local_endpoint_(endpoint), + receive_callback_(std::forward<ReceiveCallback>(receive_callback)), +#ifndef LINUX + read_msg_(nullptr, 0) +#else + iovecs_{}, + msgs_{}, + current_position_(0) +#endif + { + if (endpoint.protocol() == asio::ip::udp::v6()) { + std::error_code ec; + socket_->set_option(asio::ip::v6_only(false), ec); + // Call succeeds only on dual stack systems. + } + socket_->bind(local_endpoint_); + io_service_.post(std::bind(&UdpTunnelListener::doRecvPacket, this)); + } + + ~UdpTunnelListener(); + + void close(); + + int deleteConnector(Connector *connector) { + return (int)connectors_.erase(connector->getConnectorId()); + } + + template <typename ReceiveCallback> + void setReceiveCallback(ReceiveCallback &&callback) { + receive_callback_ = std::forward<ReceiveCallback>(callback); + } + + Connector *findConnector(Connector::Id connId) { + auto it = connectors_.find(connId); + if (it != connectors_.end()) { + return it->second.get(); + } + + return nullptr; + } + + private: + void doRecvPacket(); + + void readHandler(const std::error_code &ec); + + asio::io_service &io_service_; + std::shared_ptr<asio::io_service::strand> strand_; + std::shared_ptr<asio::ip::udp::socket> socket_; + asio::ip::udp::endpoint local_endpoint_; + asio::ip::udp::endpoint remote_endpoint_; + std::unordered_map<Connector::Id, std::shared_ptr<Connector>> connectors_; + + PacketReceivedCallback receive_callback_; + +#ifdef LINUX + struct iovec iovecs_[Connector::max_burst][8]; + struct mmsghdr msgs_[Connector::max_burst]; + struct sockaddr_storage remote_endpoints_[Connector::max_burst]; + std::uint8_t current_position_; +#else + std::pair<uint8_t *, std::size_t> read_msg_; +#endif +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/core/udp_socket_connector.cc b/libtransport/src/core/udp_socket_connector.cc deleted file mode 100644 index f5ddd6270..000000000 --- a/libtransport/src/core/udp_socket_connector.cc +++ /dev/null @@ -1,224 +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 <hicn/transport/errors/errors.h> -#include <hicn/transport/utils/log.h> -#include <hicn/transport/utils/object_pool.h> - -#include <core/udp_socket_connector.h> - -#include <thread> -#include <vector> - -namespace transport { - -namespace core { - -UdpSocketConnector::UdpSocketConnector( - 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_), - resolver_(io_service_), - connection_timer_(io_service_), - read_msg_(packet_pool_.makePtr(nullptr)), - 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_ = ConnectorState::CONNECTING; - doConnect(); -} - -void UdpSocketConnector::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) { - try { - socket_.send(asio::buffer(packet, len)); - } catch (std::system_error &err) { - TRANSPORT_LOGE( - "Sending of disconnect message to forwarder failed. Reason: %s", - err.what()); - } - } - } -} - -void UdpSocketConnector::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) { - 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_ != ConnectorState::CLOSED) { - state_ = ConnectorState::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 { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); - tryReconnect(); - } - }); -} - -void UdpSocketConnector::doRead() { - read_msg_ = getPacket(); - socket_.async_receive( - asio::buffer(read_msg_->writableData(), Connector::packet_size), - [this](std::error_code ec, std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - read_msg_->append(length); - receive_callback_(std::move(read_msg_)); - doRead(); - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); - tryReconnect(); - } - }); -} - -void UdpSocketConnector::tryReconnect() { - if (state_ == ConnectorState::CONNECTED) { - TRANSPORT_LOGE("Connection lost. Trying to reconnect...\n"); - state_ = ConnectorState::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_ = ConnectorState::CONNECTED; - doRead(); - - if (data_available_) { - data_available_ = false; - doWrite(); - } - - if (is_reconnection_) { - is_reconnection_ = false; - } - - on_reconnect_callback_(); - } else { - doConnect(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - }); -} - -bool UdpSocketConnector::checkConnected() { - return state_ == ConnectorState::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(); - TRANSPORT_LOGE("Error connecting. Is the forwarder running?\n"); - io_service_.stop(); - }); - } -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/core/udp_socket_connector.h b/libtransport/src/core/udp_socket_connector.h deleted file mode 100644 index 5fdb6aeec..000000000 --- a/libtransport/src/core/udp_socket_connector.h +++ /dev/null @@ -1,85 +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/name.h> -#include <hicn/transport/utils/branch_prediction.h> - -#include <core/connector.h> - -#include <asio.hpp> -#include <asio/steady_timer.hpp> -#include <deque> - -namespace transport { -namespace core { - -using asio::ip::udp; - -class UdpSocketConnector : public Connector { - public: - UdpSocketConnector(PacketReceivedCallback &&receive_callback, - OnReconnect &&reconnect_callback, - asio::io_service &io_service, - std::string app_name = "Libtransport"); - - ~UdpSocketConnector() 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(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_; - - utils::ObjectPool<utils::MemBuf>::Ptr read_msg_; - - bool is_reconnection_; - bool data_available_; - - std::string app_name_; -}; - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/core/vpp_forwarder_interface.h b/libtransport/src/core/vpp_forwarder_interface.h deleted file mode 100644 index 31d23b40d..000000000 --- a/libtransport/src/core/vpp_forwarder_interface.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> - -#ifdef __vpp__ - -#include <hicn/transport/core/prefix.h> - - -#ifdef always_inline -#undef always_inline -#endif -extern "C" { -#include <vapi/vapi_safe.h> -}; - -#include <core/forwarder_interface.h> -#include <core/memif_connector.h> - -#include <deque> - -namespace transport { - -namespace core { - -class VPPForwarderInterface - : public ForwarderInterface<VPPForwarderInterface, MemifConnector> { - static constexpr std::uint16_t interface_mtu = 1500; - - public: - VPPForwarderInterface(MemifConnector &connector); - - typedef MemifConnector ConnectorType; - - ~VPPForwarderInterface(); - - void connect(bool is_consumer); - - void registerRoute(Prefix &prefix); - - TRANSPORT_ALWAYS_INLINE 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) {} - - void closeConnection(); - - private: - uint32_t getMemifConfiguration(); - - void consumerConnection(); - - void producerConnection(); - - uint32_t memif_id_; - uint32_t sw_if_index_; - // A consumer socket in vpp has two faces (ipv4 and ipv6) - uint32_t face_id1_; - uint32_t face_id2_; - bool is_consumer_; - vapi_ctx_t sock_; -}; - -} // namespace core - -} // namespace transport - -#endif diff --git a/libtransport/src/http/CMakeLists.txt b/libtransport/src/http/CMakeLists.txt index 00708822d..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: @@ -11,8 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/client_connection.cc ${CMAKE_CURRENT_SOURCE_DIR}/request.cc diff --git a/libtransport/src/http/client_connection.cc b/libtransport/src/http/client_connection.cc index 7a3a636fe..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: @@ -13,14 +13,12 @@ * limitations under the License. */ +#include <glog/logging.h> +#include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <hicn/transport/http/client_connection.h> #include <hicn/transport/utils/hash.h> -#include <hicn/transport/utils/log.h> - -#include <asio.hpp> -#include <asio/steady_timer.hpp> #define DEFAULT_BETA 0.99 #define DEFAULT_GAMMA 0.07 @@ -43,12 +41,6 @@ class HTTPClientConnection::Implementation read_buffer_(nullptr), response_(std::make_shared<HTTPResponse>()), timer_(nullptr) { - consumer_.setSocketOption( - ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, - (ConsumerContentObjectVerificationCallback)std::bind( - &Implementation::verifyData, this, std::placeholders::_1, - std::placeholders::_2)); - consumer_.setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, this); consumer_.connect(); @@ -83,13 +75,10 @@ class HTTPClientConnection::Implementation success_callback_ = [this, method = std::move(method), url = std::move(url), start = std::move(start)](std::size_t size) -> void { auto end = std::chrono::steady_clock::now(); - TRANSPORT_LOGI( - "%s %s [%s] duration: %llu [usec] %zu [bytes]\n", - method_map[method].c_str(), url.c_str(), name_.str().c_str(), - (unsigned long long) - std::chrono::duration_cast<std::chrono::microseconds>(end - start) - .count(), - size); + LOG(INFO) << method_map[method].c_str() << " " << url.c_str() << " [" + << name_.str() << "] duration: " + << utils::SteadyTime::getDurationUs(start, end).count() + << " [usec] " << size << " [bytes]"; }; sendRequestGetReply(ipv6_first_word); @@ -115,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(); } @@ -124,10 +113,10 @@ class HTTPClientConnection::Implementation return *http_client_; } - HTTPClientConnection &setCertificate(const std::string &cert_path) { - if (consumer_.setSocketOption(GeneralTransportOptions::CERTIFICATE, - cert_path) == SOCKET_OPTION_NOT_SET) { - throw errors::RuntimeException("Error setting the certificate."); + HTTPClientConnection &setVerifier(std::shared_ptr<auth::Verifier> verifier) { + if (consumer_.setSocketOption(GeneralTransportOptions::VERIFIER, + verifier) == SOCKET_OPTION_NOT_SET) { + throw errors::RuntimeException("Error setting the verifier."); } return *http_client_; @@ -156,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"; @@ -177,17 +162,6 @@ class HTTPClientConnection::Implementation consumer_.stop(); } - bool verifyData(interface::ConsumerSocket &c, - const core::ContentObject &contentObject) { - if (contentObject.getPayloadType() == PayloadType::CONTENT_OBJECT) { - TRANSPORT_LOGI("VERIFY CONTENT\n"); - } else if (contentObject.getPayloadType() == PayloadType::MANIFEST) { - TRANSPORT_LOGI("VERIFY MANIFEST\n"); - } - - return true; - } - void processLeavingInterest(interface::ConsumerSocket &c, const core::Interest &interest) { if (interest.payloadSize() == 0) { @@ -219,9 +193,9 @@ class HTTPClientConnection::Implementation } } - void readError(const std::error_code ec) noexcept override { - TRANSPORT_LOGE("Error %s during download of %s", ec.message().c_str(), - current_url_.c_str()); + 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_) { read_bytes_callback_->onError(ec); } @@ -307,9 +281,9 @@ HTTPClientConnection &HTTPClientConnection::setTimeout( return implementation_->setTimeout(timeout); } -HTTPClientConnection &HTTPClientConnection::setCertificate( - const std::string &cert_path) { - return implementation_->setCertificate(cert_path); +HTTPClientConnection &HTTPClientConnection::setVerifier( + std::shared_ptr<auth::Verifier> verifier) { + return implementation_->setVerifier(verifier); } } // namespace http 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 5423a7697..c759dd964 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: @@ -11,34 +11,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - -list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.cc -) - list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/socket.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.h ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.h ) if (${OPENSSL_VERSION} VERSION_EQUAL "1.1.1a" OR ${OPENSSL_VERSION} VERSION_GREATER "1.1.1a") list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_consumer.cc - ) - - list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.h - ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_consumer.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket.cc ) endif() diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.cc b/libtransport/src/implementation/p2psecure_socket_consumer.cc deleted file mode 100644 index 9b79850d6..000000000 --- a/libtransport/src/implementation/p2psecure_socket_consumer.cc +++ /dev/null @@ -1,402 +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 <implementation/p2psecure_socket_consumer.h> -#include <interfaces/tls_socket_consumer.h> - -#include <openssl/bio.h> -#include <openssl/ssl.h> -#include <openssl/tls1.h> - -#include <random> - -namespace transport { -namespace implementation { - -void P2PSecureConsumerSocket::setInterestPayload( - interface::ConsumerSocket &c, const core::Interest &interest) { - Interest &int2 = const_cast<Interest &>(interest); - random_suffix_ = int2.getName().getSuffix(); - - if (payload_ != NULL) int2.appendPayload(std::move(payload_)); -} - -/* Return the number of read bytes in the return param */ -int readOld(BIO *b, char *buf, int size) { - if (size < 0) return size; - - P2PSecureConsumerSocket *socket; - socket = (P2PSecureConsumerSocket *)BIO_get_data(b); - - std::unique_lock<std::mutex> lck(socket->mtx_); - - if (!socket->something_to_read_) { - if (!socket->transport_protocol_->isRunning()) { - socket->network_name_.setSuffix(socket->random_suffix_); - socket->ConsumerSocket::asyncConsume(socket->network_name_); - } - - if (!socket->something_to_read_) socket->cv_.wait(lck); - } - - size_t size_to_read, read; - size_t chain_size = socket->head_->length(); - - if (socket->head_->isChained()) - chain_size = socket->head_->computeChainDataLength(); - - if (chain_size > (size_t)size) { - read = size_to_read = (size_t)size; - } else { - read = size_to_read = chain_size; - socket->something_to_read_ = false; - } - - while (size_to_read) { - if (socket->head_->length() < size_to_read) { - std::memcpy(buf, socket->head_->data(), socket->head_->length()); - size_to_read -= socket->head_->length(); - buf += socket->head_->length(); - socket->head_ = socket->head_->pop(); - } else { - std::memcpy(buf, socket->head_->data(), size_to_read); - socket->head_->trimStart(size_to_read); - size_to_read = 0; - } - } - - return read; -} - -/* Return the number of read bytes in readbytes */ -int read(BIO *b, char *buf, size_t size, size_t *readbytes) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = readOld(b, buf, (int)size); - - if (ret <= 0) { - *readbytes = 0; - return ret; - } - - *readbytes = (size_t)ret; - - return 1; -} - -/* Return the number of written bytes in the return param */ -int writeOld(BIO *b, const char *buf, int num) { - P2PSecureConsumerSocket *socket; - socket = (P2PSecureConsumerSocket *)BIO_get_data(b); - - socket->payload_ = utils::MemBuf::copyBuffer(buf, num); - - socket->ConsumerSocket::setSocketOption( - ConsumerCallbacksOptions::INTEREST_OUTPUT, - (ConsumerInterestCallback)std::bind( - &P2PSecureConsumerSocket::setInterestPayload, socket, - std::placeholders::_1, std::placeholders::_2)); - - return num; -} - -/* Return the number of written bytes in written */ -int write(BIO *b, const char *buf, size_t size, size_t *written) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = writeOld(b, buf, (int)size); - - if (ret <= 0) { - *written = 0; - return ret; - } - - *written = (size_t)ret; - - return 1; -} - -long ctrl(BIO *b, int cmd, long num, void *ptr) { return 1; } - -int P2PSecureConsumerSocket::addHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, - const unsigned char **out, - size_t *outlen, X509 *x, - size_t chainidx, int *al, - void *add_arg) { - if (ext_type == 100) { - *out = (unsigned char *)malloc(4); - *(uint32_t *)*out = 10; - *outlen = 4; - } - return 1; -} - -void P2PSecureConsumerSocket::freeHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, - const unsigned char *out, - void *add_arg) { - free(const_cast<unsigned char *>(out)); -} - -int P2PSecureConsumerSocket::parseHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, - const unsigned char *in, - size_t inlen, X509 *x, - size_t chainidx, int *al, - void *add_arg) { - P2PSecureConsumerSocket *socket = - reinterpret_cast<P2PSecureConsumerSocket *>(add_arg); - if (ext_type == 100) { - memcpy(&socket->secure_prefix_, in, sizeof(ip_prefix_t)); - } - return 1; -} - -P2PSecureConsumerSocket::P2PSecureConsumerSocket( - interface::ConsumerSocket *consumer, int handshake_protocol, - int transport_protocol) - : ConsumerSocket(consumer, handshake_protocol), - name_(), - tls_consumer_(nullptr), - buf_pool_(), - decrypted_content_(), - payload_(), - head_(), - something_to_read_(false), - content_downloaded_(false), - random_suffix_(), - secure_prefix_(), - producer_namespace_(), - read_callback_decrypted_(), - mtx_(), - cv_(), - protocol_(transport_protocol) { - /* Create the (d)TLS state */ - const SSL_METHOD *meth = TLS_client_method(); - ctx_ = SSL_CTX_new(meth); - - int result = - SSL_CTX_set_ciphersuites(ctx_, - "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_" - "SHA256:TLS_AES_128_GCM_SHA256"); - if (result != 1) { - throw errors::RuntimeException( - "Unable to set cipher list on TLS subsystem. Aborting."); - } - - SSL_CTX_set_min_proto_version(ctx_, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ctx_, TLS1_3_VERSION); - SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, NULL); - SSL_CTX_set_ssl_version(ctx_, meth); - - result = SSL_CTX_add_custom_ext( - ctx_, 100, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, - P2PSecureConsumerSocket::addHicnKeyIdCb, - P2PSecureConsumerSocket::freeHicnKeyIdCb, NULL, - P2PSecureConsumerSocket::parseHicnKeyIdCb, this); - - ssl_ = SSL_new(ctx_); - - bio_meth_ = BIO_meth_new(BIO_TYPE_CONNECT, "secure consumer socket"); - BIO_meth_set_read(bio_meth_, readOld); - BIO_meth_set_write(bio_meth_, writeOld); - BIO_meth_set_ctrl(bio_meth_, ctrl); - BIO *bio = BIO_new(bio_meth_); - BIO_set_init(bio, 1); - BIO_set_data(bio, this); - SSL_set_bio(ssl_, bio, bio); - - std::default_random_engine generator; - std::uniform_int_distribution<int> distribution( - 1, std::numeric_limits<uint32_t>::max()); - random_suffix_ = 0; - - this->ConsumerSocket::setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, - this); -}; - -P2PSecureConsumerSocket::~P2PSecureConsumerSocket() { - BIO_meth_free(bio_meth_); - SSL_shutdown(ssl_); -} - -int P2PSecureConsumerSocket::handshake() { - int result = 1; - - if (!(SSL_in_before(this->ssl_) || SSL_in_init(this->ssl_))) { - return 1; - } - - ConsumerSocket::getSocketOption(MAX_WINDOW_SIZE, old_max_win_); - ConsumerSocket::getSocketOption(CURRENT_WINDOW_SIZE, old_current_win_); - - ConsumerSocket::setSocketOption(MAX_WINDOW_SIZE, (double)1.0); - ConsumerSocket::setSocketOption(CURRENT_WINDOW_SIZE, (double)1.0); - - network_name_ = producer_namespace_.getRandomName(); - network_name_.setSuffix(0); - - TRANSPORT_LOGD("Start handshake at %s", network_name_.toString().c_str()); - result = SSL_connect(this->ssl_); - - return result; -} - -void P2PSecureConsumerSocket::initSessionSocket() { - tls_consumer_ = - std::make_shared<TLSConsumerSocket>(nullptr, this->protocol_, this->ssl_); - tls_consumer_->setInterface( - new interface::TLSConsumerSocket(tls_consumer_.get())); - - ConsumerTimerCallback *stats_summary_callback = nullptr; - this->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, - &stats_summary_callback); - - uint32_t lifetime; - this->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, lifetime); - - tls_consumer_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, - lifetime); - tls_consumer_->setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, - read_callback_decrypted_); - tls_consumer_->setSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, - *stats_summary_callback); - tls_consumer_->setSocketOption(GeneralTransportOptions::STATS_INTERVAL, - this->timer_interval_milliseconds_); - tls_consumer_->setSocketOption(MAX_WINDOW_SIZE, old_max_win_); - tls_consumer_->setSocketOption(CURRENT_WINDOW_SIZE, old_current_win_); - tls_consumer_->connect(); -} - -int P2PSecureConsumerSocket::consume(const Name &name) { - if (transport_protocol_->isRunning()) { - return CONSUMER_BUSY; - } - - if (handshake() != 1) { - throw errors::RuntimeException("Unable to perform client handshake"); - } else { - TRANSPORT_LOGD("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_ != nullptr) - return tls_consumer_->consume((prefix->mapName(name)), std::move(payload_)); - else - 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 { - TRANSPORT_LOGD("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; -} - -int P2PSecureConsumerSocket::setSocketOption( - int socket_option_key, ReadCallback *socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, ReadCallback *socket_option_value) -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::READ_CALLBACK: - read_callback_decrypted_ = socket_option_value; - break; - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); -} - -void P2PSecureConsumerSocket::getReadBuffer(uint8_t **application_buffer, - size_t *max_length){}; - -void P2PSecureConsumerSocket::readDataAvailable(size_t length) noexcept {}; - -size_t P2PSecureConsumerSocket::maxBufferSize() const { - return SSL3_RT_MAX_PLAIN_LENGTH; -} - -void P2PSecureConsumerSocket::readBufferAvailable( - std::unique_ptr<utils::MemBuf> &&buffer) noexcept { - std::unique_lock<std::mutex> lck(this->mtx_); - if (head_) { - head_->prependChain(std::move(buffer)); - } else { - head_ = std::move(buffer); - } - - something_to_read_ = true; - cv_.notify_one(); -} - -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_); - content_downloaded_ = true; - something_to_read_ = true; - cv_.notify_one(); -} - -bool P2PSecureConsumerSocket::isBufferMovable() noexcept { return true; } - -} // namespace implementation -} // namespace transport diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.h b/libtransport/src/implementation/p2psecure_socket_consumer.h deleted file mode 100644 index d4c3b26c2..000000000 --- a/libtransport/src/implementation/p2psecure_socket_consumer.h +++ /dev/null @@ -1,138 +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. - */ - -#pragma once - -#include <hicn/transport/interfaces/socket_consumer.h> - -#include <implementation/tls_socket_consumer.h> -#include <openssl/bio.h> -#include <openssl/ssl.h> - -namespace transport { -namespace implementation { - -class P2PSecureConsumerSocket : public ConsumerSocket, - public interface::ConsumerSocket::ReadCallback { - /* Return the number of read bytes in readbytes */ - friend int read(BIO *b, char *buf, size_t size, size_t *readbytes); - - /* Return the number of read bytes in the return param */ - friend int readOld(BIO *h, char *buf, int size); - - /* Return the number of written bytes in written */ - friend int write(BIO *b, const char *buf, size_t size, size_t *written); - - /* Return the number of written bytes in the return param */ - friend int writeOld(BIO *h, const char *buf, int num); - - friend long ctrl(BIO *b, int cmd, long num, void *ptr); - - public: - explicit P2PSecureConsumerSocket(interface::ConsumerSocket *consumer, - int handshake_protocol, - int transport_protocol); - - ~P2PSecureConsumerSocket(); - - int consume(const Name &name) override; - - int asyncConsume(const Name &name) override; - - void registerPrefix(const Prefix &producer_namespace); - - int setSocketOption( - int socket_option_key, - interface::ConsumerSocket::ReadCallback *socket_option_value) override; - - using ConsumerSocket::getSocketOption; - using ConsumerSocket::setSocketOption; - - protected: - /* Callback invoked once an interest has been received and its payload - * decrypted */ - ConsumerInterestCallback on_interest_input_decrypted_; - ConsumerInterestCallback on_interest_process_decrypted_; - - private: - Name name_; - std::shared_ptr<TLSConsumerSocket> tls_consumer_; - /* SSL handle */ - SSL *ssl_; - SSL_CTX *ctx_; - BIO_METHOD *bio_meth_; - /* Chain of MemBuf to be used as a temporary buffer to pass descypted data - * from the underlying layer to the application */ - utils::ObjectPool<utils::MemBuf> buf_pool_; - std::unique_ptr<utils::MemBuf> decrypted_content_; - /* Chain of MemBuf holding the payload to be written into interest or data */ - std::unique_ptr<utils::MemBuf> payload_; - /* Chain of MemBuf holding the data retrieved from the underlying layer */ - std::unique_ptr<utils::MemBuf> head_; - bool something_to_read_; - bool content_downloaded_; - double old_max_win_; - double old_current_win_; - uint32_t random_suffix_; - ip_prefix_t secure_prefix_; - Prefix producer_namespace_; - interface::ConsumerSocket::ReadCallback *read_callback_decrypted_; - std::mutex mtx_; - - /* Condition variable for the wait */ - std::condition_variable cv_; - - int protocol_; - - void setInterestPayload(interface::ConsumerSocket &c, - const core::Interest &interest); - - static int addHicnKeyIdCb(SSL *s, unsigned int ext_type, unsigned int context, - const unsigned char **out, size_t *outlen, X509 *x, - size_t chainidx, int *al, void *add_arg); - - static void freeHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, const unsigned char *out, - void *add_arg); - - static int parseHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, const unsigned char *in, - size_t inlen, X509 *x, size_t chainidx, int *al, - void *add_arg); - - virtual void getReadBuffer(uint8_t **application_buffer, - size_t *max_length) override; - - virtual void readDataAvailable(size_t length) noexcept override; - - virtual size_t maxBufferSize() const override; - - virtual void readBufferAvailable( - std::unique_ptr<utils::MemBuf> &&buffer) noexcept override; - - virtual void readError(const std::error_code ec) noexcept override; - - virtual void readSuccess(std::size_t total_size) noexcept override; - - virtual bool isBufferMovable() noexcept override; - - int handshake(); - - void initSessionSocket(); -}; - -} // namespace implementation - -} // end namespace transport diff --git a/libtransport/src/implementation/p2psecure_socket_producer.cc b/libtransport/src/implementation/p2psecure_socket_producer.cc deleted file mode 100644 index 15c7d25cd..000000000 --- a/libtransport/src/implementation/p2psecure_socket_producer.cc +++ /dev/null @@ -1,394 +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 <hicn/transport/core/interest.h> - -#include <implementation/p2psecure_socket_producer.h> -#include <implementation/tls_rtc_socket_producer.h> -#include <implementation/tls_socket_producer.h> -#include <interfaces/tls_rtc_socket_producer.h> -#include <interfaces/tls_socket_producer.h> - -#include <openssl/bio.h> -#include <openssl/rand.h> -#include <openssl/ssl.h> - -namespace transport { -namespace implementation { - -/* Workaround to prevent content with expiry time equal to 0 to be lost when - * pushed in the forwarder */ -#define HICN_HANDSHAKE_CONTENT_EXPIRY_TIME 100; - -P2PSecureProducerSocket::P2PSecureProducerSocket( - interface::ProducerSocket *producer_socket) - : ProducerSocket(producer_socket), - mtx_(), - cv_(), - map_producers(), - list_producers() {} - -P2PSecureProducerSocket::P2PSecureProducerSocket( - interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr<utils::Identity> &identity) - : ProducerSocket(producer_socket), - rtc_(rtc), - mtx_(), - cv_(), - map_producers(), - list_producers() { - /* Setup SSL context (identity and parameter to use TLS 1.3) */ - der_cert_ = parcKeyStore_GetDEREncodedCertificate( - (identity->getSigner()->getKeyStore())); - der_prk_ = parcKeyStore_GetDEREncodedPrivateKey( - (identity->getSigner()->getKeyStore())); - - int cert_size = parcBuffer_Limit(der_cert_); - int prk_size = parcBuffer_Limit(der_prk_); - const uint8_t *cert = - reinterpret_cast<uint8_t *>(parcBuffer_Overlay(der_cert_, cert_size)); - const uint8_t *prk = - reinterpret_cast<uint8_t *>(parcBuffer_Overlay(der_prk_, prk_size)); - cert_509_ = d2i_X509(NULL, &cert, cert_size); - pkey_rsa_ = d2i_AutoPrivateKey(NULL, &prk, prk_size); - - /* Set the callback so that when an interest is received we catch it and we - * decrypt the payload before passing it to the application. */ - ProducerSocket::setSocketOption( - ProducerCallbacksOptions::INTEREST_INPUT, - (ProducerInterestCallback)std::bind( - &P2PSecureProducerSocket::onInterestCallback, this, - std::placeholders::_1, std::placeholders::_2)); -} - -P2PSecureProducerSocket::~P2PSecureProducerSocket() { - if (der_cert_) parcBuffer_Release(&der_cert_); - if (der_prk_) parcBuffer_Release(&der_prk_); -} - -void P2PSecureProducerSocket::initSessionSocket( - std::unique_ptr<TLSProducerSocket> &producer) { - producer->on_content_produced_application_ = - this->on_content_produced_application_; - producer->setSocketOption(CONTENT_OBJECT_EXPIRY_TIME, - this->content_object_expiry_time_); - producer->setSocketOption(SIGNER, this->signer_); - producer->setSocketOption(MAKE_MANIFEST, this->making_manifest_); - producer->setSocketOption(DATA_PACKET_SIZE, - (uint32_t)(this->data_packet_size_)); - producer->output_buffer_.setLimit(this->output_buffer_.getLimit()); - - if (!rtc_) { - producer->setInterface(new interface::TLSProducerSocket(producer.get())); - } else { - TLSRTCProducerSocket *rtc_producer = - dynamic_cast<TLSRTCProducerSocket *>(producer.get()); - rtc_producer->setInterface( - new interface::TLSRTCProducerSocket(rtc_producer)); - } -} - -void P2PSecureProducerSocket::onInterestCallback(interface::ProducerSocket &p, - Interest &interest) { - std::unique_lock<std::mutex> lck(mtx_); - std::unique_ptr<TLSProducerSocket> tls_producer; - auto it = map_producers.find(interest.getName()); - - if (it != map_producers.end()) { - return; - } - - if (!rtc_) { - tls_producer = - std::make_unique<TLSProducerSocket>(nullptr, this, interest.getName()); - } else { - tls_producer = std::make_unique<TLSRTCProducerSocket>(nullptr, this, - interest.getName()); - } - - initSessionSocket(tls_producer); - TLSProducerSocket *tls_producer_ptr = tls_producer.get(); - map_producers.insert({interest.getName(), move(tls_producer)}); - - TRANSPORT_LOGD("Start handshake at %s", - interest.getName().toString().c_str()); - - if (!rtc_) { - tls_producer_ptr->onInterest(*tls_producer_ptr, interest); - tls_producer_ptr->async_accept(); - } else { - TLSRTCProducerSocket *rtc_producer_ptr = - dynamic_cast<TLSRTCProducerSocket *>(tls_producer_ptr); - rtc_producer_ptr->onInterest(*rtc_producer_ptr, interest); - rtc_producer_ptr->async_accept(); - } -} - -void P2PSecureProducerSocket::produce(const uint8_t *buffer, - size_t buffer_size) { - 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_); - - if (list_producers.empty()) cv_.wait(lck); - - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) { - TLSRTCProducerSocket *rtc_producer = - dynamic_cast<TLSRTCProducerSocket *>(it->get()); - rtc_producer->produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); - } -} - -uint32_t P2PSecureProducerSocket::produce( - Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, 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_); - uint32_t segments = 0; - - if (list_producers.empty()) cv_.wait(lck); - - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - segments += - (*it)->produce(content_name, buffer->clone(), is_last, start_offset); - - return segments; -} - -uint32_t P2PSecureProducerSocket::produce(Name content_name, - const uint8_t *buffer, - 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_); - uint32_t segments = 0; - if (list_producers.empty()) cv_.wait(lck); - - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - segments += (*it)->produce(content_name, buffer, buffer_size, is_last, - start_offset); - - 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) { - if (!list_producers.empty()) { - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - } - - switch (socket_option_key) { - case ProducerCallbacksOptions::INTEREST_INPUT: - on_interest_input_decrypted_ = socket_option_value; - return SOCKET_OPTION_SET; - - case ProducerCallbacksOptions::INTEREST_DROP: - on_interest_dropped_input_buffer_ = socket_option_value; - return SOCKET_OPTION_SET; - - case ProducerCallbacksOptions::INTEREST_PASS: - on_interest_inserted_input_buffer_ = socket_option_value; - return SOCKET_OPTION_SET; - - case ProducerCallbacksOptions::CACHE_HIT: - on_interest_satisfied_output_buffer_ = socket_option_value; - return SOCKET_OPTION_SET; - - case ProducerCallbacksOptions::CACHE_MISS: - on_interest_process_decrypted_ = socket_option_value; - return SOCKET_OPTION_SET; - - default: - return SOCKET_OPTION_NOT_SET; - } -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, - const std::shared_ptr<utils::Signer> &socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - switch (socket_option_key) { - case GeneralTransportOptions::SIGNER: { - signer_.reset(); - signer_ = socket_option_value; - - return SOCKET_OPTION_SET; - } - default: - return SOCKET_OPTION_NOT_SET; - } -} - -int P2PSecureProducerSocket::setSocketOption(int socket_option_key, - uint32_t socket_option_value) { - if (!list_producers.empty()) { - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - } - switch (socket_option_key) { - case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: - content_object_expiry_time_ = - socket_option_value; // HICN_HANDSHAKE_CONTENT_EXPIRY_TIME; - return SOCKET_OPTION_SET; - } - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption(int socket_option_key, - bool socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption(int socket_option_key, - Name *socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, std::list<Prefix> socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, ProducerContentObjectCallback socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, ProducerContentCallback socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - switch (socket_option_key) { - case ProducerCallbacksOptions::CONTENT_PRODUCED: - on_content_produced_application_ = socket_option_value; - break; - - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, utils::CryptoHashType socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, utils::CryptoSuite socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -int P2PSecureProducerSocket::setSocketOption( - int socket_option_key, const std::string &socket_option_value) { - if (!list_producers.empty()) - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) - (*it)->setSocketOption(socket_option_key, socket_option_value); - - return ProducerSocket::setSocketOption(socket_option_key, - socket_option_value); -} - -} // namespace implementation -} // namespace transport diff --git a/libtransport/src/implementation/p2psecure_socket_producer.h b/libtransport/src/implementation/p2psecure_socket_producer.h deleted file mode 100644 index bfc9fc2c1..000000000 --- a/libtransport/src/implementation/p2psecure_socket_producer.h +++ /dev/null @@ -1,127 +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/security/identity.h> -#include <hicn/transport/security/signer.h> - -#include <implementation/socket_producer.h> -#include <implementation/tls_rtc_socket_producer.h> -#include <implementation/tls_socket_producer.h> -#include <utils/content_store.h> - -#include <openssl/ssl.h> -#include <condition_variable> -#include <forward_list> -#include <mutex> - -namespace transport { -namespace implementation { - -class P2PSecureProducerSocket : public ProducerSocket { - friend class TLSProducerSocket; - friend class TLSRTCProducerSocket; - - public: - explicit P2PSecureProducerSocket(interface::ProducerSocket *producer_socket); - - explicit P2PSecureProducerSocket( - interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr<utils::Identity> &identity); - - ~P2PSecureProducerSocket(); - - void produce(const uint8_t *buffer, size_t buffer_size) override; - - uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, - bool is_last = true, uint32_t start_offset = 0) override; - - uint32_t produce(Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, - 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; - - void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size, - bool is_last = true, - uint32_t *start_offset = nullptr) override; - - int setSocketOption(int socket_option_key, - ProducerInterestCallback socket_option_value) override; - - int setSocketOption( - int socket_option_key, - const std::shared_ptr<utils::Signer> &socket_option_value) override; - - int setSocketOption(int socket_option_key, - uint32_t socket_option_value) override; - - int setSocketOption(int socket_option_key, bool socket_option_value) override; - - int setSocketOption(int socket_option_key, - Name *socket_option_value) override; - - int setSocketOption(int socket_option_key, - std::list<Prefix> socket_option_value) override; - - int setSocketOption( - int socket_option_key, - ProducerContentObjectCallback socket_option_value) override; - - int setSocketOption(int socket_option_key, - ProducerContentCallback socket_option_value) override; - - int setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value) override; - - int setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value) override; - - int setSocketOption(int socket_option_key, - const std::string &socket_option_value) override; - - using ProducerSocket::getSocketOption; - using ProducerSocket::onInterest; - - protected: - /* Callback invoked once an interest has been received and its payload - * decrypted */ - ProducerInterestCallback on_interest_input_decrypted_; - ProducerInterestCallback on_interest_process_decrypted_; - ProducerContentCallback on_content_produced_application_; - - private: - bool rtc_; - std::mutex mtx_; - /* Condition variable for the wait */ - std::condition_variable cv_; - PARCBuffer *der_cert_; - PARCBuffer *der_prk_; - X509 *cert_509_; - EVP_PKEY *pkey_rsa_; - std::unordered_map<core::Name, std::unique_ptr<TLSProducerSocket>, - core::hash<core::Name>, core::compare2<core::Name>> - map_producers; - std::list<std::unique_ptr<TLSProducerSocket>> list_producers; - - void onInterestCallback(interface::ProducerSocket &p, Interest &interest); - - void initSessionSocket(std::unique_ptr<TLSProducerSocket> &producer); -}; - -} // namespace implementation -} // namespace transport diff --git a/libtransport/src/implementation/rtc_socket_producer.cc b/libtransport/src/implementation/rtc_socket_producer.cc deleted file mode 100644 index a5b2b4a0e..000000000 --- a/libtransport/src/implementation/rtc_socket_producer.cc +++ /dev/null @@ -1,352 +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/interfaces/callbacks.h> - -#include <implementation/rtc_socket_producer.h> -#include <stdlib.h> -#include <time.h> - -#define NACK_HEADER_SIZE 8 // bytes -#define TIMESTAMP_LEN 8 // bytes -#define TCP_HEADER_SIZE 20 -#define IP6_HEADER_SIZE 40 -#define INIT_PACKET_PRODUCTION_RATE 100 // pps random value (almost 1Mbps) -#define STATS_INTERVAL_DURATION 500 // ms -#define INTEREST_LIFETIME_REDUCTION_FACTOR 0.8 -#define INACTIVE_TIME \ - 500 // ms without producing before the socket - // is considered inactive -#define MILLI_IN_A_SEC 1000 // ms in a second - -#define HICN_MAX_DATA_SEQ 0xefffffff - -// slow production rate param -#define MIN_PRODUCTION_RATE \ - 10 // in pacekts per sec. this value is computed - // through experiments -#define LIFETIME_FRACTION 0.5 - -// NACK HEADER -// +-----------------------------------------+ -// | 4 bytes: current segment in production | -// +-----------------------------------------+ -// | 4 bytes: production rate (bytes x sec) | -// +-----------------------------------------+ -// - -// PACKET HEADER -// +-----------------------------------------+ -// | 8 bytes: TIMESTAMP | -// +-----------------------------------------+ -// | packet | -// +-----------------------------------------+ - -namespace transport { -namespace implementation { - -RTCProducerSocket::RTCProducerSocket(interface::ProducerSocket *producer_socket) - : ProducerSocket(producer_socket), - currentSeg_(1), - producedBytes_(0), - producedPackets_(0), - bytesProductionRate_(INIT_PACKET_PRODUCTION_RATE * 1400), - packetsProductionRate_(INIT_PACKET_PRODUCTION_RATE), - perSecondFactor_(MILLI_IN_A_SEC / STATS_INTERVAL_DURATION), - timer_on_(false) { - srand((unsigned int)time(NULL)); - prodLabel_ = ((rand() % 255) << 24UL); - interests_cache_timer_ = - std::make_unique<asio::steady_timer>(this->getIoService()); - round_timer_ = std::make_unique<asio::steady_timer>(this->getIoService()); - setSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, 10000U); - scheduleRoundTimer(); -} - -RTCProducerSocket::~RTCProducerSocket() {} - -void RTCProducerSocket::registerPrefix(const Prefix &producer_namespace) { - ProducerSocket::registerPrefix(producer_namespace); - - flowName_ = producer_namespace.getName(); - auto family = flowName_.getAddressFamily(); - - switch (family) { - case AF_INET6: - headerSize_ = (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET6_TCP); - break; - case AF_INET: - headerSize_ = (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET_TCP); - break; - default: - throw errors::RuntimeException("Unknown name format."); - } -} - -void RTCProducerSocket::scheduleRoundTimer() { - round_timer_->expires_from_now( - std::chrono::milliseconds(STATS_INTERVAL_DURATION)); - round_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - updateStats(); - }); -} - -void RTCProducerSocket::updateStats() { - bytesProductionRate_ = producedBytes_.load() * perSecondFactor_; - packetsProductionRate_ = producedPackets_.load() * perSecondFactor_; - if (packetsProductionRate_.load() == 0) packetsProductionRate_ = 1; - producedBytes_ = 0; - producedPackets_ = 0; - scheduleRoundTimer(); -} - -void RTCProducerSocket::produce(std::unique_ptr<utils::MemBuf> &&buffer) { - auto buffer_size = buffer->length(); - - if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) { - return; - } - - if (TRANSPORT_EXPECT_FALSE((buffer_size + headerSize_ + TIMESTAMP_LEN) > - data_packet_size_)) { - return; - } - - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - producedBytes_ += (uint32_t)(buffer_size + headerSize_ + TIMESTAMP_LEN); - producedPackets_++; - - Name n(flowName_); - auto content_object = - std::make_shared<ContentObject>(n.setSuffix(currentSeg_.load())); - auto payload = utils::MemBuf::create(TIMESTAMP_LEN); - - memcpy(payload->writableData(), &now, TIMESTAMP_LEN); - payload->append(TIMESTAMP_LEN); - payload->prependChain(std::move(buffer)); - content_object->appendPayload(std::move(payload)); - - content_object->setLifetime(500); // XXX this should be set by the APP - - content_object->setPathLabel(prodLabel_); - - output_buffer_.insert(std::static_pointer_cast<ContentObject>( - content_object->shared_from_this())); - - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*getInterface(), *content_object); - } - - TRANSPORT_LOGD("Send content %u (produce)", - content_object->getName().getSuffix()); - portal_->sendContentObject(*content_object); - - if (on_content_object_output_) { - on_content_object_output_(*getInterface(), *content_object); - } - - uint32_t old_curr = currentSeg_.load(); - currentSeg_ = (currentSeg_.load() + 1) % HICN_MAX_DATA_SEQ; - - // remove interests from the interest cache if it exists - // this generates nacks that will tell to the consumer - // that a new data packet was produced - utils::SpinLock::Acquire locked(interests_cache_lock_); - if (!seqs_map_.empty()) { - for (auto it = seqs_map_.begin(); it != seqs_map_.end(); it++) { - if (it->first != old_curr) sendNack(it->first); - } - seqs_map_.clear(); - timers_map_.clear(); - } -} - -void RTCProducerSocket::onInterest(Interest::Ptr &&interest) { - uint32_t interestSeg = interest->getName().getSuffix(); - uint32_t lifetime = interest->getLifetime(); - - if (on_interest_input_) { - on_interest_input_(*getInterface(), *interest); - } - - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - if (interestSeg > HICN_MAX_DATA_SEQ) { - sendNack(interestSeg); - return; - } - - const std::shared_ptr<ContentObject> content_object = - output_buffer_.find(*interest); - - if (content_object) { - if (on_interest_satisfied_output_buffer_) { - on_interest_satisfied_output_buffer_(*getInterface(), *interest); - } - - if (on_content_object_output_) { - on_content_object_output_(*getInterface(), *content_object); - } - - TRANSPORT_LOGD("Send content %u (onInterest)", - content_object->getName().getSuffix()); - portal_->sendContentObject(*content_object); - return; - } else { - if (on_interest_process_) { - on_interest_process_(*getInterface(), *interest); - } - } - - // if the production rate is less than MIN_PRODUCTION_RATE we put the - // interest in a queue, otherwise we handle it in the usual way - if (packetsProductionRate_.load() < MIN_PRODUCTION_RATE && - interestSeg >= currentSeg_.load()) { - utils::SpinLock::Acquire locked(interests_cache_lock_); - - uint64_t next_timer = ~0; - if (!timers_map_.empty()) { - next_timer = timers_map_.begin()->first; - } - - uint64_t expiration = now + (lifetime * LIFETIME_FRACTION); - // check if the seq number exists already - auto it_seqs = seqs_map_.find(interestSeg); - if (it_seqs != seqs_map_.end()) { - // the seq already exists - if (expiration < it_seqs->second) { - // we need to update the timer becasue we got a smaller one - // 1) remove the entry from the multimap - // 2) update this entry - auto range = timers_map_.equal_range(it_seqs->second); - for (auto it_timers = range.first; it_timers != range.second; - it_timers++) { - if (it_timers->second == it_seqs->first) { - timers_map_.erase(it_timers); - break; - } - } - timers_map_.insert( - std::pair<uint64_t, uint32_t>(expiration, interestSeg)); - it_seqs->second = expiration; - } else { - // nothing to do here - return; - } - } else { - // add the new seq - timers_map_.insert( - std::pair<uint64_t, uint32_t>(expiration, interestSeg)); - seqs_map_.insert(std::pair<uint32_t, uint64_t>(interestSeg, expiration)); - } - - // here we have at least one interest in the queue, we need to start or - // update the timer - if (!timer_on_) { - // set timeout - timer_on_ = true; - scheduleCacheTimer(timers_map_.begin()->first - now); - } else { - // re-schedule the timer because a new interest will expires sooner - if (next_timer > timers_map_.begin()->first) { - interests_cache_timer_->cancel(); - scheduleCacheTimer(timers_map_.begin()->first - now); - } - } - return; - } - - uint32_t max_gap = (uint32_t)floor( - (double)((double)((double)lifetime * INTEREST_LIFETIME_REDUCTION_FACTOR / - 1000.0) * - (double)packetsProductionRate_.load())); - - if (interestSeg < currentSeg_.load() || - interestSeg > (max_gap + currentSeg_.load())) { - sendNack(interestSeg); - } - // else drop packet -} - -void RTCProducerSocket::scheduleCacheTimer(uint64_t wait) { - interests_cache_timer_->expires_from_now(std::chrono::milliseconds(wait)); - interests_cache_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - interestCacheTimer(); - }); -} - -void RTCProducerSocket::interestCacheTimer() { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - utils::SpinLock::Acquire locked(interests_cache_lock_); - - for (auto it_timers = timers_map_.begin(); it_timers != timers_map_.end();) { - uint64_t expire = it_timers->first; - if (expire <= now) { - uint32_t seq = it_timers->second; - sendNack(seq); - // remove the interest from the other map - seqs_map_.erase(seq); - it_timers = timers_map_.erase(it_timers); - } else { - // stop, we are done! - break; - } - } - if (timers_map_.empty()) { - timer_on_ = false; - } else { - timer_on_ = true; - scheduleCacheTimer(timers_map_.begin()->first - now); - } -} - -void RTCProducerSocket::sendNack(uint32_t sequence) { - auto nack_payload = utils::MemBuf::create(NACK_HEADER_SIZE); - nack_payload->append(NACK_HEADER_SIZE); - ContentObject nack; - - Name n(flowName_); - nack.appendPayload(std::move(nack_payload)); - nack.setName(n.setSuffix(sequence)); - - uint32_t *payload_ptr = (uint32_t *)nack.getPayload()->data(); - *payload_ptr = currentSeg_.load(); - - *(++payload_ptr) = bytesProductionRate_.load(); - - nack.setLifetime(0); - nack.setPathLabel(prodLabel_); - - if (on_content_object_output_) { - on_content_object_output_(*getInterface(), nack); - } - - TRANSPORT_LOGD("Send nack %u", sequence); - portal_->sendContentObject(nack); -} - -} // namespace implementation - -} // end namespace transport diff --git a/libtransport/src/implementation/rtc_socket_producer.h b/libtransport/src/implementation/rtc_socket_producer.h deleted file mode 100644 index 87db2121d..000000000 --- a/libtransport/src/implementation/rtc_socket_producer.h +++ /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. - */ - -#pragma once - -#include <implementation/socket_producer.h> -#include <utils/content_store.h> - -#include <atomic> -#include <map> -#include <mutex> - -namespace transport { -namespace implementation { - -class RTCProducerSocket : virtual public ProducerSocket { - public: - RTCProducerSocket(interface::ProducerSocket *producer_socket); - - ~RTCProducerSocket(); - - void registerPrefix(const Prefix &producer_namespace) override; - void produce(std::unique_ptr<utils::MemBuf> &&buffer) override; - - private: - void onInterest(Interest::Ptr &&interest) override; - void sendNack(uint32_t sequence); - void updateStats(); - void scheduleCacheTimer(uint64_t wait); - void scheduleRoundTimer(); - void interestCacheTimer(); - - std::atomic<uint32_t> currentSeg_; - uint32_t prodLabel_; - uint16_t headerSize_; - Name flowName_; - std::atomic<uint32_t> producedBytes_; - std::atomic<uint32_t> producedPackets_; - std::atomic<uint32_t> bytesProductionRate_; - std::atomic<uint32_t> packetsProductionRate_; - uint32_t perSecondFactor_; - - std::unique_ptr<asio::steady_timer> round_timer_; - - // cache for the received interests - // this map maps the expiration time of an interest to - // its sequence number. the map is sorted by timeouts - // the same timeout may be used for multiple sequence numbers - // but for each sequence number we store only the smallest - // expiry time. In this way the mapping from seqs_map_ to - // timers_map_ is unique - std::multimap<uint64_t, uint32_t> timers_map_; - // this map does the opposite, this map is not ordered - std::unordered_map<uint32_t, uint64_t> seqs_map_; - bool timer_on_; - std::unique_ptr<asio::steady_timer> interests_cache_timer_; - utils::SpinLock interests_cache_lock_; -}; - -} // namespace implementation - -} // end namespace transport diff --git a/libtransport/src/implementation/socket.cc b/libtransport/src/implementation/socket.cc new file mode 100644 index 000000000..c554d09d3 --- /dev/null +++ b/libtransport/src/implementation/socket.cc @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#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), + signer_(std::make_shared<auth::VoidSigner>()), + verifier_(std::make_shared<auth::VoidVerifier>()) {} + +int Socket::setSocketOption(int socket_option_key, + hicn_packet_format_t packet_format) { + return SOCKET_OPTION_NOT_SET; +} + +int Socket::getSocketOption(int socket_option_key, + hicn_packet_format_t &packet_format) { + return SOCKET_OPTION_NOT_GET; +} + +} // namespace implementation +} // namespace transport diff --git a/libtransport/src/implementation/socket.h b/libtransport/src/implementation/socket.h index 2e51f3027..d0d2ba2ac 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-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: @@ -15,13 +15,14 @@ #pragma once +#include <core/facade.h> +#include <hicn/transport/auth/signer.h> +#include <hicn/transport/auth/verifier.h> #include <hicn/transport/config.h> #include <hicn/transport/interfaces/callbacks.h> #include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/interfaces/socket_options_keys.h> -#include <core/facade.h> - #define SOCKET_OPTION_GET 0 #define SOCKET_OPTION_NOT_GET 1 #define SOCKET_OPTION_SET 2 @@ -32,56 +33,46 @@ namespace transport { namespace implementation { // Forward Declarations -template <typename PortalType> class Socket; -// Define the portal and its connector, depending on the compilation options -// passed by the build tool. -using HicnForwarderPortal = core::HicnForwarderPortal; - -#ifdef __linux__ -#ifndef __ANDROID__ -using RawSocketPortal = core::RawSocketPortal; -#endif -#endif - -#ifdef __vpp__ -using VPPForwarderPortal = core::VPPForwarderPortal; -using BaseSocket = Socket<VPPForwarderPortal>; -using BasePortal = VPPForwarderPortal; -#else -using BaseSocket = Socket<HicnForwarderPortal>; -using BasePortal = HicnForwarderPortal; -#endif - -template <typename PortalType> class Socket { - static_assert(std::is_same<PortalType, HicnForwarderPortal>::value -#ifdef __linux__ -#ifndef __ANDROID__ - || std::is_same<PortalType, RawSocketPortal>::value -#ifdef __vpp__ - || std::is_same<PortalType, VPPForwarderPortal>::value -#endif -#endif - , -#else - , - -#endif - "This class is not allowed as Portal"); - public: - using Portal = PortalType; - - virtual asio::io_service &getIoService() = 0; - virtual void connect() = 0; - virtual bool isRunning() = 0; + virtual asio::io_service &getIoService() { + return portal_->getThread().getIoService(); + } + + int setSocketOption(int socket_option_key, + hicn_packet_format_t packet_format); + int getSocketOption(int socket_option_key, + hicn_packet_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); + virtual ~Socket(){}; + + protected: + std::shared_ptr<core::Portal> portal_; + bool is_async_; + std::shared_ptr<auth::Signer> signer_; + std::shared_ptr<auth::Verifier> verifier_; }; } // namespace implementation diff --git a/libtransport/src/implementation/socket_consumer.h b/libtransport/src/implementation/socket_consumer.h index 87965923e..3117e21e7 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: @@ -13,15 +13,18 @@ * limitations under the License. */ +#pragma once + +#include <hicn/transport/auth/verifier.h> #include <hicn/transport/interfaces/socket_consumer.h> #include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/interfaces/statistics.h> -#include <hicn/transport/security/verifier.h> #include <hicn/transport/utils/event_thread.h> +#include <implementation/socket.h> #include <protocols/cbr.h> -#include <protocols/protocol.h> #include <protocols/raaqm.h> -#include <protocols/rtc.h> +#include <protocols/rtc/rtc.h> +#include <protocols/transport_protocol.h> namespace transport { namespace implementation { @@ -30,13 +33,13 @@ using namespace core; using namespace interface; using ReadCallback = interface::ConsumerSocket::ReadCallback; -class ConsumerSocket : public Socket<BasePortal> { +class ConsumerSocket : public Socket { private: ConsumerSocket(interface::ConsumerSocket *consumer, int protocol, - std::shared_ptr<Portal> &&portal) - : consumer_interface_(consumer), - portal_(portal), - async_downloader_(), + std::shared_ptr<core::Portal> &&portal) + : Socket(std::move(portal)), + consumer_interface_(consumer), + packet_format_(default_values::packet_format), interest_lifetime_(default_values::interest_lifetime), min_window_size_(default_values::min_window_size), max_window_size_(default_values::max_window_size), @@ -54,52 +57,53 @@ class ConsumerSocket : public Socket<BasePortal> { rate_estimation_observer_(nullptr), rate_estimation_batching_parameter_(default_values::batch), rate_estimation_choice_(0), - is_async_(false), - verifier_(std::make_shared<utils::Verifier>()), + manifest_factor_relevant_(default_values::manifest_factor_relevant), + manifest_factor_alert_(default_values::manifest_factor_alert), + verifier_(std::make_shared<auth::VoidVerifier>()), verify_signature_(false), - key_content_(false), reset_window_(false), on_interest_output_(VOID_HANDLER), on_interest_timeout_(VOID_HANDLER), on_interest_satisfied_(VOID_HANDLER), on_content_object_input_(VOID_HANDLER), - on_content_object_verification_(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), + content_sharing_mode_(false), + aggregated_interests_(false), 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::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<Portal>()) {} + : ConsumerSocket(consumer, protocol, core::Portal::createShared()) {} ConsumerSocket(interface::ConsumerSocket *consumer, int protocol, - asio::io_service &io_service) - : ConsumerSocket(consumer, protocol, - std::make_shared<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_; @@ -123,23 +127,9 @@ class ConsumerSocket : public Socket<BasePortal> { 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; } - bool verifyKeyPackets() { return transport_protocol_->verifyKeyPackets(); } - void stop() { if (transport_protocol_->isRunning()) { transport_protocol_->stop(); @@ -152,7 +142,8 @@ class ConsumerSocket : public Socket<BasePortal> { } } - asio::io_service &getIoService() { return portal_->getIoService(); } + using Socket::getSocketOption; + using Socket::setSocketOption; virtual int setSocketOption(int socket_option_key, ReadCallback *socket_option_value) { @@ -270,6 +261,23 @@ class ConsumerSocket : public Socket<BasePortal> { timer_interval_milliseconds_ = socket_option_value; break; + case RtcTransportOptions::RECOVERY_STRATEGY: + recovery_strategy_ = + (RtcTransportRecoveryStrategies)socket_option_value; + break; + + case MANIFEST_FACTOR_RELEVANT: + manifest_factor_relevant_ = socket_option_value; + break; + + case MANIFEST_FACTOR_ALERT: + manifest_factor_alert_ = socket_option_value; + break; + + case GeneralTransportOptions::PACKET_FORMAT: + packet_format_ = socket_option_value; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -315,13 +323,6 @@ class ConsumerSocket : public Socket<BasePortal> { on_content_object_input_ = VOID_HANDLER; break; } - - case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: - if (socket_option_value == VOID_HANDLER) { - on_content_object_verification_ = VOID_HANDLER; - break; - } - default: return SOCKET_OPTION_NOT_SET; } @@ -334,18 +335,23 @@ class ConsumerSocket : public Socket<BasePortal> { int result = SOCKET_OPTION_NOT_SET; if (!transport_protocol_->isRunning()) { switch (socket_option_key) { - case GeneralTransportOptions::VERIFY_SIGNATURE: - verify_signature_ = socket_option_value; + case RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET: + reset_window_ = socket_option_value; result = SOCKET_OPTION_SET; break; - case GeneralTransportOptions::KEY_CONTENT: - key_content_ = socket_option_value; + case RtcTransportOptions::AGGREGATED_DATA: + aggregated_data_ = socket_option_value; result = SOCKET_OPTION_SET; break; - case RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET: - reset_window_ = socket_option_value; + case RtcTransportOptions::CONTENT_SHARING_MODE: + content_sharing_mode_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; + + case RtcTransportOptions::AGGREGATED_INTERESTS: + aggregated_interests_ = socket_option_value; result = SOCKET_OPTION_SET; break; @@ -377,29 +383,6 @@ class ConsumerSocket : public Socket<BasePortal> { }); } - int setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback 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, - ConsumerContentObjectVerificationCallback socket_option_value) - -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: - on_content_object_verification_ = socket_option_value; - break; - - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); - } - int setSocketOption(int socket_option_key, ConsumerInterestCallback socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -433,51 +416,6 @@ class ConsumerSocket : public Socket<BasePortal> { }); } - int setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this]( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value) - -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::VERIFICATION_FAILED: - verification_failed_callback_ = socket_option_value; - break; - - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); - } - - // int setSocketOption( - // int socket_option_key, - // ConsumerContentObjectVerificationFailedCallback socket_option_value) { - // return rescheduleOnIOService( - // socket_option_key, socket_option_value, - // [this]( - // int socket_option_key, - // ConsumerContentObjectVerificationFailedCallback - // socket_option_value) - // -> int { - // switch (socket_option_key) { - // case ConsumerCallbacksOptions::VERIFICATION_FAILED: - // verification_failed_callback_ = socket_option_value; - // break; - - // default: - // return SOCKET_OPTION_NOT_SET; - // } - - // return SOCKET_OPTION_SET; - // }); - // } - int setSocketOption(int socket_option_key, IcnObserver *socket_option_value) { utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { @@ -494,45 +432,49 @@ class ConsumerSocket : public Socket<BasePortal> { int setSocketOption( int socket_option_key, - const std::shared_ptr<utils::Verifier> &socket_option_value) { - int result = SOCKET_OPTION_NOT_SET; + const std::shared_ptr<auth::Signer> &socket_option_value) { + if (!transport_protocol_->isRunning()) { + switch (socket_option_key) { + case GeneralTransportOptions::SIGNER: + signer_.reset(); + signer_ = socket_option_value; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + } + return SOCKET_OPTION_SET; + } + + int setSocketOption( + int socket_option_key, + const std::shared_ptr<auth::Verifier> &socket_option_value) { 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 GeneralTransportOptions::CERTIFICATE: - key_id_ = verifier_->addKeyFromCertificate(socket_option_value); - - if (key_id_ != nullptr) { - result = SOCKET_OPTION_SET; - } - break; - - 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; - - default: - return result; - } + } + break; + default: + return result; } return result; } @@ -558,6 +500,29 @@ class ConsumerSocket : public Socket<BasePortal> { }); } + 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) { @@ -629,6 +594,22 @@ class ConsumerSocket : public Socket<BasePortal> { socket_option_value = timer_interval_milliseconds_; break; + case RtcTransportOptions::RECOVERY_STRATEGY: + socket_option_value = recovery_strategy_; + break; + + case GeneralTransportOptions::MANIFEST_FACTOR_RELEVANT: + socket_option_value = manifest_factor_relevant_; + break; + + case GeneralTransportOptions::MANIFEST_FACTOR_ALERT: + socket_option_value = manifest_factor_alert_; + break; + + case GeneralTransportOptions::PACKET_FORMAT: + socket_option_value = packet_format_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -642,14 +623,6 @@ class ConsumerSocket : public Socket<BasePortal> { socket_option_value = transport_protocol_->isRunning(); break; - case GeneralTransportOptions::VERIFY_SIGNATURE: - socket_option_value = verify_signature_; - break; - - case GeneralTransportOptions::KEY_CONTENT: - socket_option_value = key_content_; - break; - case GeneralTransportOptions::ASYNC_MODE: socket_option_value = is_async_; break; @@ -658,6 +631,18 @@ class ConsumerSocket : public Socket<BasePortal> { socket_option_value = reset_window_; break; + case RtcTransportOptions::AGGREGATED_DATA: + socket_option_value = aggregated_data_; + break; + + case RtcTransportOptions::CONTENT_SHARING_MODE: + socket_option_value = content_sharing_mode_; + break; + + case RtcTransportOptions::AGGREGATED_INTERESTS: + socket_option_value = aggregated_interests_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -699,29 +684,6 @@ class ConsumerSocket : public Socket<BasePortal> { }); } - int getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback **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, - ConsumerContentObjectVerificationCallback **socket_option_value) - -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: - *socket_option_value = &on_content_object_verification_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); - } - int getSocketOption(int socket_option_key, ConsumerInterestCallback **socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -755,33 +717,12 @@ class ConsumerSocket : public Socket<BasePortal> { }); } - int getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback **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, - ConsumerContentObjectVerificationFailedCallback * - *socket_option_value) -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::VERIFICATION_FAILED: - *socket_option_value = &verification_failed_callback_; - break; - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); - } - int getSocketOption(int socket_option_key, - std::shared_ptr<Portal> &socket_option_value) { + IcnObserver **socket_option_value) { + utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { - case PORTAL: - socket_option_value = portal_; + case RateEstimationOptions::RATE_ESTIMATION_OBSERVER: + *socket_option_value = (rate_estimation_observer_); break; default: @@ -792,22 +733,19 @@ class ConsumerSocket : public Socket<BasePortal> { } int getSocketOption(int socket_option_key, - IcnObserver **socket_option_value) { - utils::SpinLock::Acquire locked(guard_raaqm_params_); + std::shared_ptr<auth::Signer> &socket_option_value) { switch (socket_option_key) { - case RateEstimationOptions::RATE_ESTIMATION_OBSERVER: - *socket_option_value = (rate_estimation_observer_); - break; + case GeneralTransportOptions::SIGNER: + socket_option_value = signer_; + return SOCKET_OPTION_GET; default: return SOCKET_OPTION_NOT_GET; } - - return SOCKET_OPTION_GET; } int getSocketOption(int socket_option_key, - std::shared_ptr<utils::Verifier> &socket_option_value) { + std::shared_ptr<auth::Verifier> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::VERIFIER: socket_option_value = verifier_; @@ -815,7 +753,6 @@ class ConsumerSocket : public Socket<BasePortal> { default: return SOCKET_OPTION_NOT_GET; } - return SOCKET_OPTION_GET; } @@ -827,7 +764,6 @@ class ConsumerSocket : public Socket<BasePortal> { default: return SOCKET_OPTION_NOT_GET; } - return SOCKET_OPTION_GET; } @@ -864,6 +800,29 @@ class ConsumerSocket : public Socket<BasePortal> { }); } + 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, @@ -871,14 +830,14 @@ class ConsumerSocket : public Socket<BasePortal> { // To enforce type check std::function<int(int, arg2)> func = lambda_func; int result = SOCKET_OPTION_SET; - if (transport_protocol_->isRunning()) { + if (transport_protocol_ && transport_protocol_->isRunning()) { std::mutex mtx; /* 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); @@ -898,14 +857,12 @@ class ConsumerSocket : public Socket<BasePortal> { protected: interface::ConsumerSocket *consumer_interface_; - std::shared_ptr<Portal> portal_; - 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 Name network_name_; + hicn_packet_format_t packet_format_; int interest_lifetime_; double min_window_size_; @@ -926,13 +883,12 @@ class ConsumerSocket : public Socket<BasePortal> { int rate_estimation_batching_parameter_; int rate_estimation_choice_; - bool is_async_; - // Verification parameters - std::shared_ptr<utils::Verifier> verifier_; - PARCKeyId *key_id_; + uint32_t manifest_factor_relevant_; + uint32_t manifest_factor_alert_; + std::shared_ptr<auth::Verifier> verifier_; + transport::auth::KeyId *key_id_; std::atomic_bool verify_signature_; - bool key_content_; bool reset_window_; ConsumerInterestCallback on_interest_retransmission_; @@ -940,23 +896,29 @@ class ConsumerSocket : public Socket<BasePortal> { ConsumerInterestCallback on_interest_timeout_; ConsumerInterestCallback on_interest_satisfied_; ConsumerContentObjectCallback on_content_object_input_; - ConsumerContentObjectVerificationCallback on_content_object_verification_; ConsumerTimerCallback stats_summary_; - ConsumerContentObjectVerificationFailedCallback verification_failed_callback_; + 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_; + bool content_sharing_mode_; + bool aggregated_interests_; + utils::SpinLock guard_raaqm_params_; std::string output_interface_; }; } // namespace implementation -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/implementation/socket_producer.h b/libtransport/src/implementation/socket_producer.h index a6f0f969e..c5e0929b2 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: @@ -15,9 +15,11 @@ #pragma once -#include <hicn/transport/security/signer.h> +#include <hicn/transport/auth/signer.h> #include <hicn/transport/utils/event_thread.h> #include <implementation/socket.h> +#include <protocols/prod_protocol_bytestream.h> +#include <protocols/prod_protocol_rtc.h> #include <utils/content_store.h> #include <utils/suffix_strategy.h> @@ -38,23 +40,23 @@ namespace implementation { using namespace core; using namespace interface; +using ProducerCallback = interface::ProducerSocket::Callback; -class ProducerSocket : public Socket<BasePortal>, - public BasePortal::ProducerCallback { - static constexpr uint32_t burst_size = 256; - - public: - explicit ProducerSocket(interface::ProducerSocket *producer_socket) - : producer_interface_(producer_socket), - portal_(std::make_shared<Portal>(io_service_)), +class ProducerSocket : public Socket { + private: + ProducerSocket(interface::ProducerSocket *producer_socket, int protocol, + std::shared_ptr<core::Portal> &&portal) + : Socket(std::move(portal)), + producer_interface_(producer_socket), + packet_format_(default_values::packet_format), 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), - output_buffer_(default_values::producer_socket_output_buffer_size), - async_thread_(), - registration_status_(REGISTRATION_NOT_ATTEMPTED), - making_manifest_(false), - hash_algorithm_(utils::CryptoHashType::SHA_256), - suffix_strategy_(core::NextSegmentCalculationStrategy::INCREMENTAL), + manifest_max_capacity_(default_values::manifest_max_capacity), + hash_algorithm_(auth::CryptoHashType::SHA256), + 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), @@ -65,392 +67,110 @@ class ProducerSocket : public Socket<BasePortal>, 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) {} - - virtual ~ProducerSocket() { - stop(); - if (listening_thread_.joinable()) { - listening_thread_.join(); + on_content_produced_(VOID_HANDLER), + application_callback_(nullptr) { + switch (protocol) { + case ProductionProtocolAlgorithms::RTC_PROD: + production_protocol_ = + std::make_shared<protocol::RTCProductionProtocol>(this); + break; + case ProductionProtocolAlgorithms::BYTE_STREAM: + default: + production_protocol_ = + std::make_shared<protocol::ByteStreamProductionProtocol>(this); + break; } } - interface::ProducerSocket *getInterface() { - return producer_interface_; - } - - void setInterface(interface::ProducerSocket *producer_socket) { - producer_interface_ = producer_socket; + public: + ProducerSocket(interface::ProducerSocket *producer, int protocol) + : ProducerSocket(producer, protocol, core::Portal::createShared()) { + is_async_ = true; } - void connect() override { - portal_->connect(false); - listening_thread_ = std::thread(std::bind(&ProducerSocket::listen, this)); + ProducerSocket(interface::ProducerSocket *producer, int protocol, + ::utils::EventThread &worker) + : ProducerSocket(producer, protocol, core::Portal::createShared(worker)) { } - bool isRunning() override { return !io_service_.stopped(); }; + virtual ~ProducerSocket() {} - virtual uint32_t produce(Name content_name, const uint8_t *buffer, - size_t buffer_size, bool is_last = true, - uint32_t start_offset = 0) { - return ProducerSocket::produce( - content_name, utils::MemBuf::copyBuffer(buffer, buffer_size), is_last, - start_offset); + interface::ProducerSocket *getInterface() { + return producer_interface_; } - virtual uint32_t produce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last = true, uint32_t start_offset = 0) { - if (TRANSPORT_EXPECT_FALSE(buffer->length() == 0)) { - return 0; - } - - // Copy the atomic variables to ensure they keep the same value - // during the production - std::size_t data_packet_size = data_packet_size_; - uint32_t content_object_expiry_time = content_object_expiry_time_; - utils::CryptoHashType hash_algo = hash_algorithm_; - bool making_manifest = making_manifest_; - auto suffix_strategy = utils::SuffixStrategyFactory::getSuffixStrategy( - suffix_strategy_, start_offset); - std::shared_ptr<utils::Signer> signer; - getSocketOption(GeneralTransportOptions::SIGNER, signer); - - auto buffer_size = buffer->length(); - 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; - std::shared_ptr<ContentObjectManifest> manifest; - bool is_last_manifest = false; - - // TODO Manifest may still be used for indexing - if (making_manifest && !signer) { - TRANSPORT_LOGE("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 (content_name.getType() == HNT_CONTIGUOUS_V4 || - content_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 (content_name.getType() == HNT_CONTIGUOUS_V6 || - content_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->getSignatureLength() : 0); - } else if (signer) { - format = hf_format_ah; - signature_length = signer->getSignatureLength(); - } - - 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; - - manifest.reset(ContentObjectManifest::createManifest( - content_name.setSuffix(suffix_strategy->getNextManifestSuffix()), - core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, - hash_algo, is_last_manifest, content_name, suffix_strategy_, - signer ? signer->getSignatureLength() : 0)); - manifest->setLifetime(content_object_expiry_time); - - if (is_last) { - manifest->setFinalBlockNumber(final_block_number); - } else { - manifest->setFinalBlockNumber(utils::SuffixStrategy::INVALID_SUFFIX); - } - } - - for (unsigned int packaged_segments = 0; - packaged_segments < number_of_segments; packaged_segments++) { - if (making_manifest) { - if (manifest->estimateManifestSize(2) > - data_packet_size - manifest_header_size) { - // Send the current manifest - manifest->encode(); - - // If identity set, sign manifest - if (signer) { - signer->sign(*manifest); - } - - passContentObjectToCallbacks(manifest); - TRANSPORT_LOGD("Send manifest %s", - manifest->getName().toString().c_str()); - - // Send content objects stored in the queue - while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); - TRANSPORT_LOGD( - "Send content %s", - content_queue_.front()->getName().toString().c_str()); - content_queue_.pop(); - } - - // Create new manifest. The reference to the last manifest has been - // acquired in the passContentObjectToCallbacks function, so we can - // safely release this reference - manifest.reset(ContentObjectManifest::createManifest( - content_name.setSuffix(suffix_strategy->getNextManifestSuffix()), - core::ManifestVersion::VERSION_1, - core::ManifestType::INLINE_MANIFEST, hash_algo, is_last_manifest, - content_name, suffix_strategy_, - signer ? signer->getSignatureLength() : 0)); - - manifest->setLifetime(content_object_expiry_time); - manifest->setFinalBlockNumber( - is_last ? final_block_number - : utils::SuffixStrategy::INVALID_SUFFIX); - } - } - - auto content_suffix = suffix_strategy->getNextContentSuffix(); - auto content_object = std::make_shared<ContentObject>( - content_name.setSuffix(content_suffix), format); - content_object->setLifetime(content_object_expiry_time); - - auto b = buffer->cloneOne(); - b->trimStart(free_space_for_content * packaged_segments); - b->trimEnd(b->length()); - - if (TRANSPORT_EXPECT_FALSE(packaged_segments == number_of_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(); - } - - } else { - b->append(free_space_for_content); - bytes_segmented += (int)(free_space_for_content); - } - - content_object->appendPayload(std::move(b)); - - if (making_manifest) { - using namespace std::chrono_literals; - utils::CryptoHash hash = content_object->computeDigest(hash_algo); - manifest->addSuffixHash(content_suffix, hash); - content_queue_.push(content_object); - } else { - if (signer) { - signer->sign(*content_object); - } - passContentObjectToCallbacks(content_object); - TRANSPORT_LOGD("Send content %s", - content_object->getName().toString().c_str()); - } - } - - if (making_manifest) { - if (is_last_manifest) { - manifest->setFinalManifest(is_last_manifest); - } - - manifest->encode(); - if (signer) { - signer->sign(*manifest); - } - - passContentObjectToCallbacks(manifest); - TRANSPORT_LOGD("Send manifest %s", - manifest->getName().toString().c_str()); - - while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); - TRANSPORT_LOGD("Send content %s", - content_queue_.front()->getName().toString().c_str()); - content_queue_.pop(); - } - } - - io_service_.post([this]() { - std::shared_ptr<ContentObject> co; - while (object_queue_for_callbacks_.pop(co)) { - if (on_new_segment_) { - on_new_segment_(*producer_interface_, *co); - } - - if (on_content_object_to_sign_) { - on_content_object_to_sign_(*producer_interface_, *co); - } - - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*producer_interface_, *co); - } - - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, *co); - } - } - }); - - io_service_.dispatch([this, buffer_size]() { - if (on_content_produced_) { - on_content_produced_(*producer_interface_, - std::make_error_code(std::errc(0)), buffer_size); - } - }); - - return suffix_strategy->getTotalCount(); + void setInterface(interface::ProducerSocket *producer_socket) { + producer_interface_ = producer_socket; } - virtual void produce(ContentObject &content_object) { - io_service_.dispatch([this, &content_object]() { - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*producer_interface_, - content_object); - } - }); - - output_buffer_.insert(std::static_pointer_cast<ContentObject>( - content_object.shared_from_this())); + void connect() override { portal_->connect(false); } - io_service_.dispatch([this, &content_object]() { - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, content_object); - } - }); - - portal_->sendContentObject(content_object); - } + bool isRunning() override { return production_protocol_->isRunning(); }; - virtual void produce(const uint8_t *buffer, size_t buffer_size) { - produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); + virtual uint32_t produceStream(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer, + bool is_last = true, + uint32_t start_offset = 0) { + return production_protocol_->produceStream(content_name, std::move(buffer), + is_last, start_offset); } - virtual void produce(std::unique_ptr<utils::MemBuf> &&buffer) { - // This API is meant to be used just with the RTC producer. - // Here it cannot be used since no name for the content is specified. - throw errors::NotImplementedException(); + virtual uint32_t produceStream(const Name &content_name, + const uint8_t *buffer, size_t buffer_size, + bool is_last = true, + uint32_t start_offset = 0) { + return production_protocol_->produceStream( + content_name, buffer, buffer_size, is_last, start_offset); } - virtual void asyncProduce(const Name &suffix, const uint8_t *buf, - size_t buffer_size, bool is_last = true, - uint32_t *start_offset = nullptr) { - if (!async_thread_.stopped()) { - async_thread_.add([this, suffix, buffer = buf, size = buffer_size, - is_last, start_offset]() { - if (start_offset != nullptr) { - *start_offset = ProducerSocket::produce(suffix, buffer, size, is_last, - *start_offset); - } else { - ProducerSocket::produce(suffix, buffer, size, is_last, 0); - } - }); - } + virtual uint32_t produceDatagram(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer) { + return production_protocol_->produceDatagram(content_name, + std::move(buffer)); } - void asyncProduce(const Name &suffix); - - 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 + ProducerSocket::produce(content_name, std::move(buf), - is_last, offset); - } else { - ProducerSocket::produce(content_name, std::move(buf), is_last, - offset); - } - }); - } + virtual uint32_t produceDatagram(const Name &content_name, + const uint8_t *buffer, size_t buffer_size) { + return production_protocol_->produceDatagram(content_name, buffer, + buffer_size); } - virtual void asyncProduce(ContentObject &content_object) { - if (!async_thread_.stopped()) { - auto co_ptr = std::static_pointer_cast<ContentObject>( - content_object.shared_from_this()); - async_thread_.add([this, content_object = std::move(co_ptr)]() { - ProducerSocket::produce(*content_object); - }); - } + void produce(ContentObject &content_object) { + production_protocol_->produce(content_object); } - virtual void registerPrefix(const Prefix &producer_namespace) { - served_namespaces_.push_back(producer_namespace); - } + void sendMapme() { production_protocol_->sendMapme(); } - void serveForever() { - if (listening_thread_.joinable()) { - listening_thread_.join(); - } + void registerPrefix(const Prefix &producer_namespace) { + portal_->registerRoute(producer_namespace); } - void stop() { portal_->stopEventsLoop(); } - - asio::io_service &getIoService() override { return portal_->getIoService(); }; + void start() { production_protocol_->start(); } + void stop() { production_protocol_->stop(); } - virtual void onInterest(Interest &interest) { - if (on_interest_input_) { - on_interest_input_(*producer_interface_, interest); - } - - const std::shared_ptr<ContentObject> content_object = - output_buffer_.find(interest); - - if (content_object) { - if (on_interest_satisfied_output_buffer_) { - on_interest_satisfied_output_buffer_(*producer_interface_, interest); - } + using Socket::getSocketOption; + using Socket::setSocketOption; - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, *content_object); - } + 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; + } - portal_->sendContentObject(*content_object); - } else { - if (on_interest_process_) { - on_interest_process_(*producer_interface_, interest); - } - } + return SOCKET_OPTION_SET; + }); } - virtual void onInterest(Interest::Ptr &&interest) override { - onInterest(*interest); - }; - - virtual void onError(std::error_code ec) override {} - virtual int setSocketOption(int socket_option_key, uint32_t socket_option_value) { switch (socket_option_key) { @@ -461,14 +181,29 @@ class ProducerSocket : public Socket<BasePortal>, } break; + case GeneralTransportOptions::MANIFEST_MAX_CAPACITY: + manifest_max_capacity_ = 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: - output_buffer_.setLimit(socket_option_value); + production_protocol_->setOutputBufferSize(socket_option_value); break; case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: content_object_expiry_time_ = socket_option_value; break; + case GeneralTransportOptions::PACKET_FORMAT: + packet_format_ = socket_option_value; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -533,6 +268,12 @@ class ProducerSocket : public Socket<BasePortal>, break; } + case ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN: + if (socket_option_value == VOID_HANDLER) { + on_content_object_to_sign_ = VOID_HANDLER; + break; + } + default: return SOCKET_OPTION_NOT_SET; } @@ -543,8 +284,8 @@ class ProducerSocket : public Socket<BasePortal>, 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: @@ -559,19 +300,6 @@ class ProducerSocket : public Socket<BasePortal>, return SOCKET_OPTION_NOT_SET; } - virtual int setSocketOption(int socket_option_key, - std::list<Prefix> socket_option_value) { - switch (socket_option_key) { - case GeneralTransportOptions::NETWORK_NAME: - served_namespaces_ = socket_option_value; - break; - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - } - virtual int setSocketOption( int socket_option_key, interface::ProducerContentObjectCallback socket_option_value) { @@ -594,6 +322,10 @@ class ProducerSocket : public Socket<BasePortal>, on_content_object_output_ = socket_option_value; break; + case ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN: + on_content_object_to_sign_ = socket_option_value; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -663,7 +395,7 @@ class ProducerSocket : public Socket<BasePortal>, } virtual int setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value) { + auth::CryptoHashType socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::HASH_ALGORITHM: hash_algorithm_ = socket_option_value; @@ -675,11 +407,12 @@ class ProducerSocket : public Socket<BasePortal>, return SOCKET_OPTION_SET; } - virtual int setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value) { + virtual int setSocketOption( + int socket_option_key, + const std::shared_ptr<utils::SuffixStrategy> &socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::CRYPTO_SUITE: - crypto_suite_ = socket_option_value; + case GeneralTransportOptions::SUFFIX_STRATEGY: + suffix_strategy_ = socket_option_value; break; default: return SOCKET_OPTION_NOT_SET; @@ -690,7 +423,7 @@ class ProducerSocket : public Socket<BasePortal>, virtual int setSocketOption( int socket_option_key, - const std::shared_ptr<utils::Signer> &socket_option_value) { + const std::shared_ptr<auth::Signer> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SIGNER: { utils::SpinLock::Acquire locked(signer_lock_); @@ -704,21 +437,68 @@ class ProducerSocket : public Socket<BasePortal>, return SOCKET_OPTION_SET; } + virtual int setSocketOption( + int socket_option_key, + const std::shared_ptr<auth::Verifier> &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::VERIFIER: + verifier_.reset(); + verifier_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_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::MANIFEST_MAX_CAPACITY: + socket_option_value = (uint32_t)manifest_max_capacity_; + break; + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: - socket_option_value = (uint32_t)output_buffer_.getLimit(); + socket_option_value = + (uint32_t)production_protocol_->getOutputBufferSize(); break; case GeneralTransportOptions::DATA_PACKET_SIZE: 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; + case GeneralTransportOptions::PACKET_FORMAT: + socket_option_value = packet_format_; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -729,22 +509,12 @@ class ProducerSocket : public Socket<BasePortal>, virtual int getSocketOption(int socket_option_key, bool &socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::MAKE_MANIFEST: - socket_option_value = making_manifest_; + case GeneralTransportOptions::ASYNC_MODE: + socket_option_value = is_async_; break; - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - } - - virtual int getSocketOption(int socket_option_key, - std::list<Prefix> &socket_option_value) { - switch (socket_option_key) { - case GeneralTransportOptions::NETWORK_NAME: - socket_option_value = served_namespaces_; + case RtcTransportOptions::AGGREGATED_DATA: + socket_option_value = aggregated_data_; break; default: @@ -776,6 +546,10 @@ class ProducerSocket : public Socket<BasePortal>, *socket_option_value = &on_content_object_output_; break; + case ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN: + *socket_option_value = &on_content_object_to_sign_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -828,11 +602,11 @@ class ProducerSocket : public Socket<BasePortal>, *socket_option_value = &on_interest_inserted_input_buffer_; break; - case CACHE_HIT: + case ProducerCallbacksOptions::CACHE_HIT: *socket_option_value = &on_interest_satisfied_output_buffer_; break; - case CACHE_MISS: + case ProducerCallbacksOptions::CACHE_MISS: *socket_option_value = &on_interest_process_; break; @@ -845,21 +619,7 @@ class ProducerSocket : public Socket<BasePortal>, } virtual int getSocketOption(int socket_option_key, - std::shared_ptr<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, - utils::CryptoHashType &socket_option_value) { + auth::CryptoHashType &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::HASH_ALGORITHM: socket_option_value = hash_algorithm_; @@ -871,22 +631,22 @@ class ProducerSocket : public Socket<BasePortal>, return SOCKET_OPTION_GET; } - virtual int getSocketOption(int socket_option_key, - utils::CryptoSuite &socket_option_value) { + virtual int getSocketOption( + int socket_option_key, + std::shared_ptr<utils::SuffixStrategy> &socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::HASH_ALGORITHM: - socket_option_value = crypto_suite_; + case GeneralTransportOptions::SUFFIX_STRATEGY: + socket_option_value = suffix_strategy_; break; default: return SOCKET_OPTION_NOT_GET; } - return SOCKET_OPTION_GET; } virtual int getSocketOption( int socket_option_key, - std::shared_ptr<utils::Signer> &socket_option_value) { + std::shared_ptr<auth::Signer> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SIGNER: { utils::SpinLock::Acquire locked(signer_lock_); @@ -899,27 +659,63 @@ class ProducerSocket : public Socket<BasePortal>, return SOCKET_OPTION_GET; } + int getSocketOption(int socket_option_key, + std::shared_ptr<auth::Verifier> &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::VERIFIER: + socket_option_value = verifier_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_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 // function reschedule the function on it template <typename Lambda, typename arg2> - int rescheduleOnIOService(int socket_option_key, arg2 socket_option_value, - Lambda lambda_func) { + int rescheduleOnIOServiceWithReference(int socket_option_key, + arg2 &socket_option_value, + Lambda lambda_func) { // To enforce type check - std::function<int(int, arg2)> func = lambda_func; + std::function<int(int, arg2 &)> func = lambda_func; int result = SOCKET_OPTION_SET; - if (listening_thread_.joinable() && - std::this_thread::get_id() != listening_thread_.get_id()) { + if (production_protocol_ && production_protocol_->isRunning()) { std::mutex mtx; /* Condition variable for the wait */ std::condition_variable cv; + bool done = false; - io_service_.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); @@ -939,21 +735,19 @@ class ProducerSocket : public Socket<BasePortal>, // If the thread calling lambda_func is not the same of io_service, this // function reschedule the function on it template <typename Lambda, typename arg2> - int rescheduleOnIOServiceWithReference(int socket_option_key, - arg2 &socket_option_value, - Lambda lambda_func) { + int rescheduleOnIOService(int socket_option_key, arg2 socket_option_value, + Lambda lambda_func) { // To enforce type check - std::function<int(int, arg2 &)> func = lambda_func; + std::function<int(int, arg2)> func = lambda_func; int result = SOCKET_OPTION_SET; - if (listening_thread_.joinable() && - std::this_thread::get_id() != this->listening_thread_.get_id()) { + if (production_protocol_ && production_protocol_->isRunning()) { std::mutex mtx; /* Condition variable for the wait */ std::condition_variable cv; - bool done = false; - io_service_.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); @@ -973,39 +767,24 @@ class ProducerSocket : public Socket<BasePortal>, // Threads protected: interface::ProducerSocket *producer_interface_; - std::thread listening_thread_; - asio::io_service io_service_; - std::shared_ptr<Portal> portal_; + std::atomic<hicn_packet_format_t> packet_format_; std::atomic<size_t> data_packet_size_; - std::list<Prefix> - served_namespaces_; // No need to be threadsafe, this is always modified - // by the application thread + std::atomic<size_t> max_segment_size_; std::atomic<uint32_t> content_object_expiry_time_; - utils::CircularFifo<std::shared_ptr<ContentObject>, 2048> - object_queue_for_callbacks_; - - // buffers - // ContentStore is thread-safe - utils::ContentStore output_buffer_; - - utils::EventThread async_thread_; - int registration_status_; - - std::atomic<bool> making_manifest_; + std::atomic<uint32_t> manifest_max_capacity_; + std::atomic<auth::CryptoHashType> hash_algorithm_; + std::atomic<auth::CryptoSuite> crypto_suite_; + utils::SpinLock signer_lock_; + std::shared_ptr<utils::SuffixStrategy> suffix_strategy_; - // map for storing sequence numbers for several calls of the publish - // function - std::unordered_map<Name, std::unordered_map<int, uint32_t>> seq_number_map_; + std::shared_ptr<protocol::ProductionProtocol> production_protocol_; - std::atomic<utils::CryptoHashType> hash_algorithm_; - std::atomic<utils::CryptoSuite> crypto_suite_; - utils::SpinLock signer_lock_; - std::shared_ptr<utils::Signer> signer_; - core::NextSegmentCalculationStrategy suffix_strategy_; + // RTC transport + bool aggregated_data_; - // While manifests are being built, contents are stored in a queue - std::queue<std::shared_ptr<ContentObject>> content_queue_; + // FEC setting + std::string fec_setting_; // callbacks ProducerInterestCallback on_interest_input_; @@ -1022,62 +801,7 @@ class ProducerSocket : public Socket<BasePortal>, ProducerContentCallback on_content_produced_; - private: - void listen() { - 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_->runEventsLoop(); - } - - void scheduleSendBurst() { - io_service_.post([this]() { - std::shared_ptr<ContentObject> co; - - for (uint32_t i = 0; i < burst_size; i++) { - if (object_queue_for_callbacks_.pop(co)) { - if (on_new_segment_) { - on_new_segment_(*producer_interface_, *co); - } - - if (on_content_object_to_sign_) { - on_content_object_to_sign_(*producer_interface_, *co); - } - - if (on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_(*producer_interface_, *co); - } - - if (on_content_object_output_) { - on_content_object_output_(*producer_interface_, *co); - } - } else { - break; - } - } - }); - } - - void passContentObjectToCallbacks( - const std::shared_ptr<ContentObject> &content_object) { - output_buffer_.insert(content_object); - portal_->sendContentObject(*content_object); - object_queue_for_callbacks_.push(std::move(content_object)); - - if (object_queue_for_callbacks_.size() >= burst_size) { - scheduleSendBurst(); - } - } + 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 deleted file mode 100644 index 9ef79ca23..000000000 --- a/libtransport/src/implementation/tls_rtc_socket_producer.cc +++ /dev/null @@ -1,210 +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/core/interest.h> -#include <hicn/transport/interfaces/p2psecure_socket_producer.h> - -#include <implementation/p2psecure_socket_producer.h> -#include <implementation/tls_rtc_socket_producer.h> - -#include <openssl/bio.h> -#include <openssl/rand.h> -#include <openssl/ssl.h> - -namespace transport { -namespace implementation { - -int TLSRTCProducerSocket::read(BIO *b, char *buf, size_t size, - size_t *readbytes) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = TLSRTCProducerSocket::readOld(b, buf, (int)size); - - if (ret <= 0) { - *readbytes = 0; - return ret; - } - - *readbytes = (size_t)ret; - - return 1; -} - -int TLSRTCProducerSocket::readOld(BIO *b, char *buf, int size) { - TLSRTCProducerSocket *socket; - socket = (TLSRTCProducerSocket *)BIO_get_data(b); - - std::unique_lock<std::mutex> lck(socket->mtx_); - if (!socket->something_to_read_) { - (socket->cv_).wait(lck); - } - - utils::MemBuf *membuf = socket->handshake_packet_->next(); - int size_to_read; - - if ((int)membuf->length() > size) { - size_to_read = size; - } else { - size_to_read = membuf->length(); - socket->something_to_read_ = false; - } - - std::memcpy(buf, membuf->data(), size_to_read); - membuf->trimStart(size_to_read); - - return size_to_read; -} - -int TLSRTCProducerSocket::write(BIO *b, const char *buf, size_t size, - size_t *written) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = TLSRTCProducerSocket::writeOld(b, buf, (int)size); - - if (ret <= 0) { - *written = 0; - return ret; - } - - *written = (size_t)ret; - - return 1; -} - -int TLSRTCProducerSocket::writeOld(BIO *b, const char *buf, int num) { - TLSRTCProducerSocket *socket; - socket = (TLSRTCProducerSocket *)BIO_get_data(b); - - if (socket->getHandshakeState() != SERVER_FINISHED && socket->first_) { - bool making_manifest = socket->parent_->making_manifest_; - - socket->tls_chunks_--; - socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - false); - socket->parent_->ProducerSocket::produce( - socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, 0); - socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - making_manifest); - socket->first_ = false; - - } else { - std::unique_ptr<utils::MemBuf> mbuf = - utils::MemBuf::copyBuffer(buf, (std::size_t)num, 0, 0); - auto a = mbuf.release(); - - socket->async_thread_.add([socket = socket, a]() { - socket->to_call_oncontentproduced_--; - auto mbuf = std::unique_ptr<utils::MemBuf>(a); - - socket->RTCProducerSocket::produce(std::move(mbuf)); - - ProducerContentCallback on_content_produced_application; - socket->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, - on_content_produced_application); - - if (socket->to_call_oncontentproduced_ == 0 && - on_content_produced_application) { - on_content_produced_application( - (transport::interface::ProducerSocket &)(*socket->getInterface()), - std::error_code(), 0); - } - }); - } - - return num; -} - -TLSRTCProducerSocket::TLSRTCProducerSocket( - interface::ProducerSocket *producer_socket, P2PSecureProducerSocket *parent, - const Name &handshake_name) - : ProducerSocket(producer_socket), - RTCProducerSocket(producer_socket), - TLSProducerSocket(producer_socket, parent, handshake_name) { - BIO_METHOD *bio_meth = - BIO_meth_new(BIO_TYPE_ACCEPT, "secure rtc producer socket"); - BIO_meth_set_read(bio_meth, TLSRTCProducerSocket::readOld); - BIO_meth_set_write(bio_meth, TLSRTCProducerSocket::writeOld); - BIO_meth_set_ctrl(bio_meth, TLSProducerSocket::ctrl); - BIO *bio = BIO_new(bio_meth); - BIO_set_init(bio, 1); - BIO_set_data(bio, this); - SSL_set_bio(ssl_, bio, bio); -} - -void TLSRTCProducerSocket::accept() { - HandshakeState handshake_state = getHandshakeState(); - - if (handshake_state == UNINITIATED || handshake_state == CLIENT_HELLO) { - tls_chunks_ = 1; - int result = SSL_accept(ssl_); - - if (result != 1) - throw errors::RuntimeException("Unable to perform client handshake"); - } - - TRANSPORT_LOGD("Handshake performed!"); - - parent_->list_producers.push_front( - std::move(parent_->map_producers[handshake_name_])); - parent_->map_producers.erase(handshake_name_); - - ProducerInterestCallback on_interest_process_decrypted; - getSocketOption(ProducerCallbacksOptions::CACHE_MISS, - on_interest_process_decrypted); - - if (on_interest_process_decrypted) { - Interest inter(std::move(handshake_packet_)); - on_interest_process_decrypted( - (transport::interface::ProducerSocket &)(*getInterface()), inter); - } - - parent_->cv_.notify_one(); -} - -int TLSRTCProducerSocket::async_accept() { - if (!async_thread_.stopped()) { - async_thread_.add([this]() { this->TLSRTCProducerSocket::accept(); }); - } else { - throw errors::RuntimeException( - "Async thread not running, impossible to perform handshake"); - } - - return 1; -} - -void TLSRTCProducerSocket::produce(std::unique_ptr<utils::MemBuf> &&buffer) { - HandshakeState handshake_state = getHandshakeState(); - - if (handshake_state != SERVER_FINISHED) { - throw errors::RuntimeException( - "New handshake on the same P2P secure producer socket not supported"); - } - - size_t buf_size = buffer->length(); - tls_chunks_ = ceil((float)buf_size / (float)SSL3_RT_MAX_PLAIN_LENGTH); - to_call_oncontentproduced_ = tls_chunks_; - - SSL_write(ssl_, buffer->data(), buf_size); - BIO *wbio = SSL_get_wbio(ssl_); - int i = BIO_flush(wbio); - (void)i; // To shut up gcc 5 -} - -} // namespace implementation -} // namespace transport diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.h b/libtransport/src/implementation/tls_rtc_socket_producer.h deleted file mode 100644 index 685c91244..000000000 --- a/libtransport/src/implementation/tls_rtc_socket_producer.h +++ /dev/null @@ -1,58 +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 <implementation/rtc_socket_producer.h> -#include <implementation/tls_socket_producer.h> - -namespace transport { -namespace implementation { - -class P2PSecureProducerSocket; - -class TLSRTCProducerSocket : public RTCProducerSocket, - public TLSProducerSocket { - friend class P2PSecureProducerSocket; - - public: - explicit TLSRTCProducerSocket(interface::ProducerSocket *producer_socket, - P2PSecureProducerSocket *parent, - const Name &handshake_name); - - ~TLSRTCProducerSocket() = default; - - void produce(std::unique_ptr<utils::MemBuf> &&buffer) override; - - void accept() override; - - int async_accept() override; - - using TLSProducerSocket::onInterest; - using TLSProducerSocket::produce; - - protected: - static int read(BIO *b, char *buf, size_t size, size_t *readbytes); - - static int readOld(BIO *h, char *buf, int size); - - static int write(BIO *b, const char *buf, size_t size, size_t *written); - - static int writeOld(BIO *h, const char *buf, int num); -}; - -} // namespace implementation - -} // end namespace transport diff --git a/libtransport/src/implementation/tls_socket_consumer.cc b/libtransport/src/implementation/tls_socket_consumer.cc deleted file mode 100644 index 1be6f41a7..000000000 --- a/libtransport/src/implementation/tls_socket_consumer.cc +++ /dev/null @@ -1,368 +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 <implementation/tls_socket_consumer.h> -#include <openssl/bio.h> -#include <openssl/ssl.h> -#include <openssl/tls1.h> - -#include <random> - -namespace transport { -namespace implementation { - -void TLSConsumerSocket::setInterestPayload(interface::ConsumerSocket &c, - const core::Interest &interest) { - Interest &int2 = const_cast<Interest &>(interest); - random_suffix_ = int2.getName().getSuffix(); - - if (payload_ != NULL) int2.appendPayload(std::move(payload_)); -} - -/* Return the number of read bytes in the return param */ -int readOldTLS(BIO *b, char *buf, int size) { - if (size < 0) return size; - - TLSConsumerSocket *socket; - socket = (TLSConsumerSocket *)BIO_get_data(b); - - std::unique_lock<std::mutex> lck(socket->mtx_); - - if (!socket->something_to_read_) { - if (!socket->transport_protocol_->isRunning()) { - socket->network_name_.setSuffix(socket->random_suffix_); - socket->ConsumerSocket::asyncConsume(socket->network_name_); - } - - if (!socket->something_to_read_) socket->cv_.wait(lck); - } - - size_t size_to_read, read; - size_t chain_size = socket->head_->length(); - - if (socket->head_->isChained()) - chain_size = socket->head_->computeChainDataLength(); - - if (chain_size > (size_t)size) { - read = size_to_read = (size_t)size; - } else { - read = size_to_read = chain_size; - socket->something_to_read_ = false; - } - - while (size_to_read) { - if (socket->head_->length() < size_to_read) { - std::memcpy(buf, socket->head_->data(), socket->head_->length()); - size_to_read -= socket->head_->length(); - buf += socket->head_->length(); - socket->head_ = socket->head_->pop(); - } else { - std::memcpy(buf, socket->head_->data(), size_to_read); - socket->head_->trimStart(size_to_read); - size_to_read = 0; - } - } - - return read; -} - -/* Return the number of read bytes in readbytes */ -int readTLS(BIO *b, char *buf, size_t size, size_t *readbytes) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = readOldTLS(b, buf, (int)size); - - if (ret <= 0) { - *readbytes = 0; - return ret; - } - - *readbytes = (size_t)ret; - - return 1; -} - -/* Return the number of written bytes in the return param */ -int writeOldTLS(BIO *b, const char *buf, int num) { - TLSConsumerSocket *socket; - socket = (TLSConsumerSocket *)BIO_get_data(b); - - socket->payload_ = utils::MemBuf::copyBuffer(buf, num); - - socket->ConsumerSocket::setSocketOption( - ConsumerCallbacksOptions::INTEREST_OUTPUT, - (ConsumerInterestCallback)std::bind( - &TLSConsumerSocket::setInterestPayload, socket, std::placeholders::_1, - std::placeholders::_2)); - - return num; -} - -/* Return the number of written bytes in written */ -int writeTLS(BIO *b, const char *buf, size_t size, size_t *written) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = writeOldTLS(b, buf, (int)size); - - if (ret <= 0) { - *written = 0; - return ret; - } - - *written = (size_t)ret; - - return 1; -} - -long ctrlTLS(BIO *b, int cmd, long num, void *ptr) { return 1; } - -TLSConsumerSocket::TLSConsumerSocket(interface::ConsumerSocket *consumer_socket, - int protocol, SSL *ssl) - : ConsumerSocket(consumer_socket, protocol), - name_(), - buf_pool_(), - decrypted_content_(), - payload_(), - head_(), - something_to_read_(false), - content_downloaded_(false), - random_suffix_(), - producer_namespace_(), - read_callback_decrypted_(), - mtx_(), - cv_(), - async_downloader_tls_() { - /* Create the (d)TLS state */ - const SSL_METHOD *meth = TLS_client_method(); - ctx_ = SSL_CTX_new(meth); - - int result = - SSL_CTX_set_ciphersuites(ctx_, - "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_" - "SHA256:TLS_AES_128_GCM_SHA256"); - if (result != 1) { - throw errors::RuntimeException( - "Unable to set cipher list on TLS subsystem. Aborting."); - } - - SSL_CTX_set_min_proto_version(ctx_, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ctx_, TLS1_3_VERSION); - SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, NULL); - SSL_CTX_set_ssl_version(ctx_, meth); - - ssl_ = ssl; - - BIO_METHOD *bio_meth = - BIO_meth_new(BIO_TYPE_CONNECT, "secure consumer socket"); - BIO_meth_set_read(bio_meth, readOldTLS); - BIO_meth_set_write(bio_meth, writeOldTLS); - BIO_meth_set_ctrl(bio_meth, ctrlTLS); - BIO *bio = BIO_new(bio_meth); - BIO_set_init(bio, 1); - BIO_set_data(bio, this); - SSL_set_bio(ssl_, bio, bio); - - std::default_random_engine generator; - std::uniform_int_distribution<int> distribution( - 1, std::numeric_limits<uint32_t>::max()); - random_suffix_ = 0; - - this->ConsumerSocket::setSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, - this); -}; - -/* The producer interface is not owned by the application, so is TLSSocket task - * to deallocate the memory */ -TLSConsumerSocket::~TLSConsumerSocket() { delete consumer_interface_; } - -int TLSConsumerSocket::consume(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 consume(name); -} - -int TLSConsumerSocket::consume(const Name &name) { - if (transport_protocol_->isRunning()) { - return CONSUMER_BUSY; - } - - if ((SSL_in_before(this->ssl_) || SSL_in_init(this->ssl_))) { - throw errors::RuntimeException("Handshake not performed"); - } - - return download_content(name); -} - -int TLSConsumerSocket::download_content(const Name &name) { - network_name_ = name; - network_name_.setSuffix(0); - something_to_read_ = false; - content_downloaded_ = false; - - std::size_t max_buffer_size = read_callback_decrypted_->maxBufferSize(); - std::size_t buffer_size = read_callback_decrypted_->maxBufferSize() + SSL3_RT_MAX_PLAIN_LENGTH; - decrypted_content_ = utils::MemBuf::createCombined(buffer_size); - int result = -1; - std::size_t size = 0; - - while (!content_downloaded_ || something_to_read_) { - result = SSL_read( - this->ssl_, decrypted_content_->writableTail(), SSL3_RT_MAX_PLAIN_LENGTH); - - /* SSL_read returns the data only if there were SSL3_RT_MAX_PLAIN_LENGTH of - * the data has been fully downloaded */ - - /* ASSERT((result < SSL3_RT_MAX_PLAIN_LENGTH && content_downloaded_) || */ - /* result == SSL3_RT_MAX_PLAIN_LENGTH); */ - - if (result >= 0) { - size += result; - decrypted_content_->append(result); - } else { - throw errors::RuntimeException("Unable to download content"); - } - - if (decrypted_content_->length() >= max_buffer_size) { - if (read_callback_decrypted_->isBufferMovable()) { - /* No need to perform an additional copy. The whole buffer will be - * tranferred to the application. */ - read_callback_decrypted_->readBufferAvailable( - std::move(decrypted_content_)); - decrypted_content_ = utils::MemBuf::create(buffer_size); - } else { - /* The buffer will be copied into the application-provided buffer */ - uint8_t *buffer; - std::size_t length; - std::size_t total_length = decrypted_content_->length(); - - while (decrypted_content_->length()) { - buffer = nullptr; - length = 0; - read_callback_decrypted_->getReadBuffer(&buffer, &length); - - if (!buffer || !length) { - throw errors::RuntimeException( - "Invalid buffer provided by the application."); - } - - auto to_copy = std::min(decrypted_content_->length(), length); - std::memcpy(buffer, decrypted_content_->data(), to_copy); - decrypted_content_->trimStart(to_copy); - } - - read_callback_decrypted_->readDataAvailable(total_length); - decrypted_content_->clear(); - } - } - } - - read_callback_decrypted_->readSuccess(size); - - 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; -} - -int TLSConsumerSocket::setSocketOption(int socket_option_key, - ReadCallback *socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, ReadCallback *socket_option_value) -> int { - switch (socket_option_key) { - case ConsumerCallbacksOptions::READ_CALLBACK: - read_callback_decrypted_ = socket_option_value; - break; - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); -} - -void TLSConsumerSocket::getReadBuffer(uint8_t **application_buffer, - size_t *max_length) {} - -void TLSConsumerSocket::readDataAvailable(size_t length) noexcept {} - -size_t TLSConsumerSocket::maxBufferSize() const { - return SSL3_RT_MAX_PLAIN_LENGTH; -} - -void TLSConsumerSocket::readBufferAvailable( - std::unique_ptr<utils::MemBuf> &&buffer) noexcept { - std::unique_lock<std::mutex> lck(this->mtx_); - - if (head_) { - head_->prependChain(std::move(buffer)); - } else { - head_ = std::move(buffer); - } - - something_to_read_ = true; - cv_.notify_one(); -} - -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_); - content_downloaded_ = true; - something_to_read_ = true; - cv_.notify_one(); -} - -bool TLSConsumerSocket::isBufferMovable() noexcept { return true; } - -} // namespace implementation -} // namespace transport diff --git a/libtransport/src/implementation/tls_socket_consumer.h b/libtransport/src/implementation/tls_socket_consumer.h deleted file mode 100644 index 1c5df346a..000000000 --- a/libtransport/src/implementation/tls_socket_consumer.h +++ /dev/null @@ -1,116 +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/interfaces/socket_consumer.h> - -#include <implementation/socket_consumer.h> - -#include <openssl/ssl.h> - -namespace transport { -namespace implementation { - -class TLSConsumerSocket : public ConsumerSocket, - public interface::ConsumerSocket::ReadCallback { - /* Return the number of read bytes in readbytes */ - friend int readTLS(BIO *b, char *buf, size_t size, size_t *readbytes); - - /* Return the number of read bytes in the return param */ - friend int readOldTLS(BIO *h, char *buf, int size); - - /* Return the number of written bytes in written */ - friend int writeTLS(BIO *b, const char *buf, size_t size, size_t *written); - - /* Return the number of written bytes in the return param */ - friend int writeOldTLS(BIO *h, const char *buf, int num); - - friend long ctrlTLS(BIO *b, int cmd, long num, void *ptr); - - public: - explicit TLSConsumerSocket(interface::ConsumerSocket *consumer_socket, - int protocol, SSL *ssl_); - - ~TLSConsumerSocket(); - - 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( - int socket_option_key, - interface::ConsumerSocket::ReadCallback *socket_option_value) override; - - using ConsumerSocket::getSocketOption; - using ConsumerSocket::setSocketOption; - - protected: - /* Callback invoked once an interest has been received and its payload - * decrypted */ - ConsumerInterestCallback on_interest_input_decrypted_; - ConsumerInterestCallback on_interest_process_decrypted_; - - private: - Name name_; - /* SSL handle */ - SSL *ssl_; - SSL_CTX *ctx_; - /* Chain of MemBuf to be used as a temporary buffer to pass descypted data - * from the underlying layer to the application */ - utils::ObjectPool<utils::MemBuf> buf_pool_; - std::unique_ptr<utils::MemBuf> decrypted_content_; - /* Chain of MemBuf holding the payload to be written into interest or data */ - std::unique_ptr<utils::MemBuf> payload_; - /* Chain of MemBuf holding the data retrieved from the underlying layer */ - std::unique_ptr<utils::MemBuf> head_; - bool something_to_read_; - bool content_downloaded_; - uint32_t random_suffix_; - Prefix producer_namespace_; - interface::ConsumerSocket::ReadCallback *read_callback_decrypted_; - std::mutex mtx_; - /* Condition variable for the wait */ - std::condition_variable cv_; - utils::EventThread async_downloader_tls_; - - void setInterestPayload(interface::ConsumerSocket &c, - const core::Interest &interest); - - virtual void getReadBuffer(uint8_t **application_buffer, - size_t *max_length) override; - - virtual void readDataAvailable(size_t length) noexcept override; - - virtual size_t maxBufferSize() const override; - - virtual void readBufferAvailable( - std::unique_ptr<utils::MemBuf> &&buffer) noexcept override; - - virtual void readError(const std::error_code ec) noexcept override; - - virtual void readSuccess(std::size_t total_size) noexcept override; - - virtual bool isBufferMovable() noexcept override; - - int download_content(const Name &name); -}; - -} // namespace implementation -} // end namespace transport diff --git a/libtransport/src/implementation/tls_socket_producer.cc b/libtransport/src/implementation/tls_socket_producer.cc deleted file mode 100644 index 339a1ad58..000000000 --- a/libtransport/src/implementation/tls_socket_producer.cc +++ /dev/null @@ -1,640 +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 <hicn/transport/interfaces/socket_producer.h> - -#include <implementation/p2psecure_socket_producer.h> -#include <implementation/tls_socket_producer.h> - -#include <openssl/bio.h> -#include <openssl/rand.h> -#include <openssl/ssl.h> - -namespace transport { -namespace implementation { - -/* Return the number of read bytes in readbytes */ -int TLSProducerSocket::read(BIO *b, char *buf, size_t size, size_t *readbytes) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = TLSProducerSocket::readOld(b, buf, (int)size); - - if (ret <= 0) { - *readbytes = 0; - return ret; - } - - *readbytes = (size_t)ret; - - return 1; -} - -/* Return the number of read bytes in the return param */ -int TLSProducerSocket::readOld(BIO *b, char *buf, int size) { - TLSProducerSocket *socket; - socket = (TLSProducerSocket *)BIO_get_data(b); - - std::unique_lock<std::mutex> lck(socket->mtx_); - - if (!socket->something_to_read_) { - (socket->cv_).wait(lck); - } - - /* Either there already is something to read, or the thread has been waken up. - * We must return the payload in the interest anyway */ - utils::MemBuf *membuf = socket->handshake_packet_->next(); - int size_to_read; - - if ((int)membuf->length() > size) { - size_to_read = size; - } else { - size_to_read = membuf->length(); - socket->something_to_read_ = false; - } - - std::memcpy(buf, membuf->data(), size_to_read); - membuf->trimStart(size_to_read); - - return size_to_read; -} - -/* Return the number of written bytes in written */ -int TLSProducerSocket::write(BIO *b, const char *buf, size_t size, - size_t *written) { - int ret; - - if (size > INT_MAX) size = INT_MAX; - - ret = TLSProducerSocket::writeOld(b, buf, (int)size); - - if (ret <= 0) { - *written = 0; - return ret; - } - - *written = (size_t)ret; - - return 1; -} - -/* Return the number of written bytes in the return param */ -int TLSProducerSocket::writeOld(BIO *b, const char *buf, int num) { - TLSProducerSocket *socket; - socket = (TLSProducerSocket *)BIO_get_data(b); - - if (socket->getHandshakeState() != SERVER_FINISHED && socket->first_) { - bool making_manifest = socket->parent_->making_manifest_; - - //! socket->tls_chunks_ corresponds to is_last - socket->tls_chunks_--; - socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - false); - socket->parent_->ProducerSocket::produce( - socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, - socket->last_segment_); - socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - making_manifest); - socket->first_ = false; - } else { - socket->still_writing_ = true; - - std::unique_ptr<utils::MemBuf> mbuf = - utils::MemBuf::copyBuffer(buf, (std::size_t)num, 0, 0); - auto a = mbuf.release(); - - socket->async_thread_.add([socket = socket, a]() { - auto mbuf = std::unique_ptr<utils::MemBuf>(a); - - socket->tls_chunks_--; - socket->to_call_oncontentproduced_--; - - socket->last_segment_ += socket->ProducerSocket::produce( - socket->name_, std::move(mbuf), socket->tls_chunks_ == 0, - socket->last_segment_); - - ProducerContentCallback on_content_produced_application; - socket->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, - on_content_produced_application); - - if (socket->to_call_oncontentproduced_ == 0 && - on_content_produced_application) { - on_content_produced_application(*socket->getInterface(), - std::error_code(), 0); - } - }); - } - - return num; -} - -TLSProducerSocket::TLSProducerSocket(interface::ProducerSocket *producer_socket, - P2PSecureProducerSocket *parent, - const Name &handshake_name) - : ProducerSocket(producer_socket), - on_content_produced_application_(), - mtx_(), - cv_(), - something_to_read_(false), - handshake_state_(UNINITIATED), - name_(), - handshake_packet_(), - last_segment_(0), - parent_(parent), - first_(true), - handshake_name_(handshake_name), - tls_chunks_(0), - to_call_oncontentproduced_(0), - still_writing_(false), - encryption_thread_() { - const SSL_METHOD *meth = TLS_server_method(); - ctx_ = SSL_CTX_new(meth); - - /* Setup SSL context (identity and parameter to use TLS 1.3) */ - SSL_CTX_use_certificate(ctx_, parent->cert_509_); - SSL_CTX_use_PrivateKey(ctx_, parent->pkey_rsa_); - - int result = - SSL_CTX_set_ciphersuites(ctx_, - "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_" - "SHA256:TLS_AES_128_GCM_SHA256"); - - if (result != 1) { - throw errors::RuntimeException( - "Unable to set cipher list on TLS subsystem. Aborting."); - } - - // We force it to be TLS 1.3 - SSL_CTX_set_min_proto_version(ctx_, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ctx_, TLS1_3_VERSION); - SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, NULL); - SSL_CTX_set_num_tickets(ctx_, 0); - - result = SSL_CTX_add_custom_ext( - ctx_, 100, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, - TLSProducerSocket::addHicnKeyIdCb, TLSProducerSocket::freeHicnKeyIdCb, - this, TLSProducerSocket::parseHicnKeyIdCb, NULL); - - ssl_ = SSL_new(ctx_); - - /* Setup this producer socker as the bio that TLS will use to write and read - * data (in stream mode) */ - BIO_METHOD *bio_meth = - BIO_meth_new(BIO_TYPE_ACCEPT, "secure producer socket"); - BIO_meth_set_read(bio_meth, TLSProducerSocket::readOld); - BIO_meth_set_write(bio_meth, TLSProducerSocket::writeOld); - BIO_meth_set_ctrl(bio_meth, TLSProducerSocket::ctrl); - BIO *bio = BIO_new(bio_meth); - BIO_set_init(bio, 1); - BIO_set_data(bio, this); - SSL_set_bio(ssl_, bio, bio); - - /* Set the callback so that when an interest is received we catch it and we - * decrypt the payload before passing it to the application. */ - this->ProducerSocket::setSocketOption( - ProducerCallbacksOptions::CACHE_MISS, - (ProducerInterestCallback)std::bind(&TLSProducerSocket::cacheMiss, this, - std::placeholders::_1, - std::placeholders::_2)); - - this->ProducerSocket::setSocketOption( - ProducerCallbacksOptions::CONTENT_PRODUCED, - (ProducerContentCallback)bind( - &TLSProducerSocket::onContentProduced, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3)); -} - -/* The producer interface is not owned by the application, so is TLSSocket task - * to deallocate the memory */ -TLSProducerSocket::~TLSProducerSocket() { delete producer_interface_; } - -void TLSProducerSocket::accept() { - HandshakeState handshake_state = getHandshakeState(); - - if (handshake_state == UNINITIATED || handshake_state == CLIENT_HELLO) { - tls_chunks_ = 1; - int result = SSL_accept(ssl_); - - if (result != 1) - throw errors::RuntimeException("Unable to perform client handshake"); - } - - parent_->list_producers.push_front( - std::move(parent_->map_producers[handshake_name_])); - parent_->map_producers.erase(handshake_name_); - - ProducerInterestCallback on_interest_process_decrypted; - getSocketOption(ProducerCallbacksOptions::CACHE_MISS, - on_interest_process_decrypted); - - if (on_interest_process_decrypted) { - Interest inter(std::move(handshake_packet_)); - on_interest_process_decrypted(*getInterface(), inter); - } else { - throw errors::RuntimeException( - "On interest process unset: unable to perform handshake"); - } - - handshake_state_ = SERVER_FINISHED; - TRANSPORT_LOGD("Handshake performed!"); -} - -int TLSProducerSocket::async_accept() { - if (!async_thread_.stopped()) { - async_thread_.add([this]() { this->accept(); }); - } else { - throw errors::RuntimeException( - "Async thread not running: unable to perform handshake"); - } - - return 1; -} - -void TLSProducerSocket::onInterest(ProducerSocket &p, Interest &interest) { - HandshakeState handshake_state = getHandshakeState(); - - if (handshake_state == UNINITIATED || handshake_state == CLIENT_HELLO) { - std::unique_lock<std::mutex> lck(mtx_); - - name_ = interest.getName(); - interest.separateHeaderPayload(); - handshake_packet_ = interest.acquireMemBufReference(); - something_to_read_ = true; - - cv_.notify_one(); - return; - } else if (handshake_state == SERVER_FINISHED) { - interest.separateHeaderPayload(); - handshake_packet_ = interest.acquireMemBufReference(); - something_to_read_ = true; - - if (interest.getPayload()->length() > 0) { - SSL_read( - ssl_, - const_cast<unsigned char *>(interest.getPayload()->writableData()), - interest.getPayload()->length()); - } - - ProducerInterestCallback on_interest_input_decrypted; - getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, - on_interest_input_decrypted); - - if (on_interest_input_decrypted) - (on_interest_input_decrypted)(*getInterface(), interest); - } -} - -void TLSProducerSocket::cacheMiss(interface::ProducerSocket &p, - Interest &interest) { - HandshakeState handshake_state = getHandshakeState(); - - if (handshake_state == CLIENT_HELLO) { - std::unique_lock<std::mutex> lck(mtx_); - - interest.separateHeaderPayload(); - handshake_packet_ = interest.acquireMemBufReference(); - something_to_read_ = true; - handshake_state_ = CLIENT_FINISHED; - - cv_.notify_one(); - } else if (handshake_state == SERVER_FINISHED) { - interest.separateHeaderPayload(); - handshake_packet_ = interest.acquireMemBufReference(); - something_to_read_ = true; - - if (interest.getPayload()->length() > 0) { - SSL_read( - ssl_, - const_cast<unsigned char *>(interest.getPayload()->writableData()), - interest.getPayload()->length()); - } - - if (on_interest_process_decrypted_ != VOID_HANDLER) - on_interest_process_decrypted_(*getInterface(), interest); - } -} - -TLSProducerSocket::HandshakeState TLSProducerSocket::getHandshakeState() { - if (SSL_in_before(ssl_)) { - handshake_state_ = UNINITIATED; - } - - if (SSL_in_init(ssl_) && handshake_state_ == UNINITIATED) { - handshake_state_ = CLIENT_HELLO; - } - - return handshake_state_; -} - -void TLSProducerSocket::onContentProduced(interface::ProducerSocket &p, - const std::error_code &err, - uint64_t bytes_written) {} - -uint32_t TLSProducerSocket::produce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t start_offset) { - if (getHandshakeState() != SERVER_FINISHED) { - throw errors::RuntimeException( - "New handshake on the same P2P secure producer socket not supported"); - } - - size_t buf_size = buffer->length(); - name_ = served_namespaces_.front().mapName(content_name); - tls_chunks_ = to_call_oncontentproduced_ = - ceil((float)buf_size / (float)SSL3_RT_MAX_PLAIN_LENGTH); - - if (!is_last) { - tls_chunks_++; - } - - last_segment_ = start_offset; - - SSL_write(ssl_, buffer->data(), buf_size); - BIO *wbio = SSL_get_wbio(ssl_); - int i = BIO_flush(wbio); - (void)i; // To shut up gcc 5 - - return 0; -} - -void TLSProducerSocket::asyncProduce(const Name &content_name, - const uint8_t *buf, size_t buffer_size, - bool is_last, uint32_t *start_offset) { - if (!encryption_thread_.stopped()) { - encryption_thread_.add([this, content_name, buffer = buf, - size = buffer_size, is_last, start_offset]() { - if (start_offset != NULL) { - produce(content_name, buffer, size, is_last, *start_offset); - } else { - produce(content_name, buffer, size, is_last, 0); - } - }); - } -} - -void TLSProducerSocket::asyncProduce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment) { - if (!encryption_thread_.stopped()) { - auto a = buffer.release(); - encryption_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 = &last_segment_; - } - produce(content_name, std::move(buf), is_last, offset); - }); - } -} - -void TLSProducerSocket::asyncProduce(ContentObject &content_object) { - throw errors::RuntimeException("API not supported"); -} - -void TLSProducerSocket::produce(ContentObject &content_object) { - throw errors::RuntimeException("API not supported"); -} - -long TLSProducerSocket::ctrl(BIO *b, int cmd, long num, void *ptr) { - if (cmd == BIO_CTRL_FLUSH) { - } - - return 1; -} - -int TLSProducerSocket::addHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, - const unsigned char **out, size_t *outlen, - X509 *x, size_t chainidx, int *al, - void *add_arg) { - TLSProducerSocket *socket = reinterpret_cast<TLSProducerSocket *>(add_arg); - - if (ext_type == 100) { - ip_prefix_t ip_prefix = - socket->parent_->served_namespaces_.front().toIpPrefixStruct(); - int inet_family = - socket->parent_->served_namespaces_.front().getAddressFamily(); - uint16_t prefix_len_bits = - socket->parent_->served_namespaces_.front().getPrefixLength(); - uint8_t prefix_len_bytes = prefix_len_bits / 8; - uint8_t prefix_len_u32 = prefix_len_bits / 32; - - ip_prefix_t *out_ip = (ip_prefix_t *)malloc(sizeof(ip_prefix_t)); - out_ip->family = inet_family; - out_ip->len = prefix_len_bits + 32; - u8 *out_ip_buf = const_cast<u8 *>( - ip_address_get_buffer(&(out_ip->address), inet_family)); - *out = reinterpret_cast<unsigned char *>(out_ip); - - RAND_bytes((unsigned char *)&socket->key_id_, 4); - - memcpy(out_ip_buf, ip_address_get_buffer(&(ip_prefix.address), inet_family), - prefix_len_bytes); - memcpy((out_ip_buf + prefix_len_bytes), &socket->key_id_, 4); - *outlen = sizeof(ip_prefix_t); - - ip_address_t mask = {}; - ip_address_t keyId_component = {}; - u32 *mask_buf; - u32 *keyId_component_buf; - - switch (inet_family) { - case AF_INET: - mask_buf = &(mask.v4.as_u32); - keyId_component_buf = &(keyId_component.v4.as_u32); - break; - case AF_INET6: - mask_buf = mask.v6.as_u32; - keyId_component_buf = keyId_component.v6.as_u32; - break; - default: - throw errors::RuntimeException("Unknown protocol"); - } - - if (prefix_len_bits > (inet_family == AF_INET6 ? IPV6_ADDR_LEN_BITS - 32 - : IPV4_ADDR_LEN_BITS - 32)) - throw errors::RuntimeException( - "Not enough space in the content name to add key_id"); - - mask_buf[prefix_len_u32] = 0xffffffff; - keyId_component_buf[prefix_len_u32] = socket->key_id_; - socket->last_segment_ = 0; - - socket->on_interest_process_decrypted_ = - socket->parent_->on_interest_process_decrypted_; - - socket->registerPrefix( - Prefix(socket->parent_->served_namespaces_.front().getName( - Name(inet_family, (uint8_t *)&mask), - Name(inet_family, (uint8_t *)&keyId_component), - socket->parent_->served_namespaces_.front().getName()), - out_ip->len)); - socket->connect(); - } - return 1; -} - -void TLSProducerSocket::freeHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, - const unsigned char *out, - void *add_arg) { - free(const_cast<unsigned char *>(out)); -} - -int TLSProducerSocket::parseHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, - const unsigned char *in, size_t inlen, - X509 *x, size_t chainidx, int *al, - void *add_arg) { - return 1; -} - -int TLSProducerSocket::setSocketOption( - int socket_option_key, ProducerInterestCallback socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerInterestCallback socket_option_value) -> int { - int result = SOCKET_OPTION_SET; - - switch (socket_option_key) { - case ProducerCallbacksOptions::INTEREST_INPUT: - on_interest_input_decrypted_ = socket_option_value; - break; - - case ProducerCallbacksOptions::INTEREST_DROP: - on_interest_dropped_input_buffer_ = socket_option_value; - break; - - case ProducerCallbacksOptions::INTEREST_PASS: - on_interest_inserted_input_buffer_ = socket_option_value; - break; - - case ProducerCallbacksOptions::CACHE_HIT: - on_interest_satisfied_output_buffer_ = socket_option_value; - break; - - case ProducerCallbacksOptions::CACHE_MISS: - on_interest_process_decrypted_ = socket_option_value; - break; - - default: - result = SOCKET_OPTION_NOT_SET; - break; - } - - return result; - }); -} - -int TLSProducerSocket::setSocketOption( - int socket_option_key, ProducerContentCallback socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerContentCallback socket_option_value) -> int { - switch (socket_option_key) { - case ProducerCallbacksOptions::CONTENT_PRODUCED: - on_content_produced_application_ = socket_option_value; - break; - - default: - return SOCKET_OPTION_NOT_SET; - } - - return SOCKET_OPTION_SET; - }); -} - -int TLSProducerSocket::getSocketOption( - int socket_option_key, ProducerContentCallback **socket_option_value) { - return rescheduleOnIOService( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerContentCallback **socket_option_value) -> int { - switch (socket_option_key) { - case ProducerCallbacksOptions::CONTENT_PRODUCED: - *socket_option_value = &on_content_produced_application_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); -} - -int TLSProducerSocket::getSocketOption( - int socket_option_key, ProducerContentCallback &socket_option_value) { - return rescheduleOnIOServiceWithReference( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerContentCallback &socket_option_value) -> int { - switch (socket_option_key) { - case ProducerCallbacksOptions::CONTENT_PRODUCED: - socket_option_value = on_content_produced_application_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); -} - -int TLSProducerSocket::getSocketOption( - int socket_option_key, ProducerInterestCallback &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 rescheduleOnIOServiceWithReference( - socket_option_key, socket_option_value, - [this](int socket_option_key, - ProducerInterestCallback &socket_option_value) -> int { - switch (socket_option_key) { - case ProducerCallbacksOptions::INTEREST_INPUT: - socket_option_value = on_interest_input_decrypted_; - break; - - case ProducerCallbacksOptions::INTEREST_DROP: - socket_option_value = on_interest_dropped_input_buffer_; - break; - - case ProducerCallbacksOptions::INTEREST_PASS: - socket_option_value = on_interest_inserted_input_buffer_; - break; - - case ProducerCallbacksOptions::CACHE_HIT: - socket_option_value = on_interest_satisfied_output_buffer_; - break; - - case ProducerCallbacksOptions::CACHE_MISS: - socket_option_value = on_interest_process_decrypted_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - }); -} - -} // namespace implementation -} // namespace transport diff --git a/libtransport/src/implementation/tls_socket_producer.h b/libtransport/src/implementation/tls_socket_producer.h deleted file mode 100644 index 2382e8695..000000000 --- a/libtransport/src/implementation/tls_socket_producer.h +++ /dev/null @@ -1,161 +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 <implementation/socket_producer.h> - -#include <openssl/ssl.h> -#include <condition_variable> -#include <mutex> - -namespace transport { -namespace implementation { - -class P2PSecureProducerSocket; - -class TLSProducerSocket : virtual public ProducerSocket { - friend class P2PSecureProducerSocket; - - public: - explicit TLSProducerSocket(interface::ProducerSocket *producer_socket, - P2PSecureProducerSocket *parent, - const Name &handshake_name); - - ~TLSProducerSocket(); - - uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, - bool is_last = true, uint32_t start_offset = 0) override { - return produce(content_name, utils::MemBuf::copyBuffer(buffer, buffer_size), - is_last, start_offset); - } - - uint32_t produce(Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last = true, uint32_t start_offset = 0) override; - - void produce(ContentObject &content_object) override; - - void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size, - bool is_last = true, - uint32_t *start_offset = nullptr) override; - - void asyncProduce(Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr) override; - - void asyncProduce(ContentObject &content_object) override; - - virtual void accept(); - - virtual int async_accept(); - - virtual int setSocketOption( - int socket_option_key, - ProducerInterestCallback socket_option_value) override; - - virtual int setSocketOption( - int socket_option_key, - ProducerContentCallback socket_option_value) override; - - virtual int getSocketOption( - int socket_option_key, - ProducerContentCallback **socket_option_value) override; - - int getSocketOption(int socket_option_key, - ProducerContentCallback &socket_option_value); - - int getSocketOption(int socket_option_key, - ProducerInterestCallback &socket_option_value); - - using ProducerSocket::getSocketOption; - using ProducerSocket::onInterest; - using ProducerSocket::setSocketOption; - - protected: - enum HandshakeState { - UNINITIATED, - CLIENT_HELLO, // when CLIENT_HELLO interest has been received - CLIENT_FINISHED, // when CLIENT_FINISHED interest has been received - SERVER_FINISHED, // when handshake is done - }; - /* Callback invoked once an interest has been received and its payload - * decrypted */ - ProducerInterestCallback on_interest_input_decrypted_; - ProducerInterestCallback on_interest_process_decrypted_; - ProducerContentCallback on_content_produced_application_; - std::mutex mtx_; - /* Condition variable for the wait */ - std::condition_variable cv_; - /* Bool variable, true if there is something to read (an interest arrived) */ - bool something_to_read_; - /* Bool variable, true if CLIENT_FINISHED interest has been received */ - HandshakeState handshake_state_; - /* First interest that open a secure connection */ - transport::core::Name name_; - /* SSL handle */ - SSL *ssl_; - SSL_CTX *ctx_; - Packet::MemBufPtr handshake_packet_; - std::unique_ptr<utils::MemBuf> head_; - std::uint32_t last_segment_; - std::uint32_t key_id_; - std::thread *handshake; - P2PSecureProducerSocket *parent_; - bool first_; - Name handshake_name_; - int tls_chunks_; - int to_call_oncontentproduced_; - bool still_writing_; - utils::EventThread encryption_thread_; - - void onInterest(ProducerSocket &p, Interest &interest); - - void cacheMiss(interface::ProducerSocket &p, Interest &interest); - - /* Return the number of read bytes in readbytes */ - static int read(BIO *b, char *buf, size_t size, size_t *readbytes); - - /* Return the number of read bytes in the return param */ - static int readOld(BIO *h, char *buf, int size); - - /* Return the number of written bytes in written */ - static int write(BIO *b, const char *buf, size_t size, size_t *written); - - /* Return the number of written bytes in the return param */ - static int writeOld(BIO *h, const char *buf, int num); - - static long ctrl(BIO *b, int cmd, long num, void *ptr); - - static int addHicnKeyIdCb(SSL *s, unsigned int ext_type, unsigned int context, - const unsigned char **out, size_t *outlen, X509 *x, - size_t chainidx, int *al, void *add_arg); - - static void freeHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, const unsigned char *out, - void *add_arg); - - static int parseHicnKeyIdCb(SSL *s, unsigned int ext_type, - unsigned int context, const unsigned char *in, - size_t inlen, X509 *x, size_t chainidx, int *al, - void *add_arg); - - void onContentProduced(interface::ProducerSocket &p, - const std::error_code &err, uint64_t bytes_written); - - HandshakeState getHandshakeState(); -}; - -} // namespace implementation -} // end namespace transport diff --git a/libtransport/src/interfaces/CMakeLists.txt b/libtransport/src/interfaces/CMakeLists.txt index e1d144596..bf8f8dcf8 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: @@ -11,30 +11,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.cc ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.cc ${CMAKE_CURRENT_SOURCE_DIR}/portal.cc ${CMAKE_CURRENT_SOURCE_DIR}/callbacks.cc + ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.cc ) -if (${OPENSSL_VERSION} VERSION_EQUAL "1.1.1a" OR ${OPENSSL_VERSION} VERSION_GREATER "1.1.1a") - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/p2psecure_socket_consumer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.cc - ) - - list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/tls_rtc_socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_producer.h - ${CMAKE_CURRENT_SOURCE_DIR}/tls_socket_consumer.h - ) -endif() - set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) 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/global_configuration.cc b/libtransport/src/interfaces/global_configuration.cc new file mode 100644 index 000000000..f8e7a90c3 --- /dev/null +++ b/libtransport/src/interfaces/global_configuration.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 <core/global_configuration.h> +#include <core/global_workers.h> +#include <glog/logging.h> +#include <hicn/transport/interfaces/global_conf_interface.h> + +#include <system_error> + +namespace transport { +namespace interface { +namespace global_config { + +GlobalConfigInterface::GlobalConfigInterface() { libtransportConfigInit(); } + +GlobalConfigInterface::~GlobalConfigInterface() { + libtransportConfigTerminate(); +} + +void GlobalConfigInterface::parseConfigurationFile( + const std::string &path) const { + core::GlobalConfiguration::getInstance().parseConfiguration(path); +} + +void GlobalConfigInterface::libtransportConfigInit() const { + // nothing to do +} + +void GlobalConfigInterface::libtransportConfigTerminate() const { + // cleanup workers + auto &workers = core::GlobalWorkers::getInstance().getWorkers(); + for (auto &worker : workers) { + worker.stop(); + } +} + +void ConfigurationObject::get() { + std::error_code ec; + core::GlobalConfiguration::getInstance().getConfiguration(*this, ec); + + if (ec) { + LOG(ERROR) << "Error setting global config: " << ec.message(); + } +} + +void ConfigurationObject::set() { + std::error_code ec; + core::GlobalConfiguration::getInstance().setConfiguration(*this, ec); + + if (ec) { + LOG(ERROR) << "Error setting global config: " << ec.message(); + } +} + +} // namespace global_config +} // namespace interface +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/interfaces/p2psecure_socket_consumer.cc b/libtransport/src/interfaces/p2psecure_socket_consumer.cc deleted file mode 100644 index 038441dfc..000000000 --- a/libtransport/src/interfaces/p2psecure_socket_consumer.cc +++ /dev/null @@ -1,38 +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/interfaces/p2psecure_socket_consumer.h> - -#include <implementation/p2psecure_socket_consumer.h> - -namespace transport { -namespace interface { - -P2PSecureConsumerSocket::P2PSecureConsumerSocket(int handshake_protocol, - int transport_protocol) - : ConsumerSocket() { - socket_ = std::unique_ptr<implementation::ConsumerSocket>( - new implementation::P2PSecureConsumerSocket(this, handshake_protocol, - transport_protocol)); -} - -void P2PSecureConsumerSocket::registerPrefix(const Prefix &producer_namespace) { - implementation::P2PSecureConsumerSocket &secure_consumer_socket = - *(static_cast<implementation::P2PSecureConsumerSocket *>(socket_.get())); - secure_consumer_socket.registerPrefix(producer_namespace); -} - -} // namespace interface -} // namespace transport diff --git a/libtransport/src/interfaces/p2psecure_socket_producer.cc b/libtransport/src/interfaces/p2psecure_socket_producer.cc deleted file mode 100644 index 37352259c..000000000 --- a/libtransport/src/interfaces/p2psecure_socket_producer.cc +++ /dev/null @@ -1,34 +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/interfaces/p2psecure_socket_producer.h> - -#include <implementation/p2psecure_socket_producer.h> - -namespace transport { -namespace interface { - -P2PSecureProducerSocket::P2PSecureProducerSocket() { - socket_ = std::make_unique<implementation::P2PSecureProducerSocket>(this); -} - -P2PSecureProducerSocket::P2PSecureProducerSocket( - bool rtc, const std::shared_ptr<utils::Identity> &identity) { - socket_ = std::make_unique<implementation::P2PSecureProducerSocket>(this, rtc, - identity); -} - -} // namespace interface -} // namespace transport diff --git a/libtransport/src/interfaces/portal.cc b/libtransport/src/interfaces/portal.cc index 36cbd0c3b..898766c50 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,88 +13,108 @@ * limitations under the License. */ +#include <core/portal.h> #include <hicn/transport/interfaces/portal.h> -#include <implementation/socket.h> - namespace transport { namespace interface { -using implementation::BasePortal; +class Portal::Impl { + public: + Impl() : portal_(core::Portal::createShared()) {} + Impl(::utils::EventThread &worker) + : portal_(core::Portal::createShared(worker)) {} -Portal::Portal() { implementation_ = new implementation::BasePortal(); } + void registerTransportCallback(TransportCallback *transport_callback) { + portal_->registerTransportCallback(transport_callback); + } -Portal::Portal(asio::io_service &io_service) { - implementation_ = new BasePortal(io_service); -} + 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, uint32_t lifetime, + OnContentObjectCallback &&on_content_object_callback, + OnInterestTimeoutCallback &&on_interest_timeout_callback) { + portal_->sendInterest(interest, lifetime, + 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<BasePortal *>(implementation_); } + utils::EventThread &getThread() { return portal_->getThread(); } -void Portal::setConsumerCallback(ConsumerCallback *consumer_callback) { - reinterpret_cast<BasePortal *>(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<BasePortal *>(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<BasePortal *>(implementation_)->connect(is_consumer); + implementation_->connect(is_consumer); } bool Portal::interestIsPending(const core::Name &name) { - return reinterpret_cast<BasePortal *>(implementation_) - ->interestIsPending(name); + return implementation_->interestIsPending(name); } void Portal::sendInterest( - core::Interest::Ptr &&interest, + core::Interest::Ptr &interest, uint32_t lifetime, OnContentObjectCallback &&on_content_object_callback, OnInterestTimeoutCallback &&on_interest_timeout_callback) { - reinterpret_cast<BasePortal *>(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<BasePortal *>(implementation_)->bind(config); -} - -void Portal::runEventsLoop() { - reinterpret_cast<BasePortal *>(implementation_)->runEventsLoop(); -} - -void Portal::runOneEvent() { - reinterpret_cast<BasePortal *>(implementation_)->runOneEvent(); + implementation_->sendInterest(interest, lifetime, + std::move(on_content_object_callback), + std::move(on_interest_timeout_callback)); } void Portal::sendContentObject(core::ContentObject &content_object) { - reinterpret_cast<BasePortal *>(implementation_) - ->sendContentObject(content_object); + implementation_->sendContentObject(content_object); } -void Portal::stopEventsLoop() { - reinterpret_cast<BasePortal *>(implementation_)->stopEventsLoop(); -} +void Portal::killConnection() { implementation_->killConnection(); } -void Portal::killConnection() { - reinterpret_cast<BasePortal *>(implementation_)->killConnection(); -} +void Portal::clear() { implementation_->clear(); } -void Portal::clear() { - reinterpret_cast<BasePortal *>(implementation_)->clear(); -} +utils::EventThread &Portal::getThread() { return implementation_->getThread(); } -asio::io_service &Portal::getIoService() { - return reinterpret_cast<BasePortal *>(implementation_)->getIoService(); +void Portal::registerRoute(core::Prefix &prefix) { + implementation_->registerRoute(prefix); } -void Portal::registerRoute(core::Prefix &prefix) { - reinterpret_cast<BasePortal *>(implementation_)->registerRoute(prefix); +void Portal::sendMapme() { implementation_->sendMapme(); } + +void Portal::setForwardingStrategy(core::Prefix &prefix, + std::string &strategy) { + implementation_->setForwardingStrategy(prefix, strategy); } } // namespace interface -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/interfaces/socket_consumer.cc b/libtransport/src/interfaces/socket_consumer.cc index ea0606347..afb672c95 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,31 +23,34 @@ 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(); } void ConsumerSocket::resume() { socket_->resume(); } -bool ConsumerSocket::verifyKeyPackets() { return socket_->verifyKeyPackets(); } - asio::io_service &ConsumerSocket::getIoService() { return socket_->getIoService(); } @@ -88,30 +91,24 @@ int ConsumerSocket::setSocketOption( } int ConsumerSocket::setSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback socket_option_value) { + int socket_option_key, ConsumerInterestCallback socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ConsumerSocket::setSocketOption( - int socket_option_key, ConsumerInterestCallback socket_option_value) { +int ConsumerSocket::setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ConsumerSocket::setSocketOption( int socket_option_key, - ConsumerContentObjectVerificationFailedCallback socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - -int ConsumerSocket::setSocketOption(int socket_option_key, - IcnObserver *socket_option_value) { + const std::shared_ptr<auth::Signer> &socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ConsumerSocket::setSocketOption( int socket_option_key, - const std::shared_ptr<utils::Verifier> &socket_option_value) { + const std::shared_ptr<auth::Verifier> &socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } @@ -125,6 +122,11 @@ 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::getSocketOption(int socket_option_key, double &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); @@ -152,30 +154,23 @@ int ConsumerSocket::getSocketOption( } int ConsumerSocket::getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationCallback **socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - -int ConsumerSocket::getSocketOption( int socket_option_key, ConsumerInterestCallback **socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ConsumerSocket::getSocketOption( - int socket_option_key, - ConsumerContentObjectVerificationFailedCallback **socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); -} - int ConsumerSocket::getSocketOption(int socket_option_key, IcnObserver **socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } int ConsumerSocket::getSocketOption( + int socket_option_key, std::shared_ptr<auth::Signer> &socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + +int ConsumerSocket::getSocketOption( int socket_option_key, - std::shared_ptr<utils::Verifier> &socket_option_value) { + std::shared_ptr<auth::Verifier> &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } @@ -195,6 +190,11 @@ 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); +} + } // namespace interface } // namespace transport diff --git a/libtransport/src/interfaces/socket_producer.cc b/libtransport/src/interfaces/socket_producer.cc index d030fe756..77e3bf633 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: @@ -14,7 +14,6 @@ */ #include <hicn/transport/interfaces/socket_producer.h> - #include <implementation/socket_producer.h> #include <atomic> @@ -31,53 +30,66 @@ namespace interface { using namespace core; ProducerSocket::ProducerSocket(int protocol) { - if (protocol != 0) { - throw std::runtime_error("Production protocol must be 0."); - } + socket_ = std::make_unique<implementation::ProducerSocket>(this, protocol); +} - socket_ = std::make_unique<implementation::ProducerSocket>(this); +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(); } bool ProducerSocket::isRunning() { return socket_->isRunning(); } -uint32_t ProducerSocket::produce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t start_offset) { - return socket_->produce(content_name, std::move(buffer), is_last, - start_offset); +uint32_t ProducerSocket::produceStream(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer, + bool is_last, uint32_t start_offset) { + return socket_->produceStream(content_name, std::move(buffer), is_last, + start_offset); } -void ProducerSocket::produce(ContentObject &content_object) { - return socket_->produce(content_object); +uint32_t ProducerSocket::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, bool is_last, + uint32_t start_offset) { + return socket_->produceStream(content_name, buffer, buffer_size, is_last, + start_offset); } -void ProducerSocket::produce(std::unique_ptr<utils::MemBuf> &&buffer) { - socket_->produce(std::move(buffer)); +uint32_t ProducerSocket::produceDatagram( + const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) { + return socket_->produceDatagram(content_name, std::move(buffer)); } -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); +uint32_t ProducerSocket::produceDatagram(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size) { + return socket_->produceDatagram(content_name, buffer, buffer_size); } -void ProducerSocket::asyncProduce(ContentObject &content_object) { - return socket_->asyncProduce(content_object); +void ProducerSocket::produce(ContentObject &content_object) { + return socket_->produce(content_object); } +void ProducerSocket::sendMapme() { return socket_->sendMapme(); } + void ProducerSocket::registerPrefix(const Prefix &producer_namespace) { return socket_->registerPrefix(producer_namespace); } -void ProducerSocket::serveForever() { return socket_->serveForever(); } +void ProducerSocket::start() { return socket_->start(); } void ProducerSocket::stop() { return socket_->stop(); } @@ -86,27 +98,27 @@ asio::io_service &ProducerSocket::getIoService() { }; int ProducerSocket::setSocketOption(int socket_option_key, - uint32_t socket_option_value) { + Callback *socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption(int socket_option_key, - std::nullptr_t socket_option_value) { + uint32_t socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption(int socket_option_key, - bool socket_option_value) { + std::nullptr_t socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption(int socket_option_key, - Name *socket_option_value) { + bool socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption(int socket_option_key, - std::list<Prefix> socket_option_value) { + Name *socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } @@ -126,24 +138,25 @@ int ProducerSocket::setSocketOption( } int ProducerSocket::setSocketOption(int socket_option_key, - utils::CryptoHashType socket_option_value) { + auth::CryptoHashType socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::setSocketOption(int socket_option_key, - utils::CryptoSuite socket_option_value) { +int ProducerSocket::setSocketOption( + int socket_option_key, + const std::shared_ptr<auth::Signer> &socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption( int socket_option_key, - const std::shared_ptr<utils::Signer> &socket_option_value) { + const std::shared_ptr<auth::Verifier> &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, @@ -156,11 +169,6 @@ int ProducerSocket::getSocketOption(int socket_option_key, return socket_->getSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::getSocketOption(int socket_option_key, - std::list<Prefix> &socket_option_value) { - return socket_->getSocketOption(socket_option_key, socket_option_value); -} - int ProducerSocket::getSocketOption( int socket_option_key, ProducerContentObjectCallback **socket_option_value) { @@ -177,19 +185,19 @@ int ProducerSocket::getSocketOption( return socket_->getSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::getSocketOption( - int socket_option_key, utils::CryptoHashType &socket_option_value) { +int ProducerSocket::getSocketOption(int socket_option_key, + auth::CryptoHashType &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } -int ProducerSocket::getSocketOption(int socket_option_key, - utils::CryptoSuite &socket_option_value) { +int ProducerSocket::getSocketOption( + int socket_option_key, std::shared_ptr<auth::Signer> &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::getSocketOption( int socket_option_key, - std::shared_ptr<utils::Signer> &socket_option_value) { + std::shared_ptr<auth::Verifier> &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); } diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.cc b/libtransport/src/interfaces/tls_rtc_socket_producer.cc deleted file mode 100644 index 132f34721..000000000 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.cc +++ /dev/null @@ -1,32 +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 <interfaces/tls_rtc_socket_producer.h> - -#include <implementation/tls_rtc_socket_producer.h> - -namespace transport { -namespace interface { - -TLSRTCProducerSocket::TLSRTCProducerSocket( - implementation::TLSRTCProducerSocket *implementation) { - socket_ = - std::unique_ptr<implementation::TLSRTCProducerSocket>(implementation); -} - -TLSRTCProducerSocket::~TLSRTCProducerSocket() { socket_.release(); } - -} // namespace interface -} // namespace transport diff --git a/libtransport/src/interfaces/tls_socket_consumer.cc b/libtransport/src/interfaces/tls_socket_consumer.cc deleted file mode 100644 index d87642f73..000000000 --- a/libtransport/src/interfaces/tls_socket_consumer.cc +++ /dev/null @@ -1,31 +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 <interfaces/tls_socket_consumer.h> - -#include <implementation/tls_socket_consumer.h> - -namespace transport { -namespace interface { - -TLSConsumerSocket::TLSConsumerSocket( - implementation::TLSConsumerSocket *implementation) { - socket_ = std::unique_ptr<implementation::TLSConsumerSocket>(implementation); -} - -TLSConsumerSocket::~TLSConsumerSocket() { socket_.release(); } - -} // namespace interface -} // namespace transport diff --git a/libtransport/src/interfaces/tls_socket_consumer.h b/libtransport/src/interfaces/tls_socket_consumer.h deleted file mode 100644 index 845a9181f..000000000 --- a/libtransport/src/interfaces/tls_socket_consumer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 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. - */ - -#pragma once - -#include <hicn/transport/interfaces/socket_consumer.h> - -namespace transport { - -namespace implementation { -class TLSConsumerSocket; -} - -namespace interface { - -class TLSConsumerSocket : public ConsumerSocket { - public: - TLSConsumerSocket(implementation::TLSConsumerSocket *implementation); - ~TLSConsumerSocket(); -}; - -} // namespace interface - -} // end namespace transport diff --git a/libtransport/src/interfaces/tls_socket_producer.cc b/libtransport/src/interfaces/tls_socket_producer.cc deleted file mode 100644 index 44aa0cf8b..000000000 --- a/libtransport/src/interfaces/tls_socket_producer.cc +++ /dev/null @@ -1,31 +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 <interfaces/tls_socket_producer.h> - -#include <implementation/tls_socket_producer.h> - -namespace transport { -namespace interface { - -TLSProducerSocket::TLSProducerSocket( - implementation::TLSProducerSocket *implementation) { - socket_ = std::unique_ptr<implementation::TLSProducerSocket>(implementation); -} - -TLSProducerSocket::~TLSProducerSocket() { socket_.release(); } - -} // namespace interface -} // namespace transport diff --git a/libtransport/src/io_modules/CMakeLists.txt b/libtransport/src/io_modules/CMakeLists.txt new file mode 100644 index 000000000..ce7ec221d --- /dev/null +++ b/libtransport/src/io_modules/CMakeLists.txt @@ -0,0 +1,78 @@ +# 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. + + +############################################################## +# Android case: no submodules +############################################################## +if (${CMAKE_SYSTEM_NAME} MATCHES Android OR ${CMAKE_SYSTEM_NAME} MATCHES iOS) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn-light/hicn_forwarder_module.cc + ) + + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn-light/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() +############################################################## +# Compile submodules +############################################################## + add_subdirectory(hicn-light) + add_subdirectory(forwarder) + + if (__vpp__) + add_subdirectory(memif) + endif() +endif() diff --git a/libtransport/src/io_modules/forwarder/CMakeLists.txt b/libtransport/src/io_modules/forwarder/CMakeLists.txt new file mode 100644 index 000000000..2235d842e --- /dev/null +++ b/libtransport/src/io_modules/forwarder/CMakeLists.txt @@ -0,0 +1,35 @@ +# 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. + +list(APPEND MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/endpoint.h + ${CMAKE_CURRENT_SOURCE_DIR}/errors.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.cc + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.cc +) + +build_module(forwarder_module + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT ${LIBTRANSPORT_COMPONENT}-io-modules + INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} ${Libhicn_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} +) diff --git a/libtransport/src/io_modules/forwarder/configuration.h b/libtransport/src/io_modules/forwarder/configuration.h new file mode 100644 index 000000000..fcaa5530d --- /dev/null +++ b/libtransport/src/io_modules/forwarder/configuration.h @@ -0,0 +1,89 @@ +/* + * 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 core { + +struct ListenerConfig { + std::string address; + std::uint16_t port; + std::string name; +}; + +struct ConnectorConfig { + std::string local_address; + std::uint16_t local_port; + std::string remote_address; + std::uint16_t remote_port; + std::string name; +}; + +struct RouteConfig { + std::string prefix; + uint16_t weight; + std::string connector; + std::string name; +}; + +class Configuration { + public: + Configuration() : n_threads_(1) {} + + bool empty() { + return listeners_.empty() && connectors_.empty() && routes_.empty(); + } + + Configuration& setThreadNumber(std::size_t threads) { + n_threads_ = threads; + return *this; + } + + std::size_t getThreadNumber() { return n_threads_; } + + template <typename... Args> + Configuration& addListener(Args&&... args) { + listeners_.emplace_back(std::forward<Args>(args)...); + return *this; + } + + template <typename... Args> + Configuration& addConnector(Args&&... args) { + connectors_.emplace_back(std::forward<Args>(args)...); + return *this; + } + + template <typename... Args> + Configuration& addRoute(Args&&... args) { + routes_.emplace_back(std::forward<Args>(args)...); + return *this; + } + + std::vector<ListenerConfig>& getListeners() { return listeners_; } + + std::vector<ConnectorConfig>& getConnectors() { return connectors_; } + + std::vector<RouteConfig>& getRoutes() { return routes_; } + + private: + std::vector<ListenerConfig> listeners_; + std::vector<ConnectorConfig> connectors_; + std::vector<RouteConfig> routes_; + std::size_t n_threads_; +}; + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/io_modules/forwarder/errors.cc b/libtransport/src/io_modules/forwarder/errors.cc new file mode 100644 index 000000000..6e93d0453 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/errors.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#include <io_modules/forwarder/errors.h> + +namespace transport { +namespace core { + +const std::error_category& forwarder_category() { + static forwarder_category_impl instance; + + return instance; +} + +const char* forwarder_category_impl::name() const throw() { + return "proxy::connector::error"; +} + +std::string forwarder_category_impl::message(int ev) const { + switch (static_cast<forwarder_error>(ev)) { + case forwarder_error::success: { + return "Success"; + } + case forwarder_error::disconnected: { + return "Connector is disconnected"; + } + case forwarder_error::receive_failed: { + return "Packet reception failed"; + } + case forwarder_error::send_failed: { + return "Packet send failed"; + } + case forwarder_error::memory_allocation_error: { + return "Impossible to allocate memory for packet pool"; + } + case forwarder_error::invalid_connector_type: { + return "Invalid type specified for connector."; + } + case forwarder_error::invalid_connector: { + return "Created connector was invalid."; + } + case forwarder_error::interest_cache_miss: { + return "interest cache miss."; + } + default: { + return "Unknown connector error"; + } + } +} +} // namespace core +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/errors.h b/libtransport/src/io_modules/forwarder/errors.h new file mode 100644 index 000000000..dd5cc8fe7 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/errors.h @@ -0,0 +1,91 @@ +/* + * 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 <string> +#include <system_error> + +namespace transport { +namespace core { +/** + * @brief Get the default server error category. + * @return The default server error category instance. + * + * @warning The first call to this function is thread-safe only starting with + * C++11. + */ +const std::error_category& forwarder_category(); + +/** + * The list of errors. + */ +enum class forwarder_error { + success = 0, + send_failed, + receive_failed, + disconnected, + memory_allocation_error, + invalid_connector_type, + invalid_connector, + interest_cache_miss +}; + +/** + * @brief Create an error_code instance for the given error. + * @param error The error. + * @return The error_code instance. + */ +inline std::error_code make_error_code(forwarder_error error) { + return std::error_code(static_cast<int>(error), forwarder_category()); +} + +/** + * @brief Create an error_condition instance for the given error. + * @param error The error. + * @return The error_condition instance. + */ +inline std::error_condition make_error_condition(forwarder_error error) { + return std::error_condition(static_cast<int>(error), forwarder_category()); +} + +/** + * @brief A server error category. + */ +class forwarder_category_impl : public std::error_category { + public: + /** + * @brief Get the name of the category. + * @return The name of the category. + */ + virtual const char* name() const throw(); + + /** + * @brief Get the error message for a given error. + * @param ev The error numeric value. + * @return The message associated to the error. + */ + virtual std::string message(int ev) const; +}; +} // namespace core +} // namespace transport + +namespace std { +// namespace system { +template <> +struct is_error_code_enum<::transport::core::forwarder_error> + : public std::true_type {}; +// } // namespace system +} // namespace std diff --git a/libtransport/src/io_modules/forwarder/forwarder.cc b/libtransport/src/io_modules/forwarder/forwarder.cc new file mode 100644 index 000000000..d5f0b589e --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder.cc @@ -0,0 +1,293 @@ +/* + * 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/global_configuration.h> +#include <core/global_id_counter.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> + +namespace transport { + +namespace core { + +constexpr char Forwarder::forwarder_config_section[]; + +Forwarder::Forwarder() : config_() { + using namespace std::placeholders; + GlobalConfiguration::getInstance().registerConfigurationParser( + forwarder_config_section, + std::bind(&Forwarder::parseForwarderConfiguration, this, _1, _2)); + + if (!config_.empty()) { + initThreads(); + initListeners(); + initConnectors(); + } +} + +Forwarder::~Forwarder() { + for (auto &l : listeners_) { + l->close(); + } + + for (auto &c : remote_connectors_) { + c.second->close(); + } + + GlobalConfiguration::getInstance().unregisterConfigurationParser( + forwarder_config_section); +} + +void Forwarder::initThreads() { + for (unsigned i = 0; i < config_.getThreadNumber(); i++) { + thread_pool_.emplace_back(io_service_, /* detached */ false); + } +} + +void Forwarder::initListeners() { + using namespace std::placeholders; + for (auto &l : config_.getListeners()) { + listeners_.emplace_back(std::make_shared<UdpTunnelListener>( + io_service_, + std::bind(&Forwarder::onPacketFromListener, this, _1, _2, _3), + asio::ip::udp::endpoint(asio::ip::address::from_string(l.address), + l.port))); + } +} + +void Forwarder::initConnectors() { + using namespace std::placeholders; + for (auto &c : config_.getConnectors()) { + auto id = GlobalCounter<Connector::Id>::getInstance().getNext(); + auto conn = new UdpTunnelConnector( + io_service_, std::bind(&Forwarder::onPacketReceived, this, _1, _2, _3), + std::bind(&Forwarder::onPacketSent, this, _1, _2), + std::bind(&Forwarder::onConnectorClosed, this, _1), + std::bind(&Forwarder::onConnectorReconnected, this, _1)); + conn->setConnectorId(id); + remote_connectors_.emplace(id, conn); + conn->connect(c.remote_address, c.remote_port, c.local_address, + c.local_port); + } +} + +Connector::Id Forwarder::registerLocalConnector( + asio::io_service &io_service, + Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_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, std::move(receive_callback), std::move(sent_callback), + std::move(close_callback), std::move(reconnect_callback)); + connector->setConnectorId(id); + local_connectors_.emplace(id, std::move(connector)); + return id; +} + +Forwarder &Forwarder::deleteConnector(Connector::Id id) { + utils::SpinLock::Acquire locked(connector_lock_); + auto it = local_connectors_.find(id); + if (it != local_connectors_.end()) { + it->second->close(); + local_connectors_.erase(it); + } else { + } + + return *this; +} + +Connector::Ptr Forwarder::getConnector(Connector::Id id) { + utils::SpinLock::Acquire locked(connector_lock_); + auto it = local_connectors_.find(id); + if (it != local_connectors_.end()) { + return it->second; + } + + return nullptr; +} + +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, + std::placeholders::_2, std::placeholders::_3)); + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Packet received from listener."; + + { + utils::SpinLock::Acquire locked(connector_lock_); + remote_connectors_.emplace(connector->getConnectorId(), + connector->shared_from_this()); + } + + // TODO Check if control packet or not. For the moment it is not. + onPacketReceived(connector, packets, ec); +} + +void Forwarder::onPacketReceived(Connector *connector, + const std::vector<utils::MemBuf::Ptr> &packets, + const std::error_code &ec) { + if (ec) { + LOG(ERROR) << "Error receiving packet: " << ec.message(); + return; + } + + for (auto &c : local_connectors_) { + c.second->receive(packets); + } + + // PCS Lookup + FIB lookup. Skip for now + + // Forward packet to local connectors +} + +void Forwarder::send(Packet &packet, Connector::Id connector_id) { + // TODo Here a nice PIT/CS / FIB would be required:) + // For now let's just forward the packet on the remote connector we get + for (auto &c : remote_connectors_) { + auto remote_endpoint = c.second->getRemoteEndpoint(); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending packet to: " << remote_endpoint.getAddress() << ":" + << remote_endpoint.getPort(); + c.second->send(packet); + } + + for (auto &c : local_connectors_) { + if (c.first != connector_id) { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending packet to local connector " << c.first << std::endl; + c.second->receive({packet.shared_from_this()}); + } + } +} + +void Forwarder::onPacketSent(Connector *connector, const std::error_code &ec) {} + +void Forwarder::onConnectorClosed(Connector *connector) {} + +void Forwarder::onConnectorReconnected(Connector *connector) {} + +void Forwarder::parseForwarderConfiguration( + const libconfig::Setting &forwarder_config, std::error_code &ec) { + using namespace libconfig; + + // n_thread + if (forwarder_config.exists("n_threads")) { + // Get number of threads + int n_threads = 1; + forwarder_config.lookupValue("n_threads", n_threads); + VLOG(1) << "Forwarder threads from config file: " << n_threads; + config_.setThreadNumber(n_threads); + } + + // listeners + if (forwarder_config.exists("listeners")) { + // get path where looking for modules + const Setting &listeners = forwarder_config.lookup("listeners"); + auto count = listeners.getLength(); + + for (int i = 0; i < count; i++) { + const Setting &listener = listeners[i]; + ListenerConfig list; + unsigned port; + + list.name = listener.getName(); + listener.lookupValue("local_address", list.address); + listener.lookupValue("local_port", port); + list.port = (uint16_t)(port); + + VLOG(1) << "Adding listener " << list.name << ", ( " << list.address + << ":" << list.port << ")"; + config_.addListener(std::move(list)); + } + } + + // connectors + if (forwarder_config.exists("connectors")) { + // get path where looking for modules + const Setting &connectors = forwarder_config.lookup("connectors"); + auto count = connectors.getLength(); + + for (int i = 0; i < count; i++) { + const Setting &connector = connectors[i]; + ConnectorConfig conn; + + conn.name = connector.getName(); + unsigned port = 0; + + if (!connector.lookupValue("local_address", conn.local_address)) { + conn.local_address = ""; + } + + if (!connector.lookupValue("local_port", port)) { + port = 0; + } + + conn.local_port = (uint16_t)(port); + + if (!connector.lookupValue("remote_address", conn.remote_address)) { + throw errors::RuntimeException( + "Error in configuration file: remote_address is a mandatory field " + "of Connectors."); + } + + if (!connector.lookupValue("remote_port", port)) { + throw errors::RuntimeException( + "Error in configuration file: remote_port is a mandatory field " + "of Connectors."); + } + + conn.remote_port = (uint16_t)(port); + + VLOG(1) << "Adding connector " << conn.name << ", (" << conn.local_address + << ":" << conn.local_port << " " << conn.remote_address << ":" + << conn.remote_port << ")"; + config_.addConnector(std::move(conn)); + } + } + + // Routes + if (forwarder_config.exists("routes")) { + const Setting &routes = forwarder_config.lookup("routes"); + auto count = routes.getLength(); + + for (int i = 0; i < count; i++) { + const Setting &route = routes[i]; + RouteConfig r; + unsigned weight; + + r.name = route.getName(); + route.lookupValue("prefix", r.prefix); + route.lookupValue("weight", weight); + route.lookupValue("connector", r.connector); + r.weight = (uint16_t)(weight); + + VLOG(1) << "Adding route " << r.name << " " << r.prefix << " (" + << r.connector << " " << r.weight << ")"; + config_.addRoute(std::move(r)); + } + } +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/forwarder.h b/libtransport/src/io_modules/forwarder/forwarder.h new file mode 100644 index 000000000..1022bf81b --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder.h @@ -0,0 +1,108 @@ +/* + * 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/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 <atomic> +#include <libconfig.h++> +#include <unordered_map> + +namespace transport { + +namespace core { + +class Forwarder { + static constexpr char forwarder_config_section[] = "forwarder"; + + public: + Forwarder(); + + ~Forwarder(); + + void initThreads(); + void initListeners(); + void initConnectors(); + + Connector::Id registerLocalConnector( + asio::io_service &io_service, + Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_callback, + Connector::OnReconnectCallback &&reconnect_callback); + + Forwarder &deleteConnector(Connector::Id id); + + Connector::Ptr getConnector(Connector::Id id); + + void send(Packet &packet, Connector::Id id); + + void stop(); + + private: + void onPacketFromListener(Connector *connector, + const std::vector<utils::MemBuf::Ptr> &packets, + const std::error_code &ec); + 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); + void onConnectorReconnected(Connector *connector); + + void parseForwarderConfiguration(const libconfig::Setting &io_config, + std::error_code &ec); + + asio::io_service io_service_; + utils::SpinLock connector_lock_; + + /** + * Connectors and listeners must be declares *before* thread_pool_, so that + * threads destructors will wait for them to gracefully close before being + * destroyed. + */ + std::unordered_map<Connector::Id, Connector::Ptr> remote_connectors_; + std::unordered_map<Connector::Id, Connector::Ptr> local_connectors_; + std::vector<UdpTunnelListener::Ptr> listeners_; + + std::vector<utils::EventThread> thread_pool_; + + 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 diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.cc b/libtransport/src/io_modules/forwarder/forwarder_module.cc new file mode 100644 index 000000000..ca9466f01 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder_module.cc @@ -0,0 +1,89 @@ +/* + * 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/errors/not_implemented_exception.h> +#include <io_modules/forwarder/forwarder_module.h> + +namespace transport { + +namespace core { + +ForwarderModule::ForwarderModule() + : IoModule(), + name_(""), + connector_id_(Connector::invalid_connector), + forwarder_ptr_(ForwarderGlobal::getInstance().getReference()), + forwarder_(*forwarder_ptr_) {} + +ForwarderModule::~ForwarderModule() {} + +bool ForwarderModule::isConnected() { return true; } + +void ForwarderModule::send(Packet &packet) { + IoModule::send(packet); + forwarder_.send(packet, connector_id_); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending from " << connector_id_ << " to " << 1 - connector_id_; +} + +void ForwarderModule::send(const utils::MemBuf::Ptr &buffer) { + // not supported + throw errors::NotImplementedException(); +} + +void ForwarderModule::registerRoute(const Prefix &prefix) { + // For the moment we route packets from one socket to the other. + // Next step will be to introduce a FIB + return; +} + +void ForwarderModule::closeConnection() { + forwarder_.deleteConnector(connector_id_); +} + +void ForwarderModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_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(sent_callback), + std::move(close_callback), std::move(reconnect_callback)); + name_ = app_name; +} + +void ForwarderModule::processControlMessageReply(utils::MemBuf &packet_buffer) { + return; +} + +void ForwarderModule::connect(bool is_consumer) { + forwarder_.getConnector(connector_id_) + ->setRole(is_consumer ? Connector::Role::CONSUMER + : Connector::Role::PRODUCER); +} + +std::uint32_t ForwarderModule::getMtu() { return interface_mtu; } + +bool ForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { + return false; +} + +extern "C" IoModule *create_module(void) { return new ForwarderModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.h b/libtransport/src/io_modules/forwarder/forwarder_module.h new file mode 100644 index 000000000..a48701161 --- /dev/null +++ b/libtransport/src/io_modules/forwarder/forwarder_module.h @@ -0,0 +1,76 @@ +/* + * 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/core/io_module.h> +#include <hicn/transport/core/prefix.h> +#include <io_modules/forwarder/forwarder.h> + +#include <atomic> + +namespace transport { + +namespace core { + +class Forwarder; + +class ForwarderModule : public IoModule { + static constexpr std::uint16_t interface_mtu = 1500; + + public: + ForwarderModule(); + + ~ForwarderModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const utils::MemBuf::Ptr &buffer) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") override; + + void registerRoute(const Prefix &prefix) override; + + std::uint32_t getMtu() 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_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/hicn-light/CMakeLists.txt b/libtransport/src/io_modules/hicn-light/CMakeLists.txt new file mode 100644 index 000000000..ae3aec52d --- /dev/null +++ b/libtransport/src/io_modules/hicn-light/CMakeLists.txt @@ -0,0 +1,63 @@ +# 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 ${HICN_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(hicnlight_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT ${LIBTRANSPORT_COMPONENT} + LINK_LIBRARIES PRIVATE ${LIBHICNCTRL_LIBRARIES} + INCLUDE_DIRS + PRIVATE + ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + ${Libhicn_INCLUDE_DIRS} + ${Libhicnctrl_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} +) diff --git a/libtransport/src/io_modules/hicn-light/hicn_forwarder_module.cc b/libtransport/src/io_modules/hicn-light/hicn_forwarder_module.cc new file mode 100644 index 000000000..98bd42fb5 --- /dev/null +++ b/libtransport/src/io_modules/hicn-light/hicn_forwarder_module.cc @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2021-2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <core/udp_connector.h> +#include <hicn/transport/utils/uri.h> +#include <io_modules/hicn-light/hicn_forwarder_module.h> + +extern "C" { +#include <hicn/ctrl/hicn-light.h> +} + +namespace transport { + +namespace core { + +HicnForwarderModule::ForwarderUrlInitializer + HicnForwarderModule::forwarder_url_initializer_; + +HicnForwarderModule::HicnForwarderModule() + : IoModule(), connector_(nullptr), seq_(0) {} + +HicnForwarderModule::~HicnForwarderModule() {} + +void HicnForwarderModule::connect(bool is_consumer) { + if (!connector_->isConnected()) { + // Parse forwarder URI + utils::Uri uri; + uri.parse(forwarder_url_initializer_.getForwarderUrl()); + + // Safechecks + CHECK(uri.getProtocol() == "hicn") + << "The protocol of the forwarder url should be hicn"; + uint16_t port_min = (1 << 10); + uint16_t port_max = (1 << 16) - 1; + + uint16_t port = std::stoul(uri.getPort()); + + CHECK(port > port_min && port < port_max) + << "The port should be between " << port_min << " and " << port_max; + + VLOG(1) << "Connecting to " << uri.getLocator() << ":" << uri.getPort(); + + connector_->connect(uri.getLocator(), port); + 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::OnCloseCallback &&close_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), + std::move(close_callback), 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) { + auto ret = PacketManager<>::getInstance().getMemBuf(); + 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() { + auto ret = PacketManager<>::getInstance().getMemBuf(); + 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() { + auto ret = PacketManager<>::getInstance().getMemBuf(); + auto command = reinterpret_cast<msg_mapme_add_t *>(ret->writableData()); + ret->append(sizeof(msg_mapme_add_t)); + std::memset(command, 0, sizeof(*command)); + + *command = {.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_ADD, + .length = 1, + .seq_num = seq_++, + }}; + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandSetForwardingStrategy( + std::unique_ptr<sockaddr> &&addr, uint32_t prefix_len, + std::string strategy) { + auto ret = PacketManager<>::getInstance().getMemBuf(); + 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/hicn-light/hicn_forwarder_module.h b/libtransport/src/io_modules/hicn-light/hicn_forwarder_module.h new file mode 100644 index 000000000..2378b93f8 --- /dev/null +++ b/libtransport/src/io_modules/hicn-light/hicn_forwarder_module.h @@ -0,0 +1,150 @@ +/* + * 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. + */ + +#pragma once + +#include <core/global_configuration.h> +#include <hicn/transport/core/io_module.h> +#include <hicn/transport/core/prefix.h> + +#include <libconfig.h++> + +extern "C" { +#include <hicn/ctrl/hicn-light.h> +} + +namespace transport { + +namespace core { + +class UdpTunnelConnector; + +class HicnForwarderModule : public IoModule { + static constexpr std::uint16_t interface_mtu = 1500; + + public: +#if 0 + union addressLight { + uint32_t ipv4; + struct in6_addr ipv6; + }; + + struct route_to_self_command { + uint8_t messageType; + uint8_t commandID; + uint16_t length; + uint32_t seqNum; + char symbolicOrConnid[16]; + union addressLight address; + uint16_t cost; + uint8_t addressType; + uint8_t len; + }; + + using route_to_self_command = struct route_to_self_command; +#endif + + HicnForwarderModule(); + + ~HicnForwarderModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const utils::MemBuf::Ptr &buffer) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_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(utils::MemBuf &packet_buffer) override; + + void processControlMessageReply(utils::MemBuf &packet_buffer) override; + + void closeConnection() override; + + private: + 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); + + static void parseForwarderConfiguration(const libconfig::Setting &io_config, + std::error_code &ec); + static std::string initForwarderUrl(); + + private: + std::shared_ptr<UdpTunnelConnector> connector_; + /* Sequence number used for sending control messages */ + uint32_t seq_; + + class ForwarderUrlInitializer { + static inline char default_hicnlight_url[] = "hicn://127.0.0.1:9695"; + static inline char hicnlight_configuration_section[] = "hicnlight"; + + public: + ForwarderUrlInitializer() + : forwarder_url_(ForwarderUrlInitializer::default_hicnlight_url) { + using namespace std::placeholders; + GlobalConfiguration::getInstance().registerConfigurationParser( + ForwarderUrlInitializer::hicnlight_configuration_section, + std::bind(&ForwarderUrlInitializer::parseForwarderConfiguration, this, + _1, _2)); + } + + std::string getForwarderUrl() { return forwarder_url_; } + + private: + void parseForwarderConfiguration(const libconfig::Setting &forwarder_config, + std::error_code &ec) { + using namespace libconfig; + + // forwarder url hicn://127.0.0.1:12345 + if (forwarder_config.exists("forwarder_url")) { + // Get number of threads + forwarder_config.lookupValue("forwarder_url", forwarder_url_); + VLOG(1) << "Forwarder URL from config file: " << forwarder_url_; + } + } + + // Url of the forwarder + std::string forwarder_url_; + }; + + static ForwarderUrlInitializer forwarder_url_initializer_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/memif/CMakeLists.txt b/libtransport/src/io_modules/memif/CMakeLists.txt new file mode 100644 index 000000000..134ac1db6 --- /dev/null +++ b/libtransport/src/io_modules/memif/CMakeLists.txt @@ -0,0 +1,64 @@ +# 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. + + +############################################################## +# Dependencies and third party libs +############################################################## +find_package(Vpp ${VPP_DEFAULT_VERSION} EXACT REQUIRED) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + 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 + ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.h + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_module.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.c + ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.c + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_module.cc +) + +build_module(memif_module + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT ${LIBTRANSPORT_COMPONENT}-io-modules + OBJECT_LIBRARIES ${MEMIF_THIRD_PARTY_OBJECT_LIBRARIES} + LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} ${SAFE_VAPI_LIBRARIES} + 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 ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} +) diff --git a/libtransport/src/core/hicn_vapi.c b/libtransport/src/io_modules/memif/hicn_vapi.c index be556f3aa..9fc64f720 100644 --- a/libtransport/src/core/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: @@ -14,29 +14,21 @@ */ #include <hicn/transport/config.h> - -#ifdef __vpp__ - -#include <hicn/transport/utils/log.h> -#include <core/hicn_vapi.h> +#include <io_modules/memif/hicn_vapi.h> #define HICN_VPP_PLUGIN #include <hicn/name.h> #undef HICN_VPP_PLUGIN +#include <vapi/hicn.api.vapi.h> +#include <vapi/ip.api.vapi.h> #include <vapi/vapi_safe.h> #include <vlib/vlib.h> #include <vlibapi/api.h> #include <vlibmemory/api.h> -#include <vppinfra/error.h> - #include <vnet/ip/format.h> -#include <vnet/ip/ip4_packet.h> -#include <vnet/ip/ip6_packet.h> -#include <vapi/ip.api.vapi.h> - -#include <vapi/hicn.api.vapi.h> #include <vpp_plugins/hicn/error.h> +#include <vppinfra/error.h> ///////////////////////////////////////////////////// const char *HICN_ERROR_STRING[] = { @@ -52,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) { @@ -64,14 +53,15 @@ static vapi_error_e register_prod_app_cb( if (reply == NULL) return rv; output_params->cs_reserved = reply->cs_reserved; - output_params->prod_addr = (ip_address_t *)malloc(sizeof(ip_address_t)); - memset(output_params->prod_addr, 0, sizeof(ip_address_t)); + output_params->prod_addr = + (hicn_ip_address_t *)malloc(sizeof(hicn_ip_address_t)); + memset(output_params->prod_addr, 0, sizeof(hicn_ip_address_t)); if (reply->prod_addr.af == ADDRESS_IP6) memcpy(&output_params->prod_addr->v6, reply->prod_addr.un.ip6, - sizeof(ip6_address_t)); + sizeof(ipv6_address_t)); else memcpy(&output_params->prod_addr->v4, reply->prod_addr.un.ip4, - sizeof(ip4_address_t)); + sizeof(ipv4_address_t)); output_params->face_id = reply->faceid; return reply->retval; @@ -84,13 +74,14 @@ 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 (hicn_ip_address_is_v4( + (hicn_ip_address_t *)&input_params->prefix->address)) { memcpy(&msg->payload.prefix.address.un.ip4, &input_params->prefix->address, - sizeof(ip4_address_t)); + sizeof(ipv4_address_t)); msg->payload.prefix.address.af = ADDRESS_IP4; } else { memcpy(&msg->payload.prefix.address.un.ip6, &input_params->prefix->address, - sizeof(ip6_address_t)); + sizeof(ipv6_address_t)); msg->payload.prefix.address.af = ADDRESS_IP6; } msg->payload.prefix.len = input_params->prefix->len; @@ -132,14 +123,14 @@ static vapi_error_e register_cons_app_cb( if (reply == NULL) return rv; - output_params->src6 = (ip_address_t *)malloc(sizeof(ip_address_t)); - output_params->src4 = (ip_address_t *)malloc(sizeof(ip_address_t)); - memset(output_params->src6, 0, sizeof(ip_address_t)); - memset(output_params->src4, 0, sizeof(ip_address_t)); + output_params->src6 = (hicn_ip_address_t *)malloc(sizeof(hicn_ip_address_t)); + output_params->src4 = (hicn_ip_address_t *)malloc(sizeof(hicn_ip_address_t)); + memset(output_params->src6, 0, sizeof(hicn_ip_address_t)); + memset(output_params->src4, 0, sizeof(hicn_ip_address_t)); memcpy(&output_params->src6->v6, &reply->src_addr6.un.ip6, - sizeof(ip6_address_t)); + sizeof(ipv6_address_t)); memcpy(&output_params->src4->v4, &reply->src_addr4.un.ip4, - sizeof(ip4_address_t)); + sizeof(ipv4_address_t)); output_params->face_id1 = reply->faceid1; output_params->face_id2 = reply->faceid2; @@ -196,30 +187,31 @@ 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))) { - memcpy(&msg->payload.route.prefix.address.un.ip4, &input_params->prefix->address.v4, - sizeof(ip4_address_t)); + if (hicn_ip_address_is_v4((hicn_ip_address_t *)(input_params->prod_addr))) { + memcpy(&msg->payload.route.prefix.address.un.ip4, + &input_params->prefix->address.v4, sizeof(ipv4_address_t)); msg->payload.route.prefix.address.af = ADDRESS_IP4; msg->payload.route.prefix.len = input_params->prefix->len; } else { - memcpy(&msg->payload.route.prefix.address.un.ip6, &input_params->prefix->address.v6, - sizeof(ip6_address_t)); + memcpy(&msg->payload.route.prefix.address.un.ip6, + &input_params->prefix->address.v6, sizeof(ipv6_address_t)); msg->payload.route.prefix.address.af = ADDRESS_IP6; msg->payload.route.prefix.len = input_params->prefix->len; } 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))) { - memcpy(&(msg->payload.route.paths[0].nh.address.ip4), input_params->prod_addr->v4.as_u8, sizeof(ip4_address_t)); + if (hicn_ip_address_is_v4((hicn_ip_address_t *)(input_params->prod_addr))) { + memcpy(&(msg->payload.route.paths[0].nh.address.ip4), + input_params->prod_addr->v4.as_u8, sizeof(ipv4_address_t)); msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } - else{ - memcpy(&(msg->payload.route.paths[0].nh.address.ip6), input_params->prod_addr->v6.as_u8, sizeof(ip6_address_t)); + } else { + memcpy(&(msg->payload.route.paths[0].nh.address.ip6), + input_params->prod_addr->v6.as_u8, sizeof(ipv6_address_t)); 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); @@ -231,5 +223,3 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, char *hicn_vapi_get_error_string(int ret_val) { return get_error_string(ret_val); } - -#endif // __vpp__ diff --git a/libtransport/src/core/hicn_vapi.h b/libtransport/src/io_modules/memif/hicn_vapi.h index f5d61e7ef..cfdc93fe3 100644 --- a/libtransport/src/core/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: @@ -18,10 +18,6 @@ #include <hicn/transport/config.h> #include <hicn/util/ip_address.h> -#ifdef __vpp__ - - - #ifdef __cplusplus extern "C" { #endif @@ -31,7 +27,7 @@ extern "C" { #include "stdint.h" typedef struct { - ip_prefix_t* prefix; + hicn_ip_prefix_t* prefix; uint32_t swif; uint32_t cs_reserved; } hicn_producer_input_params; @@ -46,43 +42,41 @@ typedef struct { typedef struct { uint32_t cs_reserved; - ip_address_t* prod_addr; + hicn_ip_address_t* prod_addr; uint32_t face_id; } hicn_producer_output_params; typedef struct { - ip_address_t* src4; - ip_address_t* src6; + hicn_ip_address_t* src4; + hicn_ip_address_t* src6; uint32_t face_id1; uint32_t face_id2; } hicn_consumer_output_params; typedef struct { - ip_prefix_t* prefix; - ip_address_t* prod_addr; + hicn_ip_prefix_t* prefix; + hicn_ip_address_t* prod_addr; } hicn_producer_set_route_params; -int hicn_vapi_register_prod_app( - vapi_ctx_t ctx, hicn_producer_input_params* input_params, - hicn_producer_output_params* output_params); +int hicn_vapi_register_prod_app(vapi_ctx_t ctx, + hicn_producer_input_params* input_params, + hicn_producer_output_params* output_params); -int hicn_vapi_register_cons_app( - vapi_ctx_t ctx, hicn_consumer_input_params* input_params, - hicn_consumer_output_params* output_params); +int hicn_vapi_register_cons_app(vapi_ctx_t ctx, + hicn_consumer_input_params* input_params, + hicn_consumer_output_params* output_params); -int hicn_vapi_register_route( - vapi_ctx_t ctx, hicn_producer_set_route_params* input_params); +int hicn_vapi_register_route(vapi_ctx_t ctx, + hicn_producer_set_route_params* input_params); -int hicn_vapi_face_cons_del( - vapi_ctx_t ctx, hicn_del_face_app_input_params *input_params); +int hicn_vapi_face_cons_del(vapi_ctx_t ctx, + hicn_del_face_app_input_params* input_params); -int hicn_vapi_face_prod_del( - vapi_ctx_t ctx, hicn_del_face_app_input_params *input_params); +int hicn_vapi_face_prod_del(vapi_ctx_t ctx, + hicn_del_face_app_input_params* input_params); char* hicn_vapi_get_error_string(int ret_val); #ifdef __cplusplus } #endif - -#endif // __vpp__ diff --git a/libtransport/src/core/memif_vapi.c b/libtransport/src/io_modules/memif/memif_vapi.c index ea3513306..54e2c3134 100644 --- a/libtransport/src/core/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: @@ -12,22 +12,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include <hicn/transport/config.h> - -#ifdef __vpp__ - -#include <core/memif_vapi.h> - #include <fcntl.h> +#include <hicn/transport/config.h> #include <inttypes.h> +#include <io_modules/memif/memif_vapi.h> #include <semaphore.h> #include <string.h> #include <sys/stat.h> #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) { @@ -49,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; @@ -129,5 +126,3 @@ int memif_vapi_delete_memif(vapi_ctx_t ctx, uint32_t sw_if_index) { vapi_unlock(); return ret; } - -#endif // __vpp__ diff --git a/libtransport/src/core/memif_vapi.h b/libtransport/src/io_modules/memif/memif_vapi.h index c045cf093..f5f0639e7 100644 --- a/libtransport/src/core/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: @@ -17,13 +17,12 @@ #include <hicn/transport/config.h> -#ifdef __vpp__ - #ifdef __cplusplus extern "C" { #endif #include <vapi/memif.api.vapi.h> + #include "stdint.h" typedef struct memif_create_params_s { @@ -43,18 +42,17 @@ typedef struct memif_output_params_s { uint32_t sw_if_index; } memif_output_params_t; -int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, - uint32_t *memif_id); +int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id); -int memif_vapi_create_memif(vapi_ctx_t ctx, - memif_create_params_t *input_params, - memif_output_params_t *output_params); +int memif_vapi_create_socket(vapi_ctx_t ctx, + memif_create_params_t *input_params, + memif_output_params_t *output_params); -int memif_vapi_delete_memif(vapi_ctx_t ctx, - uint32_t sw_if_index); +int memif_vapi_create_memif(vapi_ctx_t ctx, memif_create_params_t *input_params, + memif_output_params_t *output_params); + +int memif_vapi_delete_memif(vapi_ctx_t ctx, uint32_t sw_if_index); #ifdef __cplusplus } #endif - -#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/core/vpp_forwarder_interface.cc b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc index 9f7beeb37..d04c60893 100644 --- a/libtransport/src/core/vpp_forwarder_interface.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-2023 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,16 +13,16 @@ * limitations under the License. */ +#include <core/memif_connector.h> +#include <glog/logging.h> #include <hicn/transport/config.h> - -#ifdef __vpp__ - -#include <core/hicn_vapi.h> -#include <core/memif_vapi.h> -#include <core/vpp_forwarder_interface.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <io_modules/memif/hicn_vapi.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; @@ -36,23 +36,64 @@ namespace transport { namespace core { -VPPForwarderInterface::VPPForwarderInterface(MemifConnector &connector) - : ForwarderInterface<VPPForwarderInterface, MemifConnector>(connector), +VPPForwarderModule::VPPForwarderModule() + : IoModule(), + connector_(nullptr), + memif_id_(0), sw_if_index_(~0), face_id1_(~0), face_id2_(~0), is_consumer_(false) {} -VPPForwarderInterface::~VPPForwarderInterface() {} +VPPForwarderModule::~VPPForwarderModule() {} + +void VPPForwarderModule::init( + Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, const std::string &app_name) { + if (!connector_) { + connector_ = std::make_unique<MemifConnector>( + std::move(receive_callback), std::move(sent_callback), + std::move(close_callback), std::move(reconnect_callback), io_service, + app_name); + } +} + +void VPPForwarderModule::processControlMessageReply( + utils::MemBuf &packet_buffer) { + throw errors::NotImplementedException(); +} + +bool VPPForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { + return false; +} + +bool VPPForwarderModule::isConnected() { return connector_->isConnected(); }; + +void VPPForwarderModule::send(Packet &packet) { + IoModule::send(packet); + connector_->send(packet); +} + +void VPPForwarderModule::send(const utils::MemBuf::Ptr &buffer) { + counters_.tx_packets++; + counters_.tx_bytes += buffer->length(); + + // Perfect forwarding + connector_->send(buffer); +} + +std::uint32_t VPPForwarderModule::getMtu() { return interface_mtu; } /** * @brief Create a memif interface in the local VPP forwarder. */ -uint32_t VPPForwarderInterface::getMemifConfiguration() { +uint32_t VPPForwarderModule::getMemifConfiguration() { memif_create_params_t input_params = {0}; - int ret = - memif_vapi_get_next_memif_id(VPPForwarderInterface::sock_, &memif_id_); + int ret = memif_vapi_get_next_memif_id(VPPForwarderModule::sock_, &memif_id_); if (ret < 0) { throw errors::RuntimeException( @@ -69,7 +110,7 @@ uint32_t VPPForwarderInterface::getMemifConfiguration() { memif_output_params_t output_params = {0}; - ret = memif_vapi_create_memif(VPPForwarderInterface::sock_, &input_params, + ret = memif_vapi_create_memif(VPPForwarderModule::sock_, &input_params, &output_params); if (ret < 0) { @@ -80,18 +121,18 @@ uint32_t VPPForwarderInterface::getMemifConfiguration() { return output_params.sw_if_index; } -void VPPForwarderInterface::consumerConnection() { +void VPPForwarderModule::consumerConnection() { hicn_consumer_input_params input = {0}; hicn_consumer_output_params output = {0}; - ip_address_t ip4_address; - ip_address_t ip6_address; + hicn_ip_address_t ip4_address; + hicn_ip_address_t ip6_address; output.src4 = &ip4_address; output.src6 = &ip6_address; input.swif = sw_if_index_; - int ret = hicn_vapi_register_cons_app(VPPForwarderInterface::sock_, &input, - &output); + int ret = + hicn_vapi_register_cons_app(VPPForwarderModule::sock_, &input, &output); if (ret < 0) { throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); @@ -105,29 +146,27 @@ void VPPForwarderInterface::consumerConnection() { std::memcpy(inet6_address_.v6.as_u8, output.src6->v6.as_u8, IPV6_ADDR_LEN); } -void VPPForwarderInterface::producerConnection() { +void VPPForwarderModule::producerConnection() { // Producer connection will be set when we set the first route. } -void VPPForwarderInterface::connect(bool is_consumer) { +void VPPForwarderModule::connect(bool is_consumer) { int retry = 20; - - TRANSPORT_LOGI("Connecting to VPP through vapi."); + + LOG(INFO) << "Connecting to VPP through vapi."; vapi_error_e ret = vapi_connect_safe(&sock_, 0); while (ret != VAPI_OK && retry > 0) { - TRANSPORT_LOGE("Error connecting to VPP through vapi. Retrying.."); + LOG(ERROR) << "Error connecting to VPP through vapi (error: " << ret + << "). Retrying.."; --retry; ret = vapi_connect_safe(&sock_, 0); } - if (ret != VAPI_OK) { - throw std::runtime_error( - "Impossible to connect to forwarder. Is VPP running?"); - } - + CHECK_EQ(ret, VAPI_OK) + << "Impossible to connect to forwarder. Is VPP running?"; - TRANSPORT_LOGI("Connected to VPP through vapi."); + LOG(INFO) << "Connected to VPP through vapi."; sw_if_index_ = getMemifConfiguration(); @@ -136,14 +175,17 @@ void VPPForwarderInterface::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); } -void VPPForwarderInterface::registerRoute(Prefix &prefix) { - ip_prefix_t &addr = prefix.toIpPrefixStruct(); +void VPPForwarderModule::registerRoute(const Prefix &prefix) { + const hicn_ip_prefix_t &addr = prefix.toIpPrefixStruct(); - ip_prefix_t producer_prefix; - ip_address_t producer_locator; + hicn_ip_prefix_t producer_prefix; + hicn_ip_address_t producer_locator; if (face_id1_ == uint32_t(~0)) { hicn_producer_input_params input; @@ -157,21 +199,22 @@ void VPPForwarderInterface::registerRoute(Prefix &prefix) { // Here we have to ask to the actual connector what is the // memif_id, since this function should be called after the - // memif creation. + // memif creation.n input.swif = sw_if_index_; input.prefix->address = addr.address; input.prefix->family = addr.family; input.prefix->len = addr.len; input.cs_reserved = content_store_reserved_; - int ret = hicn_vapi_register_prod_app(VPPForwarderInterface::sock_, &input, - &output); + int ret = + hicn_vapi_register_prod_app(VPPForwarderModule::sock_, &input, &output); if (ret < 0) { 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 { @@ -182,7 +225,7 @@ void VPPForwarderInterface::registerRoute(Prefix &prefix) { params.prefix->len = addr.len; params.prod_addr = &producer_locator; - int ret = hicn_vapi_register_route(VPPForwarderInterface::sock_, ¶ms); + int ret = hicn_vapi_register_route(VPPForwarderModule::sock_, ¶ms); if (ret < 0) { throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); @@ -190,37 +233,36 @@ void VPPForwarderInterface::registerRoute(Prefix &prefix) { } } -void VPPForwarderInterface::closeConnection() { - if (VPPForwarderInterface::sock_) { - connector_.close(); - +void VPPForwarderModule::closeConnection() { + if (VPPForwarderModule::sock_) { if (is_consumer_) { hicn_del_face_app_input_params params; params.face_id = face_id1_; - hicn_vapi_face_cons_del(VPPForwarderInterface::sock_, ¶ms); + hicn_vapi_face_cons_del(VPPForwarderModule::sock_, ¶ms); params.face_id = face_id2_; - hicn_vapi_face_cons_del(VPPForwarderInterface::sock_, ¶ms); + hicn_vapi_face_cons_del(VPPForwarderModule::sock_, ¶ms); } else { hicn_del_face_app_input_params params; params.face_id = face_id1_; - hicn_vapi_face_prod_del(VPPForwarderInterface::sock_, ¶ms); + hicn_vapi_face_prod_del(VPPForwarderModule::sock_, ¶ms); } + connector_->close(); + if (sw_if_index_ != uint32_t(~0)) { int ret = - memif_vapi_delete_memif(VPPForwarderInterface::sock_, sw_if_index_); + memif_vapi_delete_memif(VPPForwarderModule::sock_, sw_if_index_); if (ret < 0) { - TRANSPORT_LOGE("Error deleting memif with sw idx %u.", sw_if_index_); + LOG(ERROR) << "Error deleting memif with sw idx " << sw_if_index_; } } - vapi_disconnect_safe(); - VPPForwarderInterface::sock_ = nullptr; + VPPForwarderModule::sock_ = nullptr; } } +extern "C" IoModule *create_module(void) { return new VPPForwarderModule(); } + } // namespace core } // namespace transport - -#endif diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.h b/libtransport/src/io_modules/memif/vpp_forwarder_module.h new file mode 100644 index 000000000..5a5358078 --- /dev/null +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.h @@ -0,0 +1,86 @@ +/* + * 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/core/io_module.h> +#include <hicn/transport/core/prefix.h> + +#ifdef always_inline +#undef always_inline +#endif +extern "C" { +#include <vapi/vapi_safe.h> +}; + +namespace transport { + +namespace core { + +class MemifConnector; + +class VPPForwarderModule : public IoModule { + static inline std::uint16_t interface_mtu = 1500; + static inline std::string const memif_socket_filename = "/run/vpp/memif.sock"; + + public: + VPPForwarderModule(); + ~VPPForwarderModule(); + + void connect(bool is_consumer) override; + + void send(Packet &packet) override; + void send(const utils::MemBuf::Ptr &buffer) override; + + bool isConnected() override; + + void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnCloseCallback &&close_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, + const std::string &app_name = "Libtransport") override; + + void registerRoute(const Prefix &prefix) override; + + std::uint32_t getMtu() override; + + bool isControlMessage(utils::MemBuf &packet_buffer) override; + + void processControlMessageReply(utils::MemBuf &packet_buffer) override; + + void closeConnection() override; + + private: + uint32_t getMemifConfiguration(); + void consumerConnection(); + void producerConnection(); + + private: + 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) + uint32_t face_id1_; + uint32_t face_id2_; + bool is_consumer_; + vapi_ctx_t sock_; +}; + +extern "C" IoModule *create_module(void); + +} // namespace core + +} // 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 8bfbdd6ad..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: @@ -11,50 +11,56 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/indexer.h - ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer.h - ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer_bytestream.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer_bytestream.h + ${CMAKE_CURRENT_SOURCE_DIR}/index_manager_bytestream.h ${CMAKE_CURRENT_SOURCE_DIR}/reassembly.h ${CMAKE_CURRENT_SOURCE_DIR}/datagram_reassembly.h ${CMAKE_CURRENT_SOURCE_DIR}/byte_stream_reassembly.h ${CMAKE_CURRENT_SOURCE_DIR}/congestion_window_protocol.h - ${CMAKE_CURRENT_SOURCE_DIR}/packet_manager.h ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.h - ${CMAKE_CURRENT_SOURCE_DIR}/protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/transport_protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/production_protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_bytestream.h + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_rtc.h ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.h ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.h ${CMAKE_CURRENT_SOURCE_DIR}/cbr.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h ${CMAKE_CURRENT_SOURCE_DIR}/errors.h - ${CMAKE_CURRENT_SOURCE_DIR}/verification_manager.h ${CMAKE_CURRENT_SOURCE_DIR}/data_processing_events.h + ${CMAKE_CURRENT_SOURCE_DIR}/fec_base.h ) list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/indexer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer.cc - ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer_bytestream.cc + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer_bytestream.cc + ${CMAKE_CURRENT_SOURCE_DIR}/index_manager_bytestream.cc ${CMAKE_CURRENT_SOURCE_DIR}/reassembly.cc ${CMAKE_CURRENT_SOURCE_DIR}/datagram_reassembly.cc ${CMAKE_CURRENT_SOURCE_DIR}/byte_stream_reassembly.cc - ${CMAKE_CURRENT_SOURCE_DIR}/protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/transport_protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/production_protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_bytestream.cc + ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_rtc.cc ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.cc ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.cc ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.cc ${CMAKE_CURRENT_SOURCE_DIR}/cbr.cc - ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc - ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc - ${CMAKE_CURRENT_SOURCE_DIR}/verification_manager.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,9 +73,12 @@ set(TRANSPORT_CONFIG install( FILES ${TRANSPORT_CONFIG} - DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn - COMPONENT lib${LIBTRANSPORT} + DESTINATION ${RAAQM_CONFIG_INSTALL_PREFIX} + COMPONENT ${LIBTRANSPORT_COMPONENT} ) +add_subdirectory(rtc) +add_subdirectory(fec) + set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) -set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/byte_stream_reassembly.cc b/libtransport/src/protocols/byte_stream_reassembly.cc index 6662bec3f..b9eaf3bec 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: @@ -20,7 +20,7 @@ #include <protocols/byte_stream_reassembly.h> #include <protocols/errors.h> #include <protocols/indexer.h> -#include <protocols/protocol.h> +#include <protocols/transport_protocol.h> namespace transport { @@ -33,31 +33,26 @@ ByteStreamReassembly::ByteStreamReassembly( implementation::ConsumerSocket *icn_socket, TransportProtocol *transport_protocol) : Reassembly(icn_socket, transport_protocol), - index_(IndexManager::invalid_index), + index_(Indexer::invalid_index), download_complete_(false) {} -void ByteStreamReassembly::reassemble( - std::unique_ptr<ContentObjectManifest> &&manifest) { - if (TRANSPORT_EXPECT_TRUE(manifest != nullptr) && read_buffer_->capacity()) { +void ByteStreamReassembly::reassemble(ContentObject &content_object) { + if (TRANSPORT_EXPECT_TRUE(read_buffer_->capacity())) { received_packets_.emplace( - std::make_pair(manifest->getName().getSuffix(), nullptr)); + std::make_pair(content_object.getName().getSuffix(), + content_object.shared_from_this())); assembleContent(); } } -void ByteStreamReassembly::reassemble(ContentObject::Ptr &&content_object) { - if (TRANSPORT_EXPECT_TRUE(content_object != nullptr) && - read_buffer_->capacity()) { - received_packets_.emplace(std::make_pair( - content_object->getName().getSuffix(), std::move(content_object))); - assembleContent(); - } +void ByteStreamReassembly::reassemble(utils::MemBuf &buffer, uint32_t suffix) { + throw errors::NotImplementedException(); } void ByteStreamReassembly::assembleContent() { - if (TRANSPORT_EXPECT_FALSE(index_ == IndexManager::invalid_index)) { - index_ = index_manager_->getNextReassemblySegment(); - if (index_ == IndexManager::invalid_index) { + if (TRANSPORT_EXPECT_FALSE(index_ == Indexer::invalid_index)) { + index_ = indexer_verifier_->getNextReassemblySegment(); + if (index_ == Indexer::invalid_index) { return; } } @@ -72,37 +67,44 @@ void ByteStreamReassembly::assembleContent() { } received_packets_.erase(it); - index_ = index_manager_->getNextReassemblySegment(); + index_ = indexer_verifier_->getNextReassemblySegment(); it = received_packets_.find((const unsigned int)index_); } - if (!download_complete_ && index_ != IndexManager::invalid_index) { + if (!download_complete_ && index_ != Indexer::invalid_index) { transport_protocol_->onReassemblyFailed(index_); } } -bool ByteStreamReassembly::copyContent(const ContentObject &content_object) { +bool ByteStreamReassembly::copyContent(ContentObject &content_object) { bool ret = false; - auto payload = content_object.getPayloadReference(); - auto payload_length = payload.second; - auto write_size = std::min(payload_length, read_buffer_->tailroom()); - auto additional_bytes = payload_length > read_buffer_->tailroom() - ? payload_length - read_buffer_->tailroom() - : 0; + content_object.trimStart(content_object.headerSize()); - std::memcpy(read_buffer_->writableTail(), payload.first, write_size); - read_buffer_->append(write_size); + utils::MemBuf *current = &content_object; - if (!read_buffer_->tailroom()) { - notifyApplication(); - std::memcpy(read_buffer_->writableTail(), payload.first + write_size, - additional_bytes); - read_buffer_->append(additional_bytes); - } + do { + auto payload_length = current->length(); + auto write_size = std::min(payload_length, read_buffer_->tailroom()); + auto additional_bytes = payload_length > read_buffer_->tailroom() + ? payload_length - read_buffer_->tailroom() + : 0; - download_complete_ = - index_manager_->getFinalSuffix() == content_object.getName().getSuffix(); + std::memcpy(read_buffer_->writableTail(), current->data(), write_size); + read_buffer_->append(write_size); + + if (!read_buffer_->tailroom()) { + notifyApplication(); + std::memcpy(read_buffer_->writableTail(), current->data() + write_size, + additional_bytes); + read_buffer_->append(additional_bytes); + } + + current = current->next(); + } while (current != &content_object); + + download_complete_ = indexer_verifier_->getFinalSuffix() == + content_object.getName().getSuffix(); if (TRANSPORT_EXPECT_FALSE(download_complete_)) { ret = download_complete_; @@ -115,17 +117,19 @@ bool ByteStreamReassembly::copyContent(const ContentObject &content_object) { } void ByteStreamReassembly::reInitialize() { - index_ = IndexManager::invalid_index; + index_ = Indexer::invalid_index; download_complete_ = false; received_packets_.clear(); // reset read buffer ReadCallback *read_callback; - reassembly_consumer_socket_->getSocketOption( - interface::ConsumerCallbacksOptions::READ_CALLBACK, &read_callback); - read_buffer_ = utils::MemBuf::create(read_callback->maxBufferSize()); + if (reassembly_consumer_socket_) { + reassembly_consumer_socket_->getSocketOption( + interface::ConsumerCallbacksOptions::READ_CALLBACK, &read_callback); + read_buffer_ = utils::MemBuf::create(read_callback->maxBufferSize()); + } } } // namespace protocol diff --git a/libtransport/src/protocols/byte_stream_reassembly.h b/libtransport/src/protocols/byte_stream_reassembly.h index e4f62b3a8..a1f965d5c 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: @@ -27,12 +27,11 @@ class ByteStreamReassembly : public Reassembly { TransportProtocol *transport_protocol); protected: - virtual void reassemble(core::ContentObject::Ptr &&content_object) override; + void reassemble(core::ContentObject &content_object) override; - virtual void reassemble( - std::unique_ptr<core::ContentObjectManifest> &&manifest) override; + void reassemble(utils::MemBuf &buffer, uint32_t suffix) override; - bool copyContent(const core::ContentObject &content_object); + bool copyContent(core::ContentObject &content_object); virtual void reInitialize() override; @@ -40,10 +39,6 @@ class ByteStreamReassembly : public Reassembly { void assembleContent(); protected: - // The consumer socket - // std::unique_ptr<IncrementalIndexManager> incremental_index_manager_; - // std::unique_ptr<ManifestIndexManager> manifest_index_manager_; - // IndexVerificationManager *index_manager_; std::unordered_map<std::uint32_t, core::ContentObject::Ptr> received_packets_; uint32_t index_; bool download_complete_; diff --git a/libtransport/src/protocols/cbr.cc b/libtransport/src/protocols/cbr.cc index 0bffd7d18..e3f0f1336 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: @@ -26,8 +26,6 @@ CbrTransportProtocol::CbrTransportProtocol( implementation::ConsumerSocket *icnet_socket) : RaaqmTransportProtocol(icnet_socket) {} -int CbrTransportProtocol::start() { return RaaqmTransportProtocol::start(); } - void CbrTransportProtocol::reset() { RaaqmTransportProtocol::reset(); socket_->getSocketOption(GeneralTransportOptions::CURRENT_WINDOW_SIZE, @@ -39,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::getDurationUs( + 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 20129f6a3..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: @@ -25,7 +25,8 @@ class CbrTransportProtocol : public RaaqmTransportProtocol { public: CbrTransportProtocol(implementation::ConsumerSocket *icnet_socket); - int start() override; + using RaaqmTransportProtocol::start; + using RaaqmTransportProtocol::stop; void reset() override; 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 8975c2b4a..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: @@ -24,8 +24,8 @@ namespace protocol { class ContentObjectProcessingEventCallback { public: virtual ~ContentObjectProcessingEventCallback() = default; - virtual void onPacketDropped(core::Interest::Ptr &&i, - core::ContentObject::Ptr &&c) = 0; + virtual void onPacketDropped(core::Interest &i, core::ContentObject &c, + const std::error_code &reason) = 0; virtual void onReassemblyFailed(std::uint32_t missing_segment) = 0; }; diff --git a/libtransport/src/protocols/datagram_reassembly.cc b/libtransport/src/protocols/datagram_reassembly.cc index abd7e984d..a04b0eecf 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: @@ -14,6 +14,7 @@ */ #include <protocols/datagram_reassembly.h> +#include <protocols/transport_protocol.h> namespace transport { @@ -24,8 +25,19 @@ DatagramReassembly::DatagramReassembly( TransportProtocol* transport_protocol) : Reassembly(icn_socket, transport_protocol) {} -void DatagramReassembly::reassemble(core::ContentObject::Ptr&& content_object) { - read_buffer_ = content_object->getPayload(); +void DatagramReassembly::reassemble(core::ContentObject& content_object) { + auto read_buffer = content_object.getPayload(); + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Size of payload: " << read_buffer->length() << ". Trimming " + << transport_protocol_->transportHeaderLength(false); + // here we have only src data packet + read_buffer->trimStart(transport_protocol_->transportHeaderLength(false)); + Reassembly::read_buffer_ = std::move(read_buffer); + Reassembly::notifyApplication(); +} + +void DatagramReassembly::reassemble(utils::MemBuf& buffer, uint32_t suffix) { + read_buffer_ = buffer.cloneOne(); Reassembly::notifyApplication(); } diff --git a/libtransport/src/protocols/datagram_reassembly.h b/libtransport/src/protocols/datagram_reassembly.h index 2427ae62f..cefdca93b 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: @@ -26,12 +26,10 @@ class DatagramReassembly : public Reassembly { DatagramReassembly(implementation::ConsumerSocket *icn_socket, TransportProtocol *transport_protocol); - virtual void reassemble(core::ContentObject::Ptr &&content_object) override; + virtual void reassemble(core::ContentObject &content_object) override; + void reassemble(utils::MemBuf &buffer, uint32_t suffix) override; virtual void reInitialize() override; - virtual void reassemble( - std::unique_ptr<core::ContentObjectManifest> &&manifest) override { - return; - } + bool reassembleUnverified() override { return true; } }; } // namespace protocol diff --git a/libtransport/src/protocols/errors.cc b/libtransport/src/protocols/errors.cc index eefb6f957..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: @@ -39,6 +39,9 @@ std::string protocol_category_impl::message(int ev) const { case protocol_error::integrity_verification_failed: { return "Integrity verification failed"; } + case protocol_error::verification_failed: { + return "Verification failed"; + } case protocol_error::no_verifier_provided: { return "Transport cannot get any verifier for the given data."; } @@ -52,9 +55,17 @@ std::string protocol_category_impl::message(int ev) const { case protocol_error::session_aborted: { return "The session has been aborted by the application."; } - default: { return "Unknown protocol error"; } + case protocol_error::not_reassemblable: { + return "The session has been aborted by the application."; + } + case protocol_error::duplicated_content: { + return "The session has been aborted by the application."; + } + default: { + return "Unknown protocol error"; + } } } } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/errors.h b/libtransport/src/protocols/errors.h index cb3d3474e..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: @@ -37,10 +37,14 @@ enum class protocol_error { success = 0, signature_verification_failed, integrity_verification_failed, + verification_failed, no_verifier_provided, io_error, max_retransmissions_error, session_aborted, + not_reassemblable, + delayed_reassemble, + duplicated_content }; /** diff --git a/libtransport/src/protocols/fec/CMakeLists.txt b/libtransport/src/protocols/fec/CMakeLists.txt new file mode 100644 index 000000000..8ae0a7360 --- /dev/null +++ b/libtransport/src/protocols/fec/CMakeLists.txt @@ -0,0 +1,36 @@ +# 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 HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/fec.h + ${CMAKE_CURRENT_SOURCE_DIR}/rs.h + ${CMAKE_CURRENT_SOURCE_DIR}/fec_info.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/fec.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rs.cc +) + +if (ENABLE_RELY) + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rely.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rely.cc + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/fec/fec.cc b/libtransport/src/protocols/fec/fec.cc new file mode 100644 index 000000000..d2105eb53 --- /dev/null +++ b/libtransport/src/protocols/fec/fec.cc @@ -0,0 +1,722 @@ +/* + * fec.c -- forward error correction based on Vandermonde matrices + * 980624 + * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) + * + * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), + * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari + * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * The following parameter defines how many bits are used for + * field elements. The code supports any value from 2 to 16 + * but fastest operation is achieved with 8 bit elements + * This is the only parameter you may want to change. + */ +#ifndef GF_BITS +#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ +#endif + +#include "fec.h" + +#include <assert.h> +#include <hicn/transport/portability/platform.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * XXX This disable a warning raising only in some platforms. + * TODO Check if this warning is a mistake or it is a real bug: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83404 + * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=88059 + */ +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + +/* + * You should not need to change anything beyond this point. + * The first part of the file implements linear algebra in GF. + * + * gf is the type used to store an element of the Galois Field. + * Must constain at least GF_BITS bits. + * + * Note: unsigned char will work up to GF(256) but int seems to run + * faster on the Pentium. We use int whenever have to deal with an + * index, since they are generally faster. + */ +#if (GF_BITS < 2 && GF_BITS > 16) +#error "GF_BITS must be 2 .. 16" +#endif + +#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ + +/* + * Primitive polynomials - see Lin & Costello, Appendix A, + * and Lee & Messerschmitt, p. 453. + */ +static const char *allPp[] = { + /* GF_BITS polynomial */ + NULL, /* 0 no code */ + NULL, /* 1 no code */ + "111", /* 2 1+x+x^2 */ + "1101", /* 3 1+x+x^3 */ + "11001", /* 4 1+x+x^4 */ + "101001", /* 5 1+x^2+x^5 */ + "1100001", /* 6 1+x+x^6 */ + "10010001", /* 7 1 + x^3 + x^7 */ + "101110001", /* 8 1+x^2+x^3+x^4+x^8 */ + "1000100001", /* 9 1+x^4+x^9 */ + "10010000001", /* 10 1+x^3+x^10 */ + "101000000001", /* 11 1+x^2+x^11 */ + "1100101000001", /* 12 1+x+x^4+x^6+x^12 */ + "11011000000001", /* 13 1+x+x^3+x^4+x^13 */ + "110000100010001", /* 14 1+x+x^6+x^10+x^14 */ + "1100000000000001", /* 15 1+x+x^15 */ + "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */ +}; + +/* + * To speed up computations, we have tables for logarithm, exponent + * and inverse of a number. If GF_BITS <= 8, we use a table for + * multiplication as well (it takes 64K, no big deal even on a PDA, + * especially because it can be pre-initialized an put into a ROM!), + * otherwhise we use a table of logarithms. + * In any case the macro gf_mul(x,y) takes care of multiplications. + */ + +static gf gf_exp[2 * GF_SIZE]; /* index->poly form conversion table */ +static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */ +static gf inverse[GF_SIZE + 1]; /* inverse of field elem. */ + /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */ + +/* + * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, + * without a slow divide. + */ +static inline gf modnn(int x) { + while (x >= GF_SIZE) { + x -= GF_SIZE; + x = (x >> GF_BITS) + (x & GF_SIZE); + } + return x; +} + +#define SWAP(a, b, t) \ + { \ + t tmp; \ + tmp = a; \ + a = b; \ + b = tmp; \ + } + +/* + * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much + * faster to use a multiplication table. + * + * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying + * many numbers by the same constant. In this case the first + * call sets the constant, and others perform the multiplications. + * A value related to the multiplication is held in a local variable + * declared with USE_GF_MULC . See usage in addmul1(). + */ +#if (GF_BITS <= 8) +static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1]; + +#define gf_mul(x, y) gf_mul_table[x][y] + +#define USE_GF_MULC gf *__gf_mulc_ +#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c] +#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x] + +static void init_mul_table() { + int i, j; + for (i = 0; i < GF_SIZE + 1; i++) + for (j = 0; j < GF_SIZE + 1; j++) + gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j])]; + + for (j = 0; j < GF_SIZE + 1; j++) gf_mul_table[0][j] = gf_mul_table[j][0] = 0; +} +#else /* GF_BITS > 8 */ +static inline gf gf_mul(x, y) { + if ((x) == 0 || (y) == 0) return 0; + + return gf_exp[gf_log[x] + gf_log[y]]; +} +#define init_mul_table() + +#define USE_GF_MULC register gf *__gf_mulc_ +#define GF_MULC0(c) __gf_mulc_ = &gf_exp[gf_log[c]] +#define GF_ADDMULC(dst, x) \ + { \ + if (x) dst ^= __gf_mulc_[gf_log[x]]; \ + } +#endif + +/* + * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] + * Lookup tables: + * index->polynomial form gf_exp[] contains j= \alpha^i; + * polynomial form -> index form gf_log[ j = \alpha^i ] = i + * \alpha=x is the primitive element of GF(2^m) + * + * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple + * multiplication of two numbers can be resolved without calling modnn + */ + +/* + * i use malloc so many times, it is easier to put checks all in + * one place. + */ +static void *my_malloc(int sz, const char *err_string) { + void *p = malloc(sz); + if (p == NULL) { + fprintf(stderr, "-- malloc failure allocating %s\n", err_string); + exit(1); + } + return p; +} + +#define NEW_GF_MATRIX(rows, cols) \ + (gf *)my_malloc(rows *cols * sizeof(gf), " ## __LINE__ ## ") + +/* + * initialize the data structures used for computations in GF. + */ +static void generate_gf(void) { + int i; + gf mask; + const char *Pp = allPp[GF_BITS]; + + mask = 1; /* x ** 0 = 1 */ + gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */ + /* + * first, generate the (polynomial representation of) powers of \alpha, + * which are stored in gf_exp[i] = \alpha ** i . + * At the same time build gf_log[gf_exp[i]] = i . + * The first GF_BITS powers are simply bits shifted to the left. + */ + for (i = 0; i < GF_BITS; i++, mask <<= 1) { + gf_exp[i] = mask; + gf_log[gf_exp[i]] = i; + /* + * If Pp[i] == 1 then \alpha ** i occurs in poly-repr + * gf_exp[GF_BITS] = \alpha ** GF_BITS + */ + if (Pp[i] == '1') gf_exp[GF_BITS] ^= mask; + } + /* + * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als + * compute its inverse. + */ + gf_log[gf_exp[GF_BITS]] = GF_BITS; + /* + * Poly-repr of \alpha ** (i+1) is given by poly-repr of + * \alpha ** i shifted left one-bit and accounting for any + * \alpha ** GF_BITS term that may occur when poly-repr of + * \alpha ** i is shifted. + */ + mask = 1 << (GF_BITS - 1); + for (i = GF_BITS + 1; i < GF_SIZE; i++) { + if (gf_exp[i - 1] >= mask) + gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1); + else + gf_exp[i] = gf_exp[i - 1] << 1; + gf_log[gf_exp[i]] = i; + } + /* + * log(0) is not defined, so use a special value + */ + gf_log[0] = GF_SIZE; + /* set the extended gf_exp values for fast multiply */ + for (i = 0; i < GF_SIZE; i++) gf_exp[i + GF_SIZE] = gf_exp[i]; + + /* + * again special cases. 0 has no inverse. This used to + * be initialized to GF_SIZE, but it should make no difference + * since noone is supposed to read from here. + */ + inverse[0] = 0; + inverse[1] = 1; + for (i = 2; i <= GF_SIZE; i++) inverse[i] = gf_exp[GF_SIZE - gf_log[i]]; +} + +/* + * Various linear algebra operations that i use often. + */ + +/* + * addmul() computes dst[] = dst[] + c * src[] + * This is used often, so better optimize it! Currently the loop is + * unrolled 16 times, a good value for 486 and pentium-class machines. + * The case c=0 is also optimized, whereas c=1 is not. These + * calls are unfrequent in my typical apps so I did not bother. + * + * Note that gcc on + */ +#define addmul(dst, src, c, sz) \ + if (c != 0) addmul1(dst, src, c, sz) + +#define UNROLL 16 /* 1, 4, 8, 16 */ +static void addmul1(gf *dst1, gf *src1, gf c, int sz) { + USE_GF_MULC; + gf *dst = dst1, *src = src1; + gf *lim = &dst[sz - UNROLL + 1]; + + GF_MULC0(c); + +#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */ + for (; dst < lim; dst += UNROLL, src += UNROLL) { + GF_ADDMULC(dst[0], src[0]); + GF_ADDMULC(dst[1], src[1]); + GF_ADDMULC(dst[2], src[2]); + GF_ADDMULC(dst[3], src[3]); +#if (UNROLL > 4) + GF_ADDMULC(dst[4], src[4]); + GF_ADDMULC(dst[5], src[5]); + GF_ADDMULC(dst[6], src[6]); + GF_ADDMULC(dst[7], src[7]); +#endif +#if (UNROLL > 8) + GF_ADDMULC(dst[8], src[8]); + GF_ADDMULC(dst[9], src[9]); + GF_ADDMULC(dst[10], src[10]); + GF_ADDMULC(dst[11], src[11]); + GF_ADDMULC(dst[12], src[12]); + GF_ADDMULC(dst[13], src[13]); + GF_ADDMULC(dst[14], src[14]); + GF_ADDMULC(dst[15], src[15]); +#endif + } +#endif + lim += UNROLL - 1; + for (; dst < lim; dst++, src++) /* final components */ + GF_ADDMULC(*dst, *src); +} + +/* + * computes C = AB where A is n*k, B is k*m, C is n*m + */ +static void matmul(gf *a, gf *b, gf *c, int n, int k, int m) { + int row, col, i; + + for (row = 0; row < n; row++) { + for (col = 0; col < m; col++) { + gf *pa = &a[row * k]; + gf *pb = &b[col]; + gf acc = 0; + for (i = 0; i < k; i++, pa++, pb += m) acc ^= gf_mul(*pa, *pb); + c[row * m + col] = acc; + } + } +} + +/* + * 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. + */ +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; + + int error = 1; + int *indxc = (int *)my_malloc(k * sizeof(int), "indxc"); + int *indxr = (int *)my_malloc(k * sizeof(int), "indxr"); + 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); + memset(id_row, '\0', k * sizeof(gf)); + pivloops = 0; + pivswaps = 0; /* diagnostic */ + /* + * ipiv marks elements already used as pivots. + */ + for (i = 0; i < k; i++) ipiv[i] = 0; + + for (col = 0; col < k; col++) { + gf *pivot_row; + /* + * Zeroing column 'col', look for a non-zero element. + * First try on the diagonal, if it fails, look elsewhere. + */ + irow = icol = -1; + if (ipiv[col] != 1 && src[col * k + col] != 0) { + irow = col; + icol = col; + goto found_piv; + } + for (row = 0; row < k; row++) { + if (ipiv[row] != 1) { + for (ix = 0; ix < k; ix++) { + pivloops++; + if (ipiv[ix] == 0) { + if (src[row * k + ix] != 0) { + irow = row; + icol = ix; + goto found_piv; + } + } else if (ipiv[ix] > 1) { + fprintf(stderr, "singular matrix\n"); + goto fail; + } + } + } + } + if (icol == -1) { + fprintf(stderr, "XXX pivot not found!\n"); + goto fail; + } + found_piv: + ++(ipiv[icol]); + /* + * swap rows irow and icol, so afterwards the diagonal + * element will be correct. Rarely done, not worth + * optimizing. + */ + if (irow != icol) { + for (ix = 0; ix < k; ix++) { + SWAP(src[irow * k + ix], src[icol * k + ix], gf); + } + } + indxr[col] = irow; + indxc[col] = icol; + pivot_row = &src[icol * k]; + c = pivot_row[icol]; + if (c == 0) { + fprintf(stderr, "singular matrix 2\n"); + goto fail; + } + + 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]); + } + /* + * from all rows, remove multiples of the selected row + * to zero the relevant entry (in fact, the entry is not zero + * because we know it must be zero). + * (Here, if we know that the pivot_row is the identity, + * we can optimize the addmul). + */ + id_row[icol] = 1; + 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]; + p[icol] = 0; + addmul(p, pivot_row, c, k); + } + } + } + id_row[icol] = 0; + } /* done all columns */ + for (col = k - 1; col >= 0; col--) { + if (indxr[col] < 0 || indxr[col] >= k) + fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]); + else if (indxc[col] < 0 || indxc[col] >= k) + fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]); + else if (indxr[col] != indxc[col]) { + for (row = 0; row < k; row++) { + SWAP(src[row * k + indxr[col]], src[row * k + indxc[col]], gf); + } + } + } + error = 0; +fail: + free(indxc); + free(indxr); + free(ipiv); + free(id_row); + free(temp_row); + return error; +} + +/* + * fast code for inverting a vandermonde matrix. + * XXX NOTE: It assumes that the matrix + * is not singular and _IS_ a vandermonde matrix. Only uses + * the second column of the matrix, containing the p_i's. + * + * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but + * largely revised for my purposes. + * p = coefficients of the matrix (p_i) + * q = values of the polynomial (known) + */ + +int invert_vdm(gf *src, int k) { + assert(k > 0); + + int i, j, row, col; + gf *b, *c, *p; + gf t, xx; + + if (k == 1) /* degenerate case, matrix must be p^0 = 1 */ + return 0; + /* + * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1 + * b holds the coefficient for the matrix inversion + */ + c = NEW_GF_MATRIX(1, k); + b = NEW_GF_MATRIX(1, k); + + p = NEW_GF_MATRIX(1, k); + + for (j = 1, i = 0; i < k; i++, j += k) { + c[i] = 0; + p[i] = src[j]; /* p[i] */ + } + /* + * construct coeffs. recursively. We know c[k] = 1 (implicit) + * and start P_0 = x - p_0, then at each stage multiply by + * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1} + * After k steps we are done. + */ + c[k - 1] = p[0]; /* really -p(0), but x = -x in GF(2^m) */ + for (i = 1; i < k; i++) { + gf p_i = p[i]; /* see above comment */ + for (j = k - 1 - (i - 1); j < k - 1; j++) c[j] ^= gf_mul(p_i, c[j + 1]); + c[k - 1] ^= p_i; + } + + for (row = 0; row < k; row++) { + /* + * synthetic division etc. + */ + xx = p[row]; + t = 1; + b[k - 1] = 1; /* this is in fact c[k] */ + for (i = k - 2; i >= 0; i--) { + b[i] = c[i + 1] ^ gf_mul(xx, b[i + 1]); + t = gf_mul(xx, t) ^ b[i]; + } + for (col = 0; col < k; col++) + src[col * k + row] = gf_mul(inverse[t], b[col]); + } + free(c); + free(b); + free(p); + return 0; +} + +static int fec_initialized = 0; +static void init_fec() { + generate_gf(); + init_mul_table(); + fec_initialized = 1; +} + +/* + * This section contains the proper FEC encoding/decoding routines. + * The encoding matrix is computed starting with a Vandermonde matrix, + * and then transforming it into a systematic matrix. + */ + +#define FEC_MAGIC 0xFECC0DEC + +void fec_free(struct fec_parms *p) { + if (p == NULL || p->magic != (((FEC_MAGIC ^ p->k) ^ p->n) ^ + (unsigned long)(p->enc_matrix))) { + fprintf(stderr, "bad parameters to fec_free\n"); + return; + } + free(p->enc_matrix); + free(p); +} + +/* + * create a new encoder, returning a descriptor. This contains k,n and + * the encoding matrix. + */ +struct fec_parms *fec_new(int k, int n) { + int row, col; + gf *p, *tmp_m; + + struct fec_parms *retval; + + if (fec_initialized == 0) init_fec(); + + if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n) { + fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n", k, n, GF_SIZE); + return NULL; + } + retval = (struct fec_parms *)my_malloc(sizeof(struct fec_parms), "new_code"); + retval->k = k; + retval->n = n; + retval->enc_matrix = NEW_GF_MATRIX(n, k); + retval->magic = ((FEC_MAGIC ^ k) ^ n) ^ (unsigned long)(retval->enc_matrix); + tmp_m = NEW_GF_MATRIX(n, k); + /* + * fill the matrix with powers of field elements, starting from 0. + * The first row is special, cannot be computed with exp. table. + */ + tmp_m[0] = 1; + for (col = 1; col < k; col++) tmp_m[col] = 0; + for (p = tmp_m + k, row = 0; row < n - 1; row++, p += k) { + for (col = 0; col < k; col++) p[col] = gf_exp[modnn(row * col)]; + } + + /* + * quick code to build systematic matrix: invert the top + * k*k vandermonde matrix, multiply right the bottom n-k rows + * by the inverse, and construct the identity matrix at the top. + */ + 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 + */ + 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); + return retval; +} + +/* + * fec_encode accepts as input pointers to n data packets of size sz, + * and produces as output a packet pointed to by fec, computed + * with index "index". + */ +void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) { + int i, k = code->k; + gf *p; + + if (GF_BITS > 8) sz /= 2; // NOSONAR + + if (index < k) + memcpy(fec, src[index], sz * sizeof(gf)); + else if (index < code->n) { + p = &(code->enc_matrix[index * k]); + 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); +} + +/* + * shuffle move src packets in their position + */ +static int shuffle(gf *pkt[], int index[], int k) { + int i; + + for (i = 0; i < k;) { + if (index[i] >= k || index[i] == i) + i++; + else { + /* + * put pkt in the right position (first check for conflicts). + */ + int c = index[i]; + + if (index[c] == c) { + fprintf(stderr, "\nshuffle, error at %d\n", i); + return 1; + } + SWAP(index[i], index[c], int); + SWAP(pkt[i], pkt[c], gf *); + } + } + return 0; +} + +/* + * build_decode_matrix constructs the encoding matrix given the + * 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, int index[]) { + int i, k = code->k; + gf *p, *matrix = NEW_GF_MATRIX(k, k); + + for (i = 0, p = matrix; i < k; i++, p += k) { + if (index[i] < k) { + memset(p, '\0', k * sizeof(gf)); + p[i] = 1; + } 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); + free(matrix); + return NULL; + } + } + if (invert_mat(matrix, k)) { + free(matrix); + matrix = NULL; + } + return matrix; +} + +/* + * fec_decode receives as input a vector of packets, the indexes of + * packets, and produces the correct vector as output. + * + * Input: + * code: pointer to code descriptor + * pkt: pointers to received packets. They are modified + * to store the output packets (in place) + * index: pointer to packet indexes (modified) + * sz: size of each packet + */ +int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) { + gf *m_dec; + gf **new_pkt = nullptr; + int row, col, k = code->k; + int i = 0; + + if (GF_BITS > 8) sz /= 2; // NOSONAR + + if (shuffle(pkt, index, k)) /* error if true */ + return 1; + m_dec = build_decode_matrix(code, index); + + if (m_dec == NULL) return 1; /* error */ + /* + * do the actual decoding + */ + new_pkt = pkt + k; + for (row = 0; row < k; row++) { + if (index[row] >= k) { + memset(new_pkt[i], '\0', sz * sizeof(gf)); + for (col = 0; col < k; col++) + addmul(new_pkt[i], pkt[col], m_dec[row * k + col], sz); + i++; + } + } + free(m_dec); + return 0; +}
\ No newline at end of file diff --git a/libtransport/src/protocols/fec/fec.h b/libtransport/src/protocols/fec/fec.h new file mode 100644 index 000000000..7710bb7af --- /dev/null +++ b/libtransport/src/protocols/fec/fec.h @@ -0,0 +1,65 @@ +/* + * fec.c -- forward error correction based on Vandermonde matrices + * 980614 + * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) + * + * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), + * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari + * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * The following parameter defines how many bits are used for + * field elements. The code supports any value from 2 to 16 + * but fastest operation is achieved with 8 bit elements + * This is the only parameter you may want to change. + */ +#ifndef GF_BITS +#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ +#endif + +#if (GF_BITS <= 8) +typedef unsigned char gf; +#else +typedef unsigned short gf; +#endif + +#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ + +struct fec_parms { + unsigned long magic; + int k, n; /* parameters of the code */ + gf *enc_matrix; +}; + +void fec_free(struct fec_parms *p); +struct fec_parms *fec_new(int k, int n); + +void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz); +int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz); + +/* end of file */ diff --git a/libtransport/src/protocols/fec/fec_info.h b/libtransport/src/protocols/fec/fec_info.h new file mode 100644 index 000000000..bdfc4d3af --- /dev/null +++ b/libtransport/src/protocols/fec/fec_info.h @@ -0,0 +1,62 @@ +/* + * 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/errors/not_implemented_exception.h> + +namespace transport { +namespace protocol { + +namespace fec { + +template <typename T> +struct FecInfo { + static bool isFec() { throw errors::NotImplementedException(); } + static uint32_t nextSymbol(uint32_t index) { + throw errors::NotImplementedException(); + } + static uint32_t nextSource(uint32_t index) { + throw errors::NotImplementedException(); + } +}; + +template <uint32_t K, uint32_t N> +struct Code {}; + +template <uint32_t K, uint32_t N> +struct FecInfo<Code<K, N>> { + static bool isFec(uint32_t index) { return (index % N) >= K; } + + static uint32_t nextSymbol(uint32_t index) { + if (isFec(index)) { + return index; + } + + return index + (K - (index % N)); + } + + static uint32_t nextSource(uint32_t index) { + if (!isFec(index)) { + return index; + } + + return index + (N - (index % N)); + } +}; + +} // namespace fec +} // namespace protocol +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/protocols/fec/rely.cc b/libtransport/src/protocols/fec/rely.cc new file mode 100644 index 000000000..9e0a06dd8 --- /dev/null +++ b/libtransport/src/protocols/fec/rely.cc @@ -0,0 +1,234 @@ +/* + * 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/core/global_object_pool.h> +#include <protocols/fec/rely.h> + +#include <rely/packet.hpp> + +namespace transport { +namespace protocol { +namespace fec { + +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 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_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). + DCHECK(length < max_packet_bytes()); + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Encoding packet of length " << length - sizeof(fec_metadata); + + // 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_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()); + DCHECK(packets == 1); + + // Update packet counter + current_index_ += packets; + + // Restore original packet content and increment data pointer to the correct + // position + *h = copy; + data += sizeof(fec_metadata); + + // Check position of this packet inside N size block + auto i = current_index_ % n_; + + // encoder will produce a source packet + if (i <= k_) { + // Rely modifies the payload of the packet. We replace the packet with the + // one returned by rely. + // TODO Optimize it by copying only the RELY header + + // Be sure encoder 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 = (int)(new_payload_size - length); + + 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_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 + DCHECK(rely::packet_is_systematic(produce_data())); + + // Copy rely packet replacing old source packet. + std::memcpy(data, produce_data(), new_payload_size); + + // Advance the encoder to next symbol. + produce_next(); + } + +#if 0 + if (i == k_) { + // Ensure repair are generated after k source packets + flush_repair(); + } +#endif + + // Here we should produce all the repair packets + while (can_produce()) { + // The current index MUST be k_, because we enforce n - k repair to be + // produced after k sources + DCHECK(current_index_ == k_); + + buffer packet; + if (!buffer_callback_) { + // If no callback is installed, let's allocate a buffer from global pool + packet = core::PacketManager<>::getInstance().getMemBuf(); + packet->append(produce_bytes()); + } else { + // Otherwise let's ask a buffer to the caller. + packet = buffer_callback_(produce_bytes()); + } + + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Producing symbol of size " << produce_bytes(); + + // Copy symbol to packet buffer + std::memcpy(packet->writableData(), produce_data(), produce_bytes()); + + // Push symbol in repair_packets + packets_.emplace_back(0, metadata, std::move(packet)); + + // Advance the encoder + produce_next(); + } + + // Print number of unprotected symbols + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Number of unprotected symbols: " << unprotected_symbols(); + + // If we have generated repair symbols, let's notify caller via the installed + // callback + if (packets_.size()) { + DCHECK(packets_.size() == n_ - k_); + fec_callback_(packets_); + packets_.clear(); + current_index_ = 0; + } +} + +RelyDecoder::RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) + : RelyBase(k, n, seq_offset) { + configure(kmtu, ktimeout, kmax_stream_size); +} + +void RelyDecoder::onDataPacket(core::ContentObject &content_object, + 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; + + // Pass payload to decoder + consume(data, size, getCurrentTime()); + + producePackets(); +} + +void RelyDecoder::producePackets() { + // Drain decoder if possible + while (can_produce()) { + auto fec_header_size = sizeof(fec_metadata); + auto payload_size = produce_bytes() - sizeof(fec_metadata); + + buffer packet; + if (!buffer_callback_) { + packet = core::PacketManager<>::getInstance().getMemBuf(); + packet->append(payload_size); + } else { + packet = buffer_callback_(payload_size); + } + + // Read seq number + 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; + + // Copy payload + std::memcpy(packet->writableData(), produce_data() + fec_header_size, + payload_size); + + // Save packet in buffer + packets_.emplace_back(index, metadata, std::move(packet)); + + // Advance to next packet + produce_next(); + } + + // If we produced packets, lets notify the caller via the callback + if (packets_.size() > 0) { + 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 diff --git a/libtransport/src/protocols/fec/rely.h b/libtransport/src/protocols/fec/rely.h new file mode 100644 index 000000000..cc81222b2 --- /dev/null +++ b/libtransport/src/protocols/fec/rely.h @@ -0,0 +1,215 @@ +/* + * 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/portability/endianess.h> +#include <hicn/transport/utils/chrono_typedefs.h> +#include <hicn/transport/utils/membuf.h> +#include <protocols/fec/fec_info.h> +#include <protocols/fec_base.h> + +#include <rely/decoder.hpp> +#include <rely/encoder.hpp> + +#define RELY_DEBUG 0 + +namespace transport { +namespace protocol { +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) \ + _(Rely, 6, 10) \ + _(Rely, 8, 10) \ + _(Rely, 8, 11) \ + _(Rely, 8, 12) \ + _(Rely, 8, 14) \ + _(Rely, 8, 16) \ + _(Rely, 8, 32) \ + _(Rely, 10, 30) \ + _(Rely, 10, 40) \ + _(Rely, 10, 90) \ + _(Rely, 16, 21) \ + _(Rely, 16, 23) \ + _(Rely, 16, 24) \ + _(Rely, 16, 27) \ + _(Rely, 17, 21) \ + _(Rely, 17, 34) \ + _(Rely, 32, 41) \ + _(Rely, 32, 46) \ + _(Rely, 32, 54) \ + _(Rely, 34, 42) \ + _(Rely, 35, 70) \ + _(Rely, 52, 62) + +/** + * @brief Base class to store common fields. + */ +class RelyBase : public virtual FECBase { + protected: + static const constexpr size_t kmax_stream_size = 125U; + static const constexpr size_t kmtu = 1500U; + static const constexpr size_t ktimeout = 100U; + /** + * @brief FEC Header, added to each packet to get sequence number upon + * decoding operations. It may be removed once we know the meaning of the + * fields in the rely header. + */ + class fec_metadata { + public: + void setSeqNumberBase(uint32_t suffix) { + seq_number = portability::host_to_net(suffix); + } + uint32_t getSeqNumberBase() const { + return portability::net_to_host(seq_number); + } + + void setMetadataBase(uint32_t value) { + metadata = portability::host_to_net(value); + } + uint32_t getMetadataBase() const { + return portability::net_to_host(metadata); + } + + private: + uint32_t seq_number; + uint32_t metadata; + }; + + /** + * @brief Construct a new Rely Base object. + * + * @param k The number of source symbol needed to generate n - k repair + * symbols + * @param n The sum of source packets and repair packets in a `block` + * @param seq_offset offset to use if production suffixes starts from an index + * != 0 + */ + RelyBase(uint32_t k, uint32_t n, uint32_t seq_offset = 0) + : k_(k), + n_(n), + seq_offset_(seq_offset % n_), + current_index_(seq_offset) +#if RELY_DEBUG + , + time_(0) +#endif + { + } + + /** + * @brief Get the current time in milliseconds + * + * @return int64_t Current time in milliseconds + */ + int64_t getCurrentTime() { + // Get the current time +#if RELY_DEBUG + return time_++; +#else + return utils::SteadyTime::nowMs().count(); +#endif + } + + 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. + */ + BufferArray packets_; + + /** + * @brief Current index to be used for local packet count. + * + */ + uint32_t current_index_; +#if RELY_DEBUG + uint32_t time_; +#endif +}; + +/** + * @brief The Rely Encoder implementation. + * + */ +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, + uint32_t metadata = FECBase::INVALID_METADATA) override; + + /** + * @brief Get the fec header size, if added to source packets + * there is not need to distinguish between source and FEC packets here + */ + std::size_t getFecHeaderSize(bool isFEC) override { + return header_bytes() + sizeof(fec_metadata) + 4; + } + + void reset() override { + // Nothing to do here + } +}; + +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, + uint32_t metadata = FECBase::INVALID_METADATA) override; + + /** + * @brief Get the fec header size, if added to source packets + * there is not need to distinguish between source and FEC packets here + */ + std::size_t getFecHeaderSize(bool isFEC) override { + return header_bytes() + sizeof(fec_metadata); + } + + void reset() override { + // Nothing to do here + } + + private: + void producePackets(); + void flushOutOfOrder(); +}; + +} // namespace fec + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/fec/rs.cc b/libtransport/src/protocols/fec/rs.cc new file mode 100644 index 000000000..d42740c32 --- /dev/null +++ b/libtransport/src/protocols/fec/rs.cc @@ -0,0 +1,452 @@ + +/* + * 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/core/global_object_pool.h> +#include <protocols/fec/fec.h> +#include <protocols/fec/rs.h> + +#include <cassert> + +namespace transport { +namespace protocol { +namespace fec { + +BlockCode::BlockCode(uint32_t k, uint32_t n, uint32_t seq_offset, + struct fec_parms *code, rs ¶ms) + : Packets(), + k_(k), + n_(n), + seq_offset_(seq_offset), + code_(code), + max_buffer_size_(0), + current_block_size_(0), + to_decode_(false), + params_(params) { + sorted_index_.reserve(n); + UNUSED(seq_offset_); +} + +bool BlockCode::addRepairSymbol(const fec::buffer &packet, uint32_t i, + uint32_t offset) { + // Get index + 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, + FECBase::INVALID_METADATA); +} + +bool BlockCode::addSourceSymbol(const fec::buffer &packet, uint32_t i, + 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, metadata); +} + +bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i, + uint32_t offset, std::size_t size, + uint32_t metadata) { + if (size > max_buffer_size_) { + max_buffer_size_ = size; + } + + operator[](current_block_size_) = RSBufferInfo(offset, i, metadata, packet); + current_block_size_++; + + if (current_block_size_ >= k_) { + if (to_decode_) { + decode(); + } else { + encode(); + } + + clear(); + return false; + } + + return true; +} + +void BlockCode::encode() { + gf *data[n_]; + uint32_t base = operator[](0).getIndex(); + + // Set packet length in first 2 bytes + for (uint32_t i = 0; i < k_; 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); + if (TRANSPORT_EXPECT_FALSE(ret == false)) { + throw errors::RuntimeException( + "Provided packet is not suitable to be used as FEC source packet. " + "Aborting."); + } + + // Buffers should hold 2 *after* the padding, in order to be + // able to set the length for the encoding operation. + // packet->trimStart(offset); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ + offset); + auto buffer_length = packet->length() - offset; + metadata->setPacketLength(buffer_length); + metadata->setMetadataBase(metadata_base); + + DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current buffer size: " << packet->length(); + + data[i] = packet->writableData() + offset; + } + + // Finish to fill source block with the buffers to hold the repair symbols + auto length = max_buffer_size_ + sizeof(fec_header) + METADATA_BYTES; + for (uint32_t i = k_; i < n_; i++) { + buffer packet; + if (!params_.buffer_callback_) { + // If no callback is installed, let's allocate a buffer from global pool + packet = core::PacketManager<>::getInstance().getMemBuf(); + packet->append(length); + } else { + // Otherwise let's ask a buffer to the caller. + packet = params_.buffer_callback_(length); + } + + fec_header *fh = reinterpret_cast<fec_header *>(packet->writableData()); + + fh->setSeqNumberBase(base); + fh->setNFecSymbols(n_ - k_); + fh->setEncodedSymbolId(i); + fh->setSourceBlockLen(n_); + + packet->trimStart(sizeof(fec_header)); + + DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current symbol size: " << packet->length(); + + data[i] = packet->writableData(); + 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, + (int)(max_buffer_size_ + METADATA_BYTES)); + } + + // Re-include header in repair packets + for (uint32_t i = k_; i < n_; 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(); + } +} + +void BlockCode::decode() { + 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 = 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(); + // This is a source packet. We need to fill + // additional space to 0 and append the length + + // Buffers should hold 2 bytes at the end, in order to be + // able to set the length for the encoding operation + packet->trimStart(offset); + packet->ensureCapacityAndFillUnused(max_buffer_size_, 0); + 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), + (int)max_buffer_size_); + + // Find the index in the block for recovered packets + 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 = operator[](i).getBuffer(); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ - METADATA_BYTES); + DCHECK(metadata->getPacketLength() <= packet->capacity()); + // 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); + } + } +} + +void BlockCode::clear() { + current_block_size_ = 0; + max_buffer_size_ = 0; + sorted_index_.clear(); + to_decode_ = false; +} + +void rs::MatrixDeleter::operator()(struct fec_parms *params) { + fec_free(params); +} + +rs::Codes rs::createCodes() { + Codes ret; + +#define _(name, k, n) \ + ret.emplace(std::make_pair(k, n), Matrix(fec_new(k, n), MatrixDeleter())); + foreach_rs_fec_type +#undef _ + + return ret; +} + +rs::Codes rs::codes_ = createCodes(); + +rs::rs(uint32_t k, uint32_t n, uint32_t seq_offset) + : k_(k), n_(n), seq_offset_(seq_offset % n) {} + +RSEncoder::RSEncoder(uint32_t k, uint32_t n, uint32_t seq_offset) + : rs(k, n, seq_offset), + current_code_(codes_[std::make_pair(k, n)].get()), + source_block_(k_, n_, seq_offset_, current_code_, *this) {} + +void RSEncoder::consume(const fec::buffer &packet, uint32_t index, + 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(source_block_[i])); + } + + fec_callback_(repair_packets); + } +} + +void RSEncoder::onPacketProduced(core::ContentObject &content_object, + uint32_t offset, uint32_t metadata) { + consume(content_object.shared_from_this(), + content_object.getName().getSuffix(), offset, metadata); +} + +RSDecoder::RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) + : rs(k, n, 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; + 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++) { + src_block[i].setIndex(base_index + src_block[i].getIndex()); + source_packets[i] = FECBufferInfo(std::move(src_block[i])); + } + + setProcessed(src_block_it->first); + + fec_callback_(source_packets); + processed_source_blocks_.emplace(src_block_it->first); + + auto it = parked_packets_.find(src_block_it->first); + if (it != parked_packets_.end()) { + parked_packets_.erase(it); + } + + src_blocks_.erase(src_block_it); +} + +void RSDecoder::consumeSource(const fec::buffer &packet, uint32_t index, + uint32_t offset, uint32_t metadata) { + // Normalize index + DCHECK(index >= seq_offset_); + auto i = (index - seq_offset_) % n_; + + // Get base + uint32_t base = index - i; + + if (processed(base)) { + return; + } + + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Decoder consume called for source symbol. BASE = " << base + << ", index = " << index << " and i = " << i; + + // check if a source block already exist for this symbol. If it does not + // exist, we lazily park this packet until we receive a repair symbol for the + // same block. This is done for 2 reason: + // 1) If we receive all the source packets of a block, we do not need to + // recover anything. + // 2) Sender may change n and k at any moment, so we construct the source + // block based on the (n, k) values written in the fec header. This is + // actually not used right now, since we use fixed value of n and k passed + // at construction time, but it paves the ground for a more dynamic + // 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, metadata); + if (!ret) { + recoverPackets(it); + } + } else { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding to parked source packets"; + 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); + } + } +} + +void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { + // Repair symbol! Get index and base source block. + fec_header *h = + reinterpret_cast<fec_header *>(packet->writableData() + offset); + auto i = h->getEncodedSymbolId(); + auto base = h->getSeqNumberBase(); + auto n = h->getSourceBlockLen(); + auto k = n - h->getNFecSymbols(); + + if (processed(base)) { + return; + } + + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Decoder consume called for repair symbol. BASE = " << base + << ", 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); + if (it == src_blocks_.end()) { + // Create new source block + auto code_it = codes_.find(std::make_pair(k, n)); + if (code_it == codes_.end()) { + LOG(ERROR) << "Code for k = " << k << " and n = " << n + << " does not exist."; + return; + } + + auto emplace_result = src_blocks_.emplace( + base, BlockCode(k, n, seq_offset_, code_it->second.get(), *this)); + it = emplace_result.first; + + // Check in the parked packets and insert any packet that is part of this + // source block + + auto it2 = parked_packets_.find(base); + if (it2 != parked_packets_.end()) { + for (auto &packet_index : it2->second) { + 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 + // eventually not used + return; + } + } + } + } + + auto ret = it->second.addRepairSymbol(packet, i, offset); + if (!ret) { + recoverPackets(it); + } +} + +void RSDecoder::onDataPacket(core::ContentObject &content_object, + uint32_t offset, uint32_t metadata) { + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Calling fec for data packet " << content_object.getName() + << ". Offset: " << offset; + + auto suffix = content_object.getName().getSuffix(); + + if (isSymbol(suffix)) { + consumeRepair(content_object.shared_from_this(), offset); + } else { + consumeSource(content_object.shared_from_this(), suffix, offset, metadata); + } +} + +} // namespace fec +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/fec/rs.h b/libtransport/src/protocols/fec/rs.h new file mode 100644 index 000000000..6672eaa6b --- /dev/null +++ b/libtransport/src/protocols/fec/rs.h @@ -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. + */ + +#pragma once + +#include <arpa/inet.h> +#include <hicn/transport/portability/c_portability.h> +#include <hicn/transport/portability/endianess.h> +#include <hicn/transport/utils/membuf.h> +#include <protocols/fec/fec_info.h> +#include <protocols/fec_base.h> + +#include <array> +#include <cstdint> +#include <map> +#include <unordered_set> +#include <vector> + +namespace transport { +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) \ + _(RS, 8, 12) \ + _(RS, 8, 14) \ + _(RS, 8, 16) \ + _(RS, 8, 32) \ + _(RS, 10, 20) \ + _(RS, 10, 25) \ + _(RS, 10, 30) \ + _(RS, 10, 40) \ + _(RS, 10, 60) \ + _(RS, 10, 90) \ + _(RS, 16, 18) \ + _(RS, 16, 21) \ + _(RS, 16, 23) \ + _(RS, 16, 24) \ + _(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; + +/** + * We use a std::array in place of std::vector to avoid to allocate a new vector + * in the heap every time we build a new source block, which would be bad if + * the decoder has to allocate several source blocks for many concurrent bases. + * std::array allows to be constructed in place, saving the allocation at the + * price os knowing in advance its 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. + */ +struct fec_header { + /** + * The base source packet seq_number this FES symbol refers to + */ + uint32_t seq_number; + + /** + * The index of the symbol inside the source block, between k and n - 1 + */ + uint8_t encoded_symbol_id; + + /** + * Total length of source block (n) + */ + uint8_t source_block_len; + + /** + * Total number of symbols (n - k) + */ + uint8_t n_fec_symbols; + + /** + * Align header to 64 bits + */ + uint8_t padding; + + void setSeqNumberBase(uint32_t suffix) { + seq_number = portability::host_to_net(suffix); + } + uint32_t getSeqNumberBase() { return portability::net_to_host(seq_number); } + void setEncodedSymbolId(uint8_t esi) { encoded_symbol_id = esi; } + uint8_t getEncodedSymbolId() { return encoded_symbol_id; } + void setSourceBlockLen(uint8_t k) { source_block_len = k; } + uint8_t getSourceBlockLen() { return source_block_len; } + void setNFecSymbols(uint8_t n_r) { n_fec_symbols = n_r; } + uint8_t getNFecSymbols() { return n_fec_symbols; } +}; + +static_assert(sizeof(fec_header) <= 8, "fec_header is too large"); + +class rs; + +/** + * This class models the source block itself. + */ +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 = portability::host_to_net(length); + } + uint32_t getPacketLength() { + return portability::net_to_host(packet_length); + } + + void setMetadataBase(uint32_t value) { + metadata = portability::host_to_net(value); + } + uint32_t getMetadataBase() { return portability::net_to_host(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 METADATA_BYTES = sizeof(fec_metadata); + + public: + BlockCode(uint32_t k, uint32_t n, uint32_t seq_offset, struct fec_parms *code, + rs ¶ms); + + /** + * Add a repair symbol to the dource block. + */ + bool addRepairSymbol(const fec::buffer &packet, uint32_t i, + uint32_t offset = 0); + + /** + * Add a source symbol to the source block. + */ + bool addSourceSymbol(const fec::buffer &packet, uint32_t i, + uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); + + /** + * Get current length of source block. + */ + std::size_t length() { return current_block_size_; } + + /** + * Get N + */ + uint32_t getN() { return n_; } + + /** + * Get K + */ + uint32_t getK() { return k_; } + + /** + * Clear source block + */ + void clear(); + + private: + /** + * Add symbol to source block + **/ + bool addSymbol(const fec::buffer &packet, uint32_t i, uint32_t offset, + std::size_t size, uint32_t metadata); + + /** + * Starting from k source symbols, get the n - k repair symbols + */ + void encode(); + + /** + * Starting from k symbols (mixed repair and source), get k source symbols. + * NOTE: It does not make sense to retrieve the k source symbols using the + * very same k source symbols. With the current implementation that case can + * never happen. + */ + void decode(); + + private: + uint32_t k_; + uint32_t n_; + uint32_t seq_offset_; + struct fec_parms *code_; + std::size_t max_buffer_size_; + std::size_t current_block_size_; + std::vector<uint32_t> sorted_index_; + bool to_decode_; + rs ¶ms_; +}; + +/** + * This class contains common parameters between the fec encoder and decoder. + * In particular it contains: + * - The callback to be called when symbols are encoded / decoded + * - The reference to the static reed-solomon parameters, allocated at program + * startup + * - N and K. Ideally they are useful only for the encoder (the decoder can + * retrieve them from the FEC header). However right now we assume sender and + * receiver agreed on the parameters k and n to use. We will introduce a control + * message later to negotiate them, so that decoder cah dynamically change them + * during the download. + */ +class rs : public virtual FECBase { + friend class BlockCode; + + /** + * Deleter for static preallocated reed-solomon parameters. + */ + struct MatrixDeleter { + void operator()(struct fec_parms *params); + }; + + /** + * unique_ptr to reed-solomon parameters, with custom deleter to call fec_free + * at the end of the program + */ + using Matrix = std::unique_ptr<struct fec_parms, MatrixDeleter>; + + /** + * Key to retrieve static preallocated reed-solomon parameters. It is pair of + * k and n + */ + using Code = std::pair<std::uint32_t /* k */, std::uint32_t /* n */>; + + /** + * Custom hash function for (k, n) pair. + */ + struct CodeHasher { + std::size_t operator()(const Code &code) const { + uint64_t ret = uint64_t(code.first) << 32 | uint64_t(code.second); + return std::hash<uint64_t>{}(ret); + } + }; + + protected: + /** + * 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. + */ + using PacketsReady = std::function<void(std::vector<buffer> &)>; + + /** + * The sequence number base. + */ + using SNBase = std::uint32_t; + + /** + * The map of source blocks, used at the decoder side. For the encoding + * operation we can use one source block only, since packet are produced in + * order. + */ + using SourceBlocks = std::unordered_map<SNBase, BlockCode>; + + /** + * Map (k, n) -> reed-solomon parameter + */ + using Codes = std::unordered_map<Code, Matrix, CodeHasher>; + + public: + rs(uint32_t k, uint32_t n, uint32_t seq_offset = 0); + ~rs() = default; + + virtual void clear() { processed_source_blocks_.clear(); } + + bool isSymbol(uint32_t index) { return ((index - seq_offset_) % n_) >= k_; } + + private: + /** + * Create reed-solomon codes at program startup. + */ + static Codes createCodes(); + + protected: + bool processed(SNBase seq_base) { + return processed_source_blocks_.find(seq_base) != + processed_source_blocks_.end(); + } + + void setProcessed(SNBase seq_base) { + processed_source_blocks_.emplace(seq_base); + } + + std::uint32_t k_; + std::uint32_t n_; + std::uint32_t seq_offset_; + + /** + * Keep track of processed source blocks + */ + std::unordered_set<SNBase> processed_source_blocks_; + + static Codes codes_; +}; + +/** + * The reed-solomon encoder. It is feeded with source symbols and it provide + * repair-symbols through the fec_callback_ + */ +class RSEncoder : public rs, public ProducerFEC { + public: + RSEncoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0); + /** + * Always consume source symbols. + */ + 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, + uint32_t metadata = FECBase::INVALID_METADATA) override; + + /** + * @brief Get the fec header size, if added to source packets + * in RS the source packets do not transport any FEC header + */ + std::size_t getFecHeaderSize(bool isFEC) override { + return isFEC ? sizeof(fec_header) : 0; + } + + void clear() override { + rs::clear(); + source_block_.clear(); + } + + void reset() override { clear(); } + + private: + struct fec_parms *current_code_; + /** + * The source block. As soon as it is filled with k source symbols, the + * encoder calls the callback fec_callback_ and the resets the block 0, ready + * to accept another batch of k source symbols. + */ + BlockCode source_block_; +}; + +/** + * The reed-solomon encoder. It is feeded with source/repair symbols and it + * provides the original source symbols through the fec_callback_ + */ +class RSDecoder : public rs, public ConsumerFEC { + public: + RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0); + + /** + * Consume source symbol + */ + void consumeSource(const fec::buffer &packet, uint32_t i, uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); + + /** + * Consume repair symbol + */ + void consumeRepair(const fec::buffer &packet, uint32_t offset = 0); + + /** + * Consumers will call this function when they receive a data packet + */ + 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 + * in RS the source packets do not transport any FEC header + */ + std::size_t getFecHeaderSize(bool isFEC) override { + return isFEC ? sizeof(fec_header) : 0; + } + + /** + * Clear decoder to reuse + */ + void clear() override { + rs::clear(); + src_blocks_.clear(); + parked_packets_.clear(); + } + + void reset() override { clear(); } + + private: + void recoverPackets(SourceBlocks::iterator &src_block_it); + + private: + /** + * Map of source blocks. We use a map because we may receive symbols belonging + * to diffreent source blocks at the same time, so we need to be able to + * decode many source symbols at the same time. + */ + SourceBlocks src_blocks_; + + /** + * Unordered Map of source symbols for which we did not receive any repair + * symbol in the same source block. Notably this happens when: + * + * - We receive the source symbols first and the repair symbols after + * - We received only source symbols for a given block. In that case it does + * not make any sense to build the source block, since we received all the + * source packet of the block. + */ + using BufferInfoArray = std::vector<RSBufferInfo>; + std::unordered_map<uint32_t, BufferInfoArray> parked_packets_; +}; + +} // namespace fec + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/fec_base.h b/libtransport/src/protocols/fec_base.h new file mode 100644 index 000000000..28f6a820a --- /dev/null +++ b/libtransport/src/protocols/fec_base.h @@ -0,0 +1,173 @@ +/* + * 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/core/asio_wrapper.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/errors/not_implemented_exception.h> + +#include <functional> + +namespace transport { +namespace protocol { + +namespace fec { + +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: + 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. + */ + using PacketsReady = std::function<void(BufferArray &)>; + + /** + * Callback to be called when a new buffer (for encoding / decoding) needs to + * be allocated. + */ + using BufferRequested = std::function<buffer(std::size_t size)>; + + /** + * @brief Get size of FEC header. + * the fec header size may be different if a packet is a data packet or a FEC + * packet + */ + virtual std::size_t getFecHeaderSize(bool isFEC) = 0; + + /** + * Set callback to call after packet encoding / decoding + */ + template <typename Handler> + void setFECCallback(Handler &&callback) { + fec_callback_ = std::forward<Handler>(callback); + } + + /** + * Set a callback to request a buffer. + */ + template <typename Handler> + void setBufferCallback(Handler &&buffer_callback) { + 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_; +}; + +/** + * Interface classes to integrate FEC inside any producer transport protocol + */ +class ProducerFEC : public virtual FECBase { + public: + virtual ~ProducerFEC() = default; + /** + * Producers will call this function upon production of a new packet. + */ + virtual void onPacketProduced( + core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) = 0; +}; + +/** + * Interface classes to integrate FEC inside any consumer transport protocol + */ +class ConsumerFEC : public virtual FECBase { + public: + virtual ~ConsumerFEC() = default; + + /** + * Consumers will call this function when they receive a data packet + */ + virtual void onDataPacket(core::ContentObject &content_object, + uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) = 0; +}; + +} // namespace fec +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/fec_utils.h b/libtransport/src/protocols/fec_utils.h new file mode 100644 index 000000000..d70ff1c09 --- /dev/null +++ b/libtransport/src/protocols/fec_utils.h @@ -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. + */ + +#pragma once + +#include <hicn/transport/config.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <protocols/fec/rs.h> + +#if ENABLE_RELY +#include <protocols/fec/rely.h> +#endif + +#include <functional> + +namespace transport { +namespace protocol { + +namespace fec { + +#if ENABLE_RELY +#define foreach_fec_type foreach_rs_fec_type foreach_rely_fec_type +#else +#define foreach_fec_type foreach_rs_fec_type +#endif + +#define ENUM_FROM_MACRO(name, k, n) name##_K##k##_N##n +#define ENUM_FROM_MACRO_STR(name, k, n) #name "_K" #k "_N" #n + +enum class FECType : uint8_t { +#define _(name, k, n) ENUM_FROM_MACRO(name, k, n), + foreach_fec_type +#undef _ + UNKNOWN +}; + +#define ENUM_FROM_MACRO2(name, k, n) FECType::ENUM_FROM_MACRO(name, k, n) + +class FECUtils { + public: + static FECType fecTypeFromString(const char *fec_type) { +#define _(name, k, n) \ + do { \ + if (strncmp(fec_type, ENUM_FROM_MACRO_STR(name, k, n), \ + strlen(ENUM_FROM_MACRO_STR(name, k, n))) == 0) { \ + return ENUM_FROM_MACRO2(name, k, n); \ + } \ + } while (0); + foreach_fec_type +#undef _ + + return FECType::UNKNOWN; + } + + static bool isFec(FECType fec_type, uint32_t index, uint32_t seq_offset = 0) { + switch (fec_type) { +#define _(name, k, n) \ + case ENUM_FROM_MACRO2(name, k, n): \ + return FecInfo<Code<k, n>>::isFec(index - (seq_offset % n)); + + foreach_fec_type +#undef _ + default : return false; + } + } + + static uint32_t nextSource(FECType fec_type, uint32_t index, + uint32_t seq_offset = 0) { + switch (fec_type) { +#define _(name, k, n) \ + case ENUM_FROM_MACRO2(name, k, n): \ + return FecInfo<Code<k, n>>::nextSource(index) + (seq_offset % n); + + foreach_fec_type +#undef _ + default : throw std::runtime_error("Unknown fec type"); + } + } + + static uint32_t getSourceSymbols(FECType fec_type) { + switch (fec_type) { +#define _(name, k, n) \ + case ENUM_FROM_MACRO2(name, k, n): \ + return k; + foreach_fec_type +#undef _ + default : throw std::runtime_error("Unknown fec type"); + } + } + + static uint32_t getBlockSymbols(FECType fec_type) { + switch (fec_type) { +#define _(name, k, n) \ + case ENUM_FROM_MACRO2(name, k, n): \ + return n; + foreach_fec_type +#undef _ + default : throw std::runtime_error("Unknown fec type"); + } + } + + static std::unique_ptr<ProducerFEC> getEncoder(FECType fec_type, + uint32_t seq_offset = 0) { + return factoryEncoder(fec_type, seq_offset); + } + + static std::unique_ptr<ConsumerFEC> getDecoder(FECType fec_type, + uint32_t seq_offset = 0) { + return factoryDencoder(fec_type, seq_offset); + } + + private: + static std::unique_ptr<ProducerFEC> factoryEncoder(FECType fec_type, + uint32_t seq_offset) { + switch (fec_type) { +#define _(name, k, n) \ + case ENUM_FROM_MACRO2(name, k, n): \ + return std::make_unique<name##Encoder>(k, n, seq_offset); + + foreach_fec_type +#undef _ + default : throw std::runtime_error("Unknown fec type"); + } + } + + static std::unique_ptr<ConsumerFEC> factoryDencoder(FECType fec_type, + uint32_t seq_offset) { + switch (fec_type) { +#define _(name, k, n) \ + case ENUM_FROM_MACRO2(name, k, n): \ + return std::make_unique<name##Decoder>(k, n, seq_offset); + + foreach_fec_type +#undef _ + default : throw std::runtime_error("Unknown fec type"); + } + } +}; // namespace fec + +} // namespace fec +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/incremental_indexer.cc b/libtransport/src/protocols/incremental_indexer.cc deleted file mode 100644 index 0872c4554..000000000 --- a/libtransport/src/protocols/incremental_indexer.cc +++ /dev/null @@ -1,55 +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 <protocols/incremental_indexer.h> - -#include <hicn/transport/interfaces/socket_consumer.h> -#include <protocols/protocol.h> - -namespace transport { -namespace protocol { - -void IncrementalIndexer::onContentObject( - core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) { - using namespace interface; - - TRANSPORT_LOGD("Receive content %s", content_object->getName().toString().c_str()); - - if (TRANSPORT_EXPECT_FALSE(content_object->testRst())) { - final_suffix_ = content_object->getName().getSuffix(); - } - - auto ret = verification_manager_->onPacketToVerify(*content_object); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - reassembly_->reassemble(std::move(content_object)); - break; - } - case VerificationPolicy::DROP_PACKET: { - transport_protocol_->onPacketDropped(std::move(interest), - std::move(content_object)); - break; - } - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } - } -} - -} // namespace protocol -} // namespace transport diff --git a/libtransport/src/protocols/incremental_indexer.h b/libtransport/src/protocols/incremental_indexer.h deleted file mode 100644 index 20c5e4759..000000000 --- a/libtransport/src/protocols/incremental_indexer.h +++ /dev/null @@ -1,143 +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/errors/runtime_exception.h> -#include <hicn/transport/errors/unexpected_manifest_exception.h> -#include <hicn/transport/utils/literals.h> - -#include <protocols/indexer.h> -#include <protocols/reassembly.h> -#include <protocols/verification_manager.h> - -#include <deque> - -namespace transport { - -namespace interface { -class ConsumerSocket; -} - -namespace protocol { - -class Reassembly; -class TransportProtocol; - -class IncrementalIndexer : public Indexer { - public: - IncrementalIndexer(implementation::ConsumerSocket *icn_socket, - TransportProtocol *transport, Reassembly *reassembly) - : socket_(icn_socket), - reassembly_(reassembly), - transport_protocol_(transport), - final_suffix_(std::numeric_limits<uint32_t>::max()), - first_suffix_(0), - next_download_suffix_(0), - next_reassembly_suffix_(0), - verification_manager_( - std::make_unique<SignatureVerificationManager>(icn_socket)) { - if (reassembly_) { - reassembly_->setIndexer(this); - } - } - - IncrementalIndexer(const IncrementalIndexer &) = delete; - - IncrementalIndexer(IncrementalIndexer &&other) - : socket_(other.socket_), - reassembly_(other.reassembly_), - transport_protocol_(other.transport_protocol_), - final_suffix_(other.final_suffix_), - first_suffix_(other.first_suffix_), - next_download_suffix_(other.next_download_suffix_), - next_reassembly_suffix_(other.next_reassembly_suffix_), - verification_manager_(std::move(other.verification_manager_)) { - if (reassembly_) { - reassembly_->setIndexer(this); - } - } - - /** - * - */ - virtual ~IncrementalIndexer() {} - - TRANSPORT_ALWAYS_INLINE virtual void reset( - std::uint32_t offset = 0) override { - final_suffix_ = std::numeric_limits<uint32_t>::max(); - next_download_suffix_ = offset; - next_reassembly_suffix_ = offset; - } - - /** - * Retrieve from the manifest the next suffix to retrieve. - */ - TRANSPORT_ALWAYS_INLINE virtual uint32_t getNextSuffix() override { - return next_download_suffix_ <= final_suffix_ ? next_download_suffix_++ - : IndexManager::invalid_index; - } - - TRANSPORT_ALWAYS_INLINE virtual void setFirstSuffix( - uint32_t suffix) override { - first_suffix_ = suffix; - } - - /** - * Retrive the next segment to be reassembled. - */ - TRANSPORT_ALWAYS_INLINE virtual uint32_t getNextReassemblySegment() override { - return next_reassembly_suffix_ <= final_suffix_ - ? next_reassembly_suffix_++ - : IndexManager::invalid_index; - } - - TRANSPORT_ALWAYS_INLINE virtual bool isFinalSuffixDiscovered() override { - return final_suffix_ != std::numeric_limits<uint32_t>::max(); - } - - TRANSPORT_ALWAYS_INLINE virtual uint32_t getFinalSuffix() override { - return final_suffix_; - } - - void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) override; - - TRANSPORT_ALWAYS_INLINE void setReassembly(Reassembly *reassembly) { - reassembly_ = reassembly; - - if (reassembly_) { - reassembly_->setIndexer(this); - } - } - - TRANSPORT_ALWAYS_INLINE bool onKeyToVerify() override { - return verification_manager_->onKeyToVerify(); - } - - protected: - implementation::ConsumerSocket *socket_; - Reassembly *reassembly_; - TransportProtocol *transport_protocol_; - uint32_t final_suffix_; - uint32_t first_suffix_; - uint32_t next_download_suffix_; - uint32_t next_reassembly_suffix_; - std::unique_ptr<VerificationManager> verification_manager_; -}; - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.cc b/libtransport/src/protocols/incremental_indexer_bytestream.cc new file mode 100644 index 000000000..b94f229e5 --- /dev/null +++ b/libtransport/src/protocols/incremental_indexer_bytestream.cc @@ -0,0 +1,65 @@ +/* + * 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 <protocols/errors.h> +#include <protocols/incremental_indexer_bytestream.h> +#include <protocols/transport_protocol.h> + +namespace transport { +namespace protocol { + +void IncrementalIndexer::onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly) { + using namespace interface; + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Received content " << content_object.getName(); + + DCHECK(reassembly_); + + if (TRANSPORT_EXPECT_FALSE(content_object.isLast())) { + final_suffix_ = content_object.getName().getSuffix(); + } + + 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; + } + } +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.h b/libtransport/src/protocols/incremental_indexer_bytestream.h new file mode 100644 index 000000000..4f9b6126f --- /dev/null +++ b/libtransport/src/protocols/incremental_indexer_bytestream.h @@ -0,0 +1,119 @@ +/* + * 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/errors/errors.h> +#include <hicn/transport/interfaces/callbacks.h> +#include <hicn/transport/utils/literals.h> +#include <implementation/socket_consumer.h> +#include <protocols/indexer.h> +#include <protocols/reassembly.h> + +#include <deque> + +namespace transport { + +namespace interface { +class ConsumerSocket; +} + +namespace protocol { + +class Reassembly; +class TransportProtocol; + +class IncrementalIndexer : public Indexer { + public: + IncrementalIndexer(implementation::ConsumerSocket *icn_socket, + TransportProtocol *transport) + : Indexer(icn_socket, transport), + final_suffix_(Indexer::invalid_index), + first_suffix_(0), + next_download_suffix_(0), + next_reassembly_suffix_(0) {} + + IncrementalIndexer(const IncrementalIndexer &other) = delete; + + IncrementalIndexer(IncrementalIndexer &&other) + : Indexer(other), + final_suffix_(other.final_suffix_), + first_suffix_(other.first_suffix_), + next_download_suffix_(other.next_download_suffix_), + next_reassembly_suffix_(other.next_reassembly_suffix_) {} + + virtual ~IncrementalIndexer() {} + + virtual void reset() override { + final_suffix_ = Indexer::invalid_index; + next_download_suffix_ = first_suffix_; + next_reassembly_suffix_ = first_suffix_; + } + + virtual uint32_t checkNextSuffix() const override { + return next_download_suffix_ <= final_suffix_ ? next_download_suffix_ + : Indexer::invalid_index; + } + + virtual uint32_t getNextSuffix() override { + return next_download_suffix_ <= final_suffix_ ? next_download_suffix_++ + : Indexer::invalid_index; + } + + virtual void setFirstSuffix(uint32_t suffix) override { + first_suffix_ = suffix; + } + + uint32_t getFirstSuffix() const override { return first_suffix_; } + + virtual uint32_t jumpToIndex(uint32_t index) override { + next_download_suffix_ = index; + return next_download_suffix_; + } + + /** + * Retrive the next segment to be reassembled. + */ + virtual uint32_t getNextReassemblySegment() override { + return next_reassembly_suffix_ <= final_suffix_ ? next_reassembly_suffix_++ + : Indexer::invalid_index; + } + + virtual bool isFinalSuffixDiscovered() override { + return final_suffix_ != Indexer::invalid_index; + } + + 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() const override { return 0; } + + virtual void onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly) override; + + protected: + uint32_t final_suffix_; + uint32_t first_suffix_; + uint32_t next_download_suffix_; + uint32_t next_reassembly_suffix_; +}; + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/index_manager_bytestream.cc b/libtransport/src/protocols/index_manager_bytestream.cc new file mode 100644 index 000000000..952f36e0e --- /dev/null +++ b/libtransport/src/protocols/index_manager_bytestream.cc @@ -0,0 +1,72 @@ +/* + * 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 <protocols/index_manager_bytestream.h> +#include <protocols/manifest_incremental_indexer_bytestream.h> +#include <protocols/transport_protocol.h> + +namespace transport { +namespace protocol { + +IndexManager::IndexManager(implementation::ConsumerSocket *icn_socket, + TransportProtocol *transport) + : IncrementalIndexer(icn_socket, transport), + indexer_(std::make_unique<IncrementalIndexer>(icn_socket, transport)), + first_segment_received_(false) {} + +void IndexManager::onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly) { + if (first_segment_received_) { + return indexer_->onContentObject(interest, content_object, reassembly); + } else { + std::uint32_t segment_number = interest.getName().getSuffix(); + + if (segment_number == 0) { + // Check if manifest + if (content_object.getPayloadType() == core::PayloadType::MANIFEST) { + IncrementalIndexer *indexer = + static_cast<IncrementalIndexer *>(indexer_.release()); + indexer_ = + std::make_unique<ManifestIncrementalIndexer>(std::move(*indexer)); + delete indexer; + } + + indexer_->onContentObject(interest, content_object); + auto it = interest_data_set_.begin(); + while (it != interest_data_set_.end()) { + indexer_->onContentObject(*it->first, *it->second); + it = interest_data_set_.erase(it); + } + + first_segment_received_ = true; + } else { + interest_data_set_.emplace(interest.shared_from_this(), + content_object.shared_from_this()); + } + } +} + +void IndexManager::reset() { + indexer_ = std::make_unique<IncrementalIndexer>(socket_, transport_); + indexer_->setReassembly(this->reassembly_); + indexer_->reset(); + first_segment_received_ = false; + interest_data_set_.clear(); +} + +} // namespace protocol +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/protocols/index_manager_bytestream.h b/libtransport/src/protocols/index_manager_bytestream.h new file mode 100644 index 000000000..7ea31dfa5 --- /dev/null +++ b/libtransport/src/protocols/index_manager_bytestream.h @@ -0,0 +1,97 @@ +/* + * 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/incremental_indexer_bytestream.h> + +#include <list> + +namespace transport { + +namespace implementation { +class ConsumerSocket; +} + +namespace protocol { + +class TransportProtocol; + +class IndexManager : public IncrementalIndexer { + public: + IndexManager(implementation::ConsumerSocket *icn_socket, + TransportProtocol *transport); + + uint32_t getNextSuffix() override { return indexer_->getNextSuffix(); } + + void setFirstSuffix(uint32_t suffix) override { + indexer_->setFirstSuffix(suffix); + } + + uint32_t getFirstSuffix() const override { + return indexer_->getFirstSuffix(); + } + + uint32_t getNextReassemblySegment() override { + return indexer_->getNextReassemblySegment(); + } + + bool isFinalSuffixDiscovered() override { + return indexer_->isFinalSuffixDiscovered(); + } + + 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() const override { return indexer_->getNFec(); } + + void enableFec(fec::FECType fec_type) override { + return indexer_->enableFec(fec_type); + } + + double getFecOverhead() const override { return indexer_->getFecOverhead(); } + + double getMaxFecOverhead() const override { + return indexer_->getMaxFecOverhead(); + } + + void disableFec() override { return indexer_->disableFec(); } + + void reset() override; + + void setReassembly(Reassembly *reassembly) override { + Indexer::setReassembly(reassembly); + indexer_->setReassembly(reassembly); + } + + void onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly) override; + + private: + std::unique_ptr<Indexer> indexer_; + bool first_segment_received_; + std::set<std::pair<core::Interest::Ptr, core::ContentObject::Ptr>> + interest_data_set_; +}; + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/indexer.cc b/libtransport/src/protocols/indexer.cc index ca12330a6..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: @@ -13,67 +13,58 @@ * limitations under the License. */ -#include <hicn/transport/utils/branch_prediction.h> - -#include <protocols/incremental_indexer.h> +#include <implementation/socket_consumer.h> +#include <protocols/errors.h> #include <protocols/indexer.h> -#include <protocols/manifest_incremental_indexer.h> -#include <protocols/protocol.h> namespace transport { namespace protocol { -IndexManager::IndexManager(implementation::ConsumerSocket *icn_socket, - TransportProtocol *transport, Reassembly *reassembly) - : indexer_(std::make_unique<IncrementalIndexer>(icn_socket, transport, - reassembly)), - first_segment_received_(false), - icn_socket_(icn_socket), - transport_(transport), - reassembly_(reassembly) {} - -void IndexManager::onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) { - if (first_segment_received_) { - indexer_->onContentObject(std::move(interest), std::move(content_object)); - } else { - std::uint32_t segment_number = interest->getName().getSuffix(); +using namespace interface; - if (segment_number == 0) { - // Check if manifest - if (content_object->getPayloadType() == PayloadType::MANIFEST) { - IncrementalIndexer *indexer = - static_cast<IncrementalIndexer *>(indexer_.release()); - indexer_ = - std::make_unique<ManifestIncrementalIndexer>(std::move(*indexer)); - delete indexer; - } +const constexpr uint32_t Indexer::invalid_index; - indexer_->onContentObject(std::move(interest), std::move(content_object)); - auto it = interest_data_set_.begin(); - while (it != interest_data_set_.end()) { - indexer_->onContentObject( - std::move(const_cast<core::Interest::Ptr &&>(it->first)), - std::move(const_cast<core::ContentObject::Ptr &&>(it->second))); - it = interest_data_set_.erase(it); - } +Indexer::Indexer(implementation::ConsumerSocket *socket, + TransportProtocol *transport) + : socket_(socket), transport_(transport) { + setVerifier(); +} - first_segment_received_ = true; - } else { - interest_data_set_.emplace(std::move(interest), - std::move(content_object)); - } +void Indexer::setVerifier() { + if (socket_) { + socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier_); } } -bool IndexManager::onKeyToVerify() { return indexer_->onKeyToVerify(); } +void Indexer::applyPolicy(core::Interest &interest, + core::ContentObject &content_object, bool reassembly, + auth::VerificationPolicy policy) const { + DCHECK(reassembly_ != nullptr); -void IndexManager::reset(std::uint32_t offset) { - indexer_ = std::make_unique<IncrementalIndexer>(icn_socket_, transport_, - reassembly_); - first_segment_received_ = false; - interest_data_set_.clear(); + 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; + } + } } -} // namespace protocol -} // namespace transport +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/indexer.h b/libtransport/src/protocols/indexer.h index 8213a1503..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,8 +15,10 @@ #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> #include <set> @@ -33,72 +35,87 @@ class TransportProtocol; class Indexer { public: - /** - * - */ + static const constexpr uint32_t invalid_index = + (std::numeric_limits<uint32_t>::max() - 1); + + Indexer(implementation::ConsumerSocket *socket, TransportProtocol *transport); + virtual ~Indexer() = default; + /** - * Retrieve from the manifest the next suffix to retrieve. + * Suffix getters */ + virtual uint32_t checkNextSuffix() const = 0; virtual uint32_t getNextSuffix() = 0; + virtual uint32_t getNextReassemblySegment() = 0; + /** + * Set first suffix from where to start. + */ virtual void setFirstSuffix(uint32_t suffix) = 0; + virtual uint32_t getFirstSuffix() const = 0; /** - * Retrive the next segment to be reassembled. + * Functions to set/enable/disable fec */ - virtual uint32_t getNextReassemblySegment() = 0; + virtual void setNFec(uint32_t n_fec) = 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() const { return 0.0; } + virtual double getMaxFecOverhead() const { return 0.0; } + /** + * Final suffix helpers. + */ virtual bool isFinalSuffixDiscovered() = 0; + virtual uint32_t getFinalSuffix() const = 0; - virtual uint32_t getFinalSuffix() = 0; - - virtual void reset(std::uint32_t offset = 0) = 0; - - virtual void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) = 0; - - virtual bool onKeyToVerify() = 0; -}; - -class IndexManager : Indexer { - public: - static constexpr uint32_t invalid_index = ~0; - - IndexManager(implementation::ConsumerSocket *icn_socket, - TransportProtocol *transport, Reassembly *reassembly); - - uint32_t getNextSuffix() override { return indexer_->getNextSuffix(); } - - void setFirstSuffix(uint32_t suffix) override { - indexer_->setFirstSuffix(suffix); - } - - uint32_t getNextReassemblySegment() override { - return indexer_->getNextReassemblySegment(); - } - - bool isFinalSuffixDiscovered() override { - return indexer_->isFinalSuffixDiscovered(); + /** + * Set reassembly protocol + */ + virtual void setReassembly(Reassembly *reassembly) { + reassembly_ = reassembly; } - uint32_t getFinalSuffix() override { return indexer_->getFinalSuffix(); } + /** + * Set verifier using socket + */ + virtual void setVerifier(); - void reset(std::uint32_t offset = 0) override; + /** + * 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 + * implementation). + */ + virtual uint32_t jumpToIndex(uint32_t index) = 0; - void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) override; + /** + * Reset the indexer. + */ + virtual void reset() = 0; - bool onKeyToVerify() override; + /** + * Process incoming content objects. + */ + virtual void onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly = true) = 0; - private: - std::unique_ptr<Indexer> indexer_; - bool first_segment_received_; - std::set<std::pair<core::Interest::Ptr, core::ContentObject::Ptr>> - interest_data_set_; - implementation::ConsumerSocket *icn_socket_; + protected: + implementation::ConsumerSocket *socket_; 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.cc b/libtransport/src/protocols/manifest_incremental_indexer.cc deleted file mode 100644 index da835b577..000000000 --- a/libtransport/src/protocols/manifest_incremental_indexer.cc +++ /dev/null @@ -1,236 +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 <implementation/socket_consumer.h> - -#include <protocols/manifest_incremental_indexer.h> -#include <protocols/protocol.h> - -#include <cmath> -#include <deque> - -namespace transport { - -namespace protocol { - -using namespace interface; - -ManifestIncrementalIndexer::ManifestIncrementalIndexer( - implementation::ConsumerSocket *icn_socket, TransportProtocol *transport, - Reassembly *reassembly) - : IncrementalIndexer(icn_socket, transport, reassembly), - suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy( - NextSegmentCalculationStrategy::INCREMENTAL, next_download_suffix_, - 0)) {} - -void ManifestIncrementalIndexer::onContentObject( - core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) { - // Check if manifest or not - if (content_object->getPayloadType() == PayloadType::MANIFEST) { - TRANSPORT_LOGD("Receive content %s", content_object->getName().toString().c_str()); - onUntrustedManifest(std::move(interest), std::move(content_object)); - } else if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) { - TRANSPORT_LOGD("Receive manifest %s", content_object->getName().toString().c_str()); - onUntrustedContentObject(std::move(interest), std::move(content_object)); - } -} - -void ManifestIncrementalIndexer::onUntrustedManifest( - core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) { - auto ret = verification_manager_->onPacketToVerify(*content_object); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - processTrustedManifest(std::move(content_object)); - break; - } - case VerificationPolicy::DROP_PACKET: - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } - } -} - -void ManifestIncrementalIndexer::processTrustedManifest( - ContentObject::Ptr &&content_object) { - auto manifest = - std::make_unique<ContentObjectManifest>(std::move(*content_object)); - manifest->decode(); - - if (TRANSPORT_EXPECT_FALSE(manifest->getVersion() != - core::ManifestVersion::VERSION_1)) { - throw errors::RuntimeException("Received manifest with unknown version."); - } - - switch (manifest->getManifestType()) { - case core::ManifestType::INLINE_MANIFEST: { - auto _it = manifest->getSuffixList().begin(); - auto _end = manifest->getSuffixList().end(); - - suffix_strategy_->setFinalSuffix(manifest->getFinalBlockNumber()); - - for (; _it != _end; _it++) { - auto hash = - std::make_pair(std::vector<uint8_t>(_it->second, _it->second + 32), - manifest->getHashAlgorithm()); - - if (!checkUnverifiedSegments(_it->first, hash)) { - suffix_hash_map_[_it->first] = std::move(hash); - } - } - - reassembly_->reassemble(std::move(manifest)); - - break; - } - case core::ManifestType::FLIC_MANIFEST: { - throw errors::NotImplementedException(); - } - case core::ManifestType::FINAL_CHUNK_NUMBER: { - throw errors::NotImplementedException(); - } - } -} - -bool ManifestIncrementalIndexer::checkUnverifiedSegments( - std::uint32_t suffix, const HashEntry &hash) { - auto it = unverified_segments_.find(suffix); - - if (it != unverified_segments_.end()) { - auto ret = verifyContentObject(hash, *it->second.second); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - reassembly_->reassemble(std::move(it->second.second)); - break; - } - case VerificationPolicy::DROP_PACKET: { - transport_protocol_->onPacketDropped(std::move(it->second.first), - std::move(it->second.second)); - break; - } - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } - } - - unverified_segments_.erase(it); - return true; - } - - return false; -} - -VerificationPolicy ManifestIncrementalIndexer::verifyContentObject( - const HashEntry &manifest_hash, const ContentObject &content_object) { - VerificationPolicy ret; - - auto hash_type = static_cast<utils::CryptoHashType>(manifest_hash.second); - auto data_packet_digest = content_object.computeDigest(manifest_hash.second); - auto data_packet_digest_bytes = - data_packet_digest.getDigest<uint8_t>().data(); - const std::vector<uint8_t> &manifest_digest_bytes = manifest_hash.first; - - if (utils::CryptoHash::compareBinaryDigest( - data_packet_digest_bytes, manifest_digest_bytes.data(), hash_type)) { - ret = VerificationPolicy::ACCEPT_PACKET; - } else { - ConsumerContentObjectVerificationFailedCallback - *verification_failed_callback = VOID_HANDLER; - socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED, - &verification_failed_callback); - ret = (*verification_failed_callback)( - *socket_->getInterface(), content_object, - make_error_code(protocol_error::integrity_verification_failed)); - } - - return ret; -} - -void ManifestIncrementalIndexer::onUntrustedContentObject( - Interest::Ptr &&i, ContentObject::Ptr &&c) { - auto suffix = c->getName().getSuffix(); - auto it = suffix_hash_map_.find(suffix); - - if (it != suffix_hash_map_.end()) { - auto ret = verifyContentObject(it->second, *c); - - switch (ret) { - case VerificationPolicy::ACCEPT_PACKET: { - suffix_hash_map_.erase(it); - reassembly_->reassemble(std::move(c)); - break; - } - case VerificationPolicy::DROP_PACKET: { - transport_protocol_->onPacketDropped(std::move(i), std::move(c)); - break; - } - case VerificationPolicy::ABORT_SESSION: { - transport_protocol_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } - } - } else { - unverified_segments_[suffix] = std::make_pair(std::move(i), std::move(c)); - } -} - -uint32_t ManifestIncrementalIndexer::getNextSuffix() { - auto ret = suffix_strategy_->getNextSuffix(); - - if (ret <= suffix_strategy_->getFinalSuffix() && - ret != utils::SuffixStrategy::INVALID_SUFFIX) { - suffix_queue_.push(ret); - return ret; - } - - return IndexManager::invalid_index; -} - -uint32_t ManifestIncrementalIndexer::getFinalSuffix() { - return suffix_strategy_->getFinalSuffix(); -} - -bool ManifestIncrementalIndexer::isFinalSuffixDiscovered() { - return IncrementalIndexer::isFinalSuffixDiscovered(); -} - -uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() { - if (suffix_queue_.empty()) { - return IndexManager::invalid_index; - } - - auto ret = suffix_queue_.front(); - suffix_queue_.pop(); - return ret; -} - -void ManifestIncrementalIndexer::reset(std::uint32_t offset) { - IncrementalIndexer::reset(offset); - suffix_hash_map_.clear(); - unverified_segments_.clear(); - SuffixQueue empty; - std::swap(suffix_queue_, empty); - suffix_strategy_->reset(offset); -} - -} // namespace protocol - -} // namespace transport diff --git a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc new file mode 100644 index 000000000..0b15559a4 --- /dev/null +++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc @@ -0,0 +1,204 @@ +/* + * 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 <implementation/socket_consumer.h> +#include <protocols/errors.h> +#include <protocols/manifest_incremental_indexer_bytestream.h> +#include <protocols/transport_protocol.h> + +#include <cmath> +#include <deque> + +namespace transport { + +namespace protocol { + +using namespace interface; + +ManifestIncrementalIndexer::ManifestIncrementalIndexer( + implementation::ConsumerSocket *icn_socket, TransportProtocol *transport) + : IncrementalIndexer(icn_socket, transport), + suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy( + utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) {} + +void ManifestIncrementalIndexer::onContentObject( + core::Interest &interest, core::ContentObject &content_object, + bool reassembly) { + switch (content_object.getPayloadType()) { + case PayloadType::DATA: { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Received content " << content_object.getName(); + onUntrustedContentObject(interest, content_object, reassembly); + break; + } + case PayloadType::MANIFEST: { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Received manifest " << content_object.getName(); + onUntrustedManifest(interest, content_object, reassembly); + break; + } + default: { + return; + } + } +} + +void ManifestIncrementalIndexer::onUntrustedManifest( + core::Interest &interest, core::ContentObject &content_object, + bool reassembly) { + auth::VerificationPolicy policy = verifier_->verifyPackets(&content_object); + + if (policy != auth::VerificationPolicy::ACCEPT) { + transport_->onContentReassembled( + make_error_code(protocol_error::session_aborted)); + return; + } + + core::ContentObjectManifest manifest(content_object.shared_from_this()); + manifest.decode(); + + processTrustedManifest(interest, manifest, reassembly); +} + +void ManifestIncrementalIndexer::processTrustedManifest( + core::Interest &interest, core::ContentObjectManifest &manifest, + bool reassembly) { + switch (manifest.getType()) { + case core::ManifestType::INLINE_MANIFEST: { + suffix_strategy_->setFinalSuffix( + manifest.getParamsBytestream().final_segment); + + // The packets to verify with the received manifest + std::vector<auth::PacketPtr> packets; + + // Convert the received manifest to a map of packet suffixes to hashes + auth::Verifier::SuffixMap suffix_map = manifest.getSuffixMap(); + + // Update 'suffix_map_' with new hashes from the received manifest and + // build 'packets' + for (auto it = suffix_map.begin(); it != suffix_map.end();) { + if (unverified_segments_.find(it->first) == + unverified_segments_.end()) { + suffix_map_[it->first] = std::move(it->second); + suffix_map.erase(it++); + continue; + } + + packets.push_back(std::get<1>(unverified_segments_[it->first]).get()); + it++; + } + + // Verify unverified segments using the received manifest + auth::Verifier::PolicyMap policies = + verifier_->verifyPackets(packets, suffix_map); + + for (unsigned int i = 0; i < packets.size(); ++i) { + auth::Suffix suffix = packets[i]->getName().getSuffix(); + + auto it = unverified_segments_.find(suffix); + + if (policies[suffix] != auth::VerificationPolicy::UNKNOWN) { + unverified_segments_.erase(it); + continue; + } + + applyPolicy(*std::get<0>(it->second), *std::get<1>(it->second), + std::get<2>(it->second), policies[suffix]); + } + + if (reassembly) { + auto manifest_co = + std::dynamic_pointer_cast<ContentObject>(manifest.getPacket()); + reassembly_->reassemble(*manifest_co); + } + break; + } + case core::ManifestType::FLIC_MANIFEST: { + throw errors::NotImplementedException(); + } + case core::ManifestType::FINAL_CHUNK_NUMBER: { + throw errors::NotImplementedException(); + } + } +} + +void ManifestIncrementalIndexer::onUntrustedContentObject( + Interest &interest, ContentObject &content_object, bool reassembly) { + auth::Suffix suffix = content_object.getName().getSuffix(); + auth::VerificationPolicy policy = + verifier_->verifyPackets(&content_object, suffix_map_); + + switch (policy) { + case auth::VerificationPolicy::UNKNOWN: { + unverified_segments_[suffix] = + std::make_tuple(interest.shared_from_this(), + content_object.shared_from_this(), reassembly); + break; + } + default: { + suffix_map_.erase(suffix); + break; + } + } + + applyPolicy(interest, content_object, reassembly, policy); +} + +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::MAX_SUFFIX) { + suffix_queue_.push(ret); + return ret; + } + + return Indexer::invalid_index; +} + +uint32_t ManifestIncrementalIndexer::getFinalSuffix() const { + return suffix_strategy_->getFinalSuffix(); +} + +bool ManifestIncrementalIndexer::isFinalSuffixDiscovered() { + return IncrementalIndexer::isFinalSuffixDiscovered(); +} + +uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() { + if (suffix_queue_.empty()) { + return Indexer::invalid_index; + } + + auto ret = suffix_queue_.front(); + suffix_queue_.pop(); + return ret; +} + +void ManifestIncrementalIndexer::reset() { + IncrementalIndexer::reset(); + suffix_map_.clear(); + unverified_segments_.clear(); + SuffixQueue empty; + std::swap(suffix_queue_, empty); + suffix_strategy_->reset(first_suffix_); +} + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/manifest_incremental_indexer.h b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h index 38b01533e..8527b55c1 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer.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: @@ -15,14 +15,14 @@ #pragma once +#include <hicn/transport/auth/common.h> #include <implementation/socket.h> -#include <protocols/incremental_indexer.h> +#include <protocols/incremental_indexer_bytestream.h> #include <utils/suffix_strategy.h> #include <list> namespace transport { - namespace protocol { class ManifestIncrementalIndexer : public IncrementalIndexer { @@ -30,17 +30,16 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { public: using SuffixQueue = std::queue<uint32_t>; - using HashEntry = std::pair<std::vector<uint8_t>, utils::CryptoHashType>; + using InterestContentPair = + std::tuple<core::Interest::Ptr, core::ContentObject::Ptr, bool>; ManifestIncrementalIndexer(implementation::ConsumerSocket *icn_socket, - TransportProtocol *transport, - Reassembly *reassembly); + TransportProtocol *transport); 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); } @@ -48,10 +47,13 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { virtual ~ManifestIncrementalIndexer() = default; - void reset(std::uint32_t offset = 0) override; + void reset() override; + + void onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly) override; - void onContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object) override; + uint32_t checkNextSuffix() const override; uint32_t getNextSuffix() override; @@ -59,32 +61,26 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { bool isFinalSuffixDiscovered() override; - uint32_t getFinalSuffix() override; - - private: - void onUntrustedManifest(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object); - void onUntrustedContentObject(core::Interest::Ptr &&interest, - core::ContentObject::Ptr &&content_object); - void processTrustedManifest(core::ContentObject::Ptr &&content_object); - void onManifestReceived(core::Interest::Ptr &&i, - core::ContentObject::Ptr &&c); - void onManifestTimeout(core::Interest::Ptr &&i); - VerificationPolicy verifyContentObject( - const HashEntry &manifest_hash, - const core::ContentObject &content_object); - bool checkUnverifiedSegments(std::uint32_t suffix, const HashEntry &hash); + uint32_t getFinalSuffix() const override; protected: std::unique_ptr<utils::SuffixStrategy> suffix_strategy_; SuffixQueue suffix_queue_; // Hash verification - std::unordered_map<uint32_t, HashEntry> suffix_hash_map_; + auth::Verifier::SuffixMap suffix_map_; + std::unordered_map<auth::Suffix, InterestContentPair> unverified_segments_; - std::unordered_map<uint32_t, - std::pair<core::Interest::Ptr, core::ContentObject::Ptr>> - unverified_segments_; + private: + void onUntrustedManifest(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly); + void processTrustedManifest(core::Interest &interest, + core::ContentObjectManifest &manifest, + bool reassembly); + void onUntrustedContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly); }; } // end namespace protocol diff --git a/libtransport/src/protocols/packet_manager.h b/libtransport/src/protocols/packet_manager.h deleted file mode 100644 index a552607ea..000000000 --- a/libtransport/src/protocols/packet_manager.h +++ /dev/null @@ -1,65 +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/utils/object_pool.h> - -namespace transport { - -namespace protocol { - -using namespace core; - -template <typename PacketType, std::size_t packet_pool_size = 4096> -class PacketManager { - static_assert(std::is_base_of<Packet, PacketType>::value, - "The packet manager support just Interest and Data."); - - public: - PacketManager(std::size_t size = packet_pool_size) : size_(0) { - // Create pool of interests - increasePoolSize(size); - } - - TRANSPORT_ALWAYS_INLINE void increasePoolSize(std::size_t size) { - for (std::size_t i = 0; i < size; i++) { - interest_pool_.add(new PacketType()); - } - - size_ += size; - } - - TRANSPORT_ALWAYS_INLINE typename PacketType::Ptr getPacket() { - auto result = interest_pool_.get(); - - while (TRANSPORT_EXPECT_FALSE(!result.first)) { - // Add packets to the pool - increasePoolSize(size_); - result = interest_pool_.get(); - } - - result.second->resetPayload(); - return std::move(result.second); - } - - private: - utils::ObjectPool<PacketType> interest_pool_; - std::size_t size_; -}; - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_bytestream.cc b/libtransport/src/protocols/prod_protocol_bytestream.cc new file mode 100644 index 000000000..7f103e12b --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_bytestream.cc @@ -0,0 +1,369 @@ +/* + * 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 <implementation/socket_producer.h> +#include <protocols/prod_protocol_bytestream.h> + +#include <atomic> + +namespace transport { + +namespace protocol { + +using namespace core; +using namespace implementation; + +ByteStreamProductionProtocol::ByteStreamProductionProtocol( + implementation::ProducerSocket *icn_socket) + : ProductionProtocol(icn_socket) {} + +ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { stop(); } + +uint32_t ByteStreamProductionProtocol::produceDatagram( + const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) { + throw errors::NotImplementedException(); +} + +uint32_t ByteStreamProductionProtocol::produceDatagram(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size) { + throw errors::NotImplementedException(); +} + +uint32_t ByteStreamProductionProtocol::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, + bool is_last, + uint32_t start_offset) { + if (!buffer_size) { + return 0; + } + + return produceStream(content_name, + utils::MemBuf::copyBuffer(buffer, buffer_size), is_last, + start_offset); +} + +uint32_t ByteStreamProductionProtocol::produceStream( + const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer, + bool is_last, uint32_t start_offset) { + if (TRANSPORT_EXPECT_FALSE(buffer->length() == 0)) { + return 0; + } + + // 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, + content_object_expiry_time); + + // Hash algorithm + auth::CryptoHashType hash_algo; + socket_->getSocketOption(GeneralTransportOptions::HASH_ALGORITHM, hash_algo); + + // Suffix calculation strategy + std::shared_ptr<utils::SuffixStrategy> suffix_strategy; + socket_->getSocketOption(GeneralTransportOptions::SUFFIX_STRATEGY, + suffix_strategy); + suffix_strategy->reset(start_offset); + + // 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; + + // 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 = manifest_max_capacity_; + bool is_last_manifest = false; + ParamsBytestream transport_params; + + manifest_format = Packet::toAHFormat(default_format); + content_format = !manifest_max_capacity_ ? Packet::toAHFormat(default_format) + : default_format; + + content_header_size = (uint32_t)core::Packet::getHeaderSizeFromFormat( + content_format, signature_length); + manifest_header_size = (uint32_t)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++; + } + + if (manifest_max_capacity_) { + 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 = ContentObjectManifest::createContentManifest( + manifest_format, + name.setSuffix(suffix_strategy->getNextManifestSuffix()), + signature_length); + manifest->setHeaders(core::ManifestType::INLINE_MANIFEST, + manifest_max_capacity_, hash_algo, is_last_manifest, + name); + manifest->setParamsBytestream(transport_params); + manifest->getPacket()->setLifetime(content_object_expiry_time); + } + + auto self = shared_from_this(); + for (unsigned int packaged_segments = 0; packaged_segments < nb_segments; + packaged_segments++) { + if (manifest_max_capacity_) { + if (manifest->Encoder::manifestSize(1) > manifest_free_space) { + manifest->encode(); + auto manifest_co = + std::dynamic_pointer_cast<ContentObject>(manifest->getPacket()); + + signer_->signPacket(manifest_co.get()); + + // Send the current manifest + passContentObjectToCallbacks(manifest_co, self); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Send manifest " << manifest_co->getName(); + + // Send content objects stored in the queue + while (!content_queue_.empty()) { + passContentObjectToCallbacks(content_queue_.front(), self); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Send content " << content_queue_.front()->getName(); + content_queue_.pop(); + } + + // Create new manifest. The reference to the last manifest has been + // acquired in the passContentObjectToCallbacks function, so we can + // safely release this reference. + manifest = ContentObjectManifest::createContentManifest( + manifest_format, + name.setSuffix(suffix_strategy->getNextManifestSuffix()), + signature_length); + manifest->setHeaders(core::ManifestType::INLINE_MANIFEST, + manifest_max_capacity_, hash_algo, + is_last_manifest, name); + manifest->setParamsBytestream(transport_params); + manifest->getPacket()->setLifetime(content_object_expiry_time); + } + } + + // Create content object + uint32_t content_suffix = suffix_strategy->getNextContentSuffix(); + auto content_object = std::make_shared<ContentObject>( + name.setSuffix(content_suffix), content_format, + !manifest_max_capacity_ ? signature_length : 0); + content_object->setLifetime(content_object_expiry_time); + + auto b = buffer->cloneOne(); + b->trimStart(content_free_space * packaged_segments); + b->trimEnd(b->length()); + + // 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 && manifest_max_capacity_) { + is_last_manifest = true; + } else if (is_last) { + content_object->setLast(); + } + + } else { + 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 (manifest_max_capacity_) { + auth::CryptoHash hash = content_object->computeDigest(hash_algo); + manifest->addEntry(content_suffix, hash); + content_queue_.push(content_object); + } else { + 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 (manifest_max_capacity_) { + if (is_last_manifest) { + manifest->setIsLast(is_last_manifest); + } + + manifest->encode(); + auto manifest_co = + std::dynamic_pointer_cast<ContentObject>(manifest->getPacket()); + + signer_->signPacket(manifest_co.get()); + + passContentObjectToCallbacks(manifest_co, self); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest_co->getName(); + + while (!content_queue_.empty()) { + passContentObjectToCallbacks(content_queue_.front(), self); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Send content " << content_queue_.front()->getName(); + content_queue_.pop(); + } + } + + portal_->getThread().add([this, self]() { + std::shared_ptr<ContentObject> co; + while (object_queue_for_callbacks_.pop(co)) { + if (*on_new_segment_) { + on_new_segment_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_to_sign_) { + on_content_object_to_sign_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()( + *socket_->getInterface(), *co); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *co); + } + } + }); + + portal_->getThread().add([this, buffer_size, self]() { + if (*on_content_produced_) { + on_content_produced_->operator()(*socket_->getInterface(), + std::make_error_code(std::errc(0)), + buffer_size); + } + }); + + return suffix_strategy->getTotalCount(); +} + +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)) { + if (*on_new_segment_) { + on_new_segment_->operator()(*socket_->getInterface(), *co); + } + + if (*on_content_object_to_sign_) { + 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); + } + } else { + break; + } + } + }); +} + +void ByteStreamProductionProtocol::passContentObjectToCallbacks( + 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(self); + } +} + +void ByteStreamProductionProtocol::onInterest(Interest &interest) { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Received interest for " << interest.getName(); + if (*on_interest_input_) { + on_interest_input_->operator()(*socket_->getInterface(), interest); + } + + const std::shared_ptr<ContentObject> content_object = + output_buffer_.find(interest.getName()); + + if (content_object) { + if (*on_interest_satisfied_output_buffer_) { + on_interest_satisfied_output_buffer_->operator()(*socket_->getInterface(), + interest); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *content_object); + } + + portal_->sendContentObject(*content_object); + } else { + if (*on_interest_process_) { + on_interest_process_->operator()(*socket_->getInterface(), interest); + } + } +} + +} // namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_bytestream.h b/libtransport/src/protocols/prod_protocol_bytestream.h new file mode 100644 index 000000000..809ad8d5c --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_bytestream.h @@ -0,0 +1,75 @@ +/* + * 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/ring_buffer.h> +#include <protocols/production_protocol.h> + +#include <atomic> +#include <queue> + +namespace transport { + +namespace protocol { + +using namespace core; + +class ByteStreamProductionProtocol : public ProductionProtocol { + static constexpr uint32_t burst_size = 256; + + public: + ByteStreamProductionProtocol(implementation::ProducerSocket *icn_socket); + + ~ByteStreamProductionProtocol() override; + + using ProductionProtocol::start; + using ProductionProtocol::stop; + + uint32_t produceStream(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer, + bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceStream(const Name &content_name, const uint8_t *buffer, + size_t buffer_size, bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer) override; + 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; + + private: + void passContentObjectToCallbacks( + 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 + std::queue<std::shared_ptr<ContentObject>> content_queue_; + utils::CircularFifo<std::shared_ptr<ContentObject>, 2048> + object_queue_for_callbacks_; +}; + +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_rtc.cc b/libtransport/src/protocols/prod_protocol_rtc.cc new file mode 100644 index 000000000..83cd23ac6 --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_rtc.cc @@ -0,0 +1,747 @@ +/* + * 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/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> + +#include <unordered_set> + +extern "C" { +#include <hicn/util/bitmap.h> +} + +namespace transport { +namespace protocol { + +using Format = core::Packet::Format; + +RTCProductionProtocol::RTCProductionProtocol( + implementation::ProducerSocket *icn_socket) + : ProductionProtocol(icn_socket), + current_seg_(1), + prev_produced_bytes_(0), + prev_produced_packets_(0), + produced_bytes_(0), + produced_packets_(0), + max_packet_production_(UINT32_MAX), + bytes_production_rate_(UINT32_MAX), + packets_production_rate_(0), + last_produced_data_ts_(0), + last_round_(utils::SteadyTime::nowMs().count()), + allow_delayed_nacks_(false), + 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; + 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); +} + +RTCProductionProtocol::~RTCProductionProtocol() {} + +void RTCProductionProtocol::setProducerParam() { + // Flow name: here we assume there is only one prefix registered in the portal + flow_name_ = portal_->getServedNamespaces().begin()->makeName(); + + // 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)); + + // Aggregated data + socket_->getSocketOption(interface::RtcTransportOptions::AGGREGATED_DATA, + data_aggregation_); + + size_t signature_size = signer_->getSignatureFieldSize(); + data_header_format_ = {!manifest_max_capacity_ + ? Packet::toAHFormat(default_format) + : default_format, + !manifest_max_capacity_ ? 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}; + + // Initialize verifier for aggregated interests + std::shared_ptr<auth::Verifier> verifier; + socket_->getSocketOption(implementation::GeneralTransportOptions::VERIFIER, + verifier); + verifier_ = std::make_shared<rtc::RTCVerifier>(verifier, 0, 0); + + // Schedule round timer + scheduleRoundTimer(); +} + +void RTCProductionProtocol::scheduleRoundTimer() { + round_timer_->expires_from_now( + std::chrono::milliseconds(rtc::PRODUCER_STATS_INTERVAL)); + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + round_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + sp->updateStats(true); + } + }); +} + +void RTCProductionProtocol::updateStats(bool new_round) { + uint64_t now = utils::SteadyTime::nowMs().count(); + uint64_t duration = now - last_round_; + if (!new_round) { + duration += rtc::PRODUCER_STATS_INTERVAL; + } else { + prev_produced_bytes_ = 0; + prev_produced_packets_ = 0; + } + + double per_second = rtc::MILLI_IN_A_SEC / duration; + + uint32_t prev_packets_production_rate = packets_production_rate_; + + // bytes_production_rate_ does not take into account FEC!!! this is because + // each client requests a differen amount of FEC packet so the client itself + // increase the production rate in the right way + bytes_production_rate_ = + ceil((double)(produced_bytes_ + prev_produced_bytes_) * per_second); + packets_production_rate_ = + ceil((double)(produced_packets_ + prev_produced_packets_) * per_second); + + if (fec_encoder_ && fec_type_ != fec::FECType::UNKNOWN) { + // add fec packets looking at the fec code. we don't use directly the number + // of fec packets produced in 1 round because it may happen that different + // numbers of blocks are generated during the rounds and this creates + // inconsistencies in the estimation of the production rate + uint32_t k = fec::FECUtils::getSourceSymbols(fec_type_); + uint32_t n = fec::FECUtils::getBlockSymbols(fec_type_); + + packets_production_rate_ += + ceil((double)packets_production_rate_ / (double)k) * (n - k); + } + + // update the production rate as soon as it increases by 10% with respect to + // the last round + max_packet_production_ = + produced_packets_ + ceil((double)produced_packets_ * 0.10); + if (max_packet_production_ < rtc::WIN_MIN) + max_packet_production_ = rtc::WIN_MIN; + + 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; + } + + if (new_round) { + prev_produced_bytes_ = produced_bytes_; + prev_produced_packets_ = produced_packets_; + produced_bytes_ = 0; + produced_packets_ = 0; + last_round_ = now; + scheduleRoundTimer(); + } +} + +uint32_t RTCProductionProtocol::produceStream( + const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer, + bool is_last, uint32_t start_offset) { + throw errors::NotImplementedException(); +} + +uint32_t RTCProductionProtocol::produceStream(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size, bool is_last, + uint32_t start_offset) { + throw errors::NotImplementedException(); +} + +void RTCProductionProtocol::produce(ContentObject &content_object) { + throw errors::NotImplementedException(); +} + +uint32_t RTCProductionProtocol::produceDatagram( + const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) { + std::size_t buffer_size = buffer->length(); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Maybe Sending content object: " << content_name; + + if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) return 0; + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending content object: " << content_name; + + uint32_t data_packet_size; + socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE, + data_packet_size); + // this is a source packet but we check the fec header size of FEC packet in + // order to leave room for the header when FEC packets will be generated + uint32_t fec_header = 0; + if (fec_encoder_) fec_encoder_->getFecHeaderSize(true); + uint32_t headers_size = + (uint32_t)Packet::getHeaderSizeFromFormat(data_header_format_.first, + data_header_format_.second) + + rtc::DATA_HEADER_SIZE + fec_header; + if (TRANSPORT_EXPECT_FALSE((headers_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>( + 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); + + // 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 (!manifest_max_capacity_) { + return; + } + + Name manifest_name = name; + + // If there is not enough hashes to fill a manifest, return early + if (manifest_entries_.size() < manifest_max_capacity_) { + return; + } + + // Create a new manifest + std::shared_ptr<core::ContentObjectManifest> manifest = + createManifest(manifest_name.setSuffix(current_seg_)); + auto manifest_co = + std::dynamic_pointer_cast<ContentObject>(manifest->getPacket()); + + // Fill the manifest with packet hashes that were previously saved + uint32_t nb_entries; + for (nb_entries = 0; nb_entries < manifest_max_capacity_; ++nb_entries) { + if (manifest_entries_.empty()) { + break; + } + std::pair<uint32_t, auth::CryptoHash> front = manifest_entries_.front(); + manifest->addEntry(front.first, front.second); + manifest_entries_.pop(); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending manifest " << manifest_co->getName().getSuffix() + << " of size " << nb_entries; + + // Encode and send the manifest + manifest->encode(); + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(manifest_co)}, 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::createContentManifest( + manifest_header_format_.first, name, manifest_header_format_.second); + manifest->setHeaders(core::ManifestType::INLINE_MANIFEST, + manifest_max_capacity_, hash_algo, false /* is_last */, + name); + + // Set connection parameters + manifest->setParamsRTC(ParamsRTC{ + .timestamp = now, + .prod_rate = bytes_production_rate_, + .prod_seg = current_seg_, + .fec_type = fec_type_, + }); + + 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 + 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); + content_object->setName(n.setSuffix(current_seg_)); + + uint32_t expiry_time = 0; + socket_->getSocketOption( + interface::GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + expiry_time); + if (expiry_time == interface::default_values::content_object_expiry_time) + expiry_time = 500; // the data expiration time should be set by the App. if + // the App does not specify it the default is 500ms + content_object->setLifetime(expiry_time); + content_object->setPathLabel(prod_label_); + + // update stats + if (!fec) { + produced_bytes_ += + content_object->headerSize() + content_object->payloadSize(); + produced_packets_++; + } + + 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 + updateStats(false); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending content object: " << n << ", is fec: " << fec; + + // pass packet to FEC encoder + if (fec_encoder_ && !fec) { + uint32_t offset = is_manifest ? (uint32_t)content_object->headerSize() + : (uint32_t)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); + + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), + *content_object); + } + + // TODO we may want to send FEC only if an interest is pending in the pit in + sendContentObject(content_object, false, fec); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *content_object); + } + + if (!fec) last_produced_data_ts_ = now; + + // Update current segment + current_seg_ = (current_seg_ + 1) % rtc::MIN_PROBE_SEQ; + + // Publish FEC packets if available + if (fec_encoder_ && !fec) { + while (!fec && pending_fec_packets_.size()) { + auto &co = pending_fec_packets_.front(); + produceInternal(std::move(co), flow_name_, true); + pending_fec_packets_.pop(); + } + } +} + +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); + } + + if (!interest.isValid()) throw std::runtime_error("Bad interest format"); + if (interest.hasManifest() && + verifier_->verify(interest) != auth::VerificationPolicy::ACCEPT) + throw std::runtime_error("Interset manifest verification failed"); + + uint32_t *suffix = interest.firstSuffix(); + uint32_t n_suffixes_in_manifest = interest.numberOfSuffixes(); + hicn_uword *request_bitmap = interest.getRequestBitmap(); + + Name name = interest.getName(); + uint32_t pos = 0; // Position of current suffix in manifest + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Received interest " << name << " (" << n_suffixes_in_manifest + << " suffixes in manifest)"; + + // Process the suffix in the interest header + // (first loop iteration), then suffixes in the manifest + do { + if (!interest.hasManifest() || + bitmap_is_set_no_check(request_bitmap, pos)) { + const std::shared_ptr<ContentObject> content_object = + output_buffer_.find(name); + + if (content_object) { + if (*on_interest_satisfied_output_buffer_) { + on_interest_satisfied_output_buffer_->operator()( + *socket_->getInterface(), interest); + } + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *content_object); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Send content %u (onInterest) " << content_object->getName(); + content_object->setPathLabel(cache_label_); + sendContentObject(content_object); + } else { + if (*on_interest_process_) { + on_interest_process_->operator()(*socket_->getInterface(), interest); + } + processInterest(name.getSuffix(), interest.getLifetime()); + } + } + + // Retrieve next suffix in the manifest + if (interest.hasManifest()) { + uint32_t seq = *suffix; + suffix++; + + name.setSuffix(seq); + interest.setName(name); + } + } while (pos++ < n_suffixes_in_manifest); +} + +void RTCProductionProtocol::processInterest(uint32_t interest_seg, + uint32_t lifetime) { + 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 (interest_seg < current_seg_) sendNack(interest_seg); +} + +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); + auto manifest_probe_co = + std::dynamic_pointer_cast<ContentObject>(manifest_probe->getPacket()); + + manifest_probe_co->setLifetime(0); + manifest_probe_co->setPathLabel(prod_label_); + manifest_probe->encode(); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *manifest_probe_co); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send init probe " << sequence; + sendContentObject(manifest_probe_co, true, false); +} + +void RTCProductionProtocol::sendNack(uint32_t sequence) { + auto nack = core::PacketManager<>::getInstance().getPacket<ContentObject>( + 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.setProductionSegment(next_packet); + nack->appendPayload((const uint8_t *)&header, rtc::NACK_HEADER_SIZE); + + Name n(flow_name_); + n.setSuffix(sequence); + nack->setName(n); + nack->setLifetime(0); + nack->setPathLabel(prod_label_); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *nack); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send nack " << sequence; + sendContentObject(nack, true, false); +} + +void RTCProductionProtocol::sendContentObject( + std::shared_ptr<ContentObject> content_object, bool nack, bool fec) { + bool is_ah = HICN_PACKET_FORMAT_IS_AH(content_object->getFormat()); + + // Compute signature + if (is_ah) { + signer_->signPacket(content_object.get()); + } + + // Compute and save data packet digest + if (manifest_max_capacity_ && !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)}); + } + + portal_->sendContentObject(*content_object); +} + +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.getBuffer()); + content_object->prepend(content_object->headerSize() + + rtc::DATA_HEADER_SIZE); + pending_fec_packets_.push(std::move(content_object)); + } +} + +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>( + 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(); + DCHECK(ret->length() >= size); + + return ret; +} + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/prod_protocol_rtc.h b/libtransport/src/protocols/prod_protocol_rtc.h new file mode 100644 index 000000000..285ccb646 --- /dev/null +++ b/libtransport/src/protocols/prod_protocol_rtc.h @@ -0,0 +1,150 @@ +/* + * 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/core/name.h> +#include <protocols/production_protocol.h> +#include <protocols/rtc/rtc_verifier.h> + +#include <atomic> +#include <map> +#include <mutex> + +namespace transport { +namespace protocol { + +class RTCProductionProtocol : public ProductionProtocol { + public: + RTCProductionProtocol(implementation::ProducerSocket *icn_socket); + ~RTCProductionProtocol() override; + + using ProductionProtocol::start; + using ProductionProtocol::stop; + void setProducerParam() override; + + void produce(ContentObject &content_object) override; + uint32_t produceStream(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer, + bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceStream(const Name &content_name, const uint8_t *buffer, + size_t buffer_size, bool is_last = true, + uint32_t start_offset = 0) override; + uint32_t produceDatagram(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer) override; + uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer, + size_t buffer_size) override { + return produceDatagram(content_name, utils::MemBuf::wrapBuffer( + buffer, buffer_size, buffer_size)); + } + + auto shared_from_this() { return utils::shared_from(this); } + + private: + // packet handlers + void onInterest(Interest &interest) 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(bool new_round); + void scheduleRoundTimer(); + + // FEC functions + 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_; + + 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 prev_produced_bytes_; // XXX clearly explain all these new vars + uint32_t prev_produced_packets_; + + uint32_t produced_bytes_; // bytes produced in the last round + uint32_t produced_packets_; // packet produed in the last round + + uint32_t max_packet_production_; // never exceed this number of packets + // without update stats + + uint32_t bytes_production_rate_; // bytes per sec + uint32_t 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 + // many nacks we the producer rate is 0. however, if the producer moves + // from a production rate higher than 0 to 0 the first round the dealyed + // should be avoided in order to notify the consumer as fast as possible + // of the new rate. + bool allow_delayed_nacks_; + + // 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 + + // Verifier for aggregated interests + std::shared_ptr<rtc::RTCVerifier> verifier_; +}; + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/production_protocol.cc b/libtransport/src/protocols/production_protocol.cc new file mode 100644 index 000000000..039a6a55a --- /dev/null +++ b/libtransport/src/protocols/production_protocol.cc @@ -0,0 +1,131 @@ +/* + * 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 <implementation/socket_producer.h> +#include <protocols/production_protocol.h> + +namespace transport { + +namespace protocol { + +using namespace interface; + +ProductionProtocol::ProductionProtocol( + implementation::ProducerSocket *icn_socket) + : Protocol(), + socket_(icn_socket), + fec_encoder_(nullptr), + on_interest_input_(VOID_HANDLER), + on_interest_dropped_input_buffer_(VOID_HANDLER), + on_interest_inserted_input_buffer_(VOID_HANDLER), + on_interest_satisfied_output_buffer_(VOID_HANDLER), + on_interest_process_(VOID_HANDLER), + on_new_segment_(VOID_HANDLER), + on_content_object_to_sign_(VOID_HANDLER), + 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), + 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() {} + +int ProductionProtocol::start() { + if (isRunning()) { + return -1; + } + + 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::MANIFEST_MAX_CAPACITY, + manifest_max_capacity_); + + 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()); + } + + portal_->registerTransportCallback(this); + setProducerParam(); + + setRunning(); + }); + + return 0; +} + +void ProductionProtocol::produce(ContentObject &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(co); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *co); + } + + portal_->sendContentObject(*co); + }); +} + +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 + +} // namespace transport diff --git a/libtransport/src/protocols/production_protocol.h b/libtransport/src/protocols/production_protocol.h new file mode 100644 index 000000000..09718631f --- /dev/null +++ b/libtransport/src/protocols/production_protocol.h @@ -0,0 +1,134 @@ +/* + * 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/interfaces/socket_producer.h> +#include <hicn/transport/interfaces/statistics.h> +#include <hicn/transport/utils/object_pool.h> +#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> +#include <thread> + +namespace transport { + +namespace protocol { + +using namespace core; + +class ProductionProtocol + : public Protocol, + public std::enable_shared_from_this<ProductionProtocol> { + public: + ProductionProtocol(implementation::ProducerSocket *icn_socket); + virtual ~ProductionProtocol(); + + virtual int start(); + 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, + uint32_t start_offset = 0) = 0; + virtual uint32_t produceStream(const Name &content_name, + const uint8_t *buffer, size_t buffer_size, + bool is_last = true, + uint32_t start_offset = 0) = 0; + virtual uint32_t produceDatagram(const Name &content_name, + std::unique_ptr<utils::MemBuf> &&buffer) = 0; + virtual uint32_t produceDatagram(const Name &content_name, + const uint8_t *buffer, + size_t buffer_size) = 0; + + void setOutputBufferSize(std::size_t size) { output_buffer_.setLimit(size); } + std::size_t getOutputBufferSize() { return output_buffer_.getLimit(); } + + protected: + // Producer callback + virtual void onInterest(core::Interest &i) override = 0; + 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 + 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); + CHECK(fec_type_ != fec::FECType::UNKNOWN); + } + + if (fec_type_ == fec::FECType::UNKNOWN) { + return; + } + + fec_encoder_ = fec::FECUtils::getEncoder(fec_type_, 1); + fec_encoder_->setFECCallback(std::forward<FECHandler>(fec_handler)); + fec_encoder_->setBufferCallback( + std::forward<AllocatorHandler>(allocator_handler)); + } + } + + protected: + implementation::ProducerSocket *socket_; + + // Thread pool responsible for IO operations (send data / receive interests) + std::vector<utils::EventThread> io_threads_; + interface::ProductionStatistics *stats_; + std::unique_ptr<fec::ProducerFEC> fec_encoder_; + + // Callbacks + interface::ProducerInterestCallback *on_interest_input_; + interface::ProducerInterestCallback *on_interest_dropped_input_buffer_; + interface::ProducerInterestCallback *on_interest_inserted_input_buffer_; + interface::ProducerInterestCallback *on_interest_satisfied_output_buffer_; + interface::ProducerInterestCallback *on_interest_process_; + + interface::ProducerContentObjectCallback *on_new_segment_; + interface::ProducerContentObjectCallback *on_content_object_to_sign_; + interface::ProducerContentObjectCallback *on_content_object_in_output_buffer_; + interface::ProducerContentObjectCallback *on_content_object_output_; + interface::ProducerContentObjectCallback + *on_content_object_evicted_from_output_buffer_; + + interface::ProducerContentCallback *on_content_produced_; + + interface::ProducerSocket::Callback *producer_callback_; + + // Output buffer + utils::ContentStore output_buffer_; + + // Signature and manifest + std::shared_ptr<auth::Signer> signer_; + uint32_t manifest_max_capacity_; + + bool is_async_; + fec::FECType fec_type_; +}; + +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/protocol.cc b/libtransport/src/protocols/protocol.cc deleted file mode 100644 index 451fef80d..000000000 --- a/libtransport/src/protocols/protocol.cc +++ /dev/null @@ -1,137 +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/interfaces/socket_consumer.h> -#include <implementation/socket_consumer.h> -#include <protocols/protocol.h> - -namespace transport { - -namespace protocol { - -using namespace interface; - -TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, - Reassembly *reassembly_protocol) - : socket_(icn_socket), - reassembly_protocol_(reassembly_protocol), - index_manager_( - std::make_unique<IndexManager>(socket_, this, reassembly_protocol)), - is_running_(false), - is_first_(false), - on_interest_retransmission_(VOID_HANDLER), - on_interest_output_(VOID_HANDLER), - on_interest_timeout_(VOID_HANDLER), - on_interest_satisfied_(VOID_HANDLER), - on_content_object_input_(VOID_HANDLER), - on_content_object_verification_(VOID_HANDLER), - stats_summary_(VOID_HANDLER), - verification_failed_callback_(VOID_HANDLER), - on_payload_(VOID_HANDLER) { - socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); - socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); -} - -int TransportProtocol::start() { - // If the protocol is already running, return otherwise set as running - if (is_running_) return -1; - - // Get all callbacks references before starting - 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::CONTENT_OBJECT_TO_VERIFY, - &on_content_object_verification_); - socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, - &stats_summary_); - socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED, - &verification_failed_callback_); - 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; - } - - return 0; -} - -void TransportProtocol::stop() { - is_running_ = false; - - if (!is_async_) { - portal_->stopEventsLoop(); - } else { - portal_->clear(); - } -} - -void TransportProtocol::resume() { - if (is_running_) return; - - is_running_ = true; - - scheduleNextInterests(); - - portal_->runEventsLoop(); - - is_running_ = false; -} - -void TransportProtocol::onContentReassembled(std::error_code ec) { - stop(); - - if (!on_payload_) { - throw errors::RuntimeException( - "The read callback must be installed in the transport before " - "starting " - "the content retrieval."); - } - - if (!ec) { - on_payload_->readSuccess(stats_->getBytesRecv()); - } else { - on_payload_->readError(ec); - } -} - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/protocol.h b/libtransport/src/protocols/protocol.h index 73a0a2c64..a9f929db9 100644 --- a/libtransport/src/protocols/protocol.h +++ b/libtransport/src/protocols/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: @@ -15,100 +15,59 @@ #pragma once -#include <hicn/transport/interfaces/callbacks.h> -#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/indexer.h> -#include <protocols/packet_manager.h> -#include <protocols/reassembly.h> +#include <core/portal.h> +#include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/utils/noncopyable.h> -#include <atomic> +#include <random> namespace transport { namespace protocol { -using namespace core; - -class IndexVerificationManager; - -using ReadCallback = interface::ConsumerSocket::ReadCallback; - -class TransportProtocolCallback { - virtual void onContentObject(const core::Interest &interest, - const core::ContentObject &content_object) = 0; - virtual void onTimeout(const core::Interest &interest) = 0; -}; - -class TransportProtocol : public implementation::BasePortal::ConsumerCallback, - public PacketManager<Interest>, - public ContentObjectProcessingEventCallback { - static constexpr std::size_t interest_pool_size = 4096; - - friend class ManifestIndexManager; - +class Protocol : public core::Portal::TransportCallback, utils::NonCopyable { public: - TransportProtocol(implementation::ConsumerSocket *icn_socket, - Reassembly *reassembly_protocol); - - virtual ~TransportProtocol() = default; - - TRANSPORT_ALWAYS_INLINE bool isRunning() { return is_running_; } - - virtual int start(); - - virtual void stop(); - - virtual void resume(); - - virtual bool verifyKeyPackets() = 0; - - virtual void scheduleNextInterests() = 0; - - // Events generated by the indexing - virtual void onContentReassembled(std::error_code ec); - virtual void onPacketDropped( - Interest::Ptr &&interest, - ContentObject::Ptr &&content_object) override = 0; - virtual void onReassemblyFailed(std::uint32_t missing_segment) override = 0; + 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: - // Consumer Callback - virtual void reset() = 0; - virtual void onContentObject(Interest::Ptr &&i, - ContentObject::Ptr &&c) override = 0; - virtual void onTimeout(Interest::Ptr &&i) override = 0; - virtual void onError(std::error_code ec) override {} + Protocol() : portal_(nullptr), is_running_(false), gen_(rd_()) {} + virtual ~Protocol() {} protected: - implementation::ConsumerSocket *socket_; - std::unique_ptr<Reassembly> reassembly_protocol_; - std::unique_ptr<IndexManager> index_manager_; - std::shared_ptr<implementation::BasePortal> portal_; - std::atomic<bool> is_running_; - // True if it si the first time we schedule an interest - std::atomic<bool> is_first_; - interface::TransportStatistics *stats_; - - // Callbacks - interface::ConsumerInterestCallback *on_interest_retransmission_; - interface::ConsumerInterestCallback *on_interest_output_; - interface::ConsumerInterestCallback *on_interest_timeout_; - interface::ConsumerInterestCallback *on_interest_satisfied_; - interface::ConsumerContentObjectCallback *on_content_object_input_; - interface::ConsumerContentObjectVerificationCallback - *on_content_object_verification_; - interface::ConsumerContentObjectCallback *on_content_object_; - interface::ConsumerTimerCallback *stats_summary_; - interface::ConsumerContentObjectVerificationFailedCallback - *verification_failed_callback_; - ReadCallback *on_payload_; - - bool is_async_; + std::shared_ptr<core::Portal> portal_; + std::atomic_bool is_running_; + + // Random engine + std::random_device rd_; + std::mt19937 gen_; }; -} // end namespace protocol -} // end namespace transport +} // 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 5023adf2e..bcbc15aef 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: @@ -13,10 +13,11 @@ * limitations under the License. */ +#include <hicn/transport/core/global_object_pool.h> #include <hicn/transport/interfaces/socket_consumer.h> #include <implementation/socket_consumer.h> #include <protocols/errors.h> -#include <protocols/indexer.h> +#include <protocols/index_manager_bytestream.h> #include <protocols/raaqm.h> #include <cstdlib> @@ -30,12 +31,14 @@ using namespace interface; RaaqmTransportProtocol::RaaqmTransportProtocol( implementation::ConsumerSocket *icn_socket) - : TransportProtocol(icn_socket, new ByteStreamReassembly(icn_socket, this)), + : TransportProtocol(icn_socket, new IndexManager(icn_socket, this), + new ByteStreamReassembly(icn_socket, this)), 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(); } @@ -46,11 +49,34 @@ RaaqmTransportProtocol::~RaaqmTransportProtocol() { } } -int RaaqmTransportProtocol::start() { +void RaaqmTransportProtocol::reset() { + // Set first segment to retrieve + TransportProtocol::reset(); + core::Name *name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + indexer_verifier_->setFirstSuffix(name->getSuffix()); + std::queue<uint32_t> empty; + std::swap(interest_to_retransmit_, empty); + stats_->reset(); + + // Reset protocol variables + interests_in_flight_ = 0; + t0_ = utils::SteadyTime::Clock::now(); + + // Optionally reset congestion window + bool reset_window; + socket_->getSocketOption(RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET, + reset_window); + if (reset_window) { + current_window_size_ = 1; + } + + // Reset rate estimator if (rate_estimator_) { rate_estimator_->onStart(); } + // If not cur_path exists, create one if (!cur_path_) { // RAAQM double drop_factor; @@ -93,41 +119,6 @@ int RaaqmTransportProtocol::start() { cur_path_ = cur_path.get(); path_table_[default_values::path_id] = std::move(cur_path); } - - portal_->setConsumerCallback(this); - return TransportProtocol::start(); -} - -void RaaqmTransportProtocol::resume() { return TransportProtocol::resume(); } - -void RaaqmTransportProtocol::reset() { - // Set first segment to retrieve - core::Name *name; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); - index_manager_->reset(); - index_manager_->setFirstSuffix(name->getSuffix()); - std::queue<Interest::Ptr> empty; - std::swap(interest_to_retransmit_, empty); - stats_->reset(); - - // Reset reassembly component - reassembly_protocol_->reInitialize(); - - // Reset protocol variables - interests_in_flight_ = 0; - t0_ = utils::SteadyClock::now(); - - // Optionally reset congestion window - bool reset_window; - socket_->getSocketOption(RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET, - reset_window); - if (reset_window) { - current_window_size_ = 1; - } -} - -bool RaaqmTransportProtocol::verifyKeyPackets() { - return index_manager_->onKeyToVerify(); } void RaaqmTransportProtocol::increaseWindow() { @@ -197,9 +188,8 @@ void RaaqmTransportProtocol::init() { lte_delay_ = 15000; if (!is) { - TRANSPORT_LOGW( - "WARNING: RAAQM parameters not found at %s, set default values", - RAAQM_CONFIG_PATH); + LOG(WARNING) << "RAAQM parameters not found at " << RAAQM_CONFIG_PATH + << ", set default values"; return; } @@ -325,75 +315,66 @@ void RaaqmTransportProtocol::init() { is.close(); } -void RaaqmTransportProtocol::onContentObject( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { - // Check whether makes sense to continue - if (TRANSPORT_EXPECT_FALSE(!is_running_)) { - return; - } +void RaaqmTransportProtocol::onContentObjectReceived( + Interest &interest, ContentObject &content_object, std::error_code &ec) { + uint32_t incremental_suffix = content_object.getName().getSuffix(); // Call application-defined callbacks if (*on_content_object_input_) { - (*on_content_object_input_)(*socket_->getInterface(), *content_object); + (*on_content_object_input_)(*socket_->getInterface(), content_object); } if (*on_interest_satisfied_) { - (*on_interest_satisfied_)(*socket_->getInterface(), *interest); - } - - if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) { - stats_->updateBytesRecv(content_object->payloadSize()); + (*on_interest_satisfied_)(*socket_->getInterface(), interest); } - onContentSegment(std::move(interest), std::move(content_object)); - scheduleNextInterests(); -} + ec = make_error_code(protocol_error::success); -void RaaqmTransportProtocol::onContentSegment( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { - uint32_t incremental_suffix = content_object->getName().getSuffix(); + if (content_object.getPayloadType() == PayloadType::DATA) { + stats_->updateBytesRecv(content_object.payloadSize()); + } // Decrease in-flight interests interests_in_flight_--; // Update stats if (!interest_retransmissions_[incremental_suffix & mask]) { - afterContentReception(*interest, *content_object); + afterContentReception(interest, content_object); } - index_manager_->onContentObject(std::move(interest), - std::move(content_object)); + // Schedule next interests + scheduleNextInterests(); } -void RaaqmTransportProtocol::onPacketDropped( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { +void RaaqmTransportProtocol::onPacketDropped(Interest &interest, + ContentObject &content_object, + const std::error_code &reason) { uint32_t max_rtx = 0; socket_->getSocketOption(GeneralTransportOptions::MAX_INTEREST_RETX, max_rtx); - uint64_t segment = interest->getName().getSuffix(); + uint64_t segment = interest.getName().getSuffix(); if (TRANSPORT_EXPECT_TRUE(interest_retransmissions_[segment & mask] < max_rtx)) { stats_->updateRetxCount(1); if (*on_interest_retransmission_) { - (*on_interest_retransmission_)(*socket_->getInterface(), *interest); + (*on_interest_retransmission_)(*socket_->getInterface(), interest); } if (*on_interest_output_) { - (*on_interest_output_)(*socket_->getInterface(), *interest); + (*on_interest_output_)(*socket_->getInterface(), interest); } - if (!is_running_) { + if (!isRunning()) { return; } interest_retransmissions_[segment & mask]++; - interest_to_retransmit_.push(std::move(interest)); + interest_to_retransmit_.push((unsigned int)segment); } else { - TRANSPORT_LOGE( - "Stop: received not trusted packet %llu times", - (unsigned long long)interest_retransmissions_[segment & mask]); + LOG(ERROR) << "Stop: received not trusted packet " + << interest_retransmissions_[segment & mask] << " times"; onContentReassembled( make_error_code(protocol_error::max_retransmissions_error)); } @@ -403,23 +384,27 @@ void RaaqmTransportProtocol::onReassemblyFailed(std::uint32_t missing_segment) { } -void RaaqmTransportProtocol::onTimeout(Interest::Ptr &&interest) { - checkForStalePaths(); - - const Name &n = interest->getName(); - - TRANSPORT_LOGW("Timeout on content %s", n.toString().c_str()); +void RaaqmTransportProtocol::sendInterest( + const Name &interest_name, + std::array<uint32_t, MAX_AGGREGATED_INTEREST> *additional_suffixes, + 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); +} - if (TRANSPORT_EXPECT_FALSE(!is_running_)) { - return; - } +void RaaqmTransportProtocol::onInterestTimeout(Interest::Ptr &interest, + const Name &n) { + checkForStalePaths(); interests_in_flight_--; uint64_t segment = n.getSuffix(); // Do not retransmit interests asking contents that do not exist. - if (segment > index_manager_->getFinalSuffix()) { + if (segment > indexer_verifier_->getFinalSuffix()) { return; } @@ -440,32 +425,34 @@ void RaaqmTransportProtocol::onTimeout(Interest::Ptr &&interest) { (*on_interest_retransmission_)(*socket_->getInterface(), *interest); } - if (!is_running_) { + if (!isRunning()) { return; } - interest_retransmissions_[segment & mask]++; - interest_to_retransmit_.push(std::move(interest)); - + interest_to_retransmit_.push((unsigned int)segment); scheduleNextInterests(); } else { - TRANSPORT_LOGE("Stop: reached max retx limit."); + LOG(ERROR) << "Stop: reached max retx limit."; onContentReassembled(std::make_error_code(std::errc(std::errc::io_error))); } } void RaaqmTransportProtocol::scheduleNextInterests() { - bool cancel = (!is_running_ && !is_first_) || !schedule_interests_; + bool cancel = (!isRunning() && !is_first_) || !schedule_interests_; if (TRANSPORT_EXPECT_FALSE(cancel)) { schedule_interests_ = true; return; } + core::Name *name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + if (TRANSPORT_EXPECT_FALSE(interests_in_flight_ >= current_window_size_ && interest_to_retransmit_.size() > 0)) { // send at least one interest if there are retransmissions to perform and // there is no space left in the window - sendInterest(std::move(interest_to_retransmit_.front())); + auto suffix = interest_to_retransmit_.front(); + sendInterest(name->setSuffix(suffix)); interest_to_retransmit_.pop(); } @@ -474,58 +461,26 @@ void RaaqmTransportProtocol::scheduleNextInterests() { // Send the interest needed for filling the window while (interests_in_flight_ < current_window_size_) { if (interest_to_retransmit_.size() > 0) { - sendInterest(std::move(interest_to_retransmit_.front())); + auto suffix = interest_to_retransmit_.front(); + sendInterest(name->setSuffix(suffix)); interest_to_retransmit_.pop(); } else { - index = index_manager_->getNextSuffix(); + if (TRANSPORT_EXPECT_FALSE(!isRunning() && !is_first_)) { + break; + } + + index = indexer_verifier_->getNextSuffix(); if (index == IndexManager::invalid_index) { break; } - sendInterest(index); + interest_retransmissions_[index & mask] = ~0; + sendInterest(name->setSuffix(index)); } } } -bool RaaqmTransportProtocol::sendInterest(std::uint64_t next_suffix) { - auto interest = getPacket(); - core::Name *name; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); - name->setSuffix((uint32_t)next_suffix); - interest->setName(*name); - - uint32_t interest_lifetime; - socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, - interest_lifetime); - interest->setLifetime(interest_lifetime); - - if (*on_interest_output_) { - on_interest_output_->operator()(*socket_->getInterface(), *interest); - } - - if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) { - return false; - } - - // This is set to ~0 so that the next interest_retransmissions_ + 1, - // performed by sendInterest, will result in 0 - interest_retransmissions_[next_suffix & mask] = ~0; - interest_timepoints_[next_suffix & mask] = utils::SteadyClock::now(); - - sendInterest(std::move(interest)); - - return true; -} - -void RaaqmTransportProtocol::sendInterest(Interest::Ptr &&interest) { - interests_in_flight_++; - interest_retransmissions_[interest->getName().getSuffix() & mask]++; - - TRANSPORT_LOGD("Send interest %s", interest->getName().toString().c_str()); - portal_->sendInterest(std::move(interest)); -} - -void RaaqmTransportProtocol::onContentReassembled(std::error_code ec) { +void RaaqmTransportProtocol::onContentReassembled(const std::error_code &ec) { rate_estimator_->onDownloadFinished(); TransportProtocol::onContentReassembled(ec); schedule_interests_ = false; @@ -535,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(); + auto rtt = utils::SteadyTime::getDurationUs( + 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()) { @@ -558,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::Microseconds &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 fce4194d4..a7ef23b68 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: @@ -18,11 +18,12 @@ #include <hicn/transport/utils/chrono_typedefs.h> #include <protocols/byte_stream_reassembly.h> #include <protocols/congestion_window_protocol.h> -#include <protocols/protocol.h> #include <protocols/raaqm_data_path.h> #include <protocols/rate_estimation.h> +#include <protocols/transport_protocol.h> #include <queue> +#include <random> #include <vector> namespace transport { @@ -32,18 +33,15 @@ namespace protocol { class RaaqmTransportProtocol : public TransportProtocol, public CWindowProtocol { public: - RaaqmTransportProtocol(implementation::ConsumerSocket *icnet_socket); + RaaqmTransportProtocol(implementation::ConsumerSocket *icn_socket); ~RaaqmTransportProtocol(); - int start() override; - - void resume() override; + using TransportProtocol::start; + using TransportProtocol::stop; void reset() override; - virtual bool verifyKeyPackets() override; - protected: static constexpr uint32_t buffer_size = 1 << interface::default_values::log_2_default_buffer_size; @@ -58,52 +56,43 @@ 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::Microseconds &rtt, + utils::SteadyTime::TimePoint &now); private: void init(); - void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) override; - - void onContentSegment(Interest::Ptr &&interest, - ContentObject::Ptr &&content_object); - - void onPacketDropped(Interest::Ptr &&interest, - ContentObject::Ptr &&content_object) override; - + void onContentObjectReceived(Interest &i, ContentObject &c, + std::error_code &ec) override; + void onPacketDropped(Interest &interest, ContentObject &content_object, + const std::error_code &reason) override; void onReassemblyFailed(std::uint32_t missing_segment) override; - - void onTimeout(Interest::Ptr &&i) override; - + void onInterestTimeout(Interest::Ptr &i, const Name &n) override; virtual void scheduleNextInterests() override; + void sendInterest(const Name &interest_name, + std::array<uint32_t, MAX_AGGREGATED_INTEREST> + *additional_suffixes = nullptr, + uint32_t len = 0) override; - bool sendInterest(std::uint64_t next_suffix); - - void sendInterest(Interest::Ptr &&interest); - - 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); - void checkDropProbability(); - 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::queue<Interest::Ptr> interest_to_retransmit_; + std::array<utils::SteadyTime::TimePoint, buffer_size> interest_timepoints_; + std::queue<uint32_t> interest_to_retransmit_; private: /** @@ -117,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 8bbbadcf2..b8e6e6285 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: @@ -14,7 +14,6 @@ */ #include <hicn/transport/utils/chrono_typedefs.h> - #include <protocols/raaqm_data_path.h> namespace transport { @@ -24,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), @@ -44,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::Microseconds &new_rtt, + const utils::SteadyTime::TimePoint &now) { + rtt_ = new_rtt.count() / 1000; + rtt_samples_.pushBack(rtt_); rtt_max_ = rtt_samples_.rBegin(); rtt_min_ = rtt_samples_.begin(); @@ -144,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 3f037bc76..dd24dad51 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: @@ -16,7 +16,6 @@ #pragma once #include <hicn/transport/utils/chrono_typedefs.h> - #include <utils/min_filter.h> #include <chrono> @@ -35,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: /* @@ -45,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::Microseconds &new_rtt, + const utils::SteadyTime::TimePoint &now); /** * @brief Update the path statistics @@ -215,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 a2cf1aefe..01c18c6cb 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: @@ -13,9 +13,9 @@ * limitations under the License. */ +#include <glog/logging.h> +#include <hicn/transport/errors/runtime_exception.h> #include <hicn/transport/interfaces/socket_options_default_values.h> -#include <hicn/transport/utils/log.h> - #include <protocols/rate_estimation.h> #include <thread> @@ -93,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() { @@ -106,33 +106,35 @@ InterRttEstimator::~InterRttEstimator() { pthread_mutex_destroy(&(this->mutex_)); } -void InterRttEstimator::onRttUpdate(double rtt) { +void InterRttEstimator::onRttUpdate( + const utils::SteadyTime::Microseconds &rtt) { pthread_mutex_lock(&(this->mutex_)); - this->rtt_ = rtt; + this->rtt_ = rtt.count() / 1000.0; 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_) { - TRANSPORT_LOGE("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)) { - TRANSPORT_LOGE("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; @@ -140,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; @@ -155,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_); @@ -193,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); } @@ -230,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_) { @@ -250,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::Microseconds &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_) { @@ -281,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(); } } @@ -298,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::Microseconds &rtt) { this->number_of_packets_++; - this->avg_rtt_ += rtt; + this->avg_rtt_ += rtt.count() / 1000.0; if (number_of_packets_ == this->batching_param_) { if (estimation_ == 0) { @@ -330,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 17f39e0b9..d809b2b7c 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,7 +16,7 @@ #pragma once #include <hicn/transport/interfaces/statistics.h> - +#include <hicn/transport/utils/noncopyable.h> #include <protocols/raaqm_data_path.h> #include <chrono> @@ -25,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::Microseconds &rtt){}; virtual void onDataReceived(int packetSize){}; @@ -49,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_; @@ -68,7 +66,7 @@ class InterRttEstimator : public IcnRateEstimator { ~InterRttEstimator(); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Microseconds &rtt); void onDataReceived(int packet_size) { if (packet_size > this->max_packet_size_) { @@ -103,7 +101,7 @@ class BatchingPacketsEstimator : public IcnRateEstimator { public: BatchingPacketsEstimator(double alpha_arg, int batchingParam); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Microseconds &rtt); void onDataReceived(int packet_size) { if (packet_size > this->max_packet_size_) { @@ -150,7 +148,7 @@ class SimpleEstimator : public IcnRateEstimator { public: SimpleEstimator(double alpha, int batching_param); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Microseconds &rtt); void onDataReceived(int packet_size); diff --git a/libtransport/src/protocols/reassembly.cc b/libtransport/src/protocols/reassembly.cc index c6602153c..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: @@ -16,7 +16,6 @@ #include <hicn/transport/interfaces/socket_consumer.h> #include <hicn/transport/utils/array.h> #include <hicn/transport/utils/membuf.h> - #include <implementation/socket_consumer.h> #include <protocols/errors.h> #include <protocols/indexer.h> @@ -32,7 +31,7 @@ void Reassembly::notifyApplication() { interface::ConsumerCallbacksOptions::READ_CALLBACK, &read_callback); if (TRANSPORT_EXPECT_FALSE(!read_callback)) { - TRANSPORT_LOGE("Read callback not installed!"); + LOG(ERROR) << "Read callback not installed!"; return; } diff --git a/libtransport/src/protocols/reassembly.h b/libtransport/src/protocols/reassembly.h index fdc9f2a05..c0c4de3d8 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, @@ -46,19 +46,42 @@ class Reassembly { virtual ~Reassembly() = default; - virtual void reassemble(core::ContentObject::Ptr &&content_object) = 0; - virtual void reassemble( - std::unique_ptr<core::ContentObjectManifest> &&manifest) = 0; + /** + * Handle reassembly of content object. + */ + virtual void reassemble(core::ContentObject &content_object) = 0; + + /** + * Handle reassembly of content object. + */ + virtual void reassemble(utils::MemBuf &buffer, uint32_t suffix) = 0; + + /** + * Reset reassembler for new round + */ virtual void reInitialize() = 0; - virtual void setIndexer(Indexer *indexer) { index_manager_ = indexer; } + + /** + * Use indexer to get next segments to reassembly + */ + virtual void setIndexer(Indexer *indexer) { indexer_verifier_ = indexer; } + + /** + * Decide if it is required to pass to application buffers whose verification + * has been delayed (e.g. because the manifest is missing). False by default. + */ + virtual bool reassembleUnverified() { return false; } protected: + /** + * Notify application there is data to read. + */ virtual void notifyApplication(); protected: implementation::ConsumerSocket *reassembly_consumer_socket_; TransportProtocol *transport_protocol_; - Indexer *index_manager_; + Indexer *indexer_verifier_; std::unique_ptr<utils::MemBuf> read_buffer_; }; diff --git a/libtransport/src/protocols/rtc.cc b/libtransport/src/protocols/rtc.cc deleted file mode 100644 index a01b8daa5..000000000 --- a/libtransport/src/protocols/rtc.cc +++ /dev/null @@ -1,984 +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/interfaces/socket_consumer.h> -#include <implementation/socket_consumer.h> -#include <math.h> -#include <protocols/rtc.h> - -#include <random> - -namespace transport { - -namespace protocol { - -using namespace interface; - -RTCTransportProtocol::RTCTransportProtocol( - implementation::ConsumerSocket *icn_socket) - : TransportProtocol(icn_socket, nullptr), - DatagramReassembly(icn_socket, this), - inflightInterests_(1 << default_values::log_2_default_buffer_size), - modMask_((1 << default_values::log_2_default_buffer_size) - 1) { - icn_socket->getSocketOption(PORTAL, portal_); - rtx_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); - probe_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); - sentinel_timer_ = - std::make_unique<asio::steady_timer>(portal_->getIoService()); - round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); - initParams(); -} - -RTCTransportProtocol::~RTCTransportProtocol() {} - -void RTCTransportProtocol::resume() { - if (is_running_) return; - - is_running_ = true; - inflightInterestsCount_ = 0; - - probeRtt(); - sentinelTimer(); - newRound(); - scheduleNextInterests(); - - portal_->runEventsLoop(); - is_running_ = false; -} - -// private -void RTCTransportProtocol::initParams() { - portal_->setConsumerCallback(this); - // controller var - currentState_ = HICN_RTC_SYNC_STATE; - - // cwin var - currentCWin_ = HICN_INITIAL_CWIN; - maxCWin_ = HICN_INITIAL_CWIN_MAX; - - // names/packets var - actualSegment_ = 0; - inflightInterestsCount_ = 0; - interestRetransmissions_.clear(); - lastSegNacked_ = 0; - lastReceived_ = 0; - lastReceivedTime_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - lastEvent_ = lastReceivedTime_; - highestReceived_ = 0; - firstSequenceInRound_ = 0; - - rtx_timer_used_ = false; - for (int i = 0; i < (1 << default_values::log_2_default_buffer_size); i++) { - inflightInterests_[i] = {0}; - } - - // stats - firstPckReceived_ = false; - receivedBytes_ = 0; - sentInterest_ = 0; - receivedData_ = 0; - packetLost_ = 0; - lossRecovered_ = 0; - avgPacketSize_ = HICN_INIT_PACKET_SIZE; - gotNack_ = false; - gotFutureNack_ = 0; - rounds_ = 0; - roundsWithoutNacks_ = 0; - pathTable_.clear(); - - // CC var - estimatedBw_ = 0.0; - lossRate_ = 0.0; - queuingDelay_ = 0.0; - protocolState_ = HICN_RTC_NORMAL_STATE; - - producerPathLabels_[0] = 0; - producerPathLabels_[1] = 0; - initied = false; - - socket_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, - (uint32_t)HICN_RTC_INTEREST_LIFETIME); -} - -// private -void RTCTransportProtocol::reset() { - initParams(); - probeRtt(); - sentinelTimer(); - newRound(); -} - -uint32_t max(uint32_t a, uint32_t b) { - if (a > b) - return a; - else - return b; -} - -uint32_t min(uint32_t a, uint32_t b) { - if (a < b) - return a; - else - return b; -} - -void RTCTransportProtocol::newRound() { - round_timer_->expires_from_now(std::chrono::milliseconds(HICN_ROUND_LEN)); - round_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - updateStats(HICN_ROUND_LEN); - newRound(); - }); -} - -void RTCTransportProtocol::updateDelayStats( - const ContentObject &content_object) { - uint32_t segmentNumber = content_object.getName().getSuffix(); - uint32_t pkt = segmentNumber & modMask_; - - if (inflightInterests_[pkt].state != sent_) return; - - if (interestRetransmissions_.find(segmentNumber) != - interestRetransmissions_.end()) - // this packet was rtx at least once - return; - - uint32_t pathLabel = content_object.getPathLabel(); - - if (pathTable_.find(pathLabel) == pathTable_.end()) { - // found a new path - std::shared_ptr<RTCDataPath> newPath = std::make_shared<RTCDataPath>(); - pathTable_[pathLabel] = newPath; - } - - // RTT measurements are useful both from NACKs and data packets - uint64_t RTT = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count() - - inflightInterests_[pkt].transmissionTime; - - pathTable_[pathLabel]->insertRttSample(RTT); - auto payload = content_object.getPayload(); - - // we collect OWD only for datapackets - if (payload->length() != HICN_NACK_HEADER_SIZE) { - uint64_t *senderTimeStamp = (uint64_t *)payload->data(); - int64_t OWD = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count() - - *senderTimeStamp; - - pathTable_[pathLabel]->insertOwdSample(OWD); - pathTable_[pathLabel]->computeInterArrivalGap(segmentNumber); - } else { - pathTable_[pathLabel]->receivedNack(); - } -} - -void RTCTransportProtocol::updateStats(uint32_t round_duration) { - if (pathTable_.empty()) return; - - if (receivedBytes_ != 0) { - double bytesPerSec = - (double)(receivedBytes_ * - ((double)HICN_MILLI_IN_A_SEC / (double)round_duration)); - estimatedBw_ = (estimatedBw_ * HICN_ESTIMATED_BW_ALPHA) + - ((1 - HICN_ESTIMATED_BW_ALPHA) * bytesPerSec); - } - - uint64_t minRtt = UINT_MAX; - uint64_t maxRtt = 0; - - for (auto it = pathTable_.begin(); it != pathTable_.end(); it++) { - it->second->roundEnd(); - if (it->second->isActive()) { - if (it->second->getMinRtt() < minRtt) { - minRtt = it->second->getMinRtt(); - producerPathLabels_[0] = it->first; - } - if (it->second->getMinRtt() > maxRtt) { - maxRtt = it->second->getMinRtt(); - producerPathLabels_[1] = it->first; - } - } - } - - if (pathTable_.find(producerPathLabels_[0]) == pathTable_.end() || - pathTable_.find(producerPathLabels_[1]) == pathTable_.end()) - return; // this should not happen - - // as a queuing delay we keep the lowest one among the two paths - // if one path is congested the forwarder should decide to do not - // use it so it does not make sense to inform the application - // that maybe we have a problem - if (pathTable_[producerPathLabels_[0]]->getQueuingDealy() < - pathTable_[producerPathLabels_[1]]->getQueuingDealy()) - queuingDelay_ = pathTable_[producerPathLabels_[0]]->getQueuingDealy(); - else - queuingDelay_ = pathTable_[producerPathLabels_[1]]->getQueuingDealy(); - - if (sentInterest_ != 0 && currentState_ == HICN_RTC_NORMAL_STATE) { - uint32_t numberTheoricallyReceivedPackets_ = - highestReceived_ - firstSequenceInRound_; - double lossRate = 0; - if (numberTheoricallyReceivedPackets_ != 0) - lossRate = (double)((double)(packetLost_ - lossRecovered_) / - (double)numberTheoricallyReceivedPackets_); - - if (lossRate < 0) lossRate = 0; - - if (initied) { - lossRate_ = lossRate_ * HICN_ESTIMATED_LOSSES_ALPHA + - (lossRate * (1 - HICN_ESTIMATED_LOSSES_ALPHA)); - } else { - lossRate_ = lossRate; - initied = true; - } - } - - if (avgPacketSize_ == 0) avgPacketSize_ = HICN_INIT_PACKET_SIZE; - - // for the BDP we use the max rtt, so that we calibrate the window on the - // RTT of the slowest path. In this way we are sure that the window will - // never be too small - uint32_t BDP = (uint32_t)ceil( - (estimatedBw_ * - (double)((double)pathTable_[producerPathLabels_[1]]->getMinRtt() / - (double)HICN_MILLI_IN_A_SEC) * - HICN_BANDWIDTH_SLACK_FACTOR) / - avgPacketSize_); - uint32_t BW = (uint32_t)ceil(estimatedBw_); - computeMaxWindow(BW, BDP); - - if (*stats_summary_) { - // Send the stats to the app - stats_->updateQueuingDelay(queuingDelay_); - stats_->updateLossRatio(lossRate_); - stats_->updateAverageRtt(pathTable_[producerPathLabels_[1]]->getMinRtt()); - (*stats_summary_)(*socket_->getInterface(), *stats_); - } - // bound also by interest lifitime* production rate - if (!gotNack_) { - roundsWithoutNacks_++; - if (currentState_ == HICN_RTC_SYNC_STATE && - roundsWithoutNacks_ >= HICN_ROUNDS_IN_SYNC_BEFORE_SWITCH) { - currentState_ = HICN_RTC_NORMAL_STATE; - } - } else { - roundsWithoutNacks_ = 0; - } - - updateCCState(); - updateWindow(); - - if (queuingDelay_ > 25.0) { - // this indicates that the client will go soon out of synch, - // switch to synch mode - if (currentState_ == HICN_RTC_NORMAL_STATE) { - currentState_ = HICN_RTC_SYNC_STATE; - } - computeMaxWindow(BW, 0); - increaseWindow(); - } - - // in any case we reset all the counters - - gotNack_ = false; - gotFutureNack_ = 0; - receivedBytes_ = 0; - sentInterest_ = 0; - receivedData_ = 0; - packetLost_ = 0; - lossRecovered_ = 0; - rounds_++; - firstSequenceInRound_ = highestReceived_; -} - -void RTCTransportProtocol::updateCCState() { - // TODO -} - -void RTCTransportProtocol::computeMaxWindow(uint32_t productionRate, - uint32_t BDPWin) { - if (productionRate == - 0) // we have no info about the producer, keep the previous maxCWin - return; - - uint32_t interestLifetime = default_values::interest_lifetime; - socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, - interestLifetime); - uint32_t maxWaintingInterest = (uint32_t)ceil( - (productionRate / avgPacketSize_) * - (double)((double)(interestLifetime * - HICN_INTEREST_LIFETIME_REDUCTION_FACTOR) / - (double)HICN_MILLI_IN_A_SEC)); - - if (currentState_ == HICN_RTC_SYNC_STATE) { - // in this case we do not limit the window with the BDP, beacuse most - // likely it is wrong - maxCWin_ = maxWaintingInterest; - return; - } - - // currentState = RTC_NORMAL_STATE - if (BDPWin != 0) { - maxCWin_ = (uint32_t)ceil((double)BDPWin + - (((double)BDPWin * 30.0) / 100.0)); // BDP + 30% - } else { - maxCWin_ = min(maxWaintingInterest, maxCWin_); - } - - if (maxCWin_ < HICN_MIN_CWIN) maxCWin_ = HICN_MIN_CWIN; -} - -void RTCTransportProtocol::updateWindow() { - if (currentState_ == HICN_RTC_SYNC_STATE) return; - - if (estimatedBw_ == 0) return; - - if (currentCWin_ < maxCWin_ * 0.9) { - currentCWin_ = - min(maxCWin_, (uint32_t)(currentCWin_ * HICN_WIN_INCREASE_FACTOR)); - } else if (currentCWin_ > maxCWin_) { - currentCWin_ = - max((uint32_t)(currentCWin_ * HICN_WIN_DECREASE_FACTOR), HICN_MIN_CWIN); - } -} - -void RTCTransportProtocol::decreaseWindow() { - // this is used only in SYNC mode - if (currentState_ == HICN_RTC_NORMAL_STATE) return; - - if (gotFutureNack_ == 1) - currentCWin_ = min((currentCWin_ - 1), - (uint32_t)ceil((double)maxCWin_ * 0.66)); // 2/3 - else - currentCWin_--; - - currentCWin_ = max(currentCWin_, HICN_MIN_CWIN); -} - -void RTCTransportProtocol::increaseWindow() { - // this is used only in SYNC mode - if (currentState_ == HICN_RTC_NORMAL_STATE) return; - - // we need to be carefull to do not increase the window to much - if (currentCWin_ < ((double)maxCWin_ * 0.7)) { - currentCWin_ = currentCWin_ + 1; // exponential - } else { - currentCWin_ = min( - maxCWin_, - (uint32_t)ceil(currentCWin_ + (1.0 / (double)currentCWin_))); // linear - } -} - -void RTCTransportProtocol::probeRtt() { - probe_timer_->expires_from_now(std::chrono::milliseconds(1000)); - probe_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - probeRtt(); - }); - - // To avoid sending the first probe, because the transport is not running yet - if (is_first_ && !is_running_) return; - - time_sent_probe_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - Name *interest_name = nullptr; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, - &interest_name); - // get a random numbe in the probe seq range - std::default_random_engine eng((std::random_device())()); - std::uniform_int_distribution<uint32_t> idis(HICN_MIN_PROBE_SEQ, - HICN_MAX_PROBE_SEQ); - probe_seq_number_ = idis(eng); - interest_name->setSuffix(probe_seq_number_); - - // we considere the probe as a rtx so that we do not incresea inFlightInt - received_probe_ = false; - TRANSPORT_LOGD("Send content interest %u (probeRtt)", - interest_name->getSuffix()); - sendInterest(interest_name, true); -} - -void RTCTransportProtocol::sendInterest(Name *interest_name, bool rtx) { - auto interest = getPacket(); - interest->setName(*interest_name); - - uint32_t interestLifetime = default_values::interest_lifetime; - socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, - interestLifetime); - interest->setLifetime(uint32_t(interestLifetime)); - - if (*on_interest_output_) { - (*on_interest_output_)(*socket_->getInterface(), *interest); - } - - if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) { - return; - } - - portal_->sendInterest(std::move(interest)); - - sentInterest_++; - - if (!rtx) { - packets_in_window_[interest_name->getSuffix()] = 0; - inflightInterestsCount_++; - } -} - -void RTCTransportProtocol::scheduleNextInterests() { - if (!is_running_ && !is_first_) return; - - TRANSPORT_LOGD("----- [window %u - inflight_interests %u = %d] -----", - currentCWin_, inflightInterestsCount_, - currentCWin_ - inflightInterestsCount_); - - while (inflightInterestsCount_ < currentCWin_) { - Name *interest_name = nullptr; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, - &interest_name); - - interest_name->setSuffix(actualSegment_); - - // if the producer socket is not stated (does not reply even with nacks) - // we keep asking for something without marking anything as lost (see - // timeout). In this way when the producer socket will start the - // consumer socket will not miss any packet - if (TRANSPORT_EXPECT_FALSE(!firstPckReceived_)) { - uint32_t pkt = actualSegment_ & modMask_; - inflightInterests_[pkt].state = sent_; - inflightInterests_[pkt].sequence = actualSegment_; - actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ; - TRANSPORT_LOGD( - "Send content interest %u (scheduleNextInterests no replies)", - interest_name->getSuffix()); - sendInterest(interest_name, false); - return; - } - - // we send the packet only if it is not pending yet - // notice that this is not true for rtx packets - if (portal_->interestIsPending(*interest_name)) { - actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ; - continue; - } - - uint32_t pkt = actualSegment_ & modMask_; - // if we already reacevied the content we don't ask it again - if (inflightInterests_[pkt].state == received_ && - inflightInterests_[pkt].sequence == actualSegment_) { - actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ; - continue; - } - - // same if the packet is lost - if (inflightInterests_[pkt].state == lost_ && - inflightInterests_[pkt].sequence == actualSegment_) { - actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ; - continue; - } - - inflightInterests_[pkt].transmissionTime = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - // here the packet can be in any state except for lost or recevied - inflightInterests_[pkt].state = sent_; - inflightInterests_[pkt].sequence = actualSegment_; - actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ; - - TRANSPORT_LOGD("Send content interest %u (scheduleNextInterests)", - interest_name->getSuffix()); - sendInterest(interest_name, false); - } - - TRANSPORT_LOGD("----- end of scheduleNextInterest -----"); -} - -bool RTCTransportProtocol::verifyKeyPackets() { - // Not yet implemented - return false; -} - -void RTCTransportProtocol::sentinelTimer() { - uint32_t wait = 50; - - if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end() && - pathTable_.find(producerPathLabels_[1]) != pathTable_.end()) { - // we have all the info to set the timers - wait = round(pathTable_[producerPathLabels_[0]]->getInterArrivalGap()); - if (wait == 0) wait = 1; - } - - sentinel_timer_->expires_from_now(std::chrono::milliseconds(wait)); - sentinel_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - if (pathTable_.find(producerPathLabels_[0]) == pathTable_.end() || - pathTable_.find(producerPathLabels_[1]) == pathTable_.end()) { - // we have no info, so we send again - - for (auto it = packets_in_window_.begin(); it != packets_in_window_.end(); - it++) { - uint32_t pkt = it->first & modMask_; - if (inflightInterests_[pkt].sequence == it->first) { - inflightInterests_[pkt].transmissionTime = now; - Name *interest_name = nullptr; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, - &interest_name); - interest_name->setSuffix(it->first); - it->second++; - sendInterest(interest_name, true); - } - } - } else { - uint64_t max_waiting_time = // wait at least 50ms - (pathTable_[producerPathLabels_[1]]->getMinRtt() - - pathTable_[producerPathLabels_[0]]->getMinRtt()) + - (ceil(pathTable_[producerPathLabels_[0]]->getInterArrivalGap()) * 50); - - if ((currentState_ == HICN_RTC_NORMAL_STATE) && - (inflightInterestsCount_ >= currentCWin_) && - ((now - lastEvent_) > max_waiting_time) && (lossRate_ >= 0.05)) { - uint64_t RTT = pathTable_[producerPathLabels_[1]]->getMinRtt(); - - for (auto it = packets_in_window_.begin(); - it != packets_in_window_.end(); it++) { - uint32_t pkt = it->first & modMask_; - if (inflightInterests_[pkt].sequence == it->first && - ((now - inflightInterests_[pkt].transmissionTime) >= RTT)) { - inflightInterests_[pkt].transmissionTime = now; - Name *interest_name = nullptr; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, - &interest_name); - interest_name->setSuffix(it->first); - it->second++; - sendInterest(interest_name, true); - } - } - } - } - - sentinelTimer(); - }); -} -void RTCTransportProtocol::addRetransmissions(uint32_t val) { - // add only val in the rtx list - addRetransmissions(val, val + 1); -} - -void RTCTransportProtocol::addRetransmissions(uint32_t start, uint32_t stop) { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - bool new_rtx = false; - for (uint32_t i = start; i < stop; i++) { - auto it = interestRetransmissions_.find(i); - if (it == interestRetransmissions_.end()) { - uint32_t pkt = i & modMask_; - if (lastSegNacked_ <= i && inflightInterests_[pkt].state != received_) { - // it must be larger than the last past nack received - packetLost_++; - interestRetransmissions_[i] = 0; - uint32_t pkt = i & modMask_; - // we reset the transmission time setting to now, so that rtx will - // happne in one RTT on waint one inter arrival gap - inflightInterests_[pkt].transmissionTime = now; - new_rtx = true; - } - } // if the retransmission is already there the rtx timer will - // take care of it - } - - // in case a new rtx is added to the map we need to run checkRtx() - if (new_rtx) { - if (rtx_timer_used_) { - // if a timer is pending we need to delete it - rtx_timer_->cancel(); - rtx_timer_used_ = false; - } - checkRtx(); - } -} - -uint64_t RTCTransportProtocol::retransmit() { - auto it = interestRetransmissions_.begin(); - - // cut len to max HICN_MAX_RTX_SIZE - // since we use a map, the smaller (and so the older) sequence number are at - // the beginnin of the map - while (interestRetransmissions_.size() > HICN_MAX_RTX_SIZE) { - it = interestRetransmissions_.erase(it); - } - - it = interestRetransmissions_.begin(); - uint64_t smallest_timeout = ULONG_MAX; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - - while (it != interestRetransmissions_.end()) { - uint32_t pkt = it->first & modMask_; - - if (inflightInterests_[pkt].sequence != it->first) { - // this packet is not anymore in the inflight buffer, erase it - it = interestRetransmissions_.erase(it); - continue; - } - - // we retransmitted the packet too many times - if (it->second >= HICN_MAX_RTX) { - it = interestRetransmissions_.erase(it); - continue; - } - - // this packet is too old - if ((lastReceived_ > it->first) && - (lastReceived_ - it->first) > HICN_MAX_RTX_MAX_AGE) { - it = interestRetransmissions_.erase(it); - continue; - } - - uint64_t rtx_time = now; - - if (it->second == 0) { - // first rtx - if (producerPathLabels_[0] != producerPathLabels_[1]) { - // multipath - if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end() && - pathTable_.find(producerPathLabels_[1]) != pathTable_.end() && - (pathTable_[producerPathLabels_[0]]->getInterArrivalGap() < - HICN_MIN_INTER_ARRIVAL_GAP)) { - rtx_time = lastReceivedTime_ + - (pathTable_[producerPathLabels_[1]]->getMinRtt() - - pathTable_[producerPathLabels_[0]]->getMinRtt()) + - pathTable_[producerPathLabels_[0]]->getInterArrivalGap(); - } // else low rate producer, send it immediatly - } else { - // single path - if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end() && - (pathTable_[producerPathLabels_[0]]->getInterArrivalGap() < - HICN_MIN_INTER_ARRIVAL_GAP)) { - rtx_time = lastReceivedTime_ + - pathTable_[producerPathLabels_[0]]->getInterArrivalGap(); - } // else low rate producer send immediatly - } - } else { - // second or plus rtx, wait for the min rtt - if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end()) { - uint64_t sent_time = inflightInterests_[pkt].transmissionTime; - rtx_time = sent_time + pathTable_[producerPathLabels_[0]]->getMinRtt(); - } // if we don't have info we send it immediatly - } - - if (now >= rtx_time) { - inflightInterests_[pkt].transmissionTime = now; - it->second++; - - Name *interest_name = nullptr; - socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, - &interest_name); - interest_name->setSuffix(it->first); - TRANSPORT_LOGD("Send content interest %u (retransmit)", - interest_name->getSuffix()); - sendInterest(interest_name, true); - } else if (rtx_time < smallest_timeout) { - smallest_timeout = rtx_time; - } - - ++it; - } - return smallest_timeout; -} - -void RTCTransportProtocol::checkRtx() { - if (interestRetransmissions_.empty()) { - rtx_timer_used_ = false; - return; - } - - uint64_t next_timeout = retransmit(); - uint64_t wait = 1; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - if (next_timeout != ULONG_MAX && now < next_timeout) { - wait = next_timeout - now; - } - rtx_timer_used_ = true; - rtx_timer_->expires_from_now(std::chrono::milliseconds(wait)); - rtx_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - rtx_timer_used_ = false; - checkRtx(); - }); -} - -void RTCTransportProtocol::onTimeout(Interest::Ptr &&interest) { - uint32_t segmentNumber = interest->getName().getSuffix(); - - if (segmentNumber >= HICN_MIN_PROBE_SEQ) { - // this is a timeout on a probe, do nothing - return; - } - - uint32_t pkt = segmentNumber & modMask_; - - if (TRANSPORT_EXPECT_FALSE(!firstPckReceived_)) { - // we do nothing, and we keep asking the same stuff over - // and over until we get at least a packet - inflightInterestsCount_--; - lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - packets_in_window_.erase(segmentNumber); - scheduleNextInterests(); - return; - } - - if (inflightInterests_[pkt].state == sent_) { - lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - packets_in_window_.erase(segmentNumber); - inflightInterestsCount_--; - } - - // check how many times we sent this packet - auto it = interestRetransmissions_.find(segmentNumber); - if (it != interestRetransmissions_.end() && it->second >= HICN_MAX_RTX) { - inflightInterests_[pkt].state = lost_; - } - - if (inflightInterests_[pkt].state == sent_) { - inflightInterests_[pkt].state = timeout1_; - } else if (inflightInterests_[pkt].state == timeout1_) { - inflightInterests_[pkt].state = timeout2_; - } else if (inflightInterests_[pkt].state == timeout2_) { - inflightInterests_[pkt].state = lost_; - } - - if (inflightInterests_[pkt].state == lost_) { - interestRetransmissions_.erase(segmentNumber); - } else { - addRetransmissions(segmentNumber); - } - - scheduleNextInterests(); -} - -bool RTCTransportProtocol::onNack(const ContentObject &content_object, - bool rtx) { - uint32_t *payload = (uint32_t *)content_object.getPayload()->data(); - uint32_t productionSeg = *payload; - uint32_t productionRate = *(++payload); - uint32_t nackSegment = content_object.getName().getSuffix(); - - bool old_nack = false; - - // if we did not received anything between lastReceived_ + 1 and productionSeg - // most likelly some packets got lost - if (lastReceived_ != 0) { - addRetransmissions(lastReceived_ + 1, productionSeg); - } - - if (!rtx) { - gotNack_ = true; - // we synch the estimated production rate with the actual one - estimatedBw_ = (double)productionRate; - } - - if (productionSeg > nackSegment) { - // we are asking for stuff produced in the past - actualSegment_ = max(productionSeg, actualSegment_) % HICN_MIN_PROBE_SEQ; - - if (!rtx) { - if (currentState_ == HICN_RTC_NORMAL_STATE) { - currentState_ = HICN_RTC_SYNC_STATE; - } - - computeMaxWindow(productionRate, 0); - increaseWindow(); - } - - lastSegNacked_ = productionSeg; - old_nack = true; - - } else if (productionSeg < nackSegment) { - actualSegment_ = productionSeg % HICN_MIN_PROBE_SEQ; - - if (!rtx) { - // we are asking stuff in the future - gotFutureNack_++; - computeMaxWindow(productionRate, 0); - decreaseWindow(); - - if (currentState_ == HICN_RTC_SYNC_STATE) { - currentState_ = HICN_RTC_NORMAL_STATE; - } - } - } else { - // we are asking the right thing, but the producer is slow - // keep doing the same until the packet is produced - actualSegment_ = productionSeg % HICN_MIN_PROBE_SEQ; - } - - return old_nack; -} - -void RTCTransportProtocol::onContentObject( - Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { - // as soon as we get a packet firstPckReceived_ will never be false - firstPckReceived_ = true; - - auto payload = content_object->getPayload(); - uint32_t payload_size = (uint32_t)payload->length(); - uint32_t segmentNumber = content_object->getName().getSuffix(); - uint32_t pkt = segmentNumber & modMask_; - - if (*on_content_object_input_) { - (*on_content_object_input_)(*socket_->getInterface(), *content_object); - } - - if (segmentNumber >= HICN_MIN_PROBE_SEQ) { - TRANSPORT_LOGD("Received probe %u", segmentNumber); - if (segmentNumber == probe_seq_number_ && !received_probe_) { - received_probe_ = true; - - uint32_t pathLabel = content_object->getPathLabel(); - if (pathTable_.find(pathLabel) == pathTable_.end()) { - std::shared_ptr<RTCDataPath> newPath = std::make_shared<RTCDataPath>(); - pathTable_[pathLabel] = newPath; - } - - // this is the expected probe, update the RTT and drop the packet - uint64_t RTT = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count() - - time_sent_probe_; - - pathTable_[pathLabel]->insertRttSample(RTT); - pathTable_[pathLabel]->receivedNack(); - } - return; - } - - // check if the packet is a rtx - bool is_rtx = false; - if (interestRetransmissions_.find(segmentNumber) != - interestRetransmissions_.end()) { - is_rtx = true; - } else { - auto it_win = packets_in_window_.find(segmentNumber); - if (it_win != packets_in_window_.end() && it_win->second != 0) - is_rtx = true; - } - - if (payload_size == HICN_NACK_HEADER_SIZE) { - TRANSPORT_LOGD("Received nack %u", segmentNumber); - - if (inflightInterests_[pkt].state == sent_) { - lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - packets_in_window_.erase(segmentNumber); - inflightInterestsCount_--; - } - - bool old_nack = false; - - if (!is_rtx) { - // this is not a retransmitted packet - old_nack = onNack(*content_object, false); - updateDelayStats(*content_object); - } else { - old_nack = onNack(*content_object, true); - } - - // the nacked_ state is used only to avoid to decrease - // inflightInterestsCount_ multiple times. In fact, every time that we - // receive an event related to an interest (timeout, nacked, content) we - // cange the state. In this way we are sure that we do not decrease twice - // the counter - if (old_nack) { - inflightInterests_[pkt].state = lost_; - interestRetransmissions_.erase(segmentNumber); - } else { - inflightInterests_[pkt].state = nacked_; - } - - } else { - TRANSPORT_LOGD("Received content %u", segmentNumber); - - avgPacketSize_ = (HICN_ESTIMATED_PACKET_SIZE * avgPacketSize_) + - ((1 - HICN_ESTIMATED_PACKET_SIZE) * payload->length()); - - receivedBytes_ += (uint32_t)(content_object->headerSize() + - content_object->payloadSize()); - - if (inflightInterests_[pkt].state == sent_) { - lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - packets_in_window_.erase(segmentNumber); - inflightInterestsCount_--; // packet sent without timeouts - } - - if (inflightInterests_[pkt].state == sent_ && !is_rtx) { - // delay stats are computed only for non retransmitted data - updateDelayStats(*content_object); - } - - addRetransmissions(lastReceived_ + 1, segmentNumber); - if (segmentNumber > highestReceived_) { - highestReceived_ = segmentNumber; - } - if (segmentNumber > lastReceived_) { - lastReceived_ = segmentNumber; - lastReceivedTime_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - } - receivedData_++; - inflightInterests_[pkt].state = received_; - - auto it = interestRetransmissions_.find(segmentNumber); - if (it != interestRetransmissions_.end()) lossRecovered_++; - - interestRetransmissions_.erase(segmentNumber); - - reassemble(std::move(content_object)); - increaseWindow(); - } - - scheduleNextInterests(); -} - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/rtc.h b/libtransport/src/protocols/rtc.h deleted file mode 100644 index 9f1bcc25b..000000000 --- a/libtransport/src/protocols/rtc.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiTC_SYNC_STATE - * Licensed under the Apache License, Version 2.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/datagram_reassembly.h> -#include <protocols/protocol.h> -#include <protocols/rtc_data_path.h> - -#include <map> -#include <queue> -#include <unordered_map> - -// algorithm state -#define HICN_RTC_SYNC_STATE 0 -#define HICN_RTC_NORMAL_STATE 1 -#define HICN_ROUNDS_IN_SYNC_BEFORE_SWITCH 3 - -// packet constants -#define HICN_INIT_PACKET_SIZE 1300 // bytes -#define HICN_PACKET_HEADER_SIZE 60 // bytes ipv6+tcp -#define HICN_NACK_HEADER_SIZE 8 // bytes -#define HICN_TIMESTAMP_SIZE 8 // bytes -#define HICN_RTC_INTEREST_LIFETIME 1000 // ms - -// rtt measurement -// normal interests for data goes from 0 to -// HICN_MIN_PROBE_SEQ, the rest is reserverd for -// probes -#define HICN_MIN_PROBE_SEQ 0xefffffff -#define HICN_MAX_PROBE_SEQ 0xffffffff - -// controller constant -#define HICN_ROUND_LEN \ - 200 // ms interval of time on which - // we take decisions / measurements -#define HICN_MAX_RTX 10 -#define HICN_MAX_RTX_SIZE 1024 -#define HICN_MAX_RTX_MAX_AGE 10000 -#define HICN_MIN_RTT_WIN 30 // rounds -#define HICN_MIN_INTER_ARRIVAL_GAP 100 // ms - -// cwin -#define HICN_INITIAL_CWIN 1 // packets -#define HICN_INITIAL_CWIN_MAX 100000 // packets -#define HICN_MIN_CWIN 10 // packets -#define HICN_WIN_INCREASE_FACTOR 1.5 -#define HICN_WIN_DECREASE_FACTOR 0.9 - -// statistics constants -#define HICN_BANDWIDTH_SLACK_FACTOR 1.8 -#define HICN_ESTIMATED_BW_ALPHA 0.7 -#define HICN_ESTIMATED_PACKET_SIZE 0.7 -#define HICN_ESTIMATED_LOSSES_ALPHA 0.8 -#define HICN_INTEREST_LIFETIME_REDUCTION_FACTOR 0.8 - -// other constants -#define HICN_NANO_IN_A_SEC 1000000000 -#define HICN_MICRO_IN_A_SEC 1000000 -#define HICN_MILLI_IN_A_SEC 1000 - -namespace transport { - -namespace protocol { - -enum packetState { sent_, nacked_, received_, timeout1_, timeout2_, lost_ }; - -typedef enum packetState packetState_t; - -struct sentInterest { - uint64_t transmissionTime; - uint32_t sequence; // sequence number of the interest sent - // to handle seq % buffer_size - packetState_t state; // see packet state -}; - -class RTCTransportProtocol : public TransportProtocol, - public DatagramReassembly { - public: - RTCTransportProtocol(implementation::ConsumerSocket *icnet_socket); - - ~RTCTransportProtocol(); - - using TransportProtocol::start; - - using TransportProtocol::stop; - - void resume() override; - - bool verifyKeyPackets() override; - - private: - // algo functions - void initParams(); - void reset() override; - - // CC functions - void updateDelayStats(const ContentObject &content_object); - void updateStats(uint32_t round_duration); - void updateCCState(); - void computeMaxWindow(uint32_t productionRate, uint32_t BDPWin); - void updateWindow(); - void decreaseWindow(); - void increaseWindow(); - void resetPreviousWindow(); - - // packet functions - void sendInterest(Name *interest_name, bool rtx); - void scheduleNextInterests() override; - void sentinelTimer(); - void addRetransmissions(uint32_t val); - void addRetransmissions(uint32_t start, uint32_t stop); - uint64_t retransmit(); - void checkRtx(); - void probeRtt(); - void newRound(); - void onTimeout(Interest::Ptr &&interest) override; - bool onNack(const ContentObject &content_object, bool rtx); - void onContentObject(Interest::Ptr &&interest, - ContentObject::Ptr &&content_object) override; - void onPacketDropped(Interest::Ptr &&interest, - ContentObject::Ptr &&content_object) override {} - void onReassemblyFailed(std::uint32_t missing_segment) override {} - - TRANSPORT_ALWAYS_INLINE virtual void reassemble( - ContentObject::Ptr &&content_object) override { - auto read_buffer = content_object->getPayload(); - read_buffer->trimStart(HICN_TIMESTAMP_SIZE); - Reassembly::read_buffer_ = std::move(read_buffer); - Reassembly::notifyApplication(); - } - - // controller var - std::unique_ptr<asio::steady_timer> round_timer_; - unsigned currentState_; - - // cwin var - uint32_t currentCWin_; - uint32_t maxCWin_; - - // names/packets var - uint32_t actualSegment_; - uint32_t inflightInterestsCount_; - // map seq to rtx - std::map<uint32_t, uint8_t> interestRetransmissions_; - bool rtx_timer_used_; - std::unique_ptr<asio::steady_timer> rtx_timer_; - std::vector<sentInterest> inflightInterests_; - uint32_t lastSegNacked_; // indicates the segment id in the last received - // past Nack. we do not ask for retransmissions - // for samething that is older than this value. - uint32_t lastReceived_; // segment of the last content object received - // indicates the base of the window on the client - uint64_t lastReceivedTime_; // time at which we recevied the - // lastReceived_ packet - - // sentinel - // if all packets in the window get lost we need something that - // wakes up our consumer socket. Interest timeouts set to 1 sec - // expire too late. This timers expire much sooner and if it - // detects that all the interest in the window may be lost - // it sends all of them again - std::unique_ptr<asio::steady_timer> sentinel_timer_; - uint64_t lastEvent_; // time at which we removed a pending - // interest from the window - std::unordered_map<uint32_t, uint8_t> packets_in_window_; - - // rtt probes - // the RTC transport tends to overestimate the RTT - // du to the production time on the server side - // once per second we send an interest for wich we know - // we will get a nack. This nack will keep our estimation - // close to the reality - std::unique_ptr<asio::steady_timer> probe_timer_; - uint64_t time_sent_probe_; - uint32_t probe_seq_number_; - bool received_probe_; - - uint32_t modMask_; - - // stats - bool firstPckReceived_; - uint32_t receivedBytes_; - uint32_t sentInterest_; - uint32_t receivedData_; - int32_t packetLost_; - int32_t lossRecovered_; - uint32_t firstSequenceInRound_; - uint32_t highestReceived_; - double avgPacketSize_; - bool gotNack_; - uint32_t gotFutureNack_; - uint32_t rounds_; - uint32_t roundsWithoutNacks_; - - // we keep track of up two paths (if only one path is in use - // the two values in the vector will be the same) - // position 0 stores the path with minRTT - // position 1 stores the path with maxRTT - uint32_t producerPathLabels_[2]; - - std::unordered_map<uint32_t, std::shared_ptr<RTCDataPath>> pathTable_; - uint32_t roundCounter_; - - // CC var - double estimatedBw_; - double lossRate_; - double queuingDelay_; - unsigned protocolState_; - - bool initied; -}; - -} // namespace protocol - -} // namespace transport diff --git a/libtransport/src/protocols/rtc/CMakeLists.txt b/libtransport/src/protocols/rtc/CMakeLists.txt new file mode 100644 index 000000000..be8e0189c --- /dev/null +++ b/libtransport/src/protocols/rtc/CMakeLists.txt @@ -0,0 +1,59 @@ +# 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 HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.h + ${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) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/rtc/probe_handler.cc b/libtransport/src/protocols/rtc/probe_handler.cc new file mode 100644 index 000000000..60eceeb19 --- /dev/null +++ b/libtransport/src/protocols/rtc/probe_handler.cc @@ -0,0 +1,141 @@ +/* + * 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/utils/chrono_typedefs.h> +#include <protocols/rtc/probe_handler.h> +#include <protocols/rtc/rtc_consts.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +ProbeHandler::ProbeHandler(SendProbeCallback &&send_callback, + asio::io_service &io_service) + : 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), + send_probe_callback_(std::move(send_callback)) {} + +ProbeHandler::~ProbeHandler() {} + +uint64_t ProbeHandler::getRtt(uint32_t seq, bool is_valid) { + auto it = pending_probes_.find(seq); + + if (it == pending_probes_.end()) return 0; + + if (!is_valid) { + // delete the probe anyway + pending_probes_.erase(it); + valid_batch_ = false; + return 0; + } + + 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() { + if (!valid_batch_) return 1.0; + return 1.0 - ((double)recv_probes_ / (double)sent_probes_); +} + +void ProbeHandler::setSuffixRange(uint32_t min, uint32_t max) { + DCHECK(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; + max_probes_ = max_probes; +} + +void ProbeHandler::stopProbes() { + probe_interval_ = 0; + max_probes_ = 0; + sent_probes_ = 0; + recv_probes_ = 0; + valid_batch_ = true; + probe_timer_->cancel(); +} + +void ProbeHandler::sendProbes() { + if (probe_interval_ == 0) return; + + std::weak_ptr<ProbeHandler> self(shared_from_this()); + probe_timer_->expires_from_now(std::chrono::microseconds(probe_interval_)); + probe_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + auto s = self.lock(); + if (s) { + s->generateProbe(); + } + }); +} + +void ProbeHandler::generateProbe() { + if (probe_interval_ == 0) return; + if (max_probes_ != 0 && sent_probes_ >= max_probes_) return; + + uint64_t now = utils::SteadyTime::nowMs().count(); + + uint32_t seq = distr_(rand_eng_); + pending_probes_.insert(std::pair<uint32_t, uint64_t>(seq, now)); + send_probe_callback_(seq); + sent_probes_++; + + // clean up + // a probe may get lost. if the pending_probes_ size becomes bigger than + // MAX_PENDING_PROBES remove all the probes older than a seconds + if (pending_probes_.size() > MAX_PENDING_PROBES) { + for (auto it = pending_probes_.begin(); it != pending_probes_.end();) { + if ((now - it->second) > 1000) + it = pending_probes_.erase(it); + else + it++; + } + } + + 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 + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/probe_handler.h b/libtransport/src/protocols/rtc/probe_handler.h new file mode 100644 index 000000000..d989194d4 --- /dev/null +++ b/libtransport/src/protocols/rtc/probe_handler.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/config.h> +#include <hicn/transport/core/asio_wrapper.h> + +#include <functional> +#include <random> +#include <unordered_map> + +namespace transport { + +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)>; + + public: + ProbeHandler(SendProbeCallback &&send_callback, asio::io_service &io_service); + + ~ProbeHandler(); + + // If the function returns 0 the probe is not valid. + uint64_t getRtt(uint32_t seq, bool is_valid); + + // 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); + + void stopProbes(); + + void sendProbes(); + + static ProbeType getProbeType(uint32_t seq); + + private: + void generateProbe(); + + uint32_t probe_interval_; // us + uint32_t max_probes_; // packets + uint32_t sent_probes_; // packets + uint32_t recv_probes_; // packets + + bool valid_batch_; // if at least one probe in a batch is considered not + // valid (e.g. prod rate == ~0) the full batch is invalid. + // the bool is set to true when sendProbe is called + + std::unique_ptr<asio::steady_timer> probe_timer_; + + // Map from packet suffixes to timestamp + std::unordered_map<uint32_t, uint64_t> pending_probes_; + + // Random generator + std::default_random_engine rand_eng_; + std::uniform_int_distribution<uint32_t> distr_; + + SendProbeCallback send_probe_callback_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc.cc b/libtransport/src/protocols/rtc/rtc.cc new file mode 100644 index 000000000..9a56269f3 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc.cc @@ -0,0 +1,1101 @@ +/* + * 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/core/global_object_pool.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <implementation/socket_consumer.h> +#include <math.h> +#include <protocols/errors.h> +#include <protocols/incremental_indexer_bytestream.h> +#include <protocols/rtc/rtc.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_indexer.h> +#include <protocols/rtc/rtc_rc_congestion_detection.h> + +#include <algorithm> + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace interface; + +RTCTransportProtocol::RTCTransportProtocol( + implementation::ConsumerSocket *icn_socket) + : TransportProtocol(icn_socket, new RtcIndexer<>(icn_socket, this), + new RtcReassembly(icn_socket, this)), + max_aggregated_interest_(1), + number_(0) { + icn_socket->getSocketOption(PORTAL, portal_); + round_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + scheduler_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + pacing_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); +} + +RTCTransportProtocol::~RTCTransportProtocol() {} + +void RTCTransportProtocol::resume() { + newRound(); + TransportProtocol::resume(); +} + +std::size_t RTCTransportProtocol::transportHeaderLength(bool isFEC) { + return DATA_HEADER_SIZE + + (fec_decoder_ != nullptr ? fec_decoder_->getFecHeaderSize(isFEC) : 0); +} + +// private +void RTCTransportProtocol::initParams() { + TransportProtocol::reset(); + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + + fwd_strategy_.setCallback([self](notification::Strategy strategy) { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (*ptr->on_fwd_strategy_) (*ptr->on_fwd_strategy_)(strategy); + } + }); + + std::shared_ptr<auth::Verifier> verifier; + socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier); + + uint32_t factor_relevant; + socket_->getSocketOption(GeneralTransportOptions::MANIFEST_FACTOR_RELEVANT, + factor_relevant); + + uint32_t factor_alert; + socket_->getSocketOption(GeneralTransportOptions::MANIFEST_FACTOR_ALERT, + factor_alert); + + rc_ = std::make_shared<RTCRateControlCongestionDetection>(); + ldr_ = std::make_shared<RTCLossDetectionAndRecovery>( + 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); + } + }, + [self](notification::Strategy strategy) { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (*ptr->on_rec_strategy_) (*ptr->on_rec_strategy_)(strategy); + } + }); + + verifier_ = + std::make_shared<RTCVerifier>(verifier, factor_relevant, factor_alert); + + state_ = std::make_shared<RTCState>( + indexer_verifier_.get(), + [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()); + + rc_->setState(state_); + rc_->turnOnRateControl(); + ldr_->setState(state_.get()); + ldr_->setRateControl(rc_.get()); + verifier_->setState(state_); + + // protocol state + start_send_interest_ = false; + current_state_ = SyncState::catch_up; + + // Cancel timer + number_++; + round_timer_->cancel(); + + scheduler_timer_->cancel(); + scheduler_timer_on_ = false; + last_interest_sent_time_ = 0; + last_interest_sent_seq_ = 0; + + // Aggregated interests setup + bool aggregated_interests_on; + socket_->getSocketOption(RtcTransportOptions::AGGREGATED_INTERESTS, + aggregated_interests_on); + if (aggregated_interests_on) { + if (const char *max_aggr = std::getenv("MAX_AGGREGATED_INTERESTS")) + max_aggregated_interest_ = (uint32_t)std::stoul(std::string(max_aggr)); + else + max_aggregated_interest_ = MAX_INTERESTS_IN_BATCH; + + max_aggregated_interest_ = std::min<uint32_t>(max_aggregated_interest_, + 1 + MAX_SUFFIXES_IN_MANIFEST); + } + LOG(INFO) << "Max Aggregated: " << max_aggregated_interest_; + + max_sent_int_ = + std::ceil((double)MAX_PACING_BATCH / (double)max_aggregated_interest_); + + pacing_timer_->cancel(); + pacing_timer_on_ = false; + + // delete all timeouts and future nacks + timeouts_or_nacks_.clear(); + + // cwin vars + current_sync_win_ = INITIAL_WIN; + max_sync_win_ = INITIAL_WIN_MAX; + + socket_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + RTC_INTEREST_LIFETIME); + + // init state params + state_->initParams(); +} + +// private +void RTCTransportProtocol::reset() { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "reset called"; + initParams(); + newRound(); +} + +void RTCTransportProtocol::inactiveProducer() { + // when the producer is inactive we reset the consumer state + // cwin vars + current_sync_win_ = INITIAL_WIN; + max_sync_win_ = INITIAL_WIN_MAX; + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Current window: " << current_sync_win_ + << ", max_sync_win_: " << max_sync_win_; + + // names/packets var + indexer_verifier_->reset(); + indexer_verifier_->enableFec(fec_type_); + indexer_verifier_->setNFec(0); + + ldr_->clear(); +} + +void RTCTransportProtocol::newRound() { + round_timer_->expires_from_now(std::chrono::milliseconds(ROUND_LEN)); + + 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() + // 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(); + + // update sync state if needed + double cache_rate = state->getPacketFromCacheRatio(); + uint32_t round_without_nacks = state->getRoundsWithoutNacks(); + + if (ptr->current_state_ == SyncState::in_sync) { + if (cache_rate > MAX_DATA_FROM_CACHE) { + ptr->current_state_ = SyncState::catch_up; + } + } else { + if (round_without_nacks >= ROUNDS_IN_SYNC_BEFORE_SWITCH && + cache_rate < MAX_DATA_FROM_CACHE) { + ptr->current_state_ = SyncState::in_sync; + } + } + + 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); + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Calling updateSyncWindow in newRound function"; + ptr->updateSyncWindow(); + + 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; + uint32_t strategy; + socket_->getSocketOption(RtcTransportOptions::RECOVERY_STRATEGY, strategy); + ldr_->changeRecoveryStrategy( + (interface::RtcTransportRecoveryStrategies)strategy); + + bool content_sharing_mode; + socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE, + content_sharing_mode); + if (content_sharing_mode) ldr_->setContentSharingMode(); + ldr_->turnOnRecovery(); + ldr_->onNewRound(false); + + // set forwarding strategy switch if selected + Name *name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + Prefix prefix(*name, 128); + fwd_strategy_.initFwdStrategy( + portal_, prefix, state_.get(), + (interface::RtcTransportRecoveryStrategies)strategy); + updateSyncWindow(); +} + +void RTCTransportProtocol::computeMaxSyncWindow() { + double production_rate = state_->getProducerRate(); + double packet_size = state_->getAveragePacketSize(); + if (production_rate == 0.0 || packet_size == 0.0) { + // the consumer has no info about the producer, + // keep the previous maxCWin + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Returning in computeMaxSyncWindow because: prod_rate: " + << (production_rate == 0.0) + << " || packet_size: " << (packet_size == 0.0); + return; + } + + bool content_sharing_mode; + socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE, + content_sharing_mode); + if (content_sharing_mode && (production_rate < MIN_PROD_RATE_SHARING_MODE)) + production_rate = MIN_PROD_RATE_SHARING_MODE; + + production_rate += (production_rate * indexer_verifier_->getMaxFecOverhead()); + + uint32_t lifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + lifetime); + double lifetime_ms = (double)lifetime / MILLI_IN_A_SEC; + + max_sync_win_ = (uint32_t)ceil( + (production_rate * lifetime_ms * INTEREST_LIFETIME_REDUCTION_FACTOR) / + packet_size); + + max_sync_win_ = std::min(max_sync_win_, rc_->getCongestionWindow()); +} + +void RTCTransportProtocol::updateSyncWindow() { + computeMaxSyncWindow(); + + if (max_sync_win_ == INITIAL_WIN_MAX) { + if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) return; + + current_sync_win_ = INITIAL_WIN; + scheduleNextInterests(); + return; + } + + double prod_rate = state_->getProducerRate(); + double rtt = (double)state_->getMinRTT() / MILLI_IN_A_SEC; + double packet_size = state_->getAveragePacketSize(); + bool content_sharing_mode; + socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE, + content_sharing_mode); + if (content_sharing_mode && (prod_rate < MIN_PROD_RATE_SHARING_MODE)) + prod_rate = MIN_PROD_RATE_SHARING_MODE; + + // 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) { + current_sync_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size); + uint32_t buffer = PRODUCER_BUFFER_MS + ((double)state_->getMinRTT() / 2.0); + + current_sync_win_ += + ceil(prod_rate * (buffer / MILLI_IN_A_SEC) / packet_size); + + if (current_state_ == SyncState::catch_up) { + 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_, min_win); + } + + scheduleNextInterests(); +} + +void RTCTransportProtocol::sendRtxInterest(uint32_t seq) { + if (!isRunning() && !is_first_) return; + + if (!start_send_interest_) return; + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "send rtx " << seq; + interest_name->setSuffix(seq); + sendInterest(*interest_name); +} + +void RTCTransportProtocol::sendProbeInterest(uint32_t seq) { + if (!isRunning() && !is_first_) return; + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send probe " << seq; + interest_name->setSuffix(seq); + sendInterest(*interest_name); +} + +void RTCTransportProtocol::sendInterestForTimeout(uint32_t seq) { + if (!isRunning() && !is_first_) return; + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + // we got a timeout for this packet so it is not pending anymore + interest_name->setSuffix(seq); + state_->onSendNewInterest(interest_name); + sendInterest(*interest_name); +} + +void RTCTransportProtocol::scheduleNextInterests() { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Schedule next interests"; + + if (!isRunning() && !is_first_) { + return; + } + + if (pacing_timer_on_) { + return; // wait pacing timer for the next send + } + + 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."; + // here we keep seding the same interest until the producer + // does not start again + if (indexer_verifier_->checkNextSuffix() != 0) { + // the producer just become inactive, reset the state + inactiveProducer(); + } + + Name *interest_name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + &interest_name); + + uint32_t next_seg = 0; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send interest " << next_seg; + interest_name->setSuffix(next_seg); + + if (portal_->interestIsPending(*interest_name)) { + // if interest 0 is already pending we return + return; + } + + sendInterest(*interest_name); + state_->onSendNewInterest(interest_name); + return; + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Pending interest number: " << state_->getPendingInterestNumber() + << " -- current_sync_win_: " << current_sync_win_; + + uint32_t pending = state_->getPendingInterestNumber(); + uint32_t pending_fec = state_->getPendingFecPackets(); + + if ((pending - pending_fec) >= current_sync_win_) + return; // no space in the window + + // 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 = 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)); + + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + scheduler_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->scheduler_timer_on_) return; + ptr->scheduler_timer_on_ = false; + ptr->scheduleNextInterests(); + } + }); + return; // wait for the timer + } + } + + scheduler_timer_on_ = false; + scheduler_timer_->cancel(); + + // skip nacked pacekts + if (indexer_verifier_->checkNextSuffix() <= state_->getLastSeqNacked()) { + indexer_verifier_->jumpToIndex(state_->getLastSeqNacked() + 1); + } + + // skip received packets + uint32_t max_received = state_->getHighestSeqReceivedInOrder(); + if (indexer_verifier_->checkNextSuffix() <= max_received) { + indexer_verifier_->jumpToIndex(max_received + 1); + } + + uint32_t sent_interests = 0; + uint32_t sent_packets = 0; + uint32_t aggregated_counter = 0; + Name *name = nullptr; + Name interest_name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + std::array<uint32_t, MAX_AGGREGATED_INTEREST> additional_suffixes; + + 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_; + + uint32_t next_seg = indexer_verifier_->getNextSuffix(); + name->setSuffix(next_seg); + + // send the packet only if: + // 1) it is not pending yet (not true for rtx) + // 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) || + 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 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; + } + + if (aggregated_counter == 0) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "(name) send interest " << next_seg; + interest_name = *name; + } else { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "(append) send interest " << next_seg; + additional_suffixes[aggregated_counter - 1] = next_seg; + } + + last_interest_sent_seq_ = next_seg; + state_->onSendNewInterest(name); + aggregated_counter++; + + if (aggregated_counter >= max_aggregated_interest_) { + sent_packets++; + sent_interests++; + sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1); + last_interest_sent_time_ = utils::SteadyTime::nowMs().count(); + aggregated_counter = 0; + } + } + + // exiting the while we may have some pending interest to send + if (aggregated_counter != 0) { + sent_packets++; + last_interest_sent_time_ = utils::SteadyTime::nowMs().count(); + sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1); + } + + 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)); + + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + scheduler_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->pacing_timer_on_) return; + + ptr->pacing_timer_on_ = false; + ptr->scheduleNextInterests(); + } + }); + } +} + +void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, + const Name &name) { + uint32_t segment_number = name.getSuffix(); + + if (ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE) { + // this is a timeout on a probe, do nothing + return; + } + + 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; + } + + timeouts_or_nacks_.insert(segment_number); + if (TRANSPORT_EXPECT_TRUE(state_->isProducerActive()) && + segment_number <= state_->getHighestSeqReceived()) { + // we retransmit packets only if the producer is active, otherwise we + // use timeouts to avoid to send too much traffic + // + // a timeout is sent using RTX only if it is an old packet. if it is for a + // seq number that we didn't reach yet, we send the packet using the normal + // schedule next interest + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "handle timeout for packet " << segment_number << " using rtx"; + if (ldr_->isRtxOn()) { + 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 { + 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(); + return; + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "handle timeout for packet " << segment_number + << " using normal interests"; + + if (segment_number < indexer_verifier_->checkNextSuffix()) { + // this is a timeout for a packet that will be generated in the future but + // we are asking for higher sequence numbers. we need to go back like in the + // case of future nacks + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "On timeout next seg = " << indexer_verifier_->checkNextSuffix() + << ", jump to " << segment_number; + // add an extra space in the window + indexer_verifier_->jumpToIndex(segment_number); + } + + state_->onTimeout(segment_number, false); + sendInterestForTimeout(segment_number); + scheduleNextInterests(); +} + +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->getProductionSegment(); + uint32_t nack_segment = content_object.getName().getSuffix(); + bool is_rtx = ldr_->isRtx(nack_segment); + + // check if the packet got a timeout + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Nack received " << nack_segment + << ". Production segment: " << production_seg; + + bool compute_stats = true; + auto tn_it = timeouts_or_nacks_.find(nack_segment); + if (tn_it != timeouts_or_nacks_.end() || is_rtx) { + compute_stats = false; + // remove packets from timeouts_or_nacks only in case of a past nack + } + + state_->onNackPacketReceived(content_object, compute_stats); + ldr_->onNackPacketReceived(content_object); + + // both in case of past and future nack we jump to the + // production segment in the nack. In case of past nack we will skip unneded + // interest (this is already done in the scheduleNextInterest in any case) + // while in case of future nacks we can go back in time and ask again for the + // content that generated the nack + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "On nack next seg = " << indexer_verifier_->checkNextSuffix() + << ", jump to " << production_seg; + indexer_verifier_->jumpToIndex(production_seg); + + if (production_seg > nack_segment) { + // remove the nack is it exists + if (tn_it != timeouts_or_nacks_.end()) timeouts_or_nacks_.erase(tn_it); + + state_->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 + if (!is_rtx) current_state_ = SyncState::catch_up; + } else { + // if production_seg == nack_segment we consider this a future nack, since + // production_seg is not yet created. this may happen in case of low + // production rate (e.g. ping at 1pps) + + // if a future nack was also retransmitted add it to the timeout_or_nacks + // set + if (is_rtx) timeouts_or_nacks_.insert(nack_segment); + + // the client is asking for content in the future + // switch to in sync state and decrease the window + current_state_ = SyncState::in_sync; + } + updateSyncWindow(); +} + +void RTCTransportProtocol::onProbe(const ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + ParamsRTC params = RTCState::getProbeParams(content_object); + + if (ProbeHandler::getProbeType(suffix) == ProbeType::INIT) { + fec::FECType fec_type = params.fec_type; + + if (fec_type != fec::FECType::UNKNOWN && !fec_decoder_) { + // Update FEC type + fec_type_ = fec_type; + + // Enable FEC + enableFEC(std::bind(&RTCTransportProtocol::onFecPackets, this, + std::placeholders::_1), + fec::FECBase::BufferRequested(0)); + + // Update FEC parameters + indexer_verifier_->enableFec(fec_type); + indexer_verifier_->setNFec(0); + ldr_->setFecParams(fec::FECUtils::getBlockSymbols(fec_type), + fec::FECUtils::getSourceSymbols(fec_type)); + fec_decoder_->setIOService(portal_->getThread().getIoService()); + } else if (fec_type == fec::FECType::UNKNOWN) { + indexer_verifier_->disableFec(); + } + } + + if (!state_->onProbePacketReceived(content_object)) return; + + // As for NACKs, set next_segment + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "on probe next seg = " << indexer_verifier_->checkNextSuffix() + << ", jump to " << params.prod_seg; + indexer_verifier_->jumpToIndex(params.prod_seg); + + bool loss_detected = ldr_->onProbePacketReceived(content_object); + // we are not out of sync here but we are starting to download content from + // the cache, maybe beacuse the production rate increased suddenly. for this + // reason we put the state to catch up to increase the window + if (loss_detected) current_state_ = SyncState::catch_up; + updateSyncWindow(); +} + +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 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); + + // 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); + } + onProbe(content_object); + return; + } + + 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); + } + onNack(content_object); + 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; + + // 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); + } + + // Do not count retransmissions or losses in stats + if (ldr_->isRtx(segment_number) || + ldr_->isPossibleLossWithNoRtx(segment_number)) { + compute_stats = false; + } + + // Fetch packet state + state = state_->getPacketState(segment_number); + + // Check if the packet is a retransmission + if (ldr_->isRtx(segment_number) && state != PacketState::RECEIVED) { + if (is_data || is_manifest) { + uint64_t rtt = ldr_->getRtxRtt(segment_number); + state_->onPacketRecoveredRtx(content_object, rtt); + + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } + + if (is_manifest) { + processManifest(interest, *content_ptr); + } + + 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); + // this is a rtx but we may need to feed it in the decoder + decodePacket(content_object, is_manifest); + return; + } + + if (is_fec) { + state_->onFecPacketRecoveredRtx(content_object); + } + } + + // 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; + } + + if (!is_fec) { + state_->dataToBeReceived(segment_number); + } + + // send packet to the decoder + decodePacket(content_object, is_manifest); + + // 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( + uint32_t retx_count, uint32_t received_bytes, uint32_t sent_interests, + uint32_t lost_data, uint32_t definitely_lost, uint32_t recovered_losses, + uint32_t received_nacks, uint32_t received_fec) { + if (*stats_summary_) { + // Send the stats to the app + stats_->updateQueuingDelay(state_->getQueuing()); + + // stats_->updateInterestFecTx(0); //todo must be implemented + // stats_->updateBytesFecRecv(0); //todo must be implemented + + stats_->updateRetxCount(retx_count); + stats_->updateBytesRecv(received_bytes); + stats_->updateInterestTx(sent_interests); + stats_->updateReceivedNacks(received_nacks); + stats_->updateReceivedFEC(received_fec); + + stats_->updateAverageWindowSize(state_->getPendingInterestNumber()); + stats_->updateLossRatio(state_->getPerSecondLossRate()); + uint64_t rtt = state_->getAvgRTT(); + stats_->updateAverageRtt(utils::SteadyTime::Microseconds(rtt * 1000)); + + 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::decodePacket(ContentObject &content_object, + bool is_manifest) { + if (!fec_decoder_) return; + + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Send packet " << content_object.getName() << " to FEC decoder"; + + uint32_t offset = + is_manifest + ? (uint32_t)content_object.headerSize() + : (uint32_t)(content_object.headerSize() + rtc::DATA_HEADER_SIZE); + uint32_t metadata = static_cast<uint32_t>(content_object.getPayloadType()); + + fec_decoder_->onDataPacket(content_object, offset, metadata); +} + +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) { + 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, (uint32_t)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(false)) { + return nullptr; + } + + size_t fec_header_size = fec_decoder_->getFecHeaderSize(false); + 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 + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc.h b/libtransport/src/protocols/rtc/rtc.h new file mode 100644 index 000000000..a8a474216 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc.h @@ -0,0 +1,151 @@ +/* + * 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_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> +#include <vector> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCTransportProtocol : public TransportProtocol { + public: + RTCTransportProtocol(implementation::ConsumerSocket *icnet_socket); + + ~RTCTransportProtocol(); + + using TransportProtocol::start; + + using TransportProtocol::stop; + + void resume() override; + + std::size_t transportHeaderLength(bool isFEC) override; + + auto shared_from_this() { return utils::shared_from(this); } + + private: + enum class SyncState { catch_up = 0, in_sync = 1, last }; + + private: + // setup functions + void initParams(); + void reset() override; + + void inactiveProducer(); + + // protocol functions + void discoveredRtt(); + void newRound(); + + // window functions + void computeMaxSyncWindow(); + void updateSyncWindow(); + + // packet functions + void sendRtxInterest(uint32_t seq); + void sendProbeInterest(uint32_t seq); + void sendInterestForTimeout(uint32_t seq); + void scheduleNextInterests() override; + void onInterestTimeout(Interest::Ptr &interest, const Name &name) override; + void onNack(const ContentObject &content_object); + void onProbe(const ContentObject &content_object); + void onContentObjectReceived(Interest &interest, + ContentObject &content_object, + std::error_code &ec) override; + 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, + uint32_t sent_interests, uint32_t lost_data, + uint32_t definitely_lost, uint32_t recovered_losses, + uint32_t received_nacks, uint32_t received_fec); + + // FEC functions + // send the received content object to the decoder + void decodePacket(ContentObject &content_object, bool is_manifest); + 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_; + + // round timer + std::unique_ptr<asio::steady_timer> round_timer_; + + // scheduler timer (postpone interest sending to explot aggregated interests) + std::unique_ptr<asio::steady_timer> scheduler_timer_; + bool scheduler_timer_on_; + uint64_t last_interest_sent_time_; + uint64_t last_interest_sent_seq_; + + // maximum aggregated interest. if the transport is connected to the forwarder + // we cannot use aggregated interests + uint32_t max_aggregated_interest_; + // maximum number of intereset that can be sent in a loop to avoid packets + // dropped by the kernel + uint32_t max_sent_int_; + + // pacing timer (do not send too many interests in a short time to avoid + // packet drops in the kernel) + std::unique_ptr<asio::steady_timer> pacing_timer_; + bool pacing_timer_on_; + + // timeouts + std::unordered_set<uint32_t> timeouts_or_nacks_; + + 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_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_consts.h b/libtransport/src/protocols/rtc/rtc_consts.h new file mode 100644 index 000000000..29b5a3a12 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_consts.h @@ -0,0 +1,214 @@ +/* + * 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_packet.h> +#include <stdint.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +// used in rtc +// protocol consts +const uint32_t ROUND_LEN = 200; +// ms interval of time on which +// we take decisions / measurements +const double INTEREST_LIFETIME_REDUCTION_FACTOR = 0.8; +// how big (in ms) should be the buffer at the producer. +// increasing this number we increase the time that an +// interest will wait for the data packet to be produced +// at the producer socket +const uint32_t PRODUCER_BUFFER_MS = 300; // ms + +// interest scheduler +// const uint32_t MAX_INTERESTS_IN_BATCH = 5; +// const uint32_t WAIT_BETWEEN_INTEREST_BATCHES = 1000; // usec +const uint32_t MAX_INTERESTS_IN_BATCH = 5; // number of seq numbers per + // aggregated interest packet + // considering the name itself +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 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 RTC_INTEREST_LIFETIME = 4000; + +// probes sequence range +const uint32_t MIN_PROBE_SEQ = 0xefffffff; +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 +// quickily estimate the RTT +const uint32_t RTT_PROBE_INTERVAL = 200000; // us +const uint32_t INIT_RTT_PROBE_INTERVAL = 500; // us +const uint32_t INIT_RTT_PROBES = 40; // number of probes to init RTT +// if the produdcer is not yet started we need to probe multple times +// to get an answer. we wait 100ms between each try +const uint32_t INIT_RTT_PROBE_RESTART = 100; // ms +// once we get the first probe we wait at most 60ms for the others +const uint32_t INIT_RTT_PROBE_WAIT = + ((INIT_RTT_PROBES * INIT_RTT_PROBE_INTERVAL) / 1000) * 2; // ms +// we reuires at least 5 probes to be recevied +const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; // ms +const uint32_t MAX_PENDING_PROBES = 10; + +// congestion +const double MAX_QUEUING_DELAY = 50.0; // ms + +// data from cache +const double MAX_DATA_FROM_CACHE = 0.10; // 10% + +// 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 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; +const double WIN_INCREASE_FACTOR = 1.5; +const uint32_t MIN_PROD_RATE_SHARING_MODE = 125000; // 1Mbps in bytes + +// round in congestion +const double ROUNDS_BEFORE_TAKE_ACTION = 5; + +// used in state +const uint8_t ROUNDS_IN_SYNC_BEFORE_SWITCH = 3; +const double PRODUCTION_RATE_FRACTION = 0.8; + +const uint32_t INIT_PACKET_SIZE = 1200; + +const double MOVING_AVG_ALPHA = 0.8; + +const double MILLI_IN_A_SEC = 1000.0; +const double MICRO_IN_A_SEC = 1000000.0; +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; + +// used in ldr +const uint32_t RTC_MAX_RTX = 100; +const uint32_t RTC_MAX_AGE = 60000; // in ms +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 = 1.0; // % +const uint32_t WAIT_BEFORE_FEC_UPDATE = ROUNDS_PER_SEC; +const uint32_t MAX_RTT_BEFORE_FEC = 60; // ms + +// used by producer +const uint32_t PRODUCER_STATS_INTERVAL = 200; // ms +const uint32_t MIN_PRODUCTION_RATE = 25; // pps, equal to min window * + // rounds in a second +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; // % + +const uint8_t FEC_MATRIX[64][10] = { + {1, 2, 2, 2, 3, 3, 4, 5, 5, 6}, // k = 1 + {1, 2, 3, 3, 4, 5, 5, 6, 7, 9}, + {2, 2, 3, 4, 5, 6, 7, 8, 9, 11}, + {2, 3, 4, 5, 5, 7, 8, 9, 11, 13}, + {2, 3, 4, 5, 6, 7, 9, 10, 12, 14}, // k = 5 + {2, 3, 4, 6, 7, 8, 10, 12, 14, 16}, + {2, 4, 5, 6, 8, 9, 11, 13, 15, 18}, + {3, 4, 5, 7, 8, 10, 12, 14, 16, 19}, + {3, 4, 6, 7, 9, 11, 13, 15, 18, 21}, + {3, 4, 6, 8, 9, 11, 14, 16, 19, 23}, // k = 10 + {3, 5, 6, 8, 10, 12, 14, 17, 20, 24}, + {3, 5, 7, 8, 10, 13, 15, 18, 21, 26}, + {3, 5, 7, 9, 11, 13, 16, 19, 23, 27}, + {3, 5, 7, 9, 12, 14, 17, 20, 24, 28}, + {4, 6, 8, 10, 12, 15, 18, 21, 25, 30}, // k = 15 + {4, 6, 8, 10, 13, 15, 19, 22, 26, 31}, + {4, 6, 8, 11, 13, 16, 19, 23, 27, 33}, + {4, 6, 9, 11, 14, 17, 20, 24, 29, 34}, + {4, 6, 9, 11, 14, 17, 21, 25, 30, 35}, + {4, 7, 9, 12, 15, 18, 22, 26, 31, 37}, // k = 20 + {4, 7, 9, 12, 15, 19, 22, 27, 32, 38}, + {4, 7, 10, 13, 16, 19, 23, 28, 33, 40}, + {5, 7, 10, 13, 16, 20, 24, 29, 34, 41}, + {5, 7, 10, 13, 17, 20, 25, 30, 35, 42}, + {5, 8, 11, 14, 17, 21, 26, 31, 37, 44}, // k = 25 + {5, 8, 11, 14, 18, 22, 26, 31, 38, 45}, + {5, 8, 11, 15, 18, 22, 27, 32, 39, 46}, + {5, 8, 11, 15, 19, 23, 28, 33, 40, 48}, + {5, 8, 12, 15, 19, 24, 28, 34, 41, 49}, + {5, 9, 12, 16, 20, 24, 29, 35, 42, 50}, // k = 30 + {5, 9, 12, 16, 20, 25, 30, 36, 43, 51}, + {5, 9, 13, 16, 21, 25, 31, 37, 44, 53}, + {6, 9, 13, 17, 21, 26, 31, 38, 45, 54}, + {6, 9, 13, 17, 22, 26, 32, 39, 46, 55}, + {6, 10, 13, 17, 22, 27, 33, 40, 47, 57}, // k = 35 + {6, 10, 14, 18, 22, 28, 34, 40, 48, 58}, + {6, 10, 14, 18, 23, 28, 34, 41, 49, 59}, + {6, 10, 14, 19, 23, 29, 35, 42, 50, 60}, + {6, 10, 14, 19, 24, 29, 36, 43, 52, 62}, + {6, 10, 15, 19, 24, 30, 36, 44, 53, 63}, // k = 40 + {6, 11, 15, 20, 25, 31, 37, 45, 54, 64}, + {6, 11, 15, 20, 25, 31, 38, 46, 55, 65}, + {7, 11, 15, 20, 26, 32, 39, 46, 56, 67}, + {7, 11, 16, 21, 26, 32, 39, 47, 57, 68}, + {7, 11, 16, 21, 27, 33, 40, 48, 58, 69}, // k = 45 + {7, 11, 16, 21, 27, 33, 41, 49, 59, 70}, + {7, 12, 16, 22, 27, 34, 41, 50, 60, 72}, + {7, 12, 17, 22, 28, 34, 42, 51, 61, 73}, + {7, 12, 17, 22, 28, 35, 43, 52, 62, 74}, + {7, 12, 17, 23, 29, 36, 43, 52, 63, 75}, // k = 50 + {7, 12, 17, 23, 29, 36, 44, 53, 64, 77}, + {7, 12, 18, 23, 30, 37, 45, 54, 65, 78}, + {7, 13, 18, 24, 30, 37, 45, 55, 66, 79}, + {8, 13, 18, 24, 31, 38, 46, 56, 67, 80}, + {8, 13, 18, 24, 31, 38, 47, 57, 68, 82}, // k = 55 + {8, 13, 19, 25, 31, 39, 47, 57, 69, 83}, + {8, 13, 19, 25, 32, 39, 48, 58, 70, 84}, + {8, 13, 19, 25, 32, 40, 49, 59, 71, 85}, + {8, 14, 19, 26, 33, 41, 50, 60, 72, 86}, + {8, 14, 20, 26, 33, 41, 50, 61, 73, 88}, // k = 60 + {8, 14, 20, 26, 34, 42, 51, 61, 74, 89}, + {8, 14, 20, 27, 34, 42, 52, 62, 75, 90}, + {8, 14, 20, 27, 34, 43, 52, 63, 76, 91}, + {8, 14, 21, 27, 35, 43, 53, 64, 77, 92}, // k = 64 +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_data_path.cc b/libtransport/src/protocols/rtc/rtc_data_path.cc new file mode 100644 index 000000000..a421396b1 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_data_path.cc @@ -0,0 +1,251 @@ +/* + * 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/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 { + +namespace protocol { + +namespace rtc { + +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 + // value is is the queueing delay. for this reason we + // keep both RTT (for the windowd calculation) and OWD + // (for congestion/quality control) + prev_min_owd(INT_MAX), + avg_owd(DBL_MAX), + queuing_delay(DBL_MAX), + jitter_(0.0), + last_owd_(0), + 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_(0), + rounds_without_packets_(0), + last_received_data_packet_(0), + min_RTT_history_(HISTORY_LEN), + max_RTT_history_(HISTORY_LEN), + OWD_history_(HISTORY_LEN){}; + +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; + + 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; + } + + received_packets_++; +} + +void RTCDataPath::insertOwdSample(int64_t owd) { + // for owd we use both min and avg + if (owd < min_owd) min_owd = owd; + + if (avg_owd != DBL_MAX) + avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC); + else { + avg_owd = owd; + } + + int64_t queueVal = owd - std::min(getMinOwd(), min_owd); + + if (queuing_delay != DBL_MAX) + queuing_delay = (queuing_delay * (1 - ALPHA_RTC)) + (queueVal * ALPHA_RTC); + else { + queuing_delay = queueVal; + } + + // keep track of the jitter computed as for RTP (RFC 3550) + int64_t diff = std::abs(owd - last_owd_); + last_owd_ = owd; + jitter_ += (1.0 / 16.0) * ((double)diff - jitter_); +} + +void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) { + // got packet in sequence, compute gap + if (largest_recv_seq_ == (segment_number - 1)) { + 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; + if (avg_inter_arrival_ == DBL_MAX) + avg_inter_arrival_ = delta; + else + avg_inter_arrival_ = + (avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC); + return; + } + + // ooo packet, update the stasts if needed + if (largest_recv_seq_ <= segment_number) { + largest_recv_seq_ = segment_number; + largest_recv_seq_time_ = utils::SteadyTime::nowMs().count(); + } +} + +void RTCDataPath::receivedNack() { received_nacks_ = true; } + +double RTCDataPath::getInterArrivalGap() { + if (avg_inter_arrival_ == DBL_MAX) return 0; + return avg_inter_arrival_; +} + +bool RTCDataPath::isValidProducer() { + if (received_nacks_ && rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS) + return true; + return false; +} + +bool RTCDataPath::isActive() { + if (rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS) return true; + return false; +} + +bool RTCDataPath::pathToProducer() { + if (received_nacks_) return true; + return false; +} + +void RTCDataPath::roundEnd() { + // reset min_rtt and add it to the history + if (min_rtt != UINT_MAX) { + prev_min_rtt = min_rtt; + } else { + // this may happen if we do not receive any packet + // from this path in the last round. in this case + // we use the measure from the previuos round + 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; + + 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) { + prev_min_owd = min_owd; + } else { + min_owd = prev_min_owd; + } + + if (min_owd != INT_MAX) { + OWD_history_.pushBack(min_owd); + min_owd = INT_MAX; + } + + if (received_packets_ == 0) + rounds_without_packets_++; + else + rounds_without_packets_ = 0; + received_packets_ = 0; +} + +uint32_t RTCDataPath::getPathId() { return path_id_; } + +double RTCDataPath::getQueuingDealy() { + if (queuing_delay == DBL_MAX) return 0; + return queuing_delay; +} + +uint64_t RTCDataPath::getMinRtt() { + 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 INT_MAX; +} + +double RTCDataPath::getJitter() { return jitter_; } + +uint64_t RTCDataPath::getLastPacketTS() { return last_received_data_packet_; } + +uint32_t RTCDataPath::getPacketsLastRound() { return received_packets_; } + +void RTCDataPath::clearRtt() { + min_RTT_history_.clear(); + max_RTT_history_.clear(); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_data_path.h b/libtransport/src/protocols/rtc/rtc_data_path.h new file mode 100644 index 000000000..ba5201fe8 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_data_path.h @@ -0,0 +1,114 @@ +/* + * 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/chrono_typedefs.h> +#include <stdint.h> +#include <utils/max_filter.h> +#include <utils/min_filter.h> + +#include <climits> + +namespace transport { + +namespace protocol { + +namespace rtc { + +const double ALPHA_RTC = 0.125; +const uint32_t HISTORY_LEN = 20; // 4 sec + +class RTCDataPath { + public: + RTCDataPath(uint32_t path_id); + + public: + 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(); // pakets recevied from this path in the last rounds + bool pathToProducer(); // path from a producer + bool isValidProducer(); // path from a producer that is also active + uint64_t getLastPacketTS(); + uint32_t getPacketsLastRound(); + + void clearRtt(); + + void roundEnd(); + + private: + uint32_t path_id_; + + int64_t getMinOwd(); + + 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; + + double avg_owd; + + double queuing_delay; + + double jitter_; + int64_t last_owd_; + + uint32_t largest_recv_seq_; + 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_; + 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> min_RTT_history_; + utils::MaxFilter<uint64_t> max_RTT_history_; + utils::MinFilter<int64_t> OWD_history_; +}; + +} // namespace rtc + +} // namespace protocol + +} // end namespace transport 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..4bbd7eac0 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc @@ -0,0 +1,217 @@ +/* + * 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_consts.h> +#include <protocols/rtc/rtc_forwarding_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace transport::interface; + +const double FWD_MAX_QUEUE = 30.0; // ms +const double FWD_MAX_RTT = MAX_RTT_BEFORE_FEC; // ms +const double FWD_MAX_LOSS_RATE = 0.1; + +RTCForwardingStrategy::RTCForwardingStrategy() + : low_rate_app_(false), + 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_ = std::move(callback); +} + +void RTCForwardingStrategy::initFwdStrategy( + std::shared_ptr<core::Portal> portal, core::Prefix& prefix, RTCState* state, + interface::RtcTransportRecoveryStrategies strategy) { + switch (strategy) { + case interface::RtcTransportRecoveryStrategies::LOW_RATE_AND_BESTPATH: + init_ = true; + low_rate_app_ = true; + selected_strategy_ = BEST_PATH; + current_strategy_ = BEST_PATH; + break; + case interface::RtcTransportRecoveryStrategies::LOW_RATE_AND_REPLICATION: + init_ = true; + low_rate_app_ = true; + selected_strategy_ = REPLICATION; + current_strategy_ = REPLICATION; + break; + case interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES: + init_ = true; + low_rate_app_ = true; + selected_strategy_ = BEST_PATH; + current_strategy_ = BEST_PATH; + break; + case interface::RtcTransportRecoveryStrategies::DELAY_AND_BESTPATH: + init_ = true; + low_rate_app_ = false; + selected_strategy_ = BEST_PATH; + current_strategy_ = BEST_PATH; + break; + case interface::RtcTransportRecoveryStrategies::DELAY_AND_REPLICATION: + init_ = true; + low_rate_app_ = false; + selected_strategy_ = REPLICATION; + current_strategy_ = REPLICATION; + break; + case interface::RtcTransportRecoveryStrategies::RECOVERY_OFF: + case interface::RtcTransportRecoveryStrategies::RTX_ONLY: + case interface::RtcTransportRecoveryStrategies::FEC_ONLY: + case interface::RtcTransportRecoveryStrategies::DELAY_BASED: + case interface::RtcTransportRecoveryStrategies::LOW_RATE: + case interface::RtcTransportRecoveryStrategies::FEC_ONLY_LOW_RES_LOSSES: + default: + // fwd strategies are not used + init_ = false; + } + + if (init_) { + rounds_since_last_set_ = 0; + prefix_ = prefix; + portal_ = portal; + state_ = state; + } +} + +void RTCForwardingStrategy::checkStrategy() { + 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; + } + + if (low_rate_app_) { + // this is used for gaming + 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); + } else { + if (rounds_since_last_set_ < 25) { // wait a least 5 sec + // between each switch + rounds_since_last_set_++; + return; + } + + double queue = state_->getQueuing(); + double rtt = state_->getAvgRTT(); + double loss_rate = state_->getPerSecondLossRate(); + + if (queue >= FWD_MAX_QUEUE || rtt >= FWD_MAX_RTT || + loss_rate > FWD_MAX_LOSS_RATE) { + // 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. + // for 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..c2227e09f --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h @@ -0,0 +1,82 @@ +/* + * 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, + interface::RtcTransportRecoveryStrategies 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 low_rate_app_; // if set to true the best path strategy will + // trigger a path switch based on the quality + // score, otherwise it will use the RTT, + // queuing delay and loss rate + 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 new file mode 100644 index 000000000..f87fcaaa2 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_indexer.h @@ -0,0 +1,174 @@ +/* + * 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/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> + +namespace transport { + +namespace interface { +class ConsumerSocket; +} + +namespace protocol { + +namespace rtc { + +template <uint32_t LIMIT = MIN_PROBE_SEQ> +class RtcIndexer : public Indexer { + public: + RtcIndexer(implementation::ConsumerSocket *icn_socket, + TransportProtocol *transport) + : Indexer(icn_socket, transport), + first_suffix_(1), + next_suffix_(first_suffix_), + fec_type_(fec::FECType::UNKNOWN), + n_fec_(0), + n_current_fec_(n_fec_) {} + + RtcIndexer(RtcIndexer &&other) : Indexer(other) {} + + ~RtcIndexer() {} + + void reset() override { + next_suffix_ = first_suffix_; + n_fec_ = 0; + } + + uint32_t checkNextSuffix() const override { return next_suffix_; } + + uint32_t getNextSuffix() override { + if (isFec(next_suffix_)) { + if (n_current_fec_) { + auto ret = next_suffix_++; + n_current_fec_--; + return ret; + } else { + n_current_fec_ = n_fec_; + next_suffix_ = nextSource(next_suffix_); + } + } else if (!n_current_fec_) { + n_current_fec_ = n_fec_; + } + + return (next_suffix_++ % LIMIT); + } + + void setFirstSuffix(uint32_t suffix) override { + first_suffix_ = suffix % LIMIT; + } + + uint32_t getFirstSuffix() const override { return first_suffix_; } + + uint32_t jumpToIndex(uint32_t index) override { + next_suffix_ = index % LIMIT; + return next_suffix_; + } + + void onContentObject(core::Interest &interest, + core::ContentObject &content_object, + bool reassembly) override { + if (reassembly) { + reassembly_->reassemble(content_object); + } + } + + /** + * Retrieve the next segment to be reassembled. + */ + uint32_t getNextReassemblySegment() override { + throw errors::RuntimeException( + "Get reassembly segment called on rtc indexer. RTC indexer does not " + "provide reassembly."); + } + + bool isFinalSuffixDiscovered() override { return true; } + + uint32_t getFinalSuffix() const override { return LIMIT; } + + void enableFec(fec::FECType fec_type) override { fec_type_ = fec_type; } + + void disableFec() override { fec_type_ = fec::FECType::UNKNOWN; } + + void setNFec(uint32_t n_fec) override { + n_fec_ = n_fec; + n_current_fec_ = 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() const override { + if (fec_type_ == fec::FECType::UNKNOWN) { + return 0; + } + + double k = (double)fec::FECUtils::getSourceSymbols(fec_type_); + return (double)n_fec_ / k; + } + + double getMaxFecOverhead() const override { + if (fec_type_ == fec::FECType::UNKNOWN) { + return 0; + } + + double k = (double)fec::FECUtils::getSourceSymbols(fec_type_); + double n = (double)fec::FECUtils::getBlockSymbols(fec_type_); + return (double)(n - k) / k; + } + + static bool isFec(fec::FECType fec_type, uint32_t index, + uint32_t first_suffix) { + if (index < LIMIT) { + return fec::FECUtils::isFec(fec_type, index, first_suffix); + } + + return false; + } + + static uint32_t nextSource(fec::FECType fec_type, uint32_t index, + uint32_t first_suffix) { + return fec::FECUtils::nextSource(fec_type, index, first_suffix) % LIMIT; + } + + private: + uint32_t nextSource(uint32_t index) { + return nextSource(fec_type_, index, first_suffix_); + } + + private: + uint32_t first_suffix_; + uint32_t next_suffix_; + fec::FECType fec_type_; + bool fec_enabled_; + uint32_t n_fec_; + uint32_t n_current_fec_; +}; + +} // namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_ldr.cc b/libtransport/src/protocols/rtc/rtc_ldr.cc new file mode 100644 index 000000000..6e88a8636 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_ldr.cc @@ -0,0 +1,236 @@ +/* + * 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_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> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCLossDetectionAndRecovery::RTCLossDetectionAndRecovery( + Indexer *indexer, asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies type, + RecoveryStrategy::SendRtxCallback &&callback, + interface::StrategyCallback &&external_callback) { + if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) { + rs_ = std::make_shared<RecoveryStrategyRecoveryOff>( + indexer, std::move(callback), io_service, type, + std::move(external_callback)); + } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED || + type == interface::RtcTransportRecoveryStrategies:: + DELAY_AND_BESTPATH || + type == interface::RtcTransportRecoveryStrategies:: + DELAY_AND_REPLICATION) { + rs_ = std::make_shared<RecoveryStrategyDelayBased>( + indexer, std::move(callback), io_service, type, + std::move(external_callback)); + } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY || + type == interface::RtcTransportRecoveryStrategies:: + FEC_ONLY_LOW_RES_LOSSES) { + rs_ = std::make_shared<RecoveryStrategyFecOnly>( + indexer, std::move(callback), io_service, type, + std::move(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, type, + std::move(external_callback)); + } else { + // default + type = interface::RtcTransportRecoveryStrategies::RTX_ONLY; + rs_ = std::make_shared<RecoveryStrategyRtxOnly>( + indexer, std::move(callback), io_service, type, + std::move(external_callback)); + } +} + +RTCLossDetectionAndRecovery::~RTCLossDetectionAndRecovery() {} + +void RTCLossDetectionAndRecovery::changeRecoveryStrategy( + interface::RtcTransportRecoveryStrategies type) { + if (type == rs_->getType()) return; + + rs_->updateType(type); + if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) { + rs_ = + std::make_shared<RecoveryStrategyRecoveryOff>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED || + type == interface::RtcTransportRecoveryStrategies:: + DELAY_AND_BESTPATH || + type == interface::RtcTransportRecoveryStrategies:: + DELAY_AND_REPLICATION) { + rs_ = std::make_shared<RecoveryStrategyDelayBased>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY || + type == interface::RtcTransportRecoveryStrategies:: + FEC_ONLY_LOW_RES_LOSSES) { + 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) { + rs_->incRoundId(); + rs_->onNewRound(in_sync); +} + +bool RTCLossDetectionAndRecovery::onTimeout(uint32_t seq, bool lost) { + if (!lost) { + return detectLoss(seq, seq + 1, false); + } else { + rs_->onLostTimeout(seq); + } + return false; +} + +bool RTCLossDetectionAndRecovery::onPacketRecoveredFec(uint32_t seq) { + rs_->receivedPacket(seq); + return false; +} + +bool RTCLossDetectionAndRecovery::onDataPacketReceived( + const core::ContentObject &content_object) { + uint32_t seq = content_object.getName().getSuffix(); + bool is_rtx = rs_->isRtx(seq); + rs_->receivedPacket(seq); + bool ret = false; + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "received data. add from " + << rs_->getState()->getHighestSeqReceived() + 1 << " to " << seq; + if (!is_rtx) + ret = detectLoss(rs_->getState()->getHighestSeqReceived() + 1, seq, false); + + rs_->getState()->updateHighestSeqReceived(seq); + return ret; +} + +bool RTCLossDetectionAndRecovery::onNackPacketReceived( + const core::ContentObject &nack) { + struct nack_packet_t *nack_pkt = + (struct nack_packet_t *)nack.getPayload()->data(); + uint32_t production_seq = nack_pkt->getProductionSegment(); + uint32_t seq = nack.getName().getSuffix(); + + // 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()->getHighestSeqReceived() + 1 + << " to " << production_seq; + + // if it is a future nack store it in the list set of nacked seq + if (production_seq <= seq) rs_->receivedFutureNack(seq); + + // call the detectLoss function using the probe flag = true. in fact the + // losses detected using nacks are the same as the one detected using probes, + // we should not increase the loss counter + return detectLoss(rs_->getState()->getHighestSeqReceived() + 1, + production_seq, true); +} + +bool RTCLossDetectionAndRecovery::onProbePacketReceived( + const core::ContentObject &probe) { + // 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 + + uint32_t production_seq = RTCState::getProbeParams(probe).prod_seg; + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "received probe. add from " + << rs_->getState()->getHighestSeqReceived() + 1 + << " to " << production_seq; + + return detectLoss(rs_->getState()->getHighestSeqReceived() + 1, + production_seq, true); +} + +bool RTCLossDetectionAndRecovery::detectLoss(uint32_t start, uint32_t stop, + bool recv_probe) { + if (start >= stop) return false; + + // skip nacked packets + if (start <= rs_->getState()->getLastSeqNacked()) { + start = rs_->getState()->getLastSeqNacked() + 1; + } + + // skip received or lost packets + if (start <= rs_->getState()->getHighestSeqReceived()) { + start = rs_->getState()->getHighestSeqReceived() + 1; + } + + bool loss_detected = false; + for (uint32_t seq = start; seq < stop; seq++) { + if (rs_->getState()->getPacketState(seq) == PacketState::UNKNOWN) { + if (rs_->lossDetected(seq)) { + loss_detected = true; + if ((recv_probe || rs_->wasNacked(seq)) && !rs_->isFecOn()) { + // these losses were detected using a probe and fec is off. + // in this case most likelly the procotol is about to go out of sync + // and the packets are not really lost (e.g. increase in prod rate). + // for this reason we do not + // count the losses in the stats. Instead we do the following + // 1. send RTX for the packets in case they were really lost + // 2. return to the RTC protocol that a loss was detected using a + // probe. the protocol will switch to catch_up mode to increase the + // size of the window + rs_->requestPossibleLostPacket(seq); + } else { + // if fec is on we don't need to mask pontetial losses, so increase + // the loss rate + rs_->notifyNewLossDetedcted(seq); + } + } + } + } + return loss_detected; +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_ldr.h b/libtransport/src/protocols/rtc/rtc_ldr.h new file mode 100644 index 000000000..24f22ffed --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_ldr.h @@ -0,0 +1,85 @@ +/* + * 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/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/rtc/rtc_recovery_strategy.h> + +#include <functional> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCLossDetectionAndRecovery + : public std::enable_shared_from_this<RTCLossDetectionAndRecovery> { + public: + RTCLossDetectionAndRecovery(Indexer *indexer, asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies type, + RecoveryStrategy::SendRtxCallback &&callback, + interface::StrategyCallback &&external_callback); + + ~RTCLossDetectionAndRecovery(); + + void setState(RTCState *state) { rs_->setState(state); } + void setRateControl(RTCRateControl *rateControl) { + rs_->setRateControl(rateControl); + } + + void setFecParams(uint32_t n, uint32_t k) { rs_->setFecParams(n, k); } + + void setContentSharingMode() { rs_->setContentSharingMode(); } + void turnOnRecovery() { rs_->turnOnRecovery(); } + bool isRtxOn() { return rs_->isRtxOn(); } + + void changeRecoveryStrategy(interface::RtcTransportRecoveryStrategies type); + + void onNewRound(bool in_sync); + + // the following functions return true if a loss is detected, false otherwise + bool onTimeout(uint32_t seq, bool lost); + bool onPacketRecoveredFec(uint32_t seq); + bool onDataPacketReceived(const core::ContentObject &content_object); + bool onNackPacketReceived(const core::ContentObject &nack); + bool onProbePacketReceived(const core::ContentObject &probe); + + void clear() { rs_->clear(); } + + bool isRtx(uint32_t seq) { return rs_->isRtx(seq); } + bool isPossibleLossWithNoRtx(uint32_t seq) { + return rs_->isPossibleLossWithNoRtx(seq); + } + + uint64_t getRtxRtt(uint32_t seq) { return rs_->getRtxRtt(seq); } + + private: + // returns true if a loss is detected, false otherwise + bool detectLoss(uint32_t start, uint32_t stop, bool recv_probe); + + std::shared_ptr<RecoveryStrategy> rs_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_packet.h b/libtransport/src/protocols/rtc/rtc_packet.h new file mode 100644 index 000000000..ffbbd78fd --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_packet.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +/* data packet + * +-----------------------------------------+ + * | uint64_t: timestamp | + * | | + * +-----------------------------------------+ + * | uint32_t: prod rate (bytes per sec) | + * +-----------------------------------------+ + * | payload | + * | ... | + */ + +/* nack packet + * +-----------------------------------------+ + * | uint64_t: timestamp | + * | | + * +-----------------------------------------+ + * | uint32_t: prod rate (bytes per sec) | + * +-----------------------------------------+ + * | uint32_t: current seg in production | + * +-----------------------------------------+ + */ + +/* 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> +#else +#include <hicn/transport/portability/win_portability.h> +#endif + +#include <hicn/transport/portability/endianess.h> + +#include <cstring> + +namespace transport { + +namespace protocol { + +namespace rtc { + +const uint32_t DATA_HEADER_SIZE = 12; // bytes + // XXX: sizeof(data_packet_t) is 16 + // beacuse of padding +const uint32_t NACK_HEADER_SIZE = 16; + +struct data_packet_t { + uint64_t timestamp; + uint32_t prod_rate; + + inline uint64_t getTimestamp() const { + return portability::net_to_host(timestamp); + } + inline void setTimestamp(uint64_t time) { + timestamp = portability::host_to_net(time); + } + + inline uint32_t getProductionRate() const { + return portability::net_to_host(prod_rate); + } + inline void setProductionRate(uint32_t rate) { + prod_rate = portability::host_to_net(rate); + } +}; + +struct nack_packet_t { + uint64_t timestamp; + uint32_t prod_rate; + uint32_t prod_seg; + + inline uint64_t getTimestamp() const { + return portability::net_to_host(timestamp); + } + inline void setTimestamp(uint64_t time) { + timestamp = portability::host_to_net(time); + } + + inline uint32_t getProductionRate() const { + return portability::net_to_host(prod_rate); + } + inline void setProductionRate(uint32_t rate) { + prod_rate = portability::host_to_net(rate); + } + + inline uint32_t getProductionSegment() const { + return portability::net_to_host(prod_seg); + } + inline void setProductionSegment(uint32_t seg) { + prod_seg = portability::host_to_net(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 portability::net_to_host(*(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) = portability::host_to_net(len); + } + } + + uint8_t *buf_; + uint8_t encoding_; + uint8_t pkt_num_; + uint16_t header_len_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc.h b/libtransport/src/protocols/rtc/rtc_rc.h new file mode 100644 index 000000000..62636ce40 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc.h @@ -0,0 +1,62 @@ +/* + * 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_state.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCRateControl : public std::enable_shared_from_this<RTCRateControl> { + public: + RTCRateControl() + : rc_on_(false), + congestion_win_(1000000), // init the win to a large number + congestion_state_(CongestionState::Normal), + protocol_state_(nullptr) {} + + virtual ~RTCRateControl() = default; + + void turnOnRateControl() { rc_on_ = true; } + void setState(std::shared_ptr<RTCState> state) { protocol_state_ = state; }; + 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, + bool compute_stats) = 0; + + protected: + enum class CongestionState { Normal = 0, Underuse = 1, Congested = 2, Last }; + + protected: + bool rc_on_; + uint32_t congestion_win_; + CongestionState congestion_state_; + + std::shared_ptr<RTCState> protocol_state_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport 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 new file mode 100644 index 000000000..ecabc5205 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.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 <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rc_queue.h> + +#include <algorithm> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlQueue::RTCRateControlQueue() + : rounds_since_last_drop_(0), + rounds_without_congestion_(0), + last_queue_(0) {} + +RTCRateControlQueue::~RTCRateControlQueue() {} + +void RTCRateControlQueue::onNewRound(double round_len) { + if (!rc_on_) return; + + double received_rate = protocol_state_->getReceivedRate(); + 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_; + + if (prev_congestion_state == CongestionState::Normal && + received_rate >= target_rate) { + // if the queue is high in this case we are most likelly fighting with + // a TCP flow and there is enough bandwidth to match the producer rate + congestion_state_ = CongestionState::Normal; + } else if (queue > MAX_QUEUING_DELAY || last_queue_ == queue) { + // here we detect congestion. in the case that last_queue == queue + // the consumer didn't receive any packet from the producer so we + // consider this case as congestion + // TODO: wath happen in case of high loss rate? + congestion_state_ = CongestionState::Congested; + } else { + // nothing bad is happening + congestion_state_ = CongestionState::Normal; + } + + last_queue_ = queue; + + if (congestion_state_ == CongestionState::Congested) { + if (prev_congestion_state == CongestionState::Normal) { + // init the congetion window using the received rate + 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) { + 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; + + congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR; + congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX); + } +} + +void RTCRateControlQueue::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_queue.h b/libtransport/src/protocols/rtc/rtc_rc_queue.h new file mode 100644 index 000000000..cdf78fd47 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.h @@ -0,0 +1,48 @@ +/* + * 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 RTCRateControlQueue : public RTCRateControl { + public: + RTCRateControlQueue(); + + ~RTCRateControlQueue(); + + 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_since_last_drop_; + 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_reassembly.cc b/libtransport/src/protocols/rtc/rtc_reassembly.cc new file mode 100644 index 000000000..b1b0fcaba --- /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(false)); + + 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/interfaces/tls_socket_producer.h b/libtransport/src/protocols/rtc/rtc_reassembly.h index 3c662176a..132004605 100644 --- a/libtransport/src/interfaces/tls_socket_producer.h +++ b/libtransport/src/protocols/rtc/rtc_reassembly.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,22 +15,29 @@ #pragma once -#include <hicn/transport/interfaces/socket_producer.h> +#include <glog/logging.h> +#include <protocols/datagram_reassembly.h> +#include <protocols/rtc/rtc_consts.h> namespace transport { -namespace implementation { -class TLSProducerSocket; -} +namespace protocol { -namespace interface { +namespace rtc { -class TLSProducerSocket : public ProducerSocket { +class RtcReassembly : public DatagramReassembly { public: - TLSProducerSocket(implementation::TLSProducerSocket *implementation); - ~TLSProducerSocket(); -}; + RtcReassembly(implementation::ConsumerSocket *icn_socket, + TransportProtocol *transport_protocol); + + void reassemble(core::ContentObject &content_object) override; + void reassemble(utils::MemBuf &buffer, uint32_t suffix) override; -} // namespace interface + private: + bool is_setup_; + bool data_aggregation_; +}; +} // namespace rtc +} // namespace protocol } // end namespace transport 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..257fdd09b --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc @@ -0,0 +1,420 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback) + : rs_type_(rs_type), + recovery_on_(false), + content_sharing_mode_(false), + rtx_during_fec_(0), + next_rtx_timer_(MAX_TIMER_RTX), + send_rtx_callback_(std::move(callback)), + indexer_(indexer), + round_id_(0), + last_fec_used_(0), + callback_(std::move(external_callback)) { + setRtxFec(use_rtx, use_fec); + timer_ = std::make_unique<asio::steady_timer>(io_service); +} + +RecoveryStrategy::RecoveryStrategy(RecoveryStrategy &&rs) + : rs_type_(rs.rs_type_), + content_sharing_mode_(rs.content_sharing_mode_), + rtx_during_fec_(0), + 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_(std::move(rs.callback_)) { + setFecParams(n_, k_); +} + +RecoveryStrategy::~RecoveryStrategy() {} + +void RecoveryStrategy::setFecParams(uint32_t n, uint32_t k) { + // if rs_type == FEC_ONLY_LOW_RES_LOSSES max k == 64 + n_ = n; + k_ = k; + + // XXX for the moment we go in steps of 5% loss rate. + uint32_t i = 0; + for (uint32_t loss_rate = 5; loss_rate < 100; loss_rate += 5) { + uint32_t fec_to_ask = 0; + if (n_ != 0 && k_ != 0) { + if (rs_type_ == + interface::RtcTransportRecoveryStrategies::FEC_ONLY_LOW_RES_LOSSES) { + // the max loss rate in the matrix is 50% + uint32_t index = i; + if (i > 9) index = 9; + fec_to_ask = FEC_MATRIX[k_ - 1][index]; + } else { + double dec_loss_rate = (double)(loss_rate + 5); + if (dec_loss_rate == 100.0) dec_loss_rate = 95.0; + dec_loss_rate = dec_loss_rate / 100.0; + double exp_losses = ceil((double)k_ * dec_loss_rate); + fec_to_ask = ceil((exp_losses / (1 - dec_loss_rate)) * 1.25); + } + } + fec_to_ask = std::min(fec_to_ask, (n_ - k_)); + fec_per_loss_rate_.push_back(fec_to_ask); + + i++; + } +} + +uint64_t RecoveryStrategy::getRtxRtt(uint32_t seq) { + auto it = rtx_state_.find(seq); + + if (it == rtx_state_.end()) return 0; + + // we can compute the RTT of an RTX only if it was send once. Infact if the + // RTX was sent twice or more the data may be alredy in flight and the RTT + // will be underestimated. This may happen also for packets that we + // retransmitted too soon. in that case the RTT will be filtered out by + // checking the path label + if (it->second.rtx_count_ != 1) return 0; + + // this a potentialy valid packet, compute the RTT + return (utils::SteadyTime::nowMs().count() - it->second.last_send_); +} + +bool RecoveryStrategy::lossDetected(uint32_t seq) { + if (isRtx(seq)) { + // this packet is already in the list of rtx + return false; + } + + auto it_fec = recover_with_fec_.find(seq); + if (it_fec != 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; + } + + auto it_nack = nacked_seq_.find(seq); + if (it_nack != nacked_seq_.end()) { + // this packet was nacked so we do not use it to determine the loss rate + return false; + } + + return true; +} + +void RecoveryStrategy::notifyNewLossDetedcted(uint32_t seq) { + // new loss detected + // first record the loss. second do what is needed to recover it + state_->onLossDetected(seq); + newPacketLoss(seq); +} + +void RecoveryStrategy::requestPossibleLostPacket(uint32_t seq) { + // these are packets for which we send a RTX but we do not increase the loss + // counter beacuse we don't know if they are lost or not + addNewRtx(seq, false); +} + +void RecoveryStrategy::receivedFutureNack(uint32_t seq) { + nacked_seq_.insert(seq); +} + +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.last_send_ = state.first_send_; // we didn't send an RTX for this + // packet yet + state.rtx_count_ = 0; + state.next_send_ = computeNextSend(seq, state.rtx_count_); + 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, uint32_t rtx_counter) { + uint64_t now = getNow(); + if (rtx_counter == 0) { + uint32_t wait = 1; + if (content_sharing_mode_) return now + wait; + + uint32_t jitter = SENTINEL_TIMER_INTERVAL; + double prod_rate = state_->getProducerRate(); + if (prod_rate != 0) jitter = ceil(state_->getJitter()); + + wait += jitter; + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "first rtx for " << seq << " in " << wait + << " ms, jitter = " << jitter; + + return now + wait; + } else { + // wait one RTT. if an edge is known use the edge RTT for the first 5 rtx + double prod_rate = state_->getProducerRate(); + if (prod_rate == 0) { + return now + SENTINEL_TIMER_INTERVAL; + } + + uint64_t rtt = 0; + // if the transport detects an edge we try first to get the RTX from the + // edge. if no interest get a reply we move to the full RTT + if (rtx_counter < 5 && (state_->getEdgeRtt() != 0)) { + rtt = state_->getEdgeRtt(); + } else { + rtt = state_->getAvgRTT(); + } + + if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL; + + if (content_sharing_mode_) return now + rtt; + + uint32_t wait = (uint32_t)rtt; + + uint32_t jitter = ceil(state_->getJitter()); + wait += jitter; + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "next rtx for " << seq << " in " << wait << " ms, rtt = " << rtt + << " jtter = " << jitter; + + 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.last_send_ = now; + rtx_it->second.next_send_ = + computeNextSend(seq, rtx_it->second.rtx_count_); + 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); + + // if fec is on increase the number of RTX send during fec + if (fec_on_) rtx_during_fec_++; + 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() { + 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; + + // 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 = (uint32_t)fec_per_loss_rate_.size() - 1; + + return fec_per_loss_rate_[bin]; +} + +void RecoveryStrategy::setRtxFec(std::optional<bool> rtx_on, + std::optional<bool> fec_on) { + if (rtx_on) rtx_on_ = *rtx_on; + if (fec_on) { + if (fec_on_ == false && (*fec_on) == true) { // turn on fec + // reset the number of RTX sent during fec + rtx_during_fec_ = 0; + } + fec_on_ = *fec_on; + } + + 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; + } + + auto it_nack = nacked_seq_.find(seq); + if (it_nack != nacked_seq_.end()) { + nacked_seq_.erase(it_nack); + return; + } + + deleteRtx(seq); +} + +} // 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..405e1ebba --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h @@ -0,0 +1,181 @@ +/* + * 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 <optional> +#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_; // first time this interest was sent + uint64_t last_send_; // last time this rtx was sent + uint64_t next_send_; // next retransmission time + uint32_t rtx_count_; // number or rtx + }; + + 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::RtcTransportRecoveryStrategies rs_type, + 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 setContentSharingMode() { content_sharing_mode_ = 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 wasNacked(uint32_t seq) { + if (nacked_seq_.find(seq) != nacked_seq_.end()) return true; + return false; + } + + interface::RtcTransportRecoveryStrategies getType() { + return rs_type_; + } + void updateType(interface::RtcTransportRecoveryStrategies type) { + rs_type_ = type; + } + bool isRtxOn() { return rtx_on_; } + bool isFecOn() { return fec_on_; } + + RTCState *getState() { return state_; } + + // if the function returns 0 it means that the packet is not an RTX or it is + // not a valid packet to safely compute the RTT + uint64_t getRtxRtt(uint32_t seq); + bool lossDetected(uint32_t seq); + void notifyNewLossDetedcted(uint32_t seq); + void requestPossibleLostPacket(uint32_t seq); + void receivedFutureNack(uint32_t seq); + void clear(); + + virtual void turnOnRecovery() = 0; + 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, uint32_t rtx_counter); + void retransmit(); + void scheduleNextRtx(); + void deleteRtx(uint32_t seq); + + // fec functions + uint32_t computeFecPacketsToAsk(); + + // common functons + void removePacketState(uint32_t seq); + + interface::RtcTransportRecoveryStrategies rs_type_; + bool recovery_on_; + bool rtx_on_; + bool fec_on_; + bool content_sharing_mode_; + + // number of RTX sent after fec turned on + // this is used to take into account jitter and out of order packets + // if we detect losses but we do not sent any RTX it means that the holes in + // the sequence are caused by the jitter + uint32_t rtx_during_fec_; + + // 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_; + + // packet for which we recived a future nack + // in case we detect a loss for a nacked packet we send an RTX but we do not + // increase the loss counter. this is done because it may happen that the + // producer rate checkes over time and in flight interest may be satified by + // data packet after the reception of nacks + std::unordered_set<uint32_t> nacked_seq_; + + // 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: + uint32_t round_id_; // number of rounds + uint32_t last_fec_used_; + std::vector<uint32_t> 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..7d7a01133 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_delay.cc @@ -0,0 +1,147 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + rs_type, + std::move(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 + switch_rounds_ = 0; + congestion_state_ = false; + probing_state_ = false; +} + +RecoveryStrategyDelayBased::~RecoveryStrategyDelayBased() {} + +void RecoveryStrategyDelayBased::turnOnRecovery() { + recovery_on_ = true; + uint64_t rtt = state_->getMinRTT(); + uint32_t fec_to_ask = computeFecPacketsToAsk(); + if (rtt > MAX_RTT_BEFORE_FEC && fec_to_ask > 0) { + // we need to start FEC (see fec only strategy for more details) + setRtxFec(true, true); + rtx_during_fec_ = 1; // avoid to stop fec + indexer_->setNFec(fec_to_ask); + } else { + // use RTX + setRtxFec(true, false); + switch_rounds_ = 0; + } +} + +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_ >= ((RTC_INTEREST_LIFETIME / ROUND_LEN) * 2) && + rtx_during_fec_ != 0) { // go to fec only if it is needed (RTX are on) + setRtxFec(false, true); + } else { + setRtxFec(true, 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_->getAvgRTT(); + + // XXX at the moment we are not looking at congestion events + // bool congestion = rc_->inCongestionState(); + + if ((!fec_on_ && rtt >= MAX_RTT_BEFORE_FEC) || + (fec_on_ && rtt > (MAX_RTT_BEFORE_FEC - 10))) { + // 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(); + softSwitchToFec(fec_to_ask); + if (rtx_during_fec_ == 0) // if we do not send any RTX the losses + // registered may be due to jitter + indexer_->setNFec(0); + else + indexer_->setNFec(fec_to_ask); + return; + } + + if ((fec_on_ && rtt <= (MAX_RTT_BEFORE_FEC - 10)) || + (!rtx_on_ && rtt <= MAX_RTT_BEFORE_FEC)) { + // 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()); +} + +} // 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..9e1c41388 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_delay.h @@ -0,0 +1,55 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback); + + RecoveryStrategyDelayBased(RecoveryStrategy &&rs); + + ~RecoveryStrategyDelayBased(); + + void turnOnRecovery(); + 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..5b10823ec --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc @@ -0,0 +1,143 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + rs_type, std::move(external_callback)), + congestion_state_(false), + probing_state_(false), + switch_rounds_(0) {} + +RecoveryStrategyFecOnly::RecoveryStrategyFecOnly(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(true, false); + switch_rounds_ = 0; + congestion_state_ = false; + probing_state_ = false; +} + +RecoveryStrategyFecOnly::~RecoveryStrategyFecOnly() {} + +void RecoveryStrategyFecOnly::turnOnRecovery() { + recovery_on_ = true; + // init strategy + uint32_t fec_to_ask = computeFecPacketsToAsk(); + if (fec_to_ask > 0) { + // the probing phase detected a lossy link. we immedialty start the fec and + // we disable the check to prevent to send fec packets before RTX. in fact + // here we know that we have losses and it is not a problem of OOO packets + setRtxFec(true, true); + rtx_during_fec_ = 1; // avoid to stop fec + indexer_->setNFec(fec_to_ask); + } else { + // keep only RTX on + setRtxFec(true, true); + } +} + +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(); + // 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_ >= ((RTC_INTEREST_LIFETIME / ROUND_LEN) * 2) && + rtx_during_fec_ != + 0) { // go to fec only if it is needed (RTX are on) + setRtxFec(false, true); + } else { + setRtxFec(true, true); // keep both + } + } + if (rtx_during_fec_ == 0) // if we do not send any RTX the losses + // registered may be due to jitter + indexer_->setNFec(0); + else + 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 to list to recover with fec + 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(); + 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..42df25bd9 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.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 RecoveryStrategyFecOnly : public RecoveryStrategy { + public: + RecoveryStrategyFecOnly(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback); + + RecoveryStrategyFecOnly(RecoveryStrategy &&rs); + + ~RecoveryStrategyFecOnly(); + + void turnOnRecovery(); + 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..dbad563cd --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc @@ -0,0 +1,174 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, true, + rs_type, + std::move(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(); + 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 = (uint32_t)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::turnOnRecovery() { + recovery_on_ = 1; + // the stategy will be init in the new round function +} + +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..0e76efaca --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h @@ -0,0 +1,71 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback); + + RecoveryStrategyLowRate(RecoveryStrategy &&rs); + + ~RecoveryStrategyLowRate(); + + void turnOnRecovery(); + 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..00c6a0504 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc @@ -0,0 +1,65 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, false, + rs_type, std::move(external_callback)) {} + +RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(false, false); +} + +RecoveryStrategyRecoveryOff::~RecoveryStrategyRecoveryOff() {} + +void RecoveryStrategyRecoveryOff::turnOnRecovery() { + // nothing to do + return; +} +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..3d59cc473 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h @@ -0,0 +1,46 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback); + + RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs); + + ~RecoveryStrategyRecoveryOff(); + + void turnOnRecovery(); + 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..4d7cf7a82 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc @@ -0,0 +1,67 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + rs_type, std::move(external_callback)) {} + +RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(true, false); +} + +RecoveryStrategyRtxOnly::~RecoveryStrategyRtxOnly() {} + +void RecoveryStrategyRtxOnly::turnOnRecovery() { + recovery_on_ = true; + setRtxFec(true, false); +} + +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..03dbed1c7 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h @@ -0,0 +1,46 @@ +/* + * 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::RtcTransportRecoveryStrategies rs_type, + interface::StrategyCallback &&external_callback); + + RecoveryStrategyRtxOnly(RecoveryStrategy &&rs); + + ~RecoveryStrategyRtxOnly(); + + void turnOnRecovery(); + 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 new file mode 100644 index 000000000..82ac0b9c1 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_state.cc @@ -0,0 +1,900 @@ +/* + * 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_state.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCState::RTCState(Indexer *indexer, + ProbeHandler::SendProbeCallback &&probe_callback, + DiscoveredRttCallback &&discovered_rtt_callback, + asio::io_service &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); +} + +RTCState::~RTCState() {} + +void RTCState::initParams() { + // packets counters (total) + sent_interests_ = 0; + sent_rtx_ = 0; + received_data_ = 0; + received_nacks_ = 0; + received_timeouts_ = 0; + received_probes_ = 0; + + // loss counters + packets_lost_ = 0; + definitely_lost_pkt_ = 0; + losses_recovered_ = 0; + first_seq_in_round_ = 0; + highest_seq_received_ = 0; + highest_seq_received_in_order_ = 0; + last_seq_nacked_ = 0; + loss_rate_ = 0.0; + avg_loss_rate_ = -1.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 + pending_fec_pkt_ = 0; + received_fec_pkt_ = 0; + + // 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 + past_nack_on_last_round_ = false; + received_nacks_last_round_ = 0; + + // packets counter + received_packets_last_round_ = 0; + received_data_last_round_ = 0; + received_data_from_cache_ = 0; + sent_interests_last_round_ = 0; + sent_rtx_last_round_ = 0; + + // round conunters + rounds_ = 0; + rounds_without_nacks_ = 0; + rounds_without_packets_ = 0; + + last_production_seq_ = 0; + producer_is_active_ = false; + last_prod_update_seq_ = 0; + + // paths stats + path_table_.clear(); + main_path_ = nullptr; + edge_path_ = nullptr; + + // packet cache (not pending anymore) + packet_cache_.clear(); + + // pending interests + pending_interests_.clear(); + + // used to keep track of the skipped interest + last_interest_sent_ = 0; + + // init rtt + first_interest_sent_time_ = ~0; + first_interest_sent_seq_ = 0; + + // start probing the producer + init_rtt_ = false; + 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 = utils::SteadyTime::nowMs().count(); + uint32_t seq = interest_name->getSuffix(); + pending_interests_.insert(std::pair<uint32_t, uint64_t>(seq, now)); + + if (sent_interests_ == 0) { + first_interest_sent_time_ = now; + first_interest_sent_seq_ = seq; + } + + if (indexer_->isFec(seq)) { + pending_fec_pkt_++; + } + + if (last_interest_sent_ == 0 && seq != 0) { + last_interest_sent_ = seq; // init last interest sent + } + + // TODO what happen in case of jumps? + 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)) { + // only fec packets can be skipped + addToPacketCache(i, PacketState::SKIPPED); + } + } + + last_interest_sent_ = seq; + + sent_interests_++; + sent_interests_last_round_++; +} + +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_++; + + if (lost) onPacketLost(seq); +} + +void RTCState::onLossDetected(uint32_t seq) { + PacketState state = getPacketState(seq); + + // if the packet is already marked with a state, do nothing + // to be considered lost the packet must be pending + if (state == PacketState::UNKNOWN && + pending_interests_.find(seq) != pending_interests_.end()) { + packets_lost_++; + addToPacketCache(seq, PacketState::LOST); + } +} + +void RTCState::onRetransmission(uint32_t seq) { + // remove the interest for the pendingInterest map only after the first rtx. + // in this way we can handle the ooo packets that come in late as normla + // packet. we consider a packet lost only if we sent at least an RTX for it. + // XXX this may become problematic if we stop the RTX transmissions + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + 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_++; + + core::ParamsRTC params = RTCState::getDataParams(content_object); + + if (last_prod_update_seq_ < seq) { + last_prod_update_seq_ = seq; + production_rate_ = (double)params.prod_rate; + } + + updatePacketSize(content_object); + updateReceivedBytes(content_object, false); + addRecvOrLost(seq, PacketState::RECEIVED); + + // the producer is responding + // it is generating valid data packets so we consider it active + producer_is_active_ = true; + + received_packets_last_round_++; +} + +void RTCState::onFecPacketReceived(const core::ContentObject &content_object) { + uint32_t seq = content_object.getName().getSuffix(); + updateReceivedBytes(content_object, true); + + PacketState state = getPacketState(seq); + if (state != PacketState::LOST) { + // increase only for not lost packets + received_fec_pkt_++; + } + addRecvOrLost(seq, PacketState::RECEIVED); + // the producer is responding + // it is generating valid data packets so we consider it active + producer_is_active_ = true; +} + +void RTCState::onNackPacketReceived(const core::ContentObject &nack, + bool compute_stats) { + 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->getProductionSegment(); + uint32_t production_rate = nack_pkt->getProductionRate(); + + if (TRANSPORT_EXPECT_FALSE(main_path_ == nullptr) || + last_prod_update_seq_ < production_seq) { + // update production rate + last_production_seq_ = production_seq; + production_rate_ = (double)production_rate; + } + + if (compute_stats) { + // this is not an RTX + updatePathStats(nack, true); + } + + // for statistics pourpose we log all nacks, also the one received for + // retransmitted packets + received_nacks_++; + received_nacks_last_round_++; + + bool to_delete = false; + if (production_seq > seq) { + // old nack, seq is lost + // update last nacked + if (last_seq_nacked_ < seq) last_seq_nacked_ = seq; + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "lost packet " << seq << " beacuse of a past nack"; + if (compute_stats) past_nack_on_last_round_ = true; + onPacketLost(seq); + } else if (seq > production_seq) { + // future nack + // remove the nack from the pending interest map + // (the packet is not received/lost yet) + to_delete = true; + } else { + // this should be a quite rear event. simply remove the + // packet from the pending interest list + 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_--; + } + } + + received_packets_last_round_++; +} + +void RTCState::onPacketLost(uint32_t seq) { + if (!indexer_->isFec(seq)) { + PacketState state = getPacketState(seq); + if (state == PacketState::LOST || + (state == PacketState::UNKNOWN && + pending_interests_.find(seq) != pending_interests_.end())) { + definitely_lost_pkt_++; + DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost"; + } + } + + addRecvOrLost(seq, PacketState::DEFINITELY_LOST); +} + +void RTCState::onPacketRecoveredRtx(const core::ContentObject &content_object, + uint64_t rtt) { + uint32_t seq = content_object.getName().getSuffix(); + packets_sent_to_app_++; + + // increase the recovered packet counter only if the packet was marked as LOST + // before. + PacketState state = getPacketState(seq); + if (state == PacketState::LOST) losses_recovered_++; + + addRecvOrLost(seq, PacketState::RECEIVED); + updateReceivedBytes(content_object, false); + + if (rtt == 0) return; // nothing to do + + uint32_t path_label = content_object.getPathLabel(); + auto path_it = path_table_.find(path_label); + if (path_it == path_table_.end()) { + // this is a new path and it must be a cache + std::shared_ptr<RTCDataPath> newPath = + std::make_shared<RTCDataPath>(path_label); + auto ret = path_table_.insert( + std::pair<uint32_t, std::shared_ptr<RTCDataPath>>(path_label, newPath)); + path_it = ret.first; + } + + auto path = path_it->second; + if (path->pathToProducer()) + return; // this packet is coming from a producer + // even if we sent an RTX. this may happen + // for RTX that are sent too fast or in + // case of multipath + + path->insertRttSample(utils::SteadyTime::Milliseconds(rtt), true); +} + +void RTCState::onFecPacketRecoveredRtx( + const core::ContentObject &content_object) { + // This is the same as onPacketRecoveredRtx, but in this is case the + // pkt is also a FEC pkt, the addRecvOrLost will be called afterwards + losses_recovered_++; + updateReceivedBytes(content_object, true); +} + +void RTCState::onPacketRecoveredFec(uint32_t seq, uint32_t size) { + losses_recovered_++; + packets_sent_to_app_++; + recovered_bytes_with_fec_ += size; + + // adding header to the count + recovered_bytes_with_fec_ += 60; // XXX get header size some where + + // the packet could be not marked as lost yet. onLossDetected checks if add in + // the packet in the lost count or not + onLossDetected(seq); + + addRecvOrLost(seq, PacketState::RECEIVED); +} + +bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { + uint32_t seq = probe.getName().getSuffix(); + core::ParamsRTC params = RTCState::getProbeParams(probe); + + bool is_valid = true; + uint32_t max = UINT32_MAX; + if (params.prod_rate == max) is_valid = false; + + uint64_t rtt; + rtt = probe_handler_->getRtt(seq, is_valid); + if (rtt == 0) return false; // this is not a valid probe + + if (!is_valid) return false; // not a valid probe + + // if we are here the producer is active + producer_is_active_ = true; + + // 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); + + if (path_it == path_table_.end()) { + // found a new path + std::shared_ptr<RTCDataPath> newPath = + std::make_shared<RTCDataPath>(path_label); + auto ret = path_table_.insert( + std::pair<uint32_t, std::shared_ptr<RTCDataPath>>(path_label, newPath)); + path_it = ret.first; + } + + auto path = path_it->second; + + path->insertRttSample(utils::SteadyTime::Milliseconds(rtt), true); + path->receivedNack(); + + uint64_t now = utils::SteadyTime::nowMs().count(); + + int64_t OWD = now - params.timestamp; + path->insertOwdSample(OWD); + + if (last_prod_update_seq_ < params.prod_seg) { + last_production_seq_ = params.prod_seg; + production_rate_ = (double)params.prod_rate; + } + + // check for init RTT. if received_probes_ is equal to 0 schedule a timer to + // wait for the INIT_RTT_PROBES. in this way if some probes get lost we don't + // wait forever + received_probes_++; + + 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. + main_path_ = path; + setInitRttTimer(INIT_RTT_PROBE_WAIT); + } + if (received_probes_ == INIT_RTT_PROBES) { + // we are done + init_rtt_timer_->cancel(); + checkInitRttTimer(); + } + } + + received_packets_last_round_++; + + // ignore probes sent before the first interest + if ((now - rtt) <= first_interest_sent_time_) return false; + return true; +} + +void RTCState::onJumpForward(uint32_t next_seq) { + for (uint32_t seq = highest_seq_received_in_order_ + 1; seq < next_seq; + seq++) { + PacketState packet_state = getPacketState(seq); + if (packet_state != PacketState::RECEIVED && + packet_state != PacketState::DEFINITELY_LOST) { + // here we considere the packet as definitely lost whitout increase the + // lost packet counter because this loss is not due to the network + // condition but the transport wants to skip the packet + onPacketLost(seq); + } + } +} + +void RTCState::onNewRound(double round_len, bool in_sync) { + if (path_table_.empty()) return; + + double bytes_per_sec = + ((double)received_bytes_ * (MILLI_IN_A_SEC / round_len)); + if (received_rate_ == 0) + received_rate_ = bytes_per_sec; + 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); + + // 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; + uint64_t min_edge_rtt = UINT_MAX; + std::shared_ptr<RTCDataPath> old_main_path = main_path_; + main_path_ = nullptr; + edge_path_ = nullptr; + + for (auto it = path_table_.begin(); it != path_table_.end(); it++) { + if (it->second->isValidProducer()) { + uint32_t pkt = it->second->getPacketsLastRound(); + if (pkt > last_round_packets) { + last_round_packets = pkt; + main_path_ = it->second; + } + } else if (it->second->isActive() && !it->second->pathToProducer()) { + // this is a path to a cache from where we are receiving content + if (it->second->getMinRtt() < min_edge_rtt) { + min_edge_rtt = it->second->getMinRtt(); + edge_path_ = it->second; + } + } + it->second->roundEnd(); + } + + if (main_path_ == nullptr) main_path_ = old_main_path; + if (edge_path_ == nullptr) edge_path_ = main_path_; + if (edge_path_->getMinRtt() >= main_path_->getMinRtt()) + edge_path_ = 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 (!past_nack_on_last_round_ && received_bytes_ > 0) { + rounds_without_nacks_++; + } else { + rounds_without_nacks_ = 0; + } + + // check if the producer is active + if (received_packets_last_round_ != 0) { + rounds_without_packets_ = 0; + } else { + rounds_without_packets_++; + if (rounds_without_packets_ >= MAX_ROUND_WHIOUT_PACKETS && + producer_is_active_ != false) { + initParams(); + } + } + + // reset counters + received_bytes_ = 0; + received_fec_bytes_ = 0; + recovered_bytes_with_fec_ = 0; + packets_lost_ = 0; + definitely_lost_pkt_ = 0; + losses_recovered_ = 0; + first_seq_in_round_ = highest_seq_received_; + + past_nack_on_last_round_ = false; + received_nacks_last_round_ = 0; + + received_packets_last_round_ = 0; + received_data_last_round_ = 0; + received_data_from_cache_ = 0; + sent_interests_last_round_ = 0; + sent_rtx_last_round_ = 0; + + received_fec_pkt_ = 0; + + rounds_++; +} + +void RTCState::updateReceivedBytes(const core::ContentObject &content_object, + bool isFec) { + if (isFec) { + received_fec_bytes_ += + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); + } else { + received_bytes_ += + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); + } +} + +void RTCState::updatePacketSize(const core::ContentObject &content_object) { + uint32_t pkt_size = + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); + avg_packet_size_ = (MOVING_AVG_ALPHA * avg_packet_size_) + + ((1 - MOVING_AVG_ALPHA) * pkt_size); +} + +void RTCState::updatePathStats(const core::ContentObject &content_object, + bool is_nack) { + // get packet path + uint32_t path_label = content_object.getPathLabel(); + auto path_it = path_table_.find(path_label); + + if (path_it == path_table_.end()) { + // found a new path + std::shared_ptr<RTCDataPath> newPath = + std::make_shared<RTCDataPath>(path_label); + auto ret = path_table_.insert( + std::pair<uint32_t, std::shared_ptr<RTCDataPath>>(path_label, newPath)); + path_it = ret.first; + } + + auto path = path_it->second; + + // compute rtt + uint32_t seq = content_object.getName().getSuffix(); + uint64_t interest_sent_time = getInterestSentTime(seq); + if (interest_sent_time == 0) + return; // this should not happen, + // it means that we are processing an interest + // that is not pending + + uint64_t now = utils::SteadyTime::nowMs().count(); + + uint64_t RTT = now - interest_sent_time; + + 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) + core::ParamsRTC params = RTCState::getDataParams(content_object); + int64_t OWD = now - params.timestamp; + path->insertOwdSample(OWD); + + // compute IAT or set path to producer + if (!is_nack) { + // compute the iat only for the content packets + uint32_t segment_number = content_object.getName().getSuffix(); + path->computeInterArrivalGap(segment_number); + if (!path->pathToProducer()) received_data_from_cache_++; + } else { + path->receivedNack(); + } +} + +void RTCState::updateLossRate(bool in_sync) { + last_round_loss_rate_ = loss_rate_; + loss_rate_ = 0.0; + + uint32_t number_theorically_received_packets_ = + highest_seq_received_ - first_seq_in_round_; + + // XXX this may be quite inefficient if the rate is high + // maybe is better to iterate over the set? + + 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_--; + } + if (indexer_->isFec(i)) fec_packets++; + } + if (indexer_->isFec(highest_seq_received_)) fec_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 (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_ == -1.0) + avg_loss_rate_ = loss_rate_; + else + avg_loss_rate_ = + avg_loss_rate_ * MOVING_AVG_ALPHA + loss_rate_ * (1 - MOVING_AVG_ALPHA); + + // 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 { + expected_packets_ = 0; + 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_); + + 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_++; +} + +void RTCState::dataToBeReceived(uint32_t seq) { + addToPacketCache(seq, PacketState::TO_BE_RECEIVED); +} + +void RTCState::updateHighestSeqReceived(uint32_t seq) { + if (seq > highest_seq_received_) highest_seq_received_ = seq; +} + +void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; + } + + addToPacketCache(seq, state); + + // keep track of the last packet received/lost + // without holes. + if (highest_seq_received_in_order_ < last_seq_nacked_) { + highest_seq_received_in_order_ = last_seq_nacked_; + } + + if ((highest_seq_received_in_order_ + 1) == seq) { + highest_seq_received_in_order_ = seq; + } 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 + // 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++) { + 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 + if (TRANSPORT_EXPECT_TRUE(i >= first_interest_sent_seq_)) { + 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_ + highest_seq_received_in_order_ = i; + } + } +} + +void RTCState::setInitRttTimer(uint32_t wait) { + init_rtt_timer_->cancel(); + init_rtt_timer_->expires_from_now(std::chrono::milliseconds(wait)); + + std::weak_ptr<RTCState> self = shared_from_this(); + init_rtt_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + if (auto ptr = self.lock()) { + ptr->checkInitRttTimer(); + } + }); +} + +void RTCState::checkInitRttTimer() { + if (received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV || + probe_handler_->getProbeLossRate() == 1.0) { + // we didn't received enough probes or they were not valid, restart + received_probes_ = 0; + 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(); + loss_history_.pushBack(probe_handler_->getProbeLossRate()); + + 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)getMinRTT() / MILLI_IN_A_SEC; + double packet_size = getAveragePacketSize(); + uint32_t pkt_in_rtt_ = std::floor(((prod_rate / packet_size) * rtt)); + last_seq_nacked_ = last_production_seq_ + pkt_in_rtt_; + + discovered_rtt_callback_(); +} + +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).shared_from_this()); + 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).shared_from_this()); + manifest.decode(); + params = manifest.getParamsRTC(); + break; + } + default: + break; + } + + return params; +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_state.h b/libtransport/src/protocols/rtc/rtc_state.h new file mode 100644 index 000000000..ac3cc621f --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_state.h @@ -0,0 +1,410 @@ +/* + * 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/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> + +namespace transport { + +namespace protocol { + +namespace rtc { + +// 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 + + public: + using DiscoveredRttCallback = std::function<void()>; + + public: + 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); + void onNackPacketReceived(const core::ContentObject &nack, + bool compute_stats); + void onPacketLost(uint32_t seq); + void onPacketRecoveredRtx(const core::ContentObject &content_object, + uint64_t rtt); + void onFecPacketRecoveredRtx(const core::ContentObject &content_object); + 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); + + // main path + uint32_t getProducerPath() const { + if (mainPathIsValid()) return main_path_->getPathId(); + return 0; + } + + // delay metrics + bool isRttDiscovered() const { return init_rtt_; } + + 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; + } + + uint64_t getEdgeRtt() const { + if (edge_path_ != nullptr) return edge_path_->getMinRtt(); + return 0; + } + + void resetRttStats() { + if (mainPathIsValid()) main_path_->clearRtt(); + } + + double getQueuing() const { + if (mainPathIsValid()) return main_path_->getQueuingDealy(); + return 0.0; + } + double getIAT() const { + if (mainPathIsValid()) return main_path_->getInterArrivalGap(); + return 0.0; + } + + double getJitter() const { + if (mainPathIsValid()) return main_path_->getJitter(); + return 0.0; + } + + // pending interests + uint64_t getInterestSentTime(uint32_t seq) { + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) return it->second; + + return 0; + } + + bool isPending(uint32_t seq) { + if (pending_interests_.find(seq) != pending_interests_.end()) return true; + return false; + } + + uint32_t getPendingInterestNumber() const { + return (uint32_t)pending_interests_.size(); + } + + 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 getPerRoundLossRate() const { return loss_rate_; } + double getPerSecondLossRate() const { return per_sec_loss_rate_; } + double getAvgLossRate() const { return avg_loss_rate_; } + double getMaxLossRate() const { + if (loss_history_.size() != 0) return loss_history_.begin(); + return 0; + } + + double getLastRoundLossRate() const { return last_round_loss_rate_; } + double getResidualLossRate() const { return residual_loss_rate_; } + + uint32_t getLostData() const { return packets_lost_; }; + uint32_t getRecoveredLosses() const { return losses_recovered_; } + + uint32_t getDefinitelyLostPackets() const { return definitely_lost_pkt_; } + + uint32_t getHighestSeqReceived() const { return highest_seq_received_; } + + uint32_t getHighestSeqReceivedInOrder() const { + return highest_seq_received_in_order_; + } + + // 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_; } + + // bandwidth/production metrics + 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 + uint32_t getRoundsWithoutNacks() const { return rounds_without_nacks_; } + uint32_t getLastSeqNacked() const { return last_seq_nacked_; } + + // producer state + bool isProducerActive() const { return producer_is_active_; } + + // packets from cache + // this should be called at the end of a round beacuse otherwise we may have + // not enough packets to get a good stat + double getPacketFromCacheRatio() const { + if (received_data_last_round_ == 0) return 0; + return (double)received_data_from_cache_ / + (double)received_data_last_round_; + } + + PendingInterestsMap::iterator getPendingInterestsMapBegin() { + return pending_interests_.begin(); + } + 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); + + void updateHighestSeqReceived(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 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, + bool isFec); + void updatePacketSize(const core::ContentObject &content_object); + void updatePathStats(const core::ContentObject &content_object, bool is_nack); + void updateLossRate(bool in_sycn); + + void addRecvOrLost(uint32_t seq, PacketState state); + + void setInitRttTimer(uint32_t wait); + void checkInitRttTimer(); + + bool mainPathIsValid() const { + if (main_path_ != nullptr) + return true; + else + return false; + } + + // packets counters (total) + uint32_t sent_interests_; + uint32_t sent_rtx_; + uint32_t received_data_; + uint32_t received_nacks_; + uint32_t received_timeouts_; + uint32_t received_probes_; + + // loss counters + int32_t packets_lost_; + int32_t losses_recovered_; + uint32_t definitely_lost_pkt_; + uint32_t first_seq_in_round_; + uint32_t highest_seq_received_; + uint32_t highest_seq_received_in_order_; + uint32_t last_seq_nacked_; // segment for which we got an oldNack + double loss_rate_; + double avg_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 (only data) + double fec_received_rate_; // fec rate recevied by the consumer + double fec_recovered_rate_; // rate recovered using fec + + // nack counters + // the bool takes tracks only about the valid past nacks (no rtx) and it is + // used to switch between the states. Instead received_nacks_last_round_ logs + // all the nacks for statistics + bool past_nack_on_last_round_; + uint32_t received_nacks_last_round_; + + // packets counters + uint32_t received_packets_last_round_; + uint32_t received_data_last_round_; + uint32_t received_data_from_cache_; + uint32_t sent_interests_last_round_; + uint32_t sent_rtx_last_round_; + + // fec counters + uint32_t received_fec_pkt_; + uint32_t pending_fec_pkt_; + + // round counters + uint32_t rounds_; + uint32_t rounds_without_nacks_; + uint32_t rounds_without_packets_; + + // init rtt + uint64_t first_interest_sent_time_; + uint32_t first_interest_sent_seq_; + + // producer state + bool + producer_is_active_; // the prodcuer is active if we receive some packets + uint32_t last_production_seq_; // last production seq received by the + // producer used to init the sync protcol + uint32_t last_prod_update_seq_; // seq number of the last packet used to + // update the update from the producer. + // assumption: the highest seq number carries + // the most up to date info. in case of + // probes we look at the produced seq number + + // paths stats + std::unordered_map<uint32_t, std::shared_ptr<RTCDataPath>> path_table_; + std::shared_ptr<RTCDataPath> main_path_; // this is the path that connects + // the consumer to the producer. in + // case of multipath the trasnport + // uses the most active path + std::shared_ptr<RTCDataPath> edge_path_; // path to the closest cache if it + // exists + + // packet received + // cache where to store info about the last MAX_CACHED_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 + PendingInterestsMap pending_interests_; + + // indexer + Indexer *indexer_; + + // used to keep track of the skipped interests + uint32_t last_interest_sent_; + + // 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_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_verifier.cc b/libtransport/src/protocols/rtc/rtc_verifier.cc new file mode 100644 index 000000000..60fce92a5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.cc @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2017-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/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 factor_relevant, uint32_t factor_alert) + : verifier_(verifier), + factor_relevant_(factor_relevant), + factor_alert_(factor_alert), + manifest_max_capacity_(std::numeric_limits<uint8_t>::max()) {} + +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::setFactorRelevant(uint32_t factor_relevant) { + factor_relevant_ = factor_relevant; +} + +void RTCVerifier::setFactorAlert(uint32_t factor_alert) { + factor_alert_ = factor_alert; +} + +auth::VerificationPolicy RTCVerifier::verify(core::Interest &interest) { + return verifier_->verifyPackets(&interest); +} + +auth::VerificationPolicy RTCVerifier::verify( + core::ContentObject &content_object, bool is_fec) { + auth::Suffix suffix = content_object.getName().getSuffix(); + auth::VerificationPolicy default_policy = auth::VerificationPolicy::ABORT; + + 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); + + verifier_->callVerificationFailedCallback(suffix, default_policy); + return default_policy; +} + +auth::VerificationPolicy RTCVerifier::verifyProbe( + core::ContentObject &content_object) { + auth::Suffix suffix = content_object.getName().getSuffix(); + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + + switch (ProbeHandler::getProbeType(suffix)) { + case ProbeType::INIT: + policy = verifyManifest(content_object); + if (policy == auth::VerificationPolicy::ACCEPT) { + policy = processManifest(content_object); + } + break; + case ProbeType::RTT: + policy = verifyNack(content_object); + break; + default: + verifier_->callVerificationFailedCallback(suffix, policy); + break; + } + + 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) { + if (HICN_PACKET_FORMAT_IS_AH(content_object.getFormat())) { + return verifier_->verifyPackets(&content_object); + } + + auth::Suffix suffix = content_object.getName().getSuffix(); + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + + uint32_t threshold_relevant = factor_relevant_ * manifest_max_capacity_; + uint32_t threshold_alert = factor_alert_ * manifest_max_capacity_; + + // Flush packets outside relevance window + for (auto it = packets_unverif_.set().begin(); + it != packets_unverif_.set().end();) { + if (it->first > current_index_ - threshold_relevant) { + break; + } + packets_unverif_erased_.insert((unsigned int)it->first); + it = packets_unverif_.remove(it); + } + + // Add packet to set of unverified packets + packets_unverif_.add({current_index_, suffix}, + content_object.computeDigest(manifest_hash_algo_)); + current_index_++; + + // Check that the number of unverified packets is below the alert threshold + if (packets_unverif_.set().size() <= threshold_alert) { + policy = auth::VerificationPolicy::ACCEPT; + } + + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; +} + +auth::VerificationPolicy RTCVerifier::verifyManifest( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::processManifest( + core::ContentObject &content_object) { + auth::Suffix suffix = content_object.getName().getSuffix(); + auth::VerificationPolicy accept_policy = auth::VerificationPolicy::ACCEPT; + + // Decode manifest + core::ContentObjectManifest manifest(content_object.shared_from_this()); + manifest.decode(); + + // Extract manifest data + manifest_max_capacity_ = manifest.getMaxCapacity(); + manifest_hash_algo_ = manifest.getHashAlgorithm(); + auth::Verifier::SuffixMap suffix_map = manifest.getSuffixMap(); + + // Return early if the manifest is empty + if (suffix_map.empty()) { + verifier_->callVerificationFailedCallback(suffix, accept_policy); + return accept_policy; + } + + // Add hashes to map of all manifest hashes + manifest_digests_.insert(suffix_map.begin(), suffix_map.end()); + + // Remove discarded and definitely lost packets from digest map + for (auto it = manifest_digests_.begin(); it != manifest_digests_.end();) { + auto it_erased = packets_unverif_erased_.find(it->first); + + if (it_erased != packets_unverif_erased_.end()) { + packets_unverif_erased_.erase(it_erased); + it = manifest_digests_.erase(it); + continue; + } + + if (rtc_state_->getPacketState(it->first) == PacketState::DEFINITELY_LOST) { + it = manifest_digests_.erase(it); + continue; + } + + ++it; + } + + // Verify packets + auth::Verifier::PolicyMap policies = + verifier_->verifyHashes(packets_unverif_.suffixMap(), manifest_digests_); + + for (const auto &p : policies) { + switch (p.second) { + case auth::VerificationPolicy::ACCEPT: { + packets_unverif_.remove(packets_unverif_.packet(p.first)); + manifest_digests_.erase(p.first); + break; + } + case auth::VerificationPolicy::UNKNOWN: + break; + case auth::VerificationPolicy::DROP: + case auth::VerificationPolicy::ABORT: + return p.second; + } + } + + verifier_->callVerificationFailedCallback(suffix, accept_policy); + return accept_policy; +} + +void RTCVerifier::onDataRecoveredFec(uint32_t suffix) { + manifest_digests_.erase(suffix); +} + +std::pair<RTCVerifier::PacketSet::iterator, bool> RTCVerifier::Packets::add( + const Packet &packet, const auth::CryptoHash &digest) { + auto inserted = packets_.insert(packet); + if (inserted.second) { + packets_map_[packet.second] = inserted.first; + suffix_map_[packet.second] = digest; + } + return inserted; +} + +RTCVerifier::PacketSet::iterator RTCVerifier::Packets::remove( + PacketSet::iterator packet_it) { + packets_map_.erase(packet_it->second); + suffix_map_.erase(packet_it->second); + return packets_.erase(packet_it); +} + +const std::set<RTCVerifier::Packet> &RTCVerifier::Packets::set() const { + return packets_; +}; + +RTCVerifier::PacketSet::iterator RTCVerifier::Packets::packet( + auth::Suffix suffix) { + return packets_map_.at(suffix); +}; + +const auth::Verifier::SuffixMap &RTCVerifier::Packets::suffixMap() const { + return suffix_map_; +} + +} // 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..c83faf08a --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017-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 + +#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: + explicit RTCVerifier(std::shared_ptr<auth::Verifier> verifier, + uint32_t factor_relevant, uint32_t factor_alert); + + virtual ~RTCVerifier() = default; + + void setState(std::shared_ptr<RTCState> rtc_state); + void setVerifier(std::shared_ptr<auth::Verifier> verifier); + void setFactorRelevant(uint32_t factor_relevant); + void setFactorAlert(uint32_t factor_alert); + + auth::VerificationPolicy verify(core::Interest &interest); + 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); + + void onDataRecoveredFec(uint32_t suffix); + + protected: + using Index = uint64_t; + using Packet = std::pair<Index, auth::Suffix>; + using PacketSet = std::set<Packet>; + + class Packets { + public: + std::pair<PacketSet::iterator, bool> add(const Packet &packet, + const auth::CryptoHash &digest); + PacketSet::iterator remove(PacketSet::iterator packet_it); + const PacketSet &set() const; + PacketSet::iterator packet(auth::Suffix suffix); + const auth::Verifier::SuffixMap &suffixMap() const; + + private: + PacketSet packets_; + std::unordered_map<auth::Suffix, PacketSet::iterator> packets_map_; + auth::Verifier::SuffixMap suffix_map_; + }; + + // The RTC state. + std::shared_ptr<RTCState> rtc_state_; + // The verifier instance. + std::shared_ptr<auth::Verifier> verifier_; + // Used to compute the relevance windows size (in packets). + uint32_t factor_relevant_; + // Used to compute the alert threshold (in packets). + uint32_t factor_alert_; + // The maximum number of entries a manifest can contain. + uint8_t manifest_max_capacity_; + // Hash algorithm used by manifests. + auth::CryptoHashType manifest_hash_algo_; + // Digests extracted from all manifests received. + auth::Verifier::SuffixMap manifest_digests_; + // The number of data packets processed. + Index current_index_; + // Unverified packets with index in relevance window. + Packets packets_unverif_; + // Unverified erased packets with index outside relevance window. + std::unordered_set<auth::Suffix> packets_unverif_erased_; +}; + +} // namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/rtc_data_path.cc b/libtransport/src/protocols/rtc_data_path.cc deleted file mode 100644 index 30644e939..000000000 --- a/libtransport/src/protocols/rtc_data_path.cc +++ /dev/null @@ -1,156 +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 <protocols/rtc_data_path.h> - -#include <cfloat> -#include <chrono> - -#define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec - -namespace transport { - -namespace protocol { - -RTCDataPath::RTCDataPath() - : min_rtt(UINT_MAX), - prev_min_rtt(UINT_MAX), - 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 - // value is is the queueing delay. for this reason we - // keep both RTT (for the windowd calculation) and OWD - // (for congestion/quality control) - prev_min_owd(INT_MAX), - avg_owd(0.0), - queuing_delay(DBL_MAX), - lastRecvSeq_(0), - lastRecvTime_(0), - avg_inter_arrival_(DBL_MAX), - received_nacks_(false), - received_packets_(false), - rounds_without_packets_(0), - RTThistory_(HISTORY_LEN), - OWDhistory_(HISTORY_LEN){}; - -void RTCDataPath::insertRttSample(uint64_t rtt) { - // for the rtt we only keep track of the min one - if (rtt < min_rtt) min_rtt = rtt; -} - -void RTCDataPath::insertOwdSample(int64_t owd) { - // for owd we use both min and avg - if (owd < min_owd) min_owd = owd; - - if (avg_owd != DBL_MAX) - avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC); - else { - avg_owd = 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; -} - -void RTCDataPath::computeInterArrivalGap(uint32_t segmentNumber) { - // got packet in sequence, compute gap - if (lastRecvSeq_ == (segmentNumber - 1)) { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t delta = now - lastRecvTime_; - lastRecvSeq_ = segmentNumber; - lastRecvTime_ = now; - if (avg_inter_arrival_ == DBL_MAX) - avg_inter_arrival_ = delta; - else - avg_inter_arrival_ = - (avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC); - return; - } - - // ooo packet, update the stasts if needed - if (lastRecvSeq_ <= segmentNumber) { - lastRecvSeq_ = segmentNumber; - lastRecvTime_ = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - } -} - -void RTCDataPath::receivedNack() { received_nacks_ = true; } - -double RTCDataPath::getInterArrivalGap() { - if (avg_inter_arrival_ == DBL_MAX) return 0; - return avg_inter_arrival_; -} - -bool RTCDataPath::isActive() { - if (received_nacks_ && rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS) - return true; - return false; -} - -void RTCDataPath::roundEnd() { - // reset min_rtt and add it to the history - if (min_rtt != UINT_MAX) { - prev_min_rtt = min_rtt; - } else { - // this may happen if we do not receive any packet - // from this path in the last round. in this case - // we use the measure from the previuos round - min_rtt = prev_min_rtt; - } - - if (min_rtt == 0) min_rtt = 1; - - RTThistory_.pushBack(min_rtt); - min_rtt = UINT_MAX; - - // do the same for min owd - if (min_owd != INT_MAX) { - prev_min_owd = min_owd; - } else { - min_owd = prev_min_owd; - } - - if (min_owd != INT_MAX) { - OWDhistory_.pushBack(min_owd); - min_owd = INT_MAX; - - // compute queuing delay - queuing_delay = avg_owd - getMinOwd(); - - } else { - queuing_delay = 0.0; - } - - if (!received_packets_) - rounds_without_packets_++; - else - rounds_without_packets_ = 0; - received_packets_ = false; -} - -double RTCDataPath::getQueuingDealy() { return queuing_delay; } - -uint64_t RTCDataPath::getMinRtt() { return RTThistory_.begin(); } - -int64_t RTCDataPath::getMinOwd() { return OWDhistory_.begin(); } - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/rtc_data_path.h b/libtransport/src/protocols/rtc_data_path.h deleted file mode 100644 index 9076b355f..000000000 --- a/libtransport/src/protocols/rtc_data_path.h +++ /dev/null @@ -1,80 +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 <stdint.h> -#include <climits> - -#include <utils/min_filter.h> - -#define ALPHA_RTC 0.125 -#define HISTORY_LEN 20 // 4 sec - -namespace transport { - -namespace protocol { - -class RTCDataPath { - public: - RTCDataPath(); - - public: - void insertRttSample(uint64_t rtt); - void insertOwdSample(int64_t owd); - void computeInterArrivalGap(uint32_t segmentNumber); - void receivedNack(); - - uint64_t getMinRtt(); - double getQueuingDealy(); - double getInterArrivalGap(); - bool isActive(); - - void roundEnd(); - - private: - int64_t getMinOwd(); - - uint64_t min_rtt; - uint64_t prev_min_rtt; - - int64_t min_owd; - int64_t prev_min_owd; - - double avg_owd; - - double queuing_delay; - - uint32_t lastRecvSeq_; - uint64_t lastRecvTime_; - double avg_inter_arrival_; - - // 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 - // for MAX_ROUNDS_WITHOUT_PKTS - // we consider the path inactive - - utils::MinFilter<uint64_t> RTThistory_; - utils::MinFilter<int64_t> OWDhistory_; -}; - -} // namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/transport_protocol.cc b/libtransport/src/protocols/transport_protocol.cc new file mode 100644 index 000000000..29d140454 --- /dev/null +++ b/libtransport/src/protocols/transport_protocol.cc @@ -0,0 +1,233 @@ +/* + * 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/transport_protocol.h> + +namespace transport { + +namespace protocol { + +using namespace interface; + +TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, + Indexer *indexer, Reassembly *reassembly) + : Protocol(), + socket_(icn_socket), + indexer_verifier_(indexer), + reassembly_(reassembly), + fec_decoder_(nullptr), + is_first_(false), + on_interest_retransmission_(VOID_HANDLER), + on_interest_output_(VOID_HANDLER), + on_interest_timeout_(VOID_HANDLER), + 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) { + socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); + socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); + + 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 (isRunning()) { + return -1; + } + + // 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_); + socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_); + + // 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(); + }); + + return 0; +} + +void TransportProtocol::resume() { + if (isRunning()) return; + + setRunning(); + + portal_->getThread().tryRunHandlerNow([this]() { scheduleNextInterests(); }); +} + +void TransportProtocol::reset() { + reassembly_->reInitialize(); + indexer_verifier_->reset(); + if (fec_decoder_) { + fec_decoder_->reset(); + } +} + +void TransportProtocol::onContentReassembled(const std::error_code &ec) { + stop(); + + if (!on_payload_) { + throw errors::RuntimeException( + "The read callback must be installed in the transport before " + "starting " + "the content retrieval."); + } + + if (!ec) { + on_payload_->readSuccess(stats_->getBytesRecv()); + } else { + on_payload_->readError(ec); + } +} + +void TransportProtocol::sendInterest( + const Name &interest_name, + std::array<uint32_t, MAX_AGGREGATED_INTEREST> *additional_suffixes, + uint32_t len) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending interest for name " << interest_name; + + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + size_t signature_size = 0; + + // If aggregated interest, add spapce for signature + if (len > 0) { + format = Packet::toAHFormat(format); + signature_size = signer_->getSignatureFieldSize(); + } + + auto interest = core::PacketManager<>::getInstance().getPacket<Interest>( + format, signature_size); + interest->setName(interest_name); + + for (uint32_t i = 0; i < len; i++) { + interest->appendSuffix(additional_suffixes->at(i)); + } + + uint32_t lifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + lifetime); + interest->setLifetime(uint32_t(lifetime)); + + if (*on_interest_output_) { + (*on_interest_output_)(*socket_->getInterface(), *interest); + } + + if (TRANSPORT_EXPECT_FALSE(!isRunning() && !is_first_)) { + return; + } + + bool content_sharing_mode; + socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE, + content_sharing_mode); + if (content_sharing_mode) lifetime = ceil((double)lifetime * 0.9); + + // Compute signature + bool is_ah = HICN_PACKET_FORMAT_IS_AH(interest->getFormat()); + if (is_ah) signer_->signPacket(interest.get()); + + portal_->sendInterest(interest, lifetime); +} + +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; + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Timeout on content " << n; + + onInterestTimeout(i, n); +} + +void TransportProtocol::onContentObject(Interest &i, ContentObject &c) { + // Check whether it makes sense to continue + if (TRANSPORT_EXPECT_FALSE(!isRunning())) { + return; + } + + // Call transport protocol function + std::error_code ec; + onContentObjectReceived(i, c, ec); + + // Call reassemble function, if packet is eligible for reassemblying + bool reassemble = false; + if (!ec) { + reassemble = true; + } + + // Perform verification and update indexer. This step may be performed offline + // - i.e. we may not get a result here (e.g. we use manifest). Verification + // failures in that case will be handled in the onPacketDropped function. + // XXX This step should be done before calling onContentObjectReceived, but + // for now we do it here since currently the indexer does not need manifests + // to move forward. + indexer_verifier_->onContentObject(i, c, reassemble); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/transport_protocol.h b/libtransport/src/protocols/transport_protocol.h new file mode 100644 index 000000000..e71992561 --- /dev/null +++ b/libtransport/src/protocols/transport_protocol.h @@ -0,0 +1,150 @@ +/* + * 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/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/statistics.h> +#include <hicn/transport/utils/object_pool.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> +#include <atomic> + +namespace transport { + +namespace protocol { + +using namespace core; + +class IndexVerificationManager; + +using ReadCallback = interface::ConsumerSocket::ReadCallback; + +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; + + public: + TransportProtocol(implementation::ConsumerSocket *icn_socket, + Indexer *indexer, Reassembly *reassembly); + + virtual ~TransportProtocol(); + + virtual int start(); + + using Protocol::stop; + + virtual void resume(); + + /** + * @brief Get the size of any additional header added by the specific + * transport implementation. + * + * @return The header length in bytes. + */ + virtual std::size_t transportHeaderLength(bool isFEC) { return 0; } + + virtual void scheduleNextInterests() = 0; + + // Events generated by the indexing + virtual void onContentReassembled(const std::error_code &ec); + virtual void onPacketDropped(Interest &interest, + ContentObject &content_object, + const std::error_code &ec) override = 0; + virtual void onReassemblyFailed(std::uint32_t missing_segment) override = 0; + + protected: + virtual void onContentObjectReceived(Interest &i, ContentObject &c, + std::error_code &ec) = 0; + virtual void onInterestTimeout(Interest::Ptr &i, const Name &n) = 0; + + virtual void sendInterest(const Name &interest_name, + std::array<uint32_t, MAX_AGGREGATED_INTEREST> + *additional_suffixes = nullptr, + uint32_t len = 0); + + template <typename FECHandler, typename AllocatorHandler> + void enableFEC(FECHandler &&fec_handler, + AllocatorHandler &&allocator_handler) { + if (!fec_decoder_) { + // Try to get FEC from environment + 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); + } + + if (fec_type_ == fec::FECType::UNKNOWN) { + return; + } + + fec_decoder_ = fec::FECUtils::getDecoder( + fec_type_, indexer_verifier_->getFirstSuffix()); + fec_decoder_->setFECCallback(std::forward<FECHandler>(fec_handler)); + fec_decoder_->setBufferCallback( + std::forward<AllocatorHandler>(allocator_handler)); + indexer_verifier_->enableFec(fec_type_); + } + } + + virtual void reset(); + + private: + // Consumer Callback + void onContentObject(Interest &i, ContentObject &c) override; + void onTimeout(Interest::Ptr &i, const Name &n) 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_; + // True if it si the first time we schedule an interest + std::atomic<bool> is_first_; + interface::TransportStatistics *stats_; + + // Callbacks + interface::ConsumerInterestCallback *on_interest_retransmission_; + interface::ConsumerInterestCallback *on_interest_output_; + interface::ConsumerInterestCallback *on_interest_timeout_; + interface::ConsumerInterestCallback *on_interest_satisfied_; + 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_; + + // Signer for aggregated interests + std::shared_ptr<auth::Signer> signer_; +}; + +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/verification_manager.cc b/libtransport/src/protocols/verification_manager.cc deleted file mode 100644 index 8eedd6106..000000000 --- a/libtransport/src/protocols/verification_manager.cc +++ /dev/null @@ -1,101 +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/interfaces/socket_consumer.h> -#include <hicn/transport/security/verifier.h> - -#include <implementation/socket_consumer.h> -#include <protocols/verification_manager.h> - -namespace transport { - -namespace protocol { - -interface::VerificationPolicy SignatureVerificationManager::onPacketToVerify( - const Packet& packet) { - using namespace interface; - - bool verify_signature = false, key_content = false; - VerificationPolicy ret = VerificationPolicy::DROP_PACKET; - - icn_socket_->getSocketOption(GeneralTransportOptions::VERIFY_SIGNATURE, - verify_signature); - icn_socket_->getSocketOption(GeneralTransportOptions::KEY_CONTENT, - key_content); - - if (!verify_signature) { - return VerificationPolicy::ACCEPT_PACKET; - } - - if (key_content) { - key_packets_.push(copyPacket(packet)); - return VerificationPolicy::ACCEPT_PACKET; - } else if (!key_packets_.empty()) { - std::queue<ContentObjectPtr>().swap(key_packets_); - } - - ConsumerContentObjectVerificationFailedCallback* - verification_failed_callback = VOID_HANDLER; - icn_socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED, - &verification_failed_callback); - - if (!verification_failed_callback) { - throw errors::RuntimeException( - "No verification failed callback provided by application. " - "Aborting."); - } - - std::shared_ptr<utils::Verifier> verifier; - icn_socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier); - - if (TRANSPORT_EXPECT_FALSE(!verifier)) { - ret = (*verification_failed_callback)( - *icn_socket_->getInterface(), - dynamic_cast<const ContentObject&>(packet), - make_error_code(protocol_error::no_verifier_provided)); - return ret; - } - - if (!verifier->verify(packet)) { - ret = (*verification_failed_callback)( - *icn_socket_->getInterface(), - dynamic_cast<const ContentObject&>(packet), - make_error_code(protocol_error::signature_verification_failed)); - } else { - ret = VerificationPolicy::ACCEPT_PACKET; - } - - return ret; -} - -bool SignatureVerificationManager::onKeyToVerify() { - if (TRANSPORT_EXPECT_FALSE(key_packets_.empty())) { - throw errors::RuntimeException("No key to verify."); - } - - while (!key_packets_.empty()) { - ContentObjectPtr packet_to_verify = key_packets_.front(); - key_packets_.pop(); - if (onPacketToVerify(*packet_to_verify) != - VerificationPolicy::ACCEPT_PACKET) - return false; - } - - return true; -} - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/protocols/verification_manager.h b/libtransport/src/protocols/verification_manager.h deleted file mode 100644 index 7d8a00a65..000000000 --- a/libtransport/src/protocols/verification_manager.h +++ /dev/null @@ -1,71 +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/interfaces/callbacks.h> -#include <hicn/transport/interfaces/verification_policy.h> - -#include <hicn/transport/core/content_object.h> - -#include <protocols/errors.h> - -#include <queue> - -namespace transport { - -namespace interface { -class ConsumerSocket; -} - -namespace protocol { - -using Packet = core::Packet; -using interface::VerificationPolicy; -using ContentObjectPtr = std::shared_ptr<core::ContentObject>; - -class VerificationManager { - public: - virtual ~VerificationManager() = default; - virtual VerificationPolicy onPacketToVerify(const Packet& packet) = 0; - virtual bool onKeyToVerify() { return false; } -}; - -class SignatureVerificationManager : public VerificationManager { - public: - SignatureVerificationManager(implementation::ConsumerSocket* icn_socket) - : icn_socket_(icn_socket), key_packets_() {} - - interface::VerificationPolicy onPacketToVerify(const Packet& packet) override; - bool onKeyToVerify() override; - - private: - implementation::ConsumerSocket* icn_socket_; - std::queue<ContentObjectPtr> key_packets_; - - ContentObjectPtr copyPacket(const Packet& packet) { - std::shared_ptr<utils::MemBuf> packet_copy = - packet.acquireMemBufReference(); - ContentObjectPtr content_object_copy = - std::make_shared<core::ContentObject>(std::move(packet_copy)); - std::unique_ptr<utils::MemBuf> payload_copy = packet.getPayload(); - content_object_copy->appendPayload(std::move(payload_copy)); - return content_object_copy; - } -}; - -} // end namespace protocol - -} // end namespace transport diff --git a/libtransport/src/security/identity.cc b/libtransport/src/security/identity.cc deleted file mode 100644 index d7a08f7b5..000000000 --- a/libtransport/src/security/identity.cc +++ /dev/null @@ -1,115 +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/security/identity.h> - -extern "C" { -#include <parc/security/parc_PublicKeySigner.h> -#include <parc/security/parc_Security.h> -} - -namespace utils { - -Identity::Identity(const std::string &keystore_name, - const std::string &keystore_password, CryptoSuite suite, - unsigned int key_length, unsigned int validity_days, - const std::string &subject_name) { - parcSecurity_Init(); - - bool success = parcPkcs12KeyStore_CreateFile( - keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(), - parcCryptoSuite_GetSigningAlgorithm(static_cast<PARCCryptoSuite>(suite)), - key_length, validity_days); - - parcAssertTrue( - success, - "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d) failed.", - keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(), - static_cast<int>(key_length), validity_days); - - PARCIdentityFile *identity_file = - parcIdentityFile_Create(keystore_name.c_str(), keystore_password.c_str()); - - identity_ = - parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); - - PARCSigner *signer = parcIdentity_CreateSigner( - identity_, - parcCryptoSuite_GetCryptoHash(static_cast<PARCCryptoSuite>(suite))); - - signer_ = std::make_shared<Signer>(signer, suite); - - parcSigner_Release(&signer); - parcIdentityFile_Release(&identity_file); -} - -Identity::Identity(const Identity &other) - : signer_(other.signer_), hash_algorithm_(other.hash_algorithm_) { - parcSecurity_Init(); - identity_ = parcIdentity_Acquire(other.identity_); -} - -Identity Identity::generateIdentity(const std::string &subject_name) { - std::string keystore_name = "keystore"; - std::string keystore_password = "password"; - std::size_t key_length = 1024; - unsigned int validity_days = 30; - CryptoSuite suite = CryptoSuite::RSA_SHA256; - - return utils::Identity(keystore_name, keystore_password, suite, - (unsigned int)key_length, validity_days, subject_name); -} - -Identity::Identity(std::string &file_name, std::string &password, - utils::CryptoHashType hash_algorithm) - : hash_algorithm_(hash_algorithm) { - parcSecurity_Init(); - - PARCIdentityFile *identity_file = - parcIdentityFile_Create(file_name.c_str(), password.c_str()); - - identity_ = - parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); - - PARCSigner *signer = parcIdentity_CreateSigner( - identity_, static_cast<PARCCryptoHashType>(hash_algorithm)); - - signer_ = std::make_shared<Signer>( - signer, CryptoSuite(parcSigner_GetCryptoSuite(signer))); - - parcSigner_Release(&signer); - parcIdentityFile_Release(&identity_file); -} - -Identity::~Identity() { - parcIdentity_Release(&identity_); - parcSecurity_Fini(); -} - -std::string Identity::getFileName() { - return std::string(parcIdentity_GetFileName(identity_)); -} - -std::string Identity::getPassword() { - return std::string(parcIdentity_GetPassWord(identity_)); -} - -std::shared_ptr<Signer> Identity::getSigner() { return signer_; } - -size_t Identity::getSignatureLength() const { - return signer_->getSignatureLength(); -} - -} // namespace utils diff --git a/libtransport/src/security/signer.cc b/libtransport/src/security/signer.cc deleted file mode 100644 index aa2751611..000000000 --- a/libtransport/src/security/signer.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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. - */ - -#include <hicn/transport/errors/malformed_ahpacket_exception.h> -#include <hicn/transport/security/key_id.h> -#include <hicn/transport/security/signer.h> -#include <hicn/transport/utils/membuf.h> - -extern "C" { -#ifndef _WIN32 -TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") -#endif -#include <hicn/hicn.h> -#include <parc/security/parc_PublicKeySigner.h> -#include <parc/security/parc_Security.h> -#include <parc/security/parc_SymmetricKeySigner.h> -} - -#include <chrono> - -#define ALLOW_UNALIGNED_READS 1 - -namespace utils { - -Signer::Signer(PARCKeyStore *keyStore, CryptoSuite suite) { - parcSecurity_Init(); - - switch (suite) { - case CryptoSuite::DSA_SHA256: - case CryptoSuite::RSA_SHA256: - case CryptoSuite::RSA_SHA512: - case CryptoSuite::ECDSA_256K1: { - this->signer_ = - parcSigner_Create(parcPublicKeySigner_Create( - keyStore, static_cast<PARCCryptoSuite>(suite)), - PARCPublicKeySignerAsSigner); - break; - } - case CryptoSuite::HMAC_SHA256: - case CryptoSuite::HMAC_SHA512: { - this->signer_ = - parcSigner_Create(parcSymmetricKeySigner_Create( - (PARCSymmetricKeyStore *)keyStore, - parcCryptoSuite_GetCryptoHash( - static_cast<PARCCryptoSuite>(suite))), - PARCSymmetricKeySignerAsSigner); - break; - } - default: { return; } - } - - suite_ = suite; - key_id_ = parcSigner_CreateKeyId(this->signer_); - signature_length_ = parcSigner_GetSignatureSize(this->signer_); -} - -Signer::Signer(const std::string &passphrase, CryptoSuite suite) { - parcSecurity_Init(); - - switch (suite) { - case CryptoSuite::HMAC_SHA256: - case CryptoSuite::HMAC_SHA512: { - PARCBufferComposer *composer = parcBufferComposer_Create(); - parcBufferComposer_PutString(composer, passphrase.c_str()); - PARCBuffer *key_buffer = parcBufferComposer_ProduceBuffer(composer); - PARCSymmetricKeyStore *symmetricKeyStore = - parcSymmetricKeyStore_Create(key_buffer); - this->signer_ = parcSigner_Create( - parcSymmetricKeySigner_Create( - symmetricKeyStore, parcCryptoSuite_GetCryptoHash( - static_cast<PARCCryptoSuite>(suite))), - PARCSymmetricKeySignerAsSigner); - - parcBuffer_Release(&key_buffer); - parcSymmetricKeyStore_Release(&symmetricKeyStore); - parcBufferComposer_Release(&composer); - break; - } - default: { return; } - } - - suite_ = suite; - key_id_ = parcSigner_CreateKeyId(this->signer_); - signature_length_ = parcSigner_GetSignatureSize(this->signer_); -} - -Signer::Signer(const PARCSigner *signer, CryptoSuite suite) - : suite_(suite), - signer_(parcSigner_Acquire(signer)), - key_id_(parcSigner_CreateKeyId(this->signer_)), - signature_length_(parcSigner_GetSignatureSize(this->signer_)) { - parcSecurity_Init(); -} - -Signer::Signer(const PARCSigner *signer) - : Signer(signer, CryptoSuite::UNKNOWN) {} - -Signer::~Signer() { - if (signer_) parcSigner_Release(&signer_); - if (key_id_) parcKeyId_Release(&key_id_); - parcSecurity_Fini(); -} - -void Signer::sign(Packet &packet) { - /* header chain points to the IP + TCP hicn header + AH Header */ - MemBuf *header_chain = packet.header_head_; - MemBuf *payload_chain = packet.payload_head_; - uint8_t *hicn_packet = (uint8_t *)header_chain->writableData(); - Packet::Format format = packet.getFormat(); - - if (!(format & HFO_AH)) { - throw errors::MalformedAHPacketException(); - } - - packet.setSignatureSize(signature_length_); - - /* Copy IP+TCP/ICMP header before zeroing them */ - hicn_header_t header_copy; - hicn_packet_copy_header(format, (const hicn_header_t *)packet.packet_start_, - &header_copy, false); - - std::size_t header_len = Packet::getHeaderSizeFromFormat(format); - - packet.resetForHash(); - - /* Fill the hicn_ah header */ - using namespace std::chrono; - auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()) - .count(); - packet.setSignatureTimestamp(now); - packet.setValidationAlgorithm(suite_); - - KeyId key_id; - key_id.first = (uint8_t *)parcBuffer_Overlay( - (PARCBuffer *)parcKeyId_GetKeyId(this->key_id_), 0); - packet.setKeyId(key_id); - - /* Calculate hash */ - CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_)); - hasher.init(); - hasher.updateBytes(hicn_packet, header_len + signature_length_); - - for (MemBuf *current = payload_chain; current != header_chain; - current = current->next()) { - hasher.updateBytes(current->data(), current->length()); - } - - CryptoHash hash = hasher.finalize(); - PARCSignature *signature = parcSigner_SignDigestNoAlloc( - this->signer_, hash.hash_, packet.getSignature(), - (uint32_t)signature_length_); - PARCBuffer *buffer = parcSignature_GetSignature(signature); - size_t bytes_len = parcBuffer_Remaining(buffer); - - if (bytes_len > signature_length_) { - throw errors::MalformedAHPacketException(); - } - - hicn_packet_copy_header(format, &header_copy, - (hicn_header_t *)packet.packet_start_, false); - - parcSignature_Release(&signature); -} - -size_t Signer::getSignatureLength() { return signature_length_; } - -PARCKeyStore *Signer::getKeyStore() { - return parcSigner_GetKeyStore(this->signer_); -} - -} // namespace utils diff --git a/libtransport/src/security/verifier.cc b/libtransport/src/security/verifier.cc deleted file mode 100644 index 4f6a2be4c..000000000 --- a/libtransport/src/security/verifier.cc +++ /dev/null @@ -1,251 +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/core/packet.h> -#include <hicn/transport/errors/malformed_ahpacket_exception.h> -#include <hicn/transport/portability/portability.h> -#include <hicn/transport/security/key_id.h> -#include <hicn/transport/security/verifier.h> -#include <hicn/transport/utils/log.h> - -extern "C" { -#ifndef _WIN32 -TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") -#endif -#include <hicn/hicn.h> -} - -#include <sys/stat.h> - -namespace utils { - -TRANSPORT_ALWAYS_INLINE bool file_exists(const std::string &name) { - struct stat buffer; - return (stat(name.c_str(), &buffer) == 0); -} - -Verifier::Verifier() { - parcSecurity_Init(); - PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create(); - this->verifier_ = - parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier); - parcInMemoryVerifier_Release(&in_memory_verifier); -} - -Verifier::~Verifier() { - if (signer_) parcSigner_Release(&signer_); - if (verifier_) parcVerifier_Release(&verifier_); - parcSecurity_Fini(); -} - -/* - * TODO: Unsupported in libparc - */ -bool Verifier::hasKey(PARCKeyId *key_id) { return false; } - -/* - * TODO: Signal errors without trap. - */ -bool Verifier::addKey(PARCKey *key) { - parcVerifier_AddKey(this->verifier_, key); - return true; -} - -PARCKeyId *Verifier::addKeyFromPassphrase(const std::string &passphrase, - CryptoSuite suite) { - PARCBufferComposer *composer = parcBufferComposer_Create(); - parcBufferComposer_PutString(composer, passphrase.c_str()); - PARCBuffer *key_buffer = parcBufferComposer_ProduceBuffer(composer); - - PARCSymmetricKeyStore *symmetricKeyStore = - parcSymmetricKeyStore_Create(key_buffer); - signer_ = parcSigner_Create( - parcSymmetricKeySigner_Create( - symmetricKeyStore, - parcCryptoSuite_GetCryptoHash(static_cast<PARCCryptoSuite>(suite))), - PARCSymmetricKeySignerAsSigner); - - PARCKeyId *key_id = parcSigner_CreateKeyId(signer_); - PARCKey *key = parcKey_CreateFromSymmetricKey( - key_id, parcSigner_GetSigningAlgorithm(signer_), key_buffer); - - addKey(key); - - parcKey_Release(&key); - parcSymmetricKeyStore_Release(&symmetricKeyStore); - parcBuffer_Release(&key_buffer); - parcBufferComposer_Release(&composer); - - return key_id; -} - -PARCKeyId *Verifier::addKeyFromCertificate(const std::string &file_name) { - PARCCertificateFactory *factory = parcCertificateFactory_Create( - PARCCertificateType_X509, PARCContainerEncoding_PEM); - parcAssertNotNull(factory, "Expected non-NULL factory"); - - if (!file_exists(file_name)) { - TRANSPORT_LOGW("Warning! The certificate %s file does not exist", - file_name.c_str()); - return nullptr; - } - - PARCCertificate *certificate = - parcCertificateFactory_CreateCertificateFromFile( - factory, (char *)file_name.c_str(), NULL); - PARCBuffer *derEncodedVersion = - parcCertificate_GetDEREncodedPublicKey(certificate); - PARCCryptoHash *keyDigest = parcCertificate_GetPublicKeyDigest(certificate); - - PARCKeyId *key_id = parcKeyId_Create(parcCryptoHash_GetDigest(keyDigest)); - PARCKey *key = parcKey_CreateFromDerEncodedPublicKey( - key_id, PARCSigningAlgorithm_RSA, derEncodedVersion); - - addKey(key); - - parcKey_Release(&key); - parcCertificate_Release(&certificate); - parcCertificateFactory_Release(&factory); - - return key_id; -} - -int Verifier::verify(const Packet &packet) { - /* Initialize packet.payload_head_ */ - const_cast<Packet *>(&packet)->separateHeaderPayload(); - - bool valid = false; - Packet::Format format = packet.getFormat(); - - if (!(format & HFO_AH)) { - throw errors::MalformedAHPacketException(); - } - - /* Copy IP+TCP/ICMP header before zeroing them */ - hicn_header_t header_copy; - hicn_packet_copy_header(format, (const hicn_header_t *)packet.packet_start_, - &header_copy, false); - - /* Get crypto suite */ - PARCCryptoSuite suite = - static_cast<PARCCryptoSuite>(packet.getValidationAlgorithm()); - PARCCryptoHashType hashtype = parcCryptoSuite_GetCryptoHash(suite); - - /* Fetch the key that we will use to verify the signature */ - KeyId _key_id = packet.getKeyId(); - PARCBuffer *buffer = - parcBuffer_Wrap(_key_id.first, _key_id.second, 0, _key_id.second); - PARCKeyId *key_id = parcKeyId_Create(buffer); - parcBuffer_Release(&buffer); - - /* Fetch signature */ - int ah_payload_len = (int)packet.getSignatureSize(); - uint8_t *_signature = packet.getSignature(); - uint8_t *signature = new uint8_t[ah_payload_len]; - /* TODO Remove signature copy at this point, by not setting to zero */ - /* the validation payload. */ - std::memcpy(signature, _signature, ah_payload_len); - - /* Prepare local computation of the signature based on the crypto suite */ - PARCCryptoHasher *hasher_ptr = nullptr; - switch (CryptoSuite(suite)) { - case CryptoSuite::DSA_SHA256: - case CryptoSuite::RSA_SHA256: - case CryptoSuite::RSA_SHA512: - case CryptoSuite::ECDSA_256K1: { - hasher_ptr = parcVerifier_GetCryptoHasher(verifier_, key_id, hashtype); - break; - } - case CryptoSuite::HMAC_SHA256: - case CryptoSuite::HMAC_SHA512: { - if (!signer_) return false; - hasher_ptr = parcSigner_GetCryptoHasher(signer_); - break; - } - default: { return false; } - } - - /* Compute the packet signature locally */ - CryptoHasher crypto_hasher(hasher_ptr); - CryptoHash hash_computed_locally = getPacketHash(packet, crypto_hasher); - - /* Create a signature object from the raw packet signature */ - PARCBuffer *bits = - parcBuffer_Wrap(signature, ah_payload_len, 0, ah_payload_len); - parcBuffer_Rewind(bits); - - /* If the signature algo is ECDSA, the signature might be shorter than the - * signature field */ - PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite); - while (algo == PARCSigningAlgorithm_ECDSA && parcBuffer_HasRemaining(bits) && - parcBuffer_GetUint8(bits) == 0) - ; - - if (algo == PARCSigningAlgorithm_ECDSA) { - parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1); - } - - if (!parcBuffer_HasRemaining(bits)) { - delete[] signature; - parcKeyId_Release(&key_id); - parcBuffer_Release(&bits); - return valid; - } - - PARCSignature *signatureToVerify = parcSignature_Create( - parcCryptoSuite_GetSigningAlgorithm(suite), hashtype, bits); - - if (algo == PARCSigningAlgorithm_RSA) { - parcBuffer_SetPosition(bits, 0); - } - - /* Compare the packet signature to the locally computed one */ - valid = parcVerifier_VerifyDigestSignature( - verifier_, key_id, hash_computed_locally.hash_, suite, signatureToVerify); - - /* Restore the fields that were reset */ - hicn_packet_copy_header(format, &header_copy, - (hicn_header_t *)packet.packet_start_, false); - - delete[] signature; - parcKeyId_Release(&key_id); - parcBuffer_Release(&bits); - parcSignature_Release(&signatureToVerify); - - return valid; -} - -CryptoHash Verifier::getPacketHash(const Packet &packet, - CryptoHasher &crypto_hasher) { - MemBuf *header_chain = packet.header_head_; - MemBuf *payload_chain = packet.payload_head_; - Packet::Format format = packet.getFormat(); - int ah_payload_len = (int)packet.getSignatureSize(); - uint8_t *hicn_packet = header_chain->writableData(); - std::size_t header_len = Packet::getHeaderSizeFromFormat(format); - - /* Reset fields that should not appear in the signature */ - const_cast<Packet &>(packet).resetForHash(); - crypto_hasher.init().updateBytes(hicn_packet, header_len + ah_payload_len); - - for (MemBuf *current = payload_chain; current != header_chain; - current = current->next()) { - crypto_hasher.updateBytes(current->data(), current->length()); - } - - return crypto_hasher.finalize(); -} - -} // namespace utils diff --git a/libtransport/src/test/CMakeLists.txt b/libtransport/src/test/CMakeLists.txt index cdebfcbee..56edcd102 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,24 +11,71 @@ # 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_core_manifest.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 + test_quadloop.cc + test_prefix.cc + test_traffic_generator.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() -list(APPEND TESTS - test_core_manifest - test_transport_producer + +############################################################## +# Link libraries +############################################################## +set(MEMIF_MODULE_LIBRARIES + ${LIBRARIES} + ${LIBTRANSPORT_SHARED} + ${GTEST_LIBRARIES} ) -foreach(test ${TESTS}) - build_executable(${test} - NO_INSTALL - SOURCES ${test}.cc - LINK_LIBRARIES ${LIBTRANSPORT_SHARED} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} - DEPENDS gtest ${LIBTRANSPORT_SHARED} - COMPONENT lib${LIBTRANSPORT} - DEFINITIONS "${COMPILER_DEFINITIONS}" - ) +############################################################## +# Build single unit test executable and add it to test list +############################################################## +build_executable(libtransport_tests + NO_INSTALL + SOURCES ${TESTS_SRC} + LINK_LIBRARIES + ${MEMIF_MODULE_LIBRARIES} + INCLUDE_DIRS + $<TARGET_PROPERTY:${LIBTRANSPORT_SHARED},INCLUDE_DIRECTORIES> + ${GTEST_INCLUDE_DIRS} + DEPENDS gtest ${LIBTRANSPORT_SHARED} + COMPONENT ${LIBTRANSPORT_COMPONENT} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} + LINK_FLAGS ${LINK_FLAGS} +) - add_test_internal(${test}) -endforeach()
\ No newline at end of file +add_test_internal(libtransport_tests) diff --git a/libtransport/src/interfaces/rtc_socket_producer.cc b/libtransport/src/test/main.cc index 07d72db7e..591ed0d5b 100644 --- a/libtransport/src/interfaces/rtc_socket_producer.cc +++ b/libtransport/src/test/main.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,17 +13,9 @@ * limitations under the License. */ -#include <hicn/transport/interfaces/rtc_socket_producer.h> +#include <gtest/gtest.h> -#include <implementation/rtc_socket_producer.h> - -namespace transport { -namespace interface { - -RTCProducerSocket::RTCProducerSocket() : ProducerSocket(false) { - socket_ = std::make_unique<implementation::RTCProducerSocket>(this); -} - -} // namespace interface - -} // namespace transport +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/libtransport/src/test/packet_samples.h b/libtransport/src/test/packet_samples.h new file mode 100644 index 000000000..216ac7f9f --- /dev/null +++ b/libtransport/src/test/packet_samples.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#define TCP_PROTO 0x06 +#define ICMP_PROTO 0x01 +#define ICMP6_PROTO 0x3a + +#define IPV6_HEADER(next_header, payload_length) \ + 0x60, 0x00, 0x00, 0x00, 0x00, payload_length, next_header, 0x40, 0xb0, 0x06, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd, 0xab, \ + 0xcd, 0xef, 0xb0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xca + +#define IPV4_HEADER(next_header, payload_length) \ + 0x45, 0x02, 0x00, payload_length + 20, 0x47, 0xc4, 0x40, 0x00, 0x25, \ + next_header, 0x6e, 0x76, 0x03, 0x7b, 0xd9, 0xd0, 0xc0, 0xa8, 0x01, 0x5c + +#define TCP_HEADER(flags) \ + 0x12, 0x34, 0x43, 0x21, 0x00, 0x00, 0x00, 0x01, 0xb2, 0x8c, 0x03, 0x1f, \ + 0x80, flags, 0x00, 0x0a, 0xb9, 0xbb, 0x00, 0x00 + +#define PAYLOAD \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x20, 0x00, 0x00 + +#define PAYLOAD_SIZE 12 + +#define ICMP_ECHO_REQUEST \ + 0x08, 0x00, 0x87, 0xdb, 0x38, 0xa7, 0x00, 0x05, 0x60, 0x2b, 0xc2, 0xcb, \ + 0x00, 0x02, 0x29, 0x7c, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, \ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, \ + 0x34, 0x35, 0x36, 0x37 + +#define ICMP6_ECHO_REQUEST \ + 0x80, 0x00, 0x86, 0x3c, 0x11, 0x0d, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, \ + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, \ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, \ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33 + +#define AH_HEADER \ + 0x00, (128 >> 2), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +#define SIGNATURE \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/libtransport/src/test/test_aggregated_header.cc b/libtransport/src/test/test_aggregated_header.cc new file mode 100644 index 000000000..4dd71f60d --- /dev/null +++ b/libtransport/src/test/test_aggregated_header.cc @@ -0,0 +1,624 @@ +/* + * 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 + +#if 0 +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]); + } +} +#endif + +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 new file mode 100644 index 000000000..5440d3741 --- /dev/null +++ b/libtransport/src/test/test_auth.cc @@ -0,0 +1,325 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> +#include <hicn/transport/auth/crypto_hash.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 { + +namespace { +class AuthTest : public ::testing::Test { + protected: + const std::string PASSPHRASE = "hunter2"; + + AuthTest() = default; + ~AuthTest() {} + void SetUp() override {} + void TearDown() override {} +}; +} // namespace + +TEST_F(AuthTest, VoidVerifier) { + // Create a content object + core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + + // Verify that VoidVerifier validates the packet + std::shared_ptr<Verifier> verifier = std::make_shared<VoidVerifier>(); + EXPECT_EQ(verifier->verifyPacket(&packet), true); + EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); +} + +TEST_F(AuthTest, AsymmetricRSA) { + // 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(HICN_PACKET_FORMAT_IPV6_TCP_AH, + signer->getSignatureSize()); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + + // Sign the packet + signer->signPacket(&packet); + + // Create the RSA verifier + std::shared_ptr<Verifier> verifier = + std::make_shared<AsymmetricVerifier>(pubKey); + + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH); + EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); + EXPECT_EQ(signer->getSuite(), CryptoSuite::RSA_SHA256); + EXPECT_EQ(signer->getSignatureSize(), 256u); + EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); +} + +TEST_F(AuthTest, AsymmetricBufferRSA) { + // 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::string payload = "bonjour"; + + std::vector<uint8_t> buffer(payload.begin(), payload.end()); + signer->signBuffer(buffer); + utils::MemBuf::Ptr sig = signer->getSignature(); + + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256); + EXPECT_EQ(res, true); +} + +TEST_F(AuthTest, AsymmetricBufferDSA) { + // 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::string payload = "bonjour"; + + std::vector<uint8_t> buffer(payload.begin(), payload.end()); + signer->signBuffer(buffer); + utils::MemBuf::Ptr sig = signer->getSignature(); + + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256); + EXPECT_EQ(res, true); +} + +TEST_F(AuthTest, AsymmetricVerifierDSA) { + // 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); + + // Create a content object + core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH, + signer->getSignatureSize()); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + // this test has to be done before the signature is compute + // EXPECT_EQ(signer->getSignatureSize(), 256u); + signer->signPacket(&packet); + std::shared_ptr<Verifier> verifier = + std::make_shared<AsymmetricVerifier>(cert); + + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH); + EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); + EXPECT_EQ(signer->getSuite(), CryptoSuite::DSA_SHA256); + EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); +} + +TEST_F(AuthTest, AsymmetricBufferECDSA) { + // 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::string payload = "bonjour"; + + std::vector<uint8_t> buffer(payload.begin(), payload.end()); + signer->signBuffer(buffer); + utils::MemBuf::Ptr sig = signer->getSignature(); + + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256); + EXPECT_EQ(res, true); +} // namespace auth + +TEST_F(AuthTest, AsymmetricVerifierECDSA) { + // 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(HICN_PACKET_FORMAT_IPV6_TCP_AH, + signer->getSignatureSize()); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + signer->signPacket(&packet); + + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH); + EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); + EXPECT_EQ(signer->getSuite(), CryptoSuite::ECDSA_SHA256); + EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); + } +} + +TEST_F(AuthTest, HMACbuffer) { + // Create the HMAC signer from a passphrase + std::shared_ptr<Signer> signer = + std::make_shared<SymmetricSigner>(CryptoSuite::HMAC_SHA256, PASSPHRASE); + + // Create a content object + core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH, + signer->getSignatureSize()); + + std::string payload = "bonjour"; + std::vector<uint8_t> buffer(payload.begin(), payload.end()); + signer->signBuffer(buffer); + utils::MemBuf::Ptr sig = signer->getSignature(); + SymmetricVerifier hmac(PASSPHRASE); + bool res = hmac.verifyBuffer(buffer, sig, CryptoSuite::RSA_SHA256); + EXPECT_EQ(res, true); +} + +TEST_F(AuthTest, HMACVerifier) { + // Create the HMAC signer from a passphrase + std::shared_ptr<SymmetricSigner> signer = + std::make_shared<SymmetricSigner>(CryptoSuite::HMAC_SHA256, PASSPHRASE); + + // Create a content object + core::ContentObject packet(HICN_PACKET_FORMAT_IPV6_TCP_AH, + signer->getSignatureSize()); + + // Fill it with bogus data + uint8_t buffer[256] = {0}; + packet.appendPayload(buffer, 256); + + // Sign the packet + signer->signPacket(&packet); + + // Create the HMAC verifier + std::shared_ptr<Verifier> verifier = + std::make_shared<SymmetricVerifier>(PASSPHRASE); + + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP_AH); + EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); + EXPECT_EQ(signer->getSuite(), CryptoSuite::HMAC_SHA256); + EXPECT_EQ(signer->getSignatureSize(), 32u); + EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); +} + +} // namespace auth +} // namespace transport diff --git a/libtransport/src/test/test_consumer_producer_rtc.cc b/libtransport/src/test/test_consumer_producer_rtc.cc new file mode 100644 index 000000000..d7378fc72 --- /dev/null +++ b/libtransport/src/test/test_consumer_producer_rtc.cc @@ -0,0 +1,172 @@ +/* + * 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/asio_wrapper.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 { + +namespace { + +class IoModuleInit { + public: + IoModuleInit() { + global_config::IoModuleConfiguration config; + config.name = "forwarder_module"; + config.set(); + } +}; + +static IoModuleInit init; + +class ConsumerProducerTest : public ::testing::Test, + public ConsumerSocket::ReadCallback { + static const constexpr char prefix[] = "b001::1/128"; + static const constexpr char name[] = "b001::1"; + static const constexpr double prod_rate = 1.0e6; + static const constexpr size_t payload_size = 1200; + static constexpr std::size_t receive_buffer_size = 1500; + static const constexpr double prod_interval_microseconds = + double(payload_size) * 8 * 1e6 / prod_rate; + + public: + ConsumerProducerTest() + : io_service_(), + rtc_timer_(io_service_), + stop_timer_(io_service_), + consumer_(TransportProtocolAlgorithms::RTC), + producer_(ProductionProtocolAlgorithms::RTC_PROD), + producer_prefix_(prefix), + consumer_name_(name), + packets_sent_(0), + packets_received_(0) {} + + virtual ~ConsumerProducerTest() { + // 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() override { + // Code here will be called immediately after the constructor (right + // before each test). + + auto ret = consumer_.setSocketOption( + ConsumerCallbacksOptions::READ_CALLBACK, this); + ASSERT_EQ(ret, SOCKET_OPTION_SET); + + consumer_.connect(); + producer_.registerPrefix(producer_prefix_); + producer_.connect(); + producer_.start(); + } + + virtual void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } + + void setTimer() { + using namespace std::chrono; + rtc_timer_.expires_from_now( + microseconds(unsigned(prod_interval_microseconds))); + rtc_timer_.async_wait(std::bind(&ConsumerProducerTest::produceRTCPacket, + this, std::placeholders::_1)); + } + + void setStopTimer() { + using namespace std::chrono; + stop_timer_.expires_from_now(seconds(unsigned(10))); + stop_timer_.async_wait( + std::bind(&ConsumerProducerTest::stop, this, std::placeholders::_1)); + } + + void produceRTCPacket(const std::error_code &ec) { + if (ec) { + io_service_.stop(); + } + + producer_.produceDatagram(consumer_name_, payload_, payload_size); + packets_sent_++; + setTimer(); + } + + void stop(const std::error_code &ec) { + rtc_timer_.cancel(); + producer_.stop(); + consumer_.stop(); + } + + // Consumer callback + bool isBufferMovable() noexcept override { return false; } + + void getReadBuffer(uint8_t **application_buffer, + size_t *max_length) override { + *application_buffer = receive_buffer_; + *max_length = receive_buffer_size; + } + + void readDataAvailable(std::size_t length) noexcept override { + packets_received_++; + } + + size_t maxBufferSize() const override { return receive_buffer_size; } + + 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 {} + + asio::io_service io_service_; + asio::steady_timer rtc_timer_; + asio::steady_timer stop_timer_; + ConsumerSocket consumer_; + ProducerSocket producer_; + core::Prefix producer_prefix_; + core::Name consumer_name_; + uint8_t payload_[payload_size]; + uint8_t receive_buffer_[payload_size]; + + uint64_t packets_sent_; + uint64_t packets_received_; +}; + +const char ConsumerProducerTest::prefix[]; +const char ConsumerProducerTest::name[]; + +} // namespace + +TEST_F(ConsumerProducerTest, EndToEnd) { + produceRTCPacket(std::error_code()); + consumer_.consume(consumer_name_); + setStopTimer(); + + io_service_.run(); + + std::cout << "Packet received: " << packets_received_ << std::endl; + std::cout << "Packet sent: " << packets_sent_ << std::endl; +} + +} // namespace interface + +} // namespace transport diff --git a/libtransport/src/test/test_core_manifest.cc b/libtransport/src/test/test_core_manifest.cc index c88ca347b..99c71a56c 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-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,11 +13,15 @@ * limitations under the License. */ +#include <core/manifest.h> #include <core/manifest_format_fixed.h> -#include <core/manifest_inline.h> #include <gtest/gtest.h> -#include <hicn/transport/security/crypto_hash_type.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> #include <random> #include <vector> @@ -29,10 +33,14 @@ namespace { // The fixture for testing class Foo. class ManifestTest : public ::testing::Test { protected: - using ContentObjectManifest = ManifestInline<ContentObject, Fixed>; - - ManifestTest() : name_("b001::123|321"), manifest1_(name_) { - // You can do set-up work for each test here. + using ContentObjectManifest = Manifest<Fixed>; + + ManifestTest() + : format_(HICN_PACKET_FORMAT_IPV6_TCP_AH), + name_("b001::123|321"), + signature_size_(0) { + manifest_ = ContentObjectManifest::createContentManifest(format_, name_, + signature_size_); } virtual ~ManifestTest() { @@ -52,10 +60,11 @@ class ManifestTest : public ::testing::Test { // before the destructor). } + Packet::Format format_; Name name_; - ContentObjectManifest manifest1_; - - std::vector<uint8_t> manifest_payload = { + std::size_t signature_size_; + std::shared_ptr<ContentObjectManifest> manifest_; + std::vector<uint8_t> manifest_payload_ = { 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00, // 0x00, 0x45, 0xa3, @@ -71,179 +80,233 @@ class ManifestTest : public ::testing::Test { } // namespace -TEST_F(ManifestTest, SetLastManifest) { - manifest1_.clear(); - - manifest1_.setFinalManifest(true); - bool fcn = manifest1_.isFinalManifest(); - - ASSERT_TRUE(fcn == true); +TEST_F(ManifestTest, ManifestConstructor) { + // Create content object with manifest in payload + ContentObject::Ptr co = + core::PacketManager<>::getInstance().getPacket<ContentObject>( + format_, signature_size_); + co->setName(name_); + co->appendPayload(manifest_payload_.data(), manifest_payload_.size()); + + uint8_t buffer[256] = {0}; + co->appendPayload(buffer, 256); + + // Copy packet payload + uint8_t packet[1500]; + auto length = co->getPayload()->length(); + std::memcpy(packet, co->getPayload()->data(), length); + + // Create manifest + ContentObjectManifest manifest(co); + + // Check manifest payload is exactly the same of content object + ASSERT_EQ(length, manifest.getPacket()->getPayload()->length()); + auto ret = + std::memcmp(packet, manifest.getPacket()->getPayload()->data(), length); + ASSERT_EQ(ret, 0); } TEST_F(ManifestTest, SetManifestType) { - manifest1_.clear(); + manifest_->Encoder::clear(); ManifestType type1 = ManifestType::INLINE_MANIFEST; ManifestType type2 = ManifestType::FLIC_MANIFEST; - manifest1_.setManifestType(type1); - ManifestType type_returned1 = manifest1_.getManifestType(); + manifest_->setType(type1); + ManifestType type_returned1 = manifest_->getType(); - manifest1_.clear(); + manifest_->Encoder::clear(); - manifest1_.setManifestType(type2); - ManifestType type_returned2 = manifest1_.getManifestType(); + manifest_->setType(type2); + ManifestType type_returned2 = manifest_->getType(); ASSERT_EQ(type1, type_returned1); ASSERT_EQ(type2, type_returned2); } +TEST_F(ManifestTest, SetMaxCapacity) { + manifest_->Encoder::clear(); + + uint8_t max_capacity1 = 0; + uint8_t max_capacity2 = 20; + + manifest_->setMaxCapacity(max_capacity1); + uint8_t max_capacity_returned1 = manifest_->getMaxCapacity(); + + manifest_->Encoder::clear(); + + manifest_->setMaxCapacity(max_capacity2); + uint8_t max_capacity_returned2 = manifest_->getMaxCapacity(); + + ASSERT_EQ(max_capacity1, max_capacity_returned1); + ASSERT_EQ(max_capacity2, max_capacity_returned2); +} + TEST_F(ManifestTest, SetHashAlgorithm) { - manifest1_.clear(); + manifest_->Encoder::clear(); - utils::CryptoHashType hash1 = utils::CryptoHashType::SHA_512; - utils::CryptoHashType hash2 = utils::CryptoHashType::CRC32C; - utils::CryptoHashType hash3 = utils::CryptoHashType::SHA_256; + auth::CryptoHashType hash1 = auth::CryptoHashType::SHA256; + auth::CryptoHashType hash2 = auth::CryptoHashType::SHA512; + auth::CryptoHashType hash3 = auth::CryptoHashType::BLAKE2B512; - manifest1_.setHashAlgorithm(hash1); - auto type_returned1 = manifest1_.getHashAlgorithm(); + manifest_->setHashAlgorithm(hash1); + auto type_returned1 = manifest_->getHashAlgorithm(); - manifest1_.clear(); + manifest_->Encoder::clear(); - manifest1_.setHashAlgorithm(hash2); - auto type_returned2 = manifest1_.getHashAlgorithm(); + manifest_->setHashAlgorithm(hash2); + auto type_returned2 = manifest_->getHashAlgorithm(); - manifest1_.clear(); + manifest_->Encoder::clear(); - manifest1_.setHashAlgorithm(hash3); - auto type_returned3 = manifest1_.getHashAlgorithm(); + manifest_->setHashAlgorithm(hash3); + auto type_returned3 = manifest_->getHashAlgorithm(); ASSERT_EQ(hash1, type_returned1); ASSERT_EQ(hash2, type_returned2); ASSERT_EQ(hash3, type_returned3); } -TEST_F(ManifestTest, SetNextSegmentCalculationStrategy) { - manifest1_.clear(); - - NextSegmentCalculationStrategy strategy1 = - NextSegmentCalculationStrategy::INCREMENTAL; +TEST_F(ManifestTest, SetLastManifest) { + manifest_->Encoder::clear(); - manifest1_.setNextSegmentCalculationStrategy(strategy1); - NextSegmentCalculationStrategy type_returned1 = - manifest1_.getNextSegmentCalculationStrategy(); + manifest_->setIsLast(true); + bool is_last = manifest_->getIsLast(); - ASSERT_EQ(strategy1, type_returned1); + ASSERT_TRUE(is_last); } TEST_F(ManifestTest, SetBaseName) { - manifest1_.clear(); + manifest_->Encoder::clear(); core::Name base_name("b001::dead"); - manifest1_.setBaseName(base_name); - core::Name ret_name = manifest1_.getBaseName(); + + manifest_->setBaseName(base_name); + core::Name ret_name = manifest_->getBaseName(); ASSERT_EQ(base_name, ret_name); } -TEST_F(ManifestTest, SetSuffixList) { - manifest1_.clear(); +TEST_F(ManifestTest, setParamsBytestream) { + manifest_->Encoder::clear(); - core::Name base_name("b001::dead"); + ParamsBytestream params{ + .final_segment = 0x0a, + }; - using random_bytes_engine = - std::independent_bits_engine<std::default_random_engine, CHAR_BIT, - unsigned char>; - random_bytes_engine rbe; + manifest_->setParamsBytestream(params); + auth::CryptoHash hash(auth::CryptoHashType::SHA256); + hash.computeDigest({0x01, 0x02, 0x03, 0x04}); + manifest_->addEntry(1, hash); - std::default_random_engine eng((std::random_device())()); - std::uniform_int_distribution<uint64_t> idis( - 0, std::numeric_limits<uint32_t>::max()); + manifest_->encode(); + manifest_->decode(); - auto entries = new std::pair<uint32_t, utils::CryptoHash>[3]; - uint32_t suffixes[3]; - std::vector<unsigned char> data[3]; + auto transport_type_returned = manifest_->getTransportType(); + auto params_returned = manifest_->getParamsBytestream(); - for (int i = 0; i < 3; i++) { - data[i].resize(32); - std::generate(std::begin(data[i]), std::end(data[i]), std::ref(rbe)); - suffixes[i] = idis(eng); - entries[i] = std::make_pair( - suffixes[i], utils::CryptoHash(data[i].data(), data[i].size(), - utils::CryptoHashType::SHA_256)); - manifest1_.addSuffixHash(entries[i].first, entries[i].second); - } - - manifest1_.setBaseName(base_name); + ASSERT_EQ(interface::ProductionProtocolAlgorithms::BYTE_STREAM, + transport_type_returned); + ASSERT_EQ(params, params_returned); +} - core::Name ret_name = manifest1_.getBaseName(); +TEST_F(ManifestTest, SetParamsRTC) { + manifest_->Encoder::clear(); - // auto & hash_list = manifest1_.getSuffixHashList(); + ParamsRTC params{ + .timestamp = 0x0a, + .prod_rate = 0x0b, + .prod_seg = 0x0c, + .fec_type = protocol::fec::FECType::UNKNOWN, + }; - // bool cond; - // int i = 0; + manifest_->setParamsRTC(params); + auth::CryptoHash hash(auth::CryptoHashType::SHA256); + hash.computeDigest({0x01, 0x02, 0x03, 0x04}); + manifest_->addEntry(1, hash); - // for (auto & item : manifest1_.getSuffixList()) { - // auto hash = manifest1_.getHash(suffixes[i]); - // cond = utils::CryptoHash::compareBinaryDigest(hash, - // entries[i].second.getDigest<uint8_t>().data(), - // entries[i].second.getType()); - // ASSERT_TRUE(cond); - // i++; - // } + manifest_->encode(); + manifest_->decode(); - ASSERT_EQ(base_name, ret_name); + auto transport_type_returned = manifest_->getTransportType(); + auto params_returned = manifest_->getParamsRTC(); - delete[] entries; + ASSERT_EQ(interface::ProductionProtocolAlgorithms::RTC_PROD, + transport_type_returned); + ASSERT_EQ(params, params_returned); } -TEST_F(ManifestTest, EstimateSize) { - manifest1_.clear(); - - auto hash1 = utils::CryptoHashType::SHA_256; - NextSegmentCalculationStrategy strategy1 = - NextSegmentCalculationStrategy::INCREMENTAL; - ManifestType type1 = ManifestType::INLINE_MANIFEST; - core::Name base_name1("b001:abcd:fede:baba:cece:d0d0:face:dead"); - - manifest1_.setFinalManifest(true); - manifest1_.setBaseName(base_name1); - manifest1_.setNextSegmentCalculationStrategy(strategy1); - manifest1_.setHashAlgorithm(hash1); - manifest1_.setManifestType(type1); +TEST_F(ManifestTest, SignManifest) { + auto signer = std::make_shared<auth::SymmetricSigner>( + auth::CryptoSuite::HMAC_SHA256, "hunter2"); + auto verifier = std::make_shared<auth::SymmetricVerifier>("hunter2"); + + // Instantiate manifest + uint8_t max_capacity = 30; + std::shared_ptr<ContentObjectManifest> manifest = + ContentObjectManifest::createContentManifest( + format_, name_, signer->getSignatureFieldSize()); + manifest->setHeaders(ManifestType::INLINE_MANIFEST, max_capacity, + signer->getHashType(), false /* is_last */, name_); + + // Add manifest entry + auth::CryptoHash hash(signer->getHashType()); + hash.computeDigest({0x01, 0x02, 0x03, 0x04}); + manifest->addEntry(1, hash); + + // Encode manifest + manifest->encode(); + auto manifest_co = + std::dynamic_pointer_cast<ContentObject>(manifest->getPacket()); + + // Sign manifest + signer->signPacket(manifest_co.get()); + + // Check size + ASSERT_EQ(manifest_co->payloadSize(), manifest->Encoder::manifestSize()); + ASSERT_EQ(manifest_co->length(), + manifest_co->headerSize() + manifest_co->payloadSize()); + + // Verify manifest + auth::VerificationPolicy policy = verifier->verifyPackets(manifest_co.get()); + ASSERT_EQ(auth::VerificationPolicy::ACCEPT, policy); +} - std::default_random_engine eng((std::random_device())()); - std::uniform_int_distribution<uint64_t> idis( - 0, std::numeric_limits<uint64_t>::max()); +TEST_F(ManifestTest, SetSuffixList) { + manifest_->Encoder::clear(); using random_bytes_engine = std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned char>; random_bytes_engine rbe; - while (manifest1_.estimateManifestSize(1) < 1440) { - uint32_t suffix = static_cast<std::uint32_t>(idis(eng)); - std::vector<unsigned char> data(32); - std::generate(std::begin(data), std::end(data), std::ref(rbe)); - auto hash = utils::CryptoHash(data.data(), data.size(), - utils::CryptoHashType::SHA_256); - manifest1_.addSuffixHash(suffix, hash); + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution<uint64_t> idis( + 0, std::numeric_limits<uint32_t>::max()); + + auto entries = new std::pair<uint32_t, auth::CryptoHash>[3]; + uint32_t suffixes[3]; + std::vector<unsigned char> data[3]; + + for (int i = 0; i < 3; i++) { + data[i].resize(32); + std::generate(std::begin(data[i]), std::end(data[i]), std::ref(rbe)); + suffixes[i] = idis(eng); + entries[i] = std::make_pair(suffixes[i], + auth::CryptoHash(data[i].data(), data[i].size(), + auth::CryptoHashType::SHA256)); + manifest_->addEntry(entries[i].first, entries[i].second); } - manifest1_.encode(); - manifest1_.decode(); + core::Name base_name("b001::dead"); + manifest_->setBaseName(base_name); - manifest1_.dump(); + core::Name ret_name = manifest_->getBaseName(); + ASSERT_EQ(base_name, ret_name); - ASSERT_GT(manifest1_.estimateManifestSize(), 0); - ASSERT_LT(manifest1_.estimateManifestSize(), 1500); + delete[] entries; } } // namespace core } // namespace transport - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}
\ No newline at end of file diff --git a/libtransport/src/test/test_event_thread.cc b/libtransport/src/test/test_event_thread.cc new file mode 100644 index 000000000..324250717 --- /dev/null +++ b/libtransport/src/test/test_event_thread.cc @@ -0,0 +1,95 @@ +/* + * 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/chrono_typedefs.h> +#include <hicn/transport/utils/event_thread.h> + +#include <cmath> + +namespace utils { + +namespace { + +class EventThreadTest : public ::testing::Test { + protected: + EventThreadTest() : event_thread_() { + // You can do set-up work for each test here. + } + + virtual ~EventThreadTest() { + // 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::EventThread event_thread_; +}; + +inline double average(const unsigned long samples[], int size) { + double sum = 0; + + for (int i = 0; i < size; i++) { + sum += samples[i]; + } + + return sum / size; +} + +inline double stdDeviation(const unsigned long samples[], int size) { + double avg = average(samples, size); + double var = 0; + + for (int i = 0; i < size; i++) { + var += (samples[i] - avg) * (samples[i] - avg); + } + + return sqrt(var / 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++) { + // event_thread_.add([t0, &samples, i]() { + // }); + // } + + // event_thread_.stop(); + + // 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); +} + +} // 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..2ee00e25e --- /dev/null +++ b/libtransport/src/test/test_fec_base_rely.cc @@ -0,0 +1,454 @@ +/* + * 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. + */ + +#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>( + HICN_PACKET_FORMAT_IPV6_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>( + HICN_PACKET_FORMAT_IPV6_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>( + HICN_PACKET_FORMAT_IPV6_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..549bb3f08 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rs.cc @@ -0,0 +1,413 @@ +/* + * 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. + */ + +#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>( + HICN_PACKET_FORMAT_IPV6_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>( + HICN_PACKET_FORMAT_IPV6_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>( + HICN_PACKET_FORMAT_IPV6_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 new file mode 100644 index 000000000..0973069b1 --- /dev/null +++ b/libtransport/src/test/test_fec_reedsolomon.cc @@ -0,0 +1,269 @@ + +/* + * 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 <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/global_object_pool.h> +#include <protocols/fec/rs.h> + +#include <algorithm> +#include <iostream> +#include <random> + +namespace transport { +namespace protocol { + +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); + + 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](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( + [&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].first != *source_packets[i].getBuffer() || + tx_block[i].second != source_packets[i].getMetadata()) { + count++; + } + } + }); + + do { + // Discard eventual packet appended in previous callback call + tx_block.erase(tx_block.begin() + k, tx_block.end()); + uint32_t _seq_offset = seq_offset; + + // Initialization. Feed encoder with first k source packets + for (int i = 0; i < k; i++) { + // Get new buffer from pool + auto packet = packet_manager.getMemBuf(); + + // 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 - dis(gen); + + // Set payload, saving 2 bytes at the beginning of the buffer for encoding + // the length + packet->append(cur_size); + std::fill(packet->writableData(), packet->writableTail(), i + 1); + + // Set first byte of payload to seq_offset, to reorder at receiver side + 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::make_pair(std::move(packet), metadata); + } + + // Create the repair packets + for (auto &tx : tx_block) { + encoder.consume(tx.first, tx.first->writableBuffer()[0], 0, tx.second); + } + + // Simulate transmission on lossy channel + 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(), 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 + 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].first); + } + } + + decoder.clear(); + encoder.clear(); + } while (++run < iterations); + + return count; +} + +void ReedSolomonMultiBlockTest(int n_sourceblocks) { + int k = 16; + int n = 24; + int size = 1000; + + 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; + std::vector<std::pair<fec::buffer, uint32_t>> rx_block; + int count = 0; + int i = 0; + + // Receiver will receive packet for n_sourceblocks in a random order. + int total_packets = n * n_sourceblocks; + int tx_packets = k * n_sourceblocks; + + 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)); + + // 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(); + }); + + // The decode callback must be called exactly n_sourceblocks times + 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 + // - (n - k) * n_sourceblocks symbols) + for (i = 0; i < total_packets; i++) { + // Get new buffer from pool + auto packet = packet_manager.getMemBuf(); + + // 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 - 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::fill(packet->writableData(), packet->writableTail(), i + 1); + + // Set first byte of payload to i, to reorder at receiver side + ((uint32_t *)packet->writableData())[0] = uint32_t(i); + + // Store packet in tx buffer + tx_block.emplace_back(packet, i); + + // Feed encoder with packet + encoder.consume(packet, i); + } + + // Here rx_block must contains k * n_sourceblocks packets + 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(), gen); + + for (auto &p : rx_block) { + int index = p.second % n; + if (index < k) { + // Source packet + decoder.consumeSource(p.first, p.second); + } else { + // Repair packet + decoder.consumeRepair(p.first); + } + } + + // Simple test to check we get all the source packets + EXPECT_EQ(count, n_sourceblocks); +} + +/** + * @brief Use foreach_rs_fec_type to automatically generate the code of the + * tests and avoid copy/paste the same function. + */ +#define _(name, k, n) \ + TEST(ReedSolomonTest, RSK##k##N##n) { \ + int K = k; \ + int N = n; \ + int seq_offset = 0; \ + int size = 1000; \ + EXPECT_LE(ReedSolomonTest(K, N, seq_offset, size), 0); \ + seq_offset = 12345; \ + EXPECT_LE(ReedSolomonTest(K, N, seq_offset, size), 0); \ + } +foreach_rs_fec_type +#undef _ + +TEST(ReedSolomonMultiBlockTest, RSMB10) { + int blocks = 1; + ReedSolomonMultiBlockTest(blocks); +} + +TEST(ReedSolomonMultiBlockTest, RSMB100) { + int blocks = 100; + ReedSolomonMultiBlockTest(blocks); +} + +TEST(ReedSolomonMultiBlockTest, RSMB1000) { + int blocks = 1000; + ReedSolomonMultiBlockTest(blocks); +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_fec_rely_wrapper.cc b/libtransport/src/test/test_fec_rely_wrapper.cc new file mode 100644 index 000000000..764e4dd2d --- /dev/null +++ b/libtransport/src/test/test_fec_rely_wrapper.cc @@ -0,0 +1,247 @@ +/* + * 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 <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 { + +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) { + stream << " " << seq; + } + + stream << "\n"; + + return stream.str(); +} + +/** + * @brief Test encode-decode operations performed using the wrapper for rely + * + * @param k Number of source symbols + * @param n Sum of source symbols and repair symbols + * @param max_packet_size The max packet size the decoder will expect. + * @param timeout The timeout used by rely + * https://rely.steinwurf.com/docs/6.1.0/design/timeout_configuration.html + * @param max_iterations The number of packets to send + * @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, + 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 + 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; + fec::ConsumerFEC &decoder = _decoder; + + // Initialize current iteration + uint32_t iterations = 0; + + // Packet allocator + auto &packet_manager = core::PacketManager<>::getInstance(); + + // Store packets to verify them in the decoder callback + 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) { + auto ret = + core::PacketManager<>::getInstance().getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); + ret->updateLength(size); + ret->append(size); + ret->trimStart(ret->headerSize()); + DCHECK(ret->length() >= size); + + return ret; + }); + + // Set callback to be called by encoder when repair packets are ready + 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([&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; + + // 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 */); + + // Recovered packet should be equal to the original one + EXPECT_TRUE(original_packet == *packet.getBuffer()); + + // Also metadata should correspond + EXPECT_TRUE(original->second.second == packet.getMetadata()); + + // Restore removed headers + original_packet.prepend(60 + 32 + 4); + + // 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 + (dis(gen)); + uint8_t payload[max_packet_size]; + std::generate(payload, payload + payload_size, gen); + + // Get a packet from global pool and set name + auto buffer = packet_manager.getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); + buffer->setName(core::Name("b001::abcd", iterations)); + + // Get offset + auto offset = buffer->headerSize(); + + // Copy payload into packet. We keep the payload to compare returned packet + // with original one (since rely encoder does modify the packet by adding + // 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 + 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, 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; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Difference is " << diff; + auto cmp = + std::memcmp(buffer->data() + offset + diff, payload, payload_size); + EXPECT_FALSE(cmp); + + // Drop condition. Id addition to the loss rate, we ensure that no drops are + // perfomed in the last 10% of the total iterations. This is done because + // rely uses a sliding-window mechanism to recover, and if we suddenly stop + // we may not be able to recover missing packets that would be recovered + // 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) \ + (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 + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << buffer->getName().getSuffix() + << " to decoder"; + decoder.onDataPacket(*buffer, offset, metadata); + } else { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet " << buffer->getName().getSuffix() << " dropped"; + } + + // 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(); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << iterations << " to decoder"; + core::ContentObject &co = (core::ContentObject &)(*packet); + decoder.onDataPacket(co, 0); + } else { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet (repair) " << iterations << " dropped"; + } + + // Remove packet from the queue + pending_repair_packets.pop(); + } + + ++iterations; + } + + // We expect this test to terminate with a full recover of all the packets and + // 0.001 residual losses + EXPECT_LE(saved_packets.size(), iterations * 0.001) + << printMissing(saved_packets); +} + +/** + * @brief Use foreach_rely_fec_type to automatically generate the code of the + * tests and avoid copy/paste the same function. + */ +#define _(name, k, n) \ + TEST(RelyTest, RelyK##k##N##n) { \ + int K = k; \ + int N = n; \ + uint32_t max_iterations = 1000; \ + int size = 1400; \ + int64_t timeout = 120; \ + int loss_rate = 10; \ + testRelyEncoderDecoder(K, N, size, timeout, max_iterations, loss_rate); \ + } +foreach_rely_fec_type +#undef _ + +} // namespace protocol +} // namespace transport 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_indexer.cc b/libtransport/src/test/test_indexer.cc new file mode 100644 index 000000000..9c12e2037 --- /dev/null +++ b/libtransport/src/test/test_indexer.cc @@ -0,0 +1,322 @@ + +/* + * 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 <protocols/incremental_indexer_bytestream.h> +#include <protocols/indexer.h> +#include <protocols/rtc/rtc_indexer.h> + +#include <algorithm> +#include <iostream> +#include <random> + +namespace transport { +namespace protocol { + +class IncrementalIndexerTest : public ::testing::Test { + protected: + IncrementalIndexerTest() : indexer_(nullptr, nullptr) { + // You can do set-up work for each test here. + } + + virtual ~IncrementalIndexerTest() { + // 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). + } + + IncrementalIndexer indexer_; +}; + +class RtcIndexerTest : public ::testing::Test { + protected: + RtcIndexerTest() : indexer_(nullptr, nullptr) { + // You can do set-up work for each test here. + } + + virtual ~RtcIndexerTest() { + // 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). + indexer_.setFirstSuffix(0); + indexer_.reset(); + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + static const constexpr uint32_t LIMIT = (1 << 31); + rtc::RtcIndexer<LIMIT> indexer_; +}; + +void testIncrement(Indexer &indexer) { + // As a first index we should get zero + auto index = indexer.getNextSuffix(); + EXPECT_EQ(index, uint32_t(0)); + + // Check if the sequence works for consecutive incremental numbers + for (uint32_t i = 1; i < 4096; i++) { + index = indexer.getNextSuffix(); + EXPECT_EQ(index, i); + } + + index = indexer.getNextSuffix(); + EXPECT_NE(index, uint32_t(0)); +} + +void testJump(Indexer &indexer) { + // Fist suffix is 0 + auto index = indexer.getNextSuffix(); + EXPECT_EQ(index, uint32_t(0)); + + // Next suffix should be 1, but we jump to 12345 + uint32_t jump = 12345; + indexer.jumpToIndex(jump); + + // This takes place immediately + index = indexer.getNextSuffix(); + EXPECT_EQ(index, jump); +} + +TEST_F(IncrementalIndexerTest, TestReset) { + testIncrement(indexer_); + + // Reset the indexer + indexer_.reset(); + + // Now it should startfrom zero again + for (uint32_t i = 0; i < 4096; i++) { + auto index = indexer_.getNextSuffix(); + EXPECT_EQ(index, i); + } +} + +TEST_F(IncrementalIndexerTest, TestGetSuffix) { testIncrement(indexer_); } + +TEST_F(IncrementalIndexerTest, TestGetNextReassemblySegment) { + // Test suffixes for reassembly are not influenced by download suffixed + // increment + for (uint32_t i = 0; i < 4096; i++) { + auto index = indexer_.getNextSuffix(); + EXPECT_EQ(index, i); + } + + for (uint32_t i = 0; i < 4096; i++) { + auto index = indexer_.getNextReassemblySegment(); + EXPECT_EQ(index, i); + } +} + +TEST_F(IncrementalIndexerTest, TestJumpToIndex) { testJump(indexer_); } + +TEST_F(IncrementalIndexerTest, TestGetFinalSuffix) { + // Since final suffix hasn't been discovered, it should be invalid_index + auto final_suffix = indexer_.getFinalSuffix(); + ASSERT_EQ(final_suffix, Indexer::invalid_index); +} + +TEST_F(IncrementalIndexerTest, TestMaxLimit) { + // Jump to max value for uint32_t + indexer_.jumpToIndex(std::numeric_limits<uint32_t>::max()); + auto ret = indexer_.getNextSuffix(); + ASSERT_EQ(ret, Indexer::invalid_index); + + // Now the indexer should always return invalid_index + for (uint32_t i = 0; i < 4096; i++) { + ret = indexer_.getNextSuffix(); + EXPECT_EQ(ret, Indexer::invalid_index); + } +} + +TEST_F(IncrementalIndexerTest, TestSetFirstSuffix) { + // Set first suffix before starting + uint32_t start = 1234567890; + indexer_.setFirstSuffix(1234567890); + + // The first suffix set should take place only after a reset + auto index = indexer_.getNextSuffix(); + EXPECT_EQ(index, uint32_t(0)); + + indexer_.reset(); + index = indexer_.getNextSuffix(); + EXPECT_EQ(index, start); +} + +TEST_F(IncrementalIndexerTest, TestIsFinalSuffixDiscovered) { + // Final suffix should not be discovererd + auto ret = indexer_.isFinalSuffixDiscovered(); + EXPECT_FALSE(ret); +} + +TEST_F(RtcIndexerTest, TestReset) { + // Without setting anything this indexer should behave exactly as the + // incremental indexer for the getNextSuffix() + testIncrement(indexer_); + + // Reset the indexer + indexer_.reset(); + + // Now it should startfrom zero again + for (uint32_t i = 0; i < 4096; i++) { + auto index = indexer_.getNextSuffix(); + EXPECT_EQ(index, i); + } +} + +TEST_F(RtcIndexerTest, TestGetNextSuffix) { + // Without setting anything this indexer should behave exactly as the + // incremental indexer for the getNextSuffix() + testIncrement(indexer_); +} + +TEST_F(RtcIndexerTest, TestGetNextReassemblySegment) { + // This indexer should not provide reassembly segments since they are not + // required for rtc + try { + indexer_.getNextReassemblySegment(); + // We should not reach this point + FAIL() << "Exception expected here"; + } catch (const errors::RuntimeException &exc) { + // OK correct exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } +} + +TEST_F(RtcIndexerTest, TestGetFinalSuffix) { + // Final suffix should be eqaul to LIMIT + ASSERT_EQ(indexer_.getFinalSuffix(), uint32_t(LIMIT)); +} + +TEST_F(RtcIndexerTest, TestJumpToIndex) { testJump(indexer_); } + +TEST_F(RtcIndexerTest, TestIsFinalSuffixDiscovered) { + // This method should always return true + EXPECT_TRUE(indexer_.isFinalSuffixDiscovered()); +} + +TEST_F(RtcIndexerTest, TestMaxLimit) { + // Once reached the LIMIT, this indexer should restart from 0 + + // Jump to max value for uint32_t + indexer_.jumpToIndex(LIMIT); + testIncrement(indexer_); +} + +TEST_F(RtcIndexerTest, TestEnableFec) { + // Here we enable the FEC and we check we receive indexes for souece packets + // only + indexer_.enableFec(fec::FECType::RS_K1_N3); + + // We did not set NFec, which should be zero. So we get only indexes for + // Source packets. + + // With this FEC type we should get one source packet every 3 (0 . . 3 . . 6) + auto index = indexer_.getNextSuffix(); + EXPECT_EQ(index, uint32_t(0)); + + index = indexer_.getNextSuffix(); + EXPECT_EQ(index, uint32_t(3)); + + index = indexer_.getNextSuffix(); + EXPECT_EQ(index, uint32_t(6)); + + // Change FEC Type + indexer_.enableFec(fec::FECType::RS_K10_N30); + + // With this FEC type we should get source packets from 7 to 9 + for (uint32_t i = 7; i < 10; i++) { + index = indexer_.getNextSuffix(); + EXPECT_EQ(index, i); + } + + // And then jump to 30 + index = indexer_.getNextSuffix(); + EXPECT_EQ(index, uint32_t(30)); + + // Let's now jump to a high value + indexer_.jumpToIndex(12365); + for (uint32_t i = 12365; i < 12369; i++) { + index = indexer_.getNextSuffix(); + EXPECT_EQ(index, i); + } +} + +TEST_F(RtcIndexerTest, TestSetNFec) { + // Here we enable the FEC and we set a max of 20 fec packets + indexer_.enableFec(fec::FECType::RS_K10_N90); + indexer_.setNFec(20); + + // We should get indexes up to 29 + uint32_t index; + for (uint32_t i = 0; i < 30; i++) { + index = indexer_.getNextSuffix(); + EXPECT_EQ(i, index); + } + + // Then it should jump to 90 + for (uint32_t i = 90; i < 99; i++) { + index = indexer_.getNextSuffix(); + EXPECT_EQ(i, index); + } + + // Let's set NFEC > 80 + indexer_.setNFec(150); +} + +TEST_F(RtcIndexerTest, TestSetNFecWithOffset) { + // Here we enable the FEC and we set a max of 20 fec packets + const constexpr uint32_t first_suffix = 7; + indexer_.setFirstSuffix(first_suffix); + indexer_.reset(); + indexer_.enableFec(fec::FECType::RS_K16_N24); + indexer_.setNFec(8); + + uint32_t index; + for (uint32_t i = first_suffix; i < 16 + first_suffix; i++) { + index = indexer_.getNextSuffix(); + EXPECT_FALSE(indexer_.isFec(index)); + EXPECT_EQ(i, index); + } + + for (uint32_t i = first_suffix + 16; i < 16 + 8 + first_suffix; i++) { + index = indexer_.getNextSuffix(); + EXPECT_TRUE(indexer_.isFec(index)); + EXPECT_EQ(i, index); + } +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_interest.cc b/libtransport/src/test/test_interest.cc new file mode 100644 index 000000000..22dc01455 --- /dev/null +++ b/libtransport/src/test/test_interest.cc @@ -0,0 +1,308 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <test/packet_samples.h> + +#include <climits> +#include <random> +#include <vector> + +namespace transport { + +namespace core { + +namespace { +// The fixture for testing class Foo. +class InterestTest : public ::testing::Test { + protected: + InterestTest() + : name_("b001::123|321"), interest_(HICN_PACKET_FORMAT_IPV6_TCP) { + // You can do set-up work for each test here. + } + + virtual ~InterestTest() { + // 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). + } + + Name name_; + + Interest interest_; + + std::vector<uint8_t> buffer_ = {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE), + // ICMP6 echo request + TCP_HEADER(0x00), + // Payload + PAYLOAD}; +}; + +void testFormatConstructor( + hicn_packet_format_t format = HICN_PACKET_FORMAT_NONE) { + try { + Interest interest(format, 0); + } catch (...) { + char buf[MAXSZ_HICN_PACKET_FORMAT]; + FAIL() << "ERROR: Unexpected exception thrown for " << buf; + } +} + +void testFormatConstructorException( + Packet::Format format = HICN_PACKET_FORMAT_NONE) { + try { + Interest interest(format, 0); + FAIL() << "We expected an exception here"; + } catch (errors::MalformedPacketException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } +} + +} // namespace + +TEST_F(InterestTest, ConstructorWithFormat) { + /** + * Without arguments it should be format = HICN_PACKET_FORMAT_NONE. + * We expect a crash. + */ + + testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP); + testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP); + testFormatConstructorException(HICN_PACKET_FORMAT_IPV4_ICMP); + testFormatConstructorException(HICN_PACKET_FORMAT_IPV6_ICMP); + testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP_AH); + testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP_AH); + testFormatConstructorException(HICN_PACKET_FORMAT_IPV4_ICMP_AH); + testFormatConstructorException(HICN_PACKET_FORMAT_IPV6_ICMP_AH); +} + +TEST_F(InterestTest, ConstructorWithName) { + /** + * Without arguments it should be format = HICN_PACKET_FORMAT_NONE. + * We expect a crash. + */ + Name n("b001::1|123"); + + try { + Interest interest(HICN_PACKET_FORMAT_IPV6_TCP, n); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown"; + } +} + +TEST_F(InterestTest, ConstructorWithBuffer) { + // Ensure buffer is interest +#if 0 + auto ret = Interest::isInterest(&buffer_[0]); + EXPECT_TRUE(ret); +#endif + + // Create interest from buffer + try { + Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size()); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown"; + } + + std::vector<uint8_t> buffer2{// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(ICMP6_PROTO, 60 + 44), + // ICMP6 echo request + TCP_HEADER(0x00), + // Payload + PAYLOAD}; + + // Ensure this throws an exception + try { + Interest interest(Interest::COPY_BUFFER, &buffer2[0], buffer2.size()); + FAIL() << "We expected an exception here"; + } catch (errors::MalformedPacketException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } +} + +TEST_F(InterestTest, SetGetName) { + // Create interest from buffer + Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size()); + + // Get name + auto n = interest.getName(); + + // ensure name is b002::ca|1 + Name n2("b002::ca|1"); + auto ret = (n == n2); + + EXPECT_TRUE(ret); + + Name n3("b003::1234|1234"); + + // Change name to b003::1234|1234 + interest.setName(n3); + + // Check name was set + n = interest.getName(); + ret = (n == n3); + EXPECT_TRUE(ret); +} + +TEST_F(InterestTest, SetGetLocator) { + // Create interest from buffer + Interest interest(Interest::COPY_BUFFER, &buffer_[0], buffer_.size()); + + // Get locator + auto l = interest.getLocator(); + + hicn_ip_address_t address; + inet_pton(AF_INET6, "b006::ab:cdab:cdef", &address); + auto ret = !hicn_ip_address_cmp(&l, &address); + + EXPECT_TRUE(ret); + + // Set different locator + inet_pton(AF_INET6, "2001::1234::4321::abcd::", &address); + + // Set it on interest + interest.setLocator(address); + + // Check it was set + l = interest.getLocator(); + ret = !hicn_ip_address_cmp(&l, &address); + + EXPECT_TRUE(ret); +} + +TEST_F(InterestTest, SetGetLifetime) { + // Create interest from buffer + Interest interest(HICN_PACKET_FORMAT_IPV6_TCP); + const constexpr uint32_t lifetime = 10000; + + // Set lifetime + interest.setLifetime(lifetime); + + // Get lifetime + auto l = interest.getLifetime(); + + // Ensure they are the same + EXPECT_EQ(l, lifetime); +} + +TEST_F(InterestTest, HasManifest) { + // Create interest from buffer + Interest interest(HICN_PACKET_FORMAT_IPV6_TCP); + + // Let's expect anexception here + try { + interest.setPayloadType(PayloadType::UNSPECIFIED); + FAIL() << "We expect an esception here"; + } catch (errors::RuntimeException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "Wrong exception thrown"; + } + + interest.setPayloadType(PayloadType::DATA); + EXPECT_FALSE(interest.hasManifest()); + + interest.setPayloadType(PayloadType::MANIFEST); + EXPECT_TRUE(interest.hasManifest()); +} + +TEST_F(InterestTest, AppendSuffixesEncodeAndIterate) { + // Create interest from buffer + Interest interest(HICN_PACKET_FORMAT_IPV6_TCP); + + // Appenad some suffixes, with some duplicates + interest.appendSuffix(1); + interest.appendSuffix(2); + interest.appendSuffix(5); + interest.appendSuffix(3); + interest.appendSuffix(4); + interest.appendSuffix(5); + interest.appendSuffix(5); + interest.appendSuffix(5); + interest.appendSuffix(5); + interest.appendSuffix(5); + + // Encode them in wire format + interest.encodeSuffixes(); + + // Iterate over them. They should be in order and without repetitions + + auto suffix = interest.firstSuffix(); + auto n_suffixes = interest.numberOfSuffixes(); + + for (uint32_t i = 0; i < n_suffixes; i++) { + EXPECT_EQ(*(suffix + i), i); + } +} + +TEST_F(InterestTest, AppendSuffixesWithGaps) { + // Create interest from buffer + Interest interest(HICN_PACKET_FORMAT_IPV6_TCP); + + // Appenad some suffixes, out of order and with gaps + interest.appendSuffix(6); + interest.appendSuffix(2); + interest.appendSuffix(5); + interest.appendSuffix(1); + + // Encode them in wire format + interest.encodeSuffixes(); + EXPECT_TRUE(interest.hasManifest()); + + // Check first suffix correctness + auto suffix = interest.firstSuffix(); + EXPECT_NE(suffix, nullptr); + EXPECT_EQ(*suffix, 0U); + + // Iterate over them. They should be in order and without repetitions + std::vector<uint32_t> expected = {interest.getName().getSuffix(), 1, 2, 5, 6}; + EXPECT_EQ(interest.numberOfSuffixes(), expected.size()); + + for (uint32_t seq : expected) { + EXPECT_EQ(*suffix, seq); + suffix++; + } +} + +TEST_F(InterestTest, InterestWithoutManifest) { + // Create interest without manifest + Interest interest(HICN_PACKET_FORMAT_IPV6_TCP); + auto suffix = interest.firstSuffix(); + + EXPECT_FALSE(interest.hasManifest()); + EXPECT_EQ(interest.numberOfSuffixes(), 0U); + EXPECT_EQ(suffix, nullptr); +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_memif_connector.cc b/libtransport/src/test/test_memif_connector.cc new file mode 100644 index 000000000..40f4df927 --- /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::getDurationUs(t0_, t1); + double rate = double(recv_counter_) * 1.0e6 / double(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 new file mode 100644 index 000000000..f2f658932 --- /dev/null +++ b/libtransport/src/test/test_packet.cc @@ -0,0 +1,932 @@ +/* + * 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. + */ + +#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> +#include <random> +#include <vector> + +#include "../../lib/src/protocol.h" + +namespace transport { + +namespace core { + +/** + * Since packet is an abstract class, we derive a concrete class to be used for + * the test. + */ +class PacketForTest : public Packet { + public: + template <typename... Args> + PacketForTest(Args &&...args) : Packet(std::forward<Args>(args)...) {} + + virtual ~PacketForTest() {} + + const Name &getName() const override { + throw errors::NotImplementedException(); + } + + Name &getWritableName() override { throw errors::NotImplementedException(); } + + void setName(const Name &name) override { + throw errors::NotImplementedException(); + } + + void setLifetime(uint32_t lifetime) override { + throw errors::NotImplementedException(); + } + + uint32_t getLifetime() const override { + throw errors::NotImplementedException(); + } + + void setLocator(const hicn_ip_address_t &locator) override { + throw errors::NotImplementedException(); + } + + void resetForHash() override { throw errors::NotImplementedException(); } + + hicn_ip_address_t getLocator() const override { + throw errors::NotImplementedException(); + } +}; + +namespace { +// The fixture for testing class Foo. +class PacketTest : public ::testing::Test { + protected: + PacketTest() + : name_("b001::123|321"), + packet(Packet::COPY_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()) { + // You can do set-up work for each test here. + } + + virtual ~PacketTest() { + // 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). + } + + Name name_; + + PacketForTest packet; + + static std::map<uint32_t, std::vector<uint8_t>> raw_packets_; + + std::vector<uint8_t> payload = { + 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00, + // 0x00, 0x45, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc9, 0x00, 0x00, + // 0x00, 0x44, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc8 + }; +}; + +std::map<uint32_t, std::vector<uint8_t>> PacketTest::raw_packets_ = { + {HICN_PACKET_FORMAT_IPV6_TCP, + + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE), + // TCP src=0x1234 dst=0x4321, seq=0x0001 + TCP_HEADER(0x00), + // Payload + PAYLOAD}}, + + {HICN_PACKET_FORMAT_IPV4_TCP, + {// IPv4 src=3.13.127.8, dst=192.168.1.92 + IPV4_HEADER(TCP_PROTO, 20 + PAYLOAD_SIZE), + // TCP src=0x1234 dst=0x4321, seq=0x0001 + TCP_HEADER(0x00), + // Other + PAYLOAD}}, + + {HICN_PACKET_FORMAT_IPV4_ICMP, + {// IPv4 src=3.13.127.8, dst=192.168.1.92 + IPV4_HEADER(ICMP_PROTO, 64), + // ICMP echo request + ICMP_ECHO_REQUEST}}, + + {HICN_PACKET_FORMAT_IPV6_ICMP, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(ICMP6_PROTO, 60), + // ICMP6 echo request + ICMP6_ECHO_REQUEST}}, + + {HICN_PACKET_FORMAT_IPV6_TCP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(TCP_PROTO, 20 + 44 + 128), + // ICMP6 echo request + TCP_HEADER(0x18), + // hICN AH header + AH_HEADER, SIGNATURE}}, + + {HICN_PACKET_FORMAT_IPV4_TCP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV4_HEADER(TCP_PROTO, 20 + 44 + 128), + // ICMP6 echo request + TCP_HEADER(0x18), + // hICN AH header + AH_HEADER, SIGNATURE}}, + + // XXX No flag defined in ICMP header to signal AH header. + {HICN_PACKET_FORMAT_IPV4_ICMP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV4_HEADER(ICMP_PROTO, 64 + 44), + // ICMP6 echo request + ICMP_ECHO_REQUEST, + // hICN AH header + AH_HEADER, SIGNATURE}}, + + {HICN_PACKET_FORMAT_IPV6_ICMP_AH, + {// IPv6 src=b001::ab:cdab:cdef, dst=b002::ca + IPV6_HEADER(ICMP6_PROTO, 60 + 44), + // ICMP6 echo request + ICMP6_ECHO_REQUEST, + // hICN AH header + AH_HEADER, SIGNATURE}}, + +}; + +void testFormatConstructor(Packet::Format format = HICN_PACKET_FORMAT_NONE) { + try { + PacketForTest packet(HICN_PACKET_TYPE_INTEREST, format); + } catch (...) { + char buf[MAXSZ_HICN_PACKET_FORMAT]; + int rc = hicn_packet_format_snprintf(buf, MAXSZ_HICN_PACKET_FORMAT, format); + if (rc < 0 || rc >= MAXSZ_HICN_PACKET_FORMAT) + snprintf(buf, MAXSZ_HICN_PACKET_FORMAT, "%s", "(error"); + FAIL() << "ERROR: Unexpected exception thrown for " << buf; + } +} + +void testFormatAndAdditionalHeaderConstructor(Packet::Format format, + std::size_t additional_header) { + PacketForTest packet(HICN_PACKET_TYPE_INTEREST, format, additional_header); + // Packet length should be the one of the normal header + the + // additional_header + + EXPECT_EQ(packet.headerSize(), + Packet::getHeaderSizeFromFormat(format) + additional_header); +} + +void testRawBufferConstructor(std::vector<uint8_t> packet, + Packet::Format format) { + try { + // Try to construct packet from correct buffer + PacketForTest p(Packet::WRAP_BUFFER, &packet[0], packet.size(), + packet.size()); + + // Check format is expected one. + EXPECT_EQ(p.getFormat(), format); + + // // Try the same using a MemBuf + // auto buf = utils::MemBuf::wrapBuffer(&packet[0], packet.size()); + // buf->append(packet.size()); + // PacketForTest p2(std::move(buf)); + + // EXPECT_EQ(p2.getFormat(), format); + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown"; + } + + try { + // Try to construct packet from wrong buffer + + // Modify next header to 0 + /* ipv6 */ + packet[6] = 0x00; + /* ipv4 */ + packet[9] = 0x00; + PacketForTest p(Packet::WRAP_BUFFER, &packet[0], packet.size(), + packet.size()); + + // Format should fallback to HICN_PACKET_FORMAT_NONE + EXPECT_EQ(p.getFormat(), HICN_PACKET_FORMAT_NONE); + } catch (errors::MalformedPacketException &exc) { + // Ok right exception + } catch (...) { + FAIL() << "ERROR: Unexpected exception thrown."; + } +} + +void getHeaderSizeFromBuffer(std::vector<uint8_t> &packet, + std::size_t expected) { + auto header_size = + PacketForTest::getHeaderSizeFromBuffer(&packet[0], packet.size()); + EXPECT_EQ(header_size, expected); +} + +void getHeaderSizeFromFormat(Packet::Format format, std::size_t expected) { + auto header_size = PacketForTest::getHeaderSizeFromFormat(format); + EXPECT_EQ(header_size, expected); +} + +void getPayloadSizeFromBuffer(std::vector<uint8_t> &packet, + std::size_t expected) { + auto payload_size = + PacketForTest::getPayloadSizeFromBuffer(&packet[0], packet.size()); + EXPECT_EQ(payload_size, expected); +} + +void getFormatFromBuffer(Packet::Format expected, + std::vector<uint8_t> &packet) { + auto format = PacketForTest::getFormatFromBuffer(&packet[0], packet.size()); + EXPECT_EQ(format, expected); +} + +void getHeaderSize(std::size_t expected, const PacketForTest &packet) { + auto size = packet.headerSize(); + EXPECT_EQ(size, expected); +} + +void testGetFormat(Packet::Format expected, const Packet &packet) { + auto format = packet.getFormat(); + EXPECT_EQ(format, expected); +} + +} // namespace + +TEST_F(PacketTest, ConstructorWithFormat) { + testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP); + testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP); + testFormatConstructor(HICN_PACKET_FORMAT_IPV4_ICMP); + testFormatConstructor(HICN_PACKET_FORMAT_IPV6_ICMP); + testFormatConstructor(HICN_PACKET_FORMAT_IPV4_TCP_AH); + testFormatConstructor(HICN_PACKET_FORMAT_IPV6_TCP_AH); + testFormatConstructor(HICN_PACKET_FORMAT_IPV4_ICMP_AH); + testFormatConstructor(HICN_PACKET_FORMAT_IPV6_ICMP_AH); +} + +TEST_F(PacketTest, ConstructorWithFormatAndAdditionalHeader) { + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_TCP, 123); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_TCP, 360); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_ICMP, 21); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_ICMP, 444); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_TCP_AH, 555); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_TCP_AH, 321); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV4_ICMP_AH, + 123); + testFormatAndAdditionalHeaderConstructor(HICN_PACKET_FORMAT_IPV6_ICMP_AH, 44); +} + +TEST_F(PacketTest, ConstructorWithNew) { + auto &_packet = raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP]; + auto packet_ptr = new PacketForTest(Packet::WRAP_BUFFER, &_packet[0], + _packet.size(), _packet.size()); + delete packet_ptr; +} + +TEST_F(PacketTest, ConstructorWithRawBufferInet6Tcp) { + auto format = HICN_PACKET_FORMAT_IPV6_TCP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInetTcp) { + auto format = HICN_PACKET_FORMAT_IPV4_TCP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInetIcmp) { + auto format = HICN_PACKET_FORMAT_IPV4_ICMP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInet6Icmp) { + auto format = HICN_PACKET_FORMAT_IPV6_ICMP; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInet6TcpAh) { + auto format = HICN_PACKET_FORMAT_IPV6_TCP_AH; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, ConstructorWithRawBufferInetTcpAh) { + auto format = HICN_PACKET_FORMAT_IPV4_TCP_AH; + testRawBufferConstructor(raw_packets_[format], format); +} + +TEST_F(PacketTest, MoveConstructor) { + PacketForTest p0(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP); + PacketForTest p1(std::move(p0)); + EXPECT_EQ(p0.getFormat(), HICN_PACKET_FORMAT_NONE); + EXPECT_EQ(p1.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP); +} + +TEST_F(PacketTest, TestGetHeaderSizeFromBuffer) { + getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP], + IPV6_HDRLEN + TCP_HDRLEN); + getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP], + IPV4_HDRLEN + TCP_HDRLEN); + getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP], + IPV6_HDRLEN + 4); + getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP], + IPV4_HDRLEN + 4); + getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH], + IPV6_HDRLEN + TCP_HDRLEN + AH_HDRLEN + 128); + getHeaderSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH], + IPV4_HDRLEN + TCP_HDRLEN + AH_HDRLEN + 128); +} + +TEST_F(PacketTest, TestGetHeaderSizeFromFormat) { + getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV6_TCP, + IPV6_HDRLEN + TCP_HDRLEN); + getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV4_TCP, + IPV4_HDRLEN + TCP_HDRLEN); + getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV6_ICMP, IPV6_HDRLEN + 4); + getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV4_ICMP, IPV4_HDRLEN + 4); + getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV6_TCP_AH, + IPV6_HDRLEN + TCP_HDRLEN + AH_HDRLEN); + getHeaderSizeFromFormat(HICN_PACKET_FORMAT_IPV4_TCP_AH, + IPV4_HDRLEN + TCP_HDRLEN + AH_HDRLEN); +} + +TEST_F(PacketTest, TestGetPayloadSizeFromBuffer) { + getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP], 12); + getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP], 12); + getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP], 56); + getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP], 60); + getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH], 0); + getPayloadSizeFromBuffer(raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH], 0); +} + +#if 0 +TEST_F(PacketTest, TestIsInterest) { + auto ret = PacketForTest::isInterest(&raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0]); + + EXPECT_TRUE(ret); +} +#endif + +TEST_F(PacketTest, TestGetFormatFromBuffer) { + getFormatFromBuffer(HICN_PACKET_FORMAT_IPV6_TCP, + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP]); + getFormatFromBuffer(HICN_PACKET_FORMAT_IPV4_TCP, + raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP]); + getFormatFromBuffer(HICN_PACKET_FORMAT_IPV6_ICMP, + raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP]); + getFormatFromBuffer(HICN_PACKET_FORMAT_IPV4_ICMP, + raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP]); + getFormatFromBuffer(HICN_PACKET_FORMAT_IPV6_TCP_AH, + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH]); + getFormatFromBuffer(HICN_PACKET_FORMAT_IPV4_TCP_AH, + raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH]); +} + +// TEST_F(PacketTest, TestReplace) { +// PacketForTest packet(Packet::WRAP_BUFFER, +// &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0], +// raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()); + +// // Replace current packet with another one +// packet.replace(&raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP][0], +// raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP].size()); + +// // Check new format +// ASSERT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV4_TCP); +// } + +TEST_F(PacketTest, TestPayloadSize) { + // Check payload size of existing packet + auto &_packet = raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP]; + PacketForTest packet(Packet::WRAP_BUFFER, &_packet[0], _packet.size(), + _packet.size()); + + EXPECT_EQ(packet.payloadSize(), std::size_t(PAYLOAD_SIZE)); + + // Check for dynamic generated packet + std::string payload0(1024, 'X'); + + // Create the packet + PacketForTest packet2(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP); + + // Payload size should now be zero + EXPECT_EQ(packet2.payloadSize(), std::size_t(0)); + + // Append payload 1 time + packet2.appendPayload((const uint8_t *)payload0.c_str(), payload0.size()); + + // size should now be 1024 + EXPECT_EQ(packet2.payloadSize(), std::size_t(1024)); + + // Append second payload + std::string payload1(1024, 'X'); + packet2.appendPayload((const uint8_t *)payload1.c_str(), payload1.size()); + + // Check size is 2048 + EXPECT_EQ(packet2.payloadSize(), std::size_t(2048)); + + // Append Membuf + packet2.appendPayload(utils::MemBuf::copyBuffer( + (const uint8_t *)payload1.c_str(), payload1.size())); + + // Check size is 3072 + EXPECT_EQ(packet2.payloadSize(), std::size_t(3072)); +} + +TEST_F(PacketTest, TestHeaderSize) { + getHeaderSize( + IPV6_HDRLEN + TCP_HDRLEN, + PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP)); + getHeaderSize( + IPV4_HDRLEN + TCP_HDRLEN, + PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP)); + getHeaderSize( + IPV6_HDRLEN + ICMP_HDRLEN, + PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_ICMP)); + getHeaderSize( + IPV4_HDRLEN + ICMP_HDRLEN, + PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_ICMP)); + getHeaderSize( + IPV6_HDRLEN + TCP_HDRLEN + AH_HDRLEN, + PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH)); + getHeaderSize( + IPV4_HDRLEN + TCP_HDRLEN + AH_HDRLEN, + PacketForTest(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP_AH)); +} + +TEST_F(PacketTest, TestMemBufReference) { + // Create packet + auto &_packet = raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP]; + + // Packet was not created as a shared_ptr. If we try to get a membuf shared + // ptr we should get an exception. + // TODO test with c++ 17 + // try { + // PacketForTest packet(&_packet[0], _packet.size()); + // auto membuf_ref = packet.acquireMemBufReference(); + // FAIL() << "The acquireMemBufReference() call should have throwed an " + // "exception!"; + // } catch (const std::bad_weak_ptr &e) { + // // Ok + // } catch (...) { + // FAIL() << "Not expected exception."; + // } + + auto packet_ptr = std::make_shared<PacketForTest>( + Packet::WRAP_BUFFER, &_packet[0], _packet.size(), _packet.size()); + PacketForTest &packet = *packet_ptr; + + // Acquire a reference to the membuf + auto membuf_ref = packet.acquireMemBufReference(); + + // Check refcount. It should be 2 + EXPECT_EQ(membuf_ref.use_count(), 2); + + // Now increment membuf references + Packet::MemBufPtr membuf = packet.acquireMemBufReference(); + + // Now reference count should be 2 + EXPECT_EQ(membuf_ref.use_count(), 3); + + // Copy again + Packet::MemBufPtr membuf2 = membuf; + + // Now reference count should be 3 + EXPECT_EQ(membuf_ref.use_count(), 4); +} + +TEST_F(PacketTest, TestReset) { + // Check everything is ok + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_IPV6_TCP); + EXPECT_EQ(packet.length(), raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()); + EXPECT_EQ(packet.headerSize(), IPV6_HDRLEN + TCP_HDRLEN); + EXPECT_EQ(packet.payloadSize(), packet.length() - packet.headerSize()); + + // Reset the packet + packet.reset(); + + // Rerun test + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_NONE); + EXPECT_EQ(packet.length(), std::size_t(0)); + EXPECT_EQ(packet.headerSize(), std::size_t(0)); + EXPECT_EQ(packet.payloadSize(), std::size_t(0)); +} + +TEST_F(PacketTest, TestAppendPayload) { + // Append payload with raw buffer + uint8_t raw_buffer[2048]; + auto original_payload_length = packet.payloadSize(); + packet.appendPayload(raw_buffer, 1024); + + EXPECT_EQ(original_payload_length + 1024, packet.payloadSize()); + + for (int i = 0; i < 10; i++) { + // Append other payload 10 times + packet.appendPayload(raw_buffer, 1024); + EXPECT_EQ(original_payload_length + 1024 + (1024) * (i + 1), + packet.payloadSize()); + } + + // Append payload using membuf + packet.appendPayload(utils::MemBuf::copyBuffer(raw_buffer, 2048)); + EXPECT_EQ(original_payload_length + 1024 + 1024 * 10 + 2048, + packet.payloadSize()); + + // Check the underlying MemBuf length is the expected one + utils::MemBuf *current = &packet; + size_t total = 0; + do { + total += current->length(); + current = current->next(); + } while (current != &packet); + + EXPECT_EQ(total, packet.headerSize() + packet.payloadSize()); + + // LEt's try now to reset this packet + packet.reset(); + + // There should be no more bufferls left in the chain + EXPECT_EQ(&packet, packet.next()); + EXPECT_EQ(packet.getFormat(), HICN_PACKET_FORMAT_NONE); + EXPECT_EQ(packet.length(), std::size_t(0)); + EXPECT_EQ(packet.headerSize(), std::size_t(0)); + EXPECT_EQ(packet.payloadSize(), std::size_t(0)); +} + +TEST_F(PacketTest, GetPayload) { + // Append payload with raw buffer + uint8_t raw_buffer[2048]; + memset(raw_buffer, 0, sizeof(raw_buffer)); + auto original_payload_length = packet.payloadSize(); + packet.appendPayload(raw_buffer, 2048); + + // Get payload + auto payload = packet.getPayload(); + // Check payload length is correct + utils::MemBuf *current = payload.get(); + size_t total = 0; + do { + total += current->length(); + current = current->next(); + } while (current != payload.get()); + + ASSERT_EQ(total, packet.payloadSize()); + + // Linearize the payload + payload->gather(total); + + // Check memory correspond + payload->trimStart(original_payload_length); + auto ret = memcmp(raw_buffer, payload->data(), 2048); + EXPECT_EQ(ret, 0); +} + +TEST_F(PacketTest, UpdateLength) { + auto original_payload_size = packet.payloadSize(); + + // Add some fake payload without using the API + packet.append(200); + + // payloadSize does not know about the new payload, yet + EXPECT_EQ(packet.payloadSize(), original_payload_size); + + // Let's now update the packet length + packet.updateLength(); + + // Now payloadSize knows + EXPECT_EQ(packet.payloadSize(), std::size_t(original_payload_size + 200)); + + // We may also update the length without adding real content. This is only + // written in the packet header. + packet.updateLength(128); + EXPECT_EQ(packet.payloadSize(), + std::size_t(original_payload_size + 200 + 128)); +} + +TEST_F(PacketTest, SetGetPayloadType) { + auto payload_type = packet.getPayloadType(); + + // It should be normal content object by default + EXPECT_EQ(payload_type, PayloadType::DATA); + + // Set it to be manifest + packet.setPayloadType(PayloadType::MANIFEST); + + // Check it is manifest + payload_type = packet.getPayloadType(); + + EXPECT_EQ(payload_type, PayloadType::MANIFEST); +} + +TEST_F(PacketTest, GetFormat) { + { + PacketForTest p0(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP].size()); + testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP, p0); + + PacketForTest p1(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()); + testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP, p1); + + PacketForTest p2(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV4_ICMP].size()); + testGetFormat(HICN_PACKET_FORMAT_IPV4_ICMP, p2); + + PacketForTest p3(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size()); + testGetFormat(HICN_PACKET_FORMAT_IPV6_ICMP, p3); + + PacketForTest p4(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH][0], + raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV4_TCP_AH].size()); + testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP_AH, p4); + + PacketForTest p5(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH][0], + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP_AH].size()); + testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP_AH, p5); + } + + // Let's try now creating empty packets + { + PacketForTest p0(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP); + testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP, p0); + + PacketForTest p1(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP); + testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP, p1); + + PacketForTest p2(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_ICMP); + testGetFormat(HICN_PACKET_FORMAT_IPV4_ICMP, p2); + + PacketForTest p3(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_ICMP); + testGetFormat(HICN_PACKET_FORMAT_IPV6_ICMP, p3); + + PacketForTest p4(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV4_TCP_AH); + testGetFormat(HICN_PACKET_FORMAT_IPV4_TCP_AH, p4); + + PacketForTest p5(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH); + testGetFormat(HICN_PACKET_FORMAT_IPV6_TCP_AH, p5); + } +} + +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 = utils::SteadyTime::nowMs().count(); + + try { + packet.setSignatureTimestamp(now); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Same fot get method + try { + auto t = packet.getSignatureTimestamp(); + // Let's make compiler happy + (void)t; + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Now let's construct a AH packet, with no additional space for signature + PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH); + p.setSignatureTimestamp(now); + uint64_t now_get = p.getSignatureTimestamp(); + + // Check we got the right value + EXPECT_EQ(now_get, now); +} + +TEST_F(PacketTest, TestSetGetValidationAlgorithm) { + // Let's try to set the validation algorithm in a packet without AH header. We + // expect an exception. + + try { + packet.setValidationAlgorithm(auth::CryptoSuite::RSA_SHA256); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Same fot get method + try { + auto v = packet.getSignatureTimestamp(); + // Let's make compiler happy + (void)v; + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Now let's construct a AH packet, with no additional space for signature + PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH); + p.setValidationAlgorithm(auth::CryptoSuite::RSA_SHA256); + auto v_get = p.getValidationAlgorithm(); + + // Check we got the right value + EXPECT_EQ(v_get, auth::CryptoSuite::RSA_SHA256); +} + +TEST_F(PacketTest, TestSetGetKeyId) { + uint8_t key[32]; + memset(key, 0, sizeof(key)); + auth::KeyId key_id = std::make_pair(key, sizeof(key)); + + try { + packet.setKeyId(key_id); + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Same for get method + try { + auto k = packet.getKeyId(); + // Let's make compiler happy + (void)k; + FAIL() << "We should not reach this point."; + } catch (const errors::RuntimeException &exc) { + /* ok right exception*/ + } catch (...) { + FAIL() << "Unexpected exception"; + } + + // Now let's construct a AH packet, with no additional space for signature + PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH); + p.setKeyId(key_id); + auto p_get = p.getKeyId(); + + // Check we got the right value + EXPECT_EQ(p_get.second, key_id.second); + + auto ret = memcmp(p_get.first, key_id.first, p_get.second); + EXPECT_EQ(ret, 0); +} + +TEST_F(PacketTest, DISABLED_TestChecksum) { + // Checksum should be wrong + bool integrity = packet.checkIntegrity(); + EXPECT_FALSE(integrity); + + // Let's fix it + packet.setChecksum(); + + // Check again + integrity = packet.checkIntegrity(); + EXPECT_TRUE(integrity); + + // Check with AH header and 300 bytes signature + PacketForTest p(HICN_PACKET_TYPE_INTEREST, HICN_PACKET_FORMAT_IPV6_TCP_AH, + 300); + std::string payload(5000, 'X'); + p.appendPayload((const uint8_t *)payload.c_str(), payload.size() / 2); + p.appendPayload((const uint8_t *)(payload.c_str() + payload.size() / 2), + payload.size() / 2); + + p.setChecksum(); + integrity = p.checkIntegrity(); + EXPECT_TRUE(integrity); +} + +TEST_F(PacketTest, TestEnsureCapacity) { + PacketForTest &p = packet; + + // This shoul be false + auto ret = + p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() + 10); + EXPECT_FALSE(ret); + + // This should be true + ret = p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()); + EXPECT_TRUE(ret); + + // This should be true + ret = p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - 10); + EXPECT_TRUE(ret); + + // Try to trim the packet start + p.trimStart(10); + // Now this should be false + ret = p.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()); + EXPECT_FALSE(ret); + + // Create a new packet + auto p2 = PacketForTest(Packet::WRAP_BUFFER, + &raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size(), + raw_packets_[HICN_PACKET_FORMAT_IPV6_ICMP].size()); + + p2.appendPayload(utils::MemBuf::createCombined(2000)); + + // This should be false, since the buffer is chained + ret = + p2.ensureCapacity(raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - 10); + EXPECT_FALSE(ret); +} + +// +// This test is disabled as it manipulates a ipv6 header with the wrong payload +// length inside. +// +TEST_F(PacketTest, DISABLED_TestEnsureCapacityAndFillUnused) { + // Create packet by excluding the payload (So only L3 + L4 headers). The + // payload will be trated as unused tailroom + PacketForTest p = PacketForTest( + Packet::WRAP_BUFFER, &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0], + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - PAYLOAD_SIZE, + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size()); + + // Copy original packet payload, which is here trated as a unused tailroom + uint8_t original_payload[PAYLOAD_SIZE]; + uint8_t *payload = &raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP][0] + + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - + PAYLOAD_SIZE; + std::memcpy(original_payload, payload, PAYLOAD_SIZE); + + // This should be true and the unused tailroom should be unmodified + auto ret = p.ensureCapacityAndFillUnused( + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() - (PAYLOAD_SIZE + 10), + 0); + EXPECT_TRUE(ret); + ret = std::memcmp(original_payload, payload, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); + + // This should fill the payload with zeros + ret = p.ensureCapacityAndFillUnused( + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size(), 0); + EXPECT_TRUE(ret); + uint8_t zeros[PAYLOAD_SIZE]; + std::memset(zeros, 0, PAYLOAD_SIZE); + ret = std::memcmp(payload, zeros, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); + + // This should fill the payload with ones + ret = p.ensureCapacityAndFillUnused( + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size(), 1); + EXPECT_TRUE(ret); + uint8_t ones[PAYLOAD_SIZE]; + std::memset(ones, 1, PAYLOAD_SIZE); + ret = std::memcmp(payload, ones, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); + + // This should return false and the payload should be unmodified + ret = p.ensureCapacityAndFillUnused( + raw_packets_[HICN_PACKET_FORMAT_IPV6_TCP].size() + 1, 1); + EXPECT_FALSE(ret); + ret = std::memcmp(payload, ones, PAYLOAD_SIZE); + EXPECT_EQ(ret, 0); +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_packet_allocator.cc b/libtransport/src/test/test_packet_allocator.cc new file mode 100644 index 000000000..0de35a817 --- /dev/null +++ b/libtransport/src/test/test_packet_allocator.cc @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#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/chrono_typedefs.h> +#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; + static inline const std::size_t counter = 1024; + static inline const std::size_t total_packets = 1024 * counter; + + // 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>(HICN_PACKET_FORMAT_IPV4_TCP); +} + +TEST_F(PacketAllocatorTest, InterestAllocation) { + allocationTest<core::Interest>(HICN_PACKET_FORMAT_IPV4_TCP); +} + +// TEST_F(PacketAllocatorTest, MemBufAllocation) { +// allocationTest<::utils::MemBuf>(); +// } + +TEST_F(PacketAllocatorTest, CheckAllocationIsCorrect) { + // Create packet + auto packet = + allocator_.getPacket<core::ContentObject>(HICN_PACKET_FORMAT_IPV4_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))); +} + +TEST_F(PacketAllocatorTest, CheckAllocationSpeed) { + // Check time needed to allocate 1 million packeauto &packet_manager = + auto &packet_manager = core::PacketManager<>::getInstance(); + + // Send 1 million packets + std::array<utils::MemBuf::Ptr, counter> packets; + auto t0 = utils::SteadyTime::now(); + std::size_t sum = 0; + for (std::size_t j = 0; j < counter; j++) { + for (std::size_t i = 0; i < counter; i++) { + packets[i] = packet_manager.getMemBuf(); + sum++; + } + } + auto t1 = utils::SteadyTime::now(); + + auto delta = utils::SteadyTime::getDurationUs(t0, t1); + auto rate = double(sum) * 1000000.0 / double(delta.count()); + + LOG(INFO) << "rate: " << rate << " packets/s"; +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_prefix.cc b/libtransport/src/test/test_prefix.cc new file mode 100644 index 000000000..3eab72bcb --- /dev/null +++ b/libtransport/src/test/test_prefix.cc @@ -0,0 +1,334 @@ +/* + * 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/prefix.h> +#include <hicn/transport/errors/invalid_ip_address_exception.h> +#include <hicn/transport/portability/endianess.h> + +#include <cstring> +#include <memory> +#include <vector> + +namespace transport { +namespace core { + +namespace { +class PrefixTest : public ::testing::Test { + protected: + static inline const char prefix_str0[] = "2001:db8:1::/64"; + static inline const char prefix_str1[] = "10.11.12.0/24"; + static inline const char prefix_str2[] = "2001:db8:1::abcd/64"; + static inline const char prefix_str3[] = "10.11.12.245/27"; + static inline const char wrong_prefix_str0[] = "10.11.12.245/45"; + static inline const char wrong_prefix_str1[] = "10.400.12.13/8"; + static inline const char wrong_prefix_str2[] = "2001:db8:1::/640"; + static inline const char wrong_prefix_str3[] = "20011::db8:1::/16"; + static inline const char wrong_prefix_str4[] = "2001::db8:1::fffff/96"; + + PrefixTest() = default; + + ~PrefixTest() override = default; + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + void SetUp() override { + // Code here will be called immediately after the constructor (right + // before each test). + } + + void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } +}; + +TEST_F(PrefixTest, ConstructorRightString) { + // Create empty prefix + Prefix p; + + // Create prefix from string + Prefix p0(prefix_str0); + // Reconstruct string and check it is equal to original address + std::string network = p0.getNetwork(); + std::uint16_t prefix_length = p0.getPrefixLength(); + EXPECT_THAT(network + "/" + std::to_string(prefix_length), + ::testing::StrEq(prefix_str0)); + + // Create prefix from string + Prefix p1(prefix_str1); + // Reconstruct string and check it is equal to original address + network = p1.getNetwork(); + prefix_length = p1.getPrefixLength(); + EXPECT_THAT(network + "/" + std::to_string(prefix_length), + ::testing::StrEq(prefix_str1)); + + // Create prefix from string + Prefix p2(prefix_str2); + // Reconstruct string and check it is equal to original address + network = p2.getNetwork(); + prefix_length = p2.getPrefixLength(); + EXPECT_THAT(network + "/" + std::to_string(prefix_length), + ::testing::StrEq(prefix_str2)); + + // Create prefix from string + Prefix p3(prefix_str3); + // Reconstruct string and check it is equal to original address + network = p3.getNetwork(); + prefix_length = p3.getPrefixLength(); + EXPECT_THAT(network + "/" + std::to_string(prefix_length), + ::testing::StrEq(prefix_str3)); + + // Create prefix from string and prefix length + Prefix p4("2001::1234", 66); + // Reconstruct string and check it is equal to original address + network = p4.getNetwork(); + prefix_length = p4.getPrefixLength(); + auto af = p4.getAddressFamily(); + EXPECT_THAT(network, ::testing::StrEq("2001::1234")); + EXPECT_THAT(prefix_length, ::testing::Eq(66)); + EXPECT_THAT(af, ::testing::Eq(AF_INET6)); +} + +TEST_F(PrefixTest, ConstructorWrongString) { + try { + Prefix p0(wrong_prefix_str0); + FAIL() << "Expected exception"; + } catch (const errors::InvalidIpAddressException &) { + // Expected exception + } + + try { + Prefix p1(wrong_prefix_str1); + FAIL() << "Expected exception"; + } catch (const errors::InvalidIpAddressException &) { + // Expected exception + } + + try { + Prefix p2(wrong_prefix_str2); + FAIL() << "Expected exception"; + } catch (const errors::InvalidIpAddressException &) { + // Expected exception + } + + try { + Prefix p3(wrong_prefix_str3); + FAIL() << "Expected exception"; + } catch (const errors::InvalidIpAddressException &) { + // Expected exception + } + + try { + Prefix p4(wrong_prefix_str4); + FAIL() << "Expected exception"; + } catch (const errors::InvalidIpAddressException &) { + // Expected exception + } +} + +TEST_F(PrefixTest, Comparison) { + Prefix p0(prefix_str0); + Prefix p1(prefix_str1); + + // Expect they are different + EXPECT_THAT(p0, ::testing::Ne(p1)); + + auto p2 = p1; + // Expect they are equal + EXPECT_THAT(p1, ::testing::Eq(p2)); +} + +TEST_F(PrefixTest, ToSockAddress) { + Prefix p0(prefix_str3); + + auto ret = p0.toSockaddr(); + auto sockaddr = reinterpret_cast<sockaddr_in *>(ret.get()); + + EXPECT_THAT(sockaddr->sin_family, ::testing::Eq(AF_INET)); + EXPECT_THAT(sockaddr->sin_addr.s_addr, portability::host_to_net(0x0a0b0cf5)); +} + +TEST_F(PrefixTest, GetPrefixLength) { + Prefix p0(prefix_str3); + EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(27)); +} + +TEST_F(PrefixTest, SetPrefixLength) { + Prefix p0(prefix_str3); + EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(27)); + p0.setPrefixLength(20); + EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(20)); + + try { + p0.setPrefixLength(33); + FAIL() << "Expected exception"; + } catch ([[maybe_unused]] const errors::InvalidIpAddressException &) { + // Expected exception + } +} + +TEST_F(PrefixTest, SetGetNetwork) { + Prefix p0(prefix_str0); + EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(64)); + p0.setNetwork("b001::1234"); + EXPECT_THAT(p0.getNetwork(), ::testing::StrEq("b001::1234")); + EXPECT_THAT(p0.getPrefixLength(), ::testing::Eq(64)); +} + +TEST_F(PrefixTest, Contains) { + // IPv6 prefix + Prefix p0(prefix_str0); + hicn_ip_address_t ip0, ip1; + + hicn_ip_address_pton("2001:db8:1::1234", &ip0); + hicn_ip_address_pton("2001:db9:1::1234", &ip1); + + EXPECT_TRUE(p0.contains(ip0)); + EXPECT_FALSE(p0.contains(ip1)); + + Prefix p1(prefix_str1); + hicn_ip_address_pton("10.11.12.12", &ip0); + hicn_ip_address_pton("10.12.12.13", &ip1); + + EXPECT_TRUE(p1.contains(ip0)); + EXPECT_FALSE(p1.contains(ip1)); + + Prefix p2(prefix_str2); + hicn_ip_address_pton("2001:db8:1::dbca", &ip0); + hicn_ip_address_pton("10.12.12.12", &ip1); + + EXPECT_TRUE(p2.contains(ip0)); + EXPECT_FALSE(p2.contains(ip1)); + + Prefix p3(prefix_str3); + hicn_ip_address_pton("10.11.12.245", &ip0); + hicn_ip_address_pton("10.11.12.1", &ip1); + + EXPECT_TRUE(p3.contains(ip0)); + EXPECT_FALSE(p3.contains(ip1)); + + // Corner cases + Prefix p4("::/0"); + hicn_ip_address_pton("7001:db8:1::1234", &ip0); + hicn_ip_address_pton("8001:db8:1::1234", &ip1); + + EXPECT_TRUE(p4.contains(ip0)); + EXPECT_TRUE(p4.contains(ip1)); + + // Corner cases + Prefix p5("b001:a:b:c:d:e:f:1/128"); + hicn_ip_address_pton("b001:a:b:c:d:e:f:1", &ip0); + hicn_ip_address_pton("b001:a:b:c:d:e:f:2", &ip1); + + EXPECT_TRUE(p5.contains(ip0)); + EXPECT_FALSE(p5.contains(ip1)); +} + +TEST_F(PrefixTest, GetAddressFamily) { + Prefix p0(prefix_str0); + auto af = p0.getAddressFamily(); + EXPECT_THAT(af, ::testing::Eq(AF_INET6)); + + Prefix p1(prefix_str1); + af = p1.getAddressFamily(); + EXPECT_THAT(af, ::testing::Eq(AF_INET)); +} + +TEST_F(PrefixTest, MakeName) { + Prefix p0(prefix_str0); + auto name0 = p0.makeName(); + EXPECT_THAT(name0.toString(), ::testing::StrEq("2001:db8:1::|0")); + + Prefix p1(prefix_str1); + auto name1 = p1.makeName(); + EXPECT_THAT(name1.toString(), ::testing::StrEq("10.11.12.0|0")); + + Prefix p2(prefix_str2); + auto name2 = p2.makeName(); + EXPECT_THAT(name2.toString(), ::testing::StrEq("2001:db8:1::|0")); + + Prefix p3(prefix_str3); + auto name3 = p3.makeName(); + EXPECT_THAT(name3.toString(), ::testing::StrEq("10.11.12.224|0")); + + Prefix p4("b001:a:b:c:d:e:f:1/128"); + auto name4 = p4.makeName(); + EXPECT_THAT(name4.toString(), ::testing::StrEq("b001:a:b:c:d:e:f:1|0")); +} + +TEST_F(PrefixTest, MakeRandomName) { + Prefix p0(prefix_str0); + auto name0 = p0.makeRandomName(); + auto name1 = p0.makeRandomName(); + auto name2 = p0.makeRandomName(); + auto name3 = p0.makeRandomName(); + + EXPECT_THAT(name0, ::testing::Not(::testing::Eq(name1))); + EXPECT_THAT(name0, ::testing::Not(::testing::Eq(name2))); + EXPECT_THAT(name0, ::testing::Not(::testing::Eq(name3))); + EXPECT_THAT(name1, ::testing::Not(::testing::Eq(name2))); + EXPECT_THAT(name1, ::testing::Not(::testing::Eq(name3))); + EXPECT_THAT(name2, ::testing::Not(::testing::Eq(name3))); + + // Corner case + Prefix p2("b001:a:b:c:d:e:f:1/128"); + name0 = p2.makeRandomName(); + name1 = p2.makeRandomName(); + name2 = p2.makeRandomName(); + name3 = p2.makeRandomName(); + + EXPECT_THAT(name0, ::testing::Eq(name1)); + EXPECT_THAT(name0, ::testing::Eq(name2)); + EXPECT_THAT(name0, ::testing::Eq(name3)); + EXPECT_THAT(name1, ::testing::Eq(name2)); + EXPECT_THAT(name1, ::testing::Eq(name3)); + EXPECT_THAT(name2, ::testing::Eq(name3)); +} + +TEST_F(PrefixTest, MakeNameWithIndex) { + Prefix p0(prefix_str0); + auto name0 = p0.makeNameWithIndex(0); + EXPECT_THAT(name0.toString(), ::testing::StrEq("2001:db8:1::|0")); + auto name1 = p0.makeNameWithIndex(1); + EXPECT_THAT(name1.toString(), ::testing::StrEq("2001:db8:1::1|0")); + auto name2 = p0.makeNameWithIndex(2); + EXPECT_THAT(name2.toString(), ::testing::StrEq("2001:db8:1::2|0")); + auto name3 = p0.makeNameWithIndex(3); + EXPECT_THAT(name3.toString(), ::testing::StrEq("2001:db8:1::3|0")); + + Prefix p1(prefix_str1); + name0 = p1.makeNameWithIndex(0); + EXPECT_THAT(name0.toString(), ::testing::StrEq("10.11.12.0|0")); + name1 = p1.makeNameWithIndex(1); + EXPECT_THAT(name1.toString(), ::testing::StrEq("10.11.12.1|0")); + name2 = p1.makeNameWithIndex(2); + EXPECT_THAT(name2.toString(), ::testing::StrEq("10.11.12.2|0")); + name3 = p1.makeNameWithIndex(3); + EXPECT_THAT(name3.toString(), ::testing::StrEq("10.11.12.3|0")); + + // Test truncation + Prefix p2("b001::/96"); + name0 = p2.makeNameWithIndex(0xffffffffffffffff); + EXPECT_THAT(name0.toString(), ::testing::StrEq("b001::ffff:ffff|0")); +} + +} // namespace + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_quadloop.cc b/libtransport/src/test/test_quadloop.cc new file mode 100644 index 000000000..6a08033aa --- /dev/null +++ b/libtransport/src/test/test_quadloop.cc @@ -0,0 +1,176 @@ +/* + * 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/portability/cache.h> + +#include <array> +#include <cstring> +#include <memory> +#include <vector> + +namespace utils { + +class LoopTest : public ::testing::Test { + protected: + static inline const std::size_t size = 256; + + LoopTest() = default; + + ~LoopTest() override = default; + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + void SetUp() override { + // Code here will be called immediately after the constructor (right + // before each test). + } + + void TearDown() override { + // Code here will be called immediately after each test (right + // before the destructor). + } +}; + +// 1 cache line struct (64 bytes) +struct Data { + std::array<uint64_t, 8> data; +}; + +TEST_F(LoopTest, QuadLoopTest) { + // Create 2 arrays of 256 elements + std::vector<std::unique_ptr<Data>> _from; + std::vector<std::unique_ptr<Data>> _to_next; + _from.reserve(size); + _to_next.reserve(size); + + int n_left_from = size; + int n_left_to_next = size; + + // Initialize the arrays + for (std::size_t i = 0; i < size; i++) { + _from.push_back(std::make_unique<Data>()); + _to_next.push_back(std::make_unique<Data>()); + + for (int j = 0; j < 8; j++) { + _from[i]->data[j] = j; + _to_next[i]->data[j] = 0; + } + } + + const std::unique_ptr<Data> *from = &_from[0]; + const std::unique_ptr<Data> *to_next = &_to_next[0]; + + clock_t start; + clock_t end; + double clocks; + + start = clock(); + // Create a quad loop + while (n_left_from > 0) { + while (n_left_from >= 4 && n_left_to_next >= 4) { + { + using namespace transport::portability::cache; + Data *d2; + Data *d3; + + d2 = from[2].get(); + d3 = from[3].get(); + + prefetch<Data, READ>(d2, sizeof(Data)); + prefetch<Data, READ>(d3, sizeof(Data)); + + d2 = to_next[2].get(); + d3 = to_next[3].get(); + + prefetch<Data, WRITE>(d2, sizeof(Data)); + prefetch<Data, WRITE>(d3, sizeof(Data)); + } + + // Do 4 iterations + std::memcpy(to_next[0].get()->data.data(), from[0].get()->data.data(), + sizeof(Data)); + std::memcpy(to_next[1].get()->data.data(), from[1].get()->data.data(), + sizeof(Data)); + n_left_from -= 2; + n_left_to_next -= 2; + from += 2; + to_next += 2; + } + + while (n_left_from > 0 && n_left_to_next > 0) { + std::memcpy(to_next[0].get()->data.data(), from[0].get()->data.data(), + sizeof(Data)); + n_left_from -= 1; + n_left_to_next -= 1; + from += 1; + to_next += 1; + } + } + end = clock(); + clocks = (double)(end - start); + + LOG(INFO) << "Time with quad loop: " << clocks << std::endl; +} + +TEST_F(LoopTest, NormalLoopTest) { + // Create 2 arrays of 256 elements + std::vector<std::unique_ptr<Data>> _from; + std::vector<std::unique_ptr<Data>> _to_next; + _from.reserve(size); + _to_next.reserve(size); + + int n_left_from = size; + int n_left_to_next = size; + + // Initialize the arrays + for (std::size_t i = 0; i < size; i++) { + _from.push_back(std::make_unique<Data>()); + _to_next.push_back(std::make_unique<Data>()); + + for (int j = 0; j < 8; j++) { + _from[i]->data[j] = j; + _to_next[i]->data[j] = 0; + } + } + + const std::unique_ptr<Data> *from = &_from[0]; + const std::unique_ptr<Data> *to_next = &_to_next[0]; + + clock_t start; + clock_t end; + double clocks; + + start = clock(); + while (n_left_from > 0) { + while (n_left_from > 0 && n_left_to_next > 0) { + std::memcpy(to_next[0].get()->data.data(), from[0].get()->data.data(), + sizeof(Data)); + n_left_from -= 1; + n_left_to_next -= 1; + from += 1; + to_next += 1; + } + } + end = clock(); + clocks = ((double)(end - start)); + + LOG(INFO) << "Time with normal loop: " << clocks << std::endl; +} + +} // namespace utils
\ 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_transport_producer.cc b/libtransport/src/test/test_thread_pool.cc index f711fb4bb..1b6b4cc81 100644 --- a/libtransport/src/test/test_transport_producer.cc +++ b/libtransport/src/test/test_thread_pool.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,25 +13,19 @@ * limitations under the License. */ +#include <gmock/gmock.h> #include <gtest/gtest.h> +#include <hicn/transport/utils/thread_pool.h> -#include <random> +namespace utils { -#include "hicn/transport/interfaces/socket_producer.h" - -namespace transport { - -namespace interface { - -namespace { -// The fixture for testing class Foo. -class ProducerTest : public ::testing::Test { +class ThreadPoolTest : public ::testing::Test { protected: - ProducerTest() : name_("b001::123|321"), producer_() { + ThreadPoolTest() : thread_pool_() { // You can do set-up work for each test here. } - virtual ~ProducerTest() { + virtual ~ThreadPoolTest() { // You can do clean-up work that doesn't throw exceptions here. } @@ -48,19 +42,29 @@ class ProducerTest : public ::testing::Test { // before the destructor). } - Name name_; - ProducerSocket producer_; + ::utils::ThreadPool thread_pool_; }; -} // namespace - -TEST_F(ProducerTest, ProduceContent) { ASSERT_TRUE(true); } - -} // namespace interface - -} // namespace transport - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}
\ No newline at end of file +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/test/test_traffic_generator.cc b/libtransport/src/test/test_traffic_generator.cc new file mode 100644 index 000000000..733acbb74 --- /dev/null +++ b/libtransport/src/test/test_traffic_generator.cc @@ -0,0 +1,119 @@ +/* + * 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 <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/utils/traffic_generator.h> + +static constexpr int NUM_PINGS = 10; +static constexpr char PREFIX[] = "b001:1:2:3::"; +static constexpr uint32_t FIRST_SUFFIX = 5; + +namespace utils { + +using transport::IncrSuffixTrafficGenerator; +using transport::RandomTrafficGenerator; + +class TrafficGeneratorTest : public ::testing::Test { + protected: + TrafficGeneratorTest() {} + virtual ~TrafficGeneratorTest() {} +}; + +TEST_F(TrafficGeneratorTest, IncrSuffixGetPrefixAndSuffix) { + auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + PREFIX, FIRST_SUFFIX, NUM_PINGS); + + std::string prefix = traffic_generator_->getPrefix(); + EXPECT_EQ(prefix, PREFIX); + uint32_t suffix = traffic_generator_->getSuffix(); + EXPECT_EQ(suffix, FIRST_SUFFIX); +} + +TEST_F(TrafficGeneratorTest, IncrSuffixGetMultipleSuffixes) { + auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + PREFIX, FIRST_SUFFIX, NUM_PINGS); + + std::string prefix = traffic_generator_->getPrefix(); + EXPECT_EQ(prefix, PREFIX); + EXPECT_EQ(prefix, traffic_generator_->getPrefix()); + + for (int i = 0; i < NUM_PINGS; i++) + EXPECT_EQ(traffic_generator_->getSuffix(), FIRST_SUFFIX + i); +} + +TEST_F(TrafficGeneratorTest, IncrSuffixReset) { + auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + PREFIX, FIRST_SUFFIX, NUM_PINGS); + + for (int i = 0; i < NUM_PINGS; i++) traffic_generator_->getSuffix(); + + traffic_generator_->reset(); + EXPECT_EQ(traffic_generator_->getPrefix(), PREFIX); + EXPECT_EQ(traffic_generator_->getSuffix(), FIRST_SUFFIX); + EXPECT_EQ(traffic_generator_->getSuffix(), FIRST_SUFFIX + 1); +} + +TEST_F(TrafficGeneratorTest, IncrSuffixRequestTooManySuffixes) { + auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + PREFIX, FIRST_SUFFIX, NUM_PINGS); + + for (int i = 0; i < NUM_PINGS; i++) traffic_generator_->getSuffix(); + EXPECT_THROW(traffic_generator_->getSuffix(), std::runtime_error); +} + +TEST_F(TrafficGeneratorTest, IncrSuffixGetPrefixAndSuffixTogether) { + auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + PREFIX, FIRST_SUFFIX, NUM_PINGS); + + auto [prefix, suffix] = traffic_generator_->getPrefixAndSuffix(); + EXPECT_EQ(prefix, PREFIX); + EXPECT_EQ(suffix, FIRST_SUFFIX); +} + +TEST_F(TrafficGeneratorTest, IncrSuffixCheckSentCount) { + auto traffic_generator_ = std::make_unique<IncrSuffixTrafficGenerator>( + PREFIX, FIRST_SUFFIX, NUM_PINGS); + + for (int i = 0; i < NUM_PINGS; i++) { + EXPECT_EQ(traffic_generator_->getSentCount(), (uint32_t)i); + EXPECT_FALSE(traffic_generator_->hasFinished()); + traffic_generator_->getSuffix(); + } + EXPECT_TRUE(traffic_generator_->hasFinished()); +} + +TEST_F(TrafficGeneratorTest, RandomGetPrefixAndSuffix) { + auto traffic_generator_ = std::make_unique<RandomTrafficGenerator>(NUM_PINGS); + + std::string prefix1 = traffic_generator_->getPrefix(); + std::string prefix2 = traffic_generator_->getPrefix(); + EXPECT_NE(prefix1, prefix2); + + uint32_t suffix1 = traffic_generator_->getSuffix(); + uint32_t suffix2 = traffic_generator_->getSuffix(); + EXPECT_NE(suffix1, suffix2); +} + +TEST_F(TrafficGeneratorTest, RandomGetPrefixAndSuffixWithNetPrefix) { + auto traffic_generator_ = std::make_unique<RandomTrafficGenerator>( + NUM_PINGS, std::string(PREFIX) + "/64"); + + for (int i = 0; i < NUM_PINGS; i++) + EXPECT_THAT(traffic_generator_->getPrefix(), + testing::StartsWith(std::string(PREFIX))); +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/transport.config b/libtransport/src/transport.config new file mode 100644 index 000000000..d5c2918a6 --- /dev/null +++ b/libtransport/src/transport.config @@ -0,0 +1,61 @@ +// Configuration for io_module +io_module = { + path = []; + name = "forwarder_module"; +}; + +// Configuration for forwarder io_module +forwarder = { + n_threads = 1; + + connectors = { + c0 = { + /* local_address and local_port are optional */ + local_address = "127.0.0.1"; + local_port = 33436; + remote_address = "127.0.0.1"; + remote_port = 33436; + } + }; + + listeners = { + l0 = { + local_address = "127.0.0.1"; + local_port = 33437; + } + }; +}; + +// Logging +log = { + // Log level (INFO (0), WARNING (1), ERROR (2), FATAL (3)) + minloglevel = 0; + + // Verbosity level for debug logs + v= 2; + + // Log to stderr + logtostderr = true; + + // Get fancy colored logs + colorlogtostderr = true; + + // Log messages above this level also to stderr + stderrthreshold = 2; + + // Set log prefix for each line log + log_prefix = true; + + // Log dir + log_dir = "/tmp"; + + // Log only specific modules. + // Example: "membuf=2,rtc=3" + vmodule = ""; + + // Max log size in MB + max_log_size = 10; + + // Log rotation + stop_logging_if_full_disk = true; +};
\ No newline at end of file diff --git a/libtransport/src/utils/CMakeLists.txt b/libtransport/src/utils/CMakeLists.txt index 1a23459b5..e7c1c03ea 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: @@ -11,20 +11,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.cc ${CMAKE_CURRENT_SOURCE_DIR}/uri.cc ${CMAKE_CURRENT_SOURCE_DIR}/log.cc ${CMAKE_CURRENT_SOURCE_DIR}/membuf.cc ${CMAKE_CURRENT_SOURCE_DIR}/content_store.cc + ${CMAKE_CURRENT_SOURCE_DIR}/traffic_generator.cc ) 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 @@ -36,10 +36,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 cb3db6d94..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: @@ -13,11 +13,11 @@ * limitations under the License. */ +#include <glog/logging.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <hicn/transport/core/name.h> -#include <hicn/transport/utils/log.h> - +#include <hicn/transport/utils/chrono_typedefs.h> #include <utils/content_store.h> namespace utils { @@ -37,9 +37,9 @@ void ContentStore::insert( if (TRANSPORT_EXPECT_FALSE(content_store_hash_table_.size() != fifo_list_.size())) { - TRANSPORT_LOGW("Inconsistent size!!!!"); - TRANSPORT_LOGW("Hash Table: %zu |||| FIFO List: %zu", - content_store_hash_table_.size(), fifo_list_.size()); + LOG(WARNING) << "Inconsistent size!!!!"; + LOG(WARNING) << "Hash Table: " << content_store_hash_table_.size() + << " |||| FIFO List: " << fifo_list_.size(); } if (content_store_hash_table_.size() >= max_content_store_size_) { @@ -57,20 +57,19 @@ 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); } -const std::shared_ptr<ContentObject> ContentStore::find( - const Interest &interest) { +std::shared_ptr<ContentObject> ContentStore::find(const Name &name) { utils::SpinLock::Acquire locked(cs_mutex_); std::shared_ptr<ContentObject> ret = empty_reference_; - auto it = content_store_hash_table_.find(interest.getName()); + auto it = content_store_hash_table_.find(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) { @@ -110,13 +109,11 @@ void ContentStore::printContent() { for (auto &item : content_store_hash_table_) { if (item.second.first.first->getPayloadType() == transport::core::PayloadType::MANIFEST) { - TRANSPORT_LOGI("Manifest: %s", - item.second.first.first->getName().toString().c_str()); + LOG(INFO) << "Manifest: " << item.second.first.first->getName(); } else { - TRANSPORT_LOGI("Data Packet: %s", - item.second.first.first->getName().toString().c_str()); + LOG(INFO) << "Data Packet: " << item.second.first.first->getName(); } } } -} // end namespace utils
\ No newline at end of file +} // end namespace utils diff --git a/libtransport/src/utils/content_store.h b/libtransport/src/utils/content_store.h index 03ce76f42..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> @@ -52,7 +51,7 @@ class ContentStore { void insert(const std::shared_ptr<ContentObject> &content_object); - const std::shared_ptr<ContentObject> find(const Interest &interest); + std::shared_ptr<ContentObject> find(const Name &name); void erase(const Name &exact_name); @@ -73,4 +72,4 @@ class ContentStore { mutable utils::SpinLock cs_mutex_; }; -} // end namespace utils
\ No newline at end of file +} // end namespace utils diff --git a/libtransport/src/utils/daemonizator.cc b/libtransport/src/utils/daemonizator.cc index c51a68d14..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: @@ -14,10 +14,9 @@ */ #ifndef _WIN32 +#include <glog/logging.h> #include <hicn/transport/errors/runtime_exception.h> #include <hicn/transport/utils/daemonizator.h> -#include <hicn/transport/utils/log.h> - #include <sys/stat.h> #include <unistd.h> @@ -37,7 +36,7 @@ void Daemonizator::daemonize(bool close_fds) { // PARENT PROCESS. Need to kill it. if (process_id > 0) { - TRANSPORT_LOGE("Process id of child process %d", process_id); + LOG(ERROR) << "Process id of child process " << process_id; // return success in exit status exit(EXIT_SUCCESS); } 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 63c08df95..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 <hicn/transport/utils/branch_prediction.h> - -#include <utils/epoll_event_reactor.h> -#include <utils/fd_deadline_timer.h> - -#include <signal.h> -#include <unistd.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)) { - TRANSPORT_LOGE("invalid fd %d", 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)) { - TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); - return -1; - } - - return 0; -} - -int EpollEventReactor::modFileDescriptor(int fd, uint32_t events) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - TRANSPORT_LOGE("invalid fd %d", 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)) { - TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), 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)) { - TRANSPORT_LOGE("invalid fd %d", 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)) { - TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), 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)) { - TRANSPORT_LOGE("epoll_pwait: %s", 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())) { - TRANSPORT_LOGE("unexpected event. fd %d", 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 { - TRANSPORT_LOGE("unexpected event. fd %d", 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)) { - TRANSPORT_LOGE("epoll_pwait: %s", 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())) { - TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd); - } else { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - callback = event_callback_map_[evt.data.fd]; - } - - callback(evt); - } - } else { - TRANSPORT_LOGE("unexpected event. fd %d", 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 4cb87ebd4..32d99c837 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,10 +15,11 @@ #pragma once +#include <glog/logging.h> #include <hicn/transport/utils/spinlock.h> +#include <sys/epoll.h> #include <utils/event_reactor.h> -#include <sys/epoll.h> #include <atomic> #include <cstddef> #include <functional> @@ -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) { @@ -47,7 +49,7 @@ class EpollEventReactor : public EventReactor { if (it == event_callback_map_.end()) { { utils::SpinLock::Acquire locked(event_callback_map_lock_); - event_callback_map_[fd] = std::forward<EventHandler &&>(callback); + event_callback_map_[fd] = std::forward<EventHandler>(callback); } ret = addFileDescriptor(fd, events); @@ -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; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + memset(&evt, 0, sizeof(evt)); - int modFileDescriptor(int fd, uint32_t events); + 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); + } - void runEventLoop(int timeout = -1) override; + 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]; + } - void runOneEvent() override; + callback(evt); + } + } else { + LOG(ERROR) << "unexpected event. fd " << evt.data.fd; + } + } - void stop() override; + void stop() override { run_event_loop_ = false; } - std::size_t mapSize(); + 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 8bc3bbca3..7ecbba69b 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,17 +16,15 @@ #pragma once #include <hicn/transport/errors/runtime_exception.h> -#include <hicn/transport/utils/log.h> - +#include <hicn/transport/utils/chrono_typedefs.h> +#include <sys/timerfd.h> +#include <unistd.h> #include <utils/deadline_timer.h> #include <utils/epoll_event_reactor.h> #include <chrono> #include <cstddef> -#include <sys/timerfd.h> -#include <unistd.h> - namespace utils { class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> { @@ -40,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> @@ -54,13 +57,13 @@ class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> { reactor_.addFileDescriptor( timer_fd_, events, - [callback = std::forward<WaitHandler &&>(callback)]( - const Event &event) -> int { + [callback = + std::forward<WaitHandler>(callback)](const Event &event) -> int { uint64_t s = 0; std::error_code ec; if (read(event.data.fd, &s, sizeof(s)) == -1) { - TRANSPORT_LOGE("Read error!!"); + LOG(ERROR) << "Read error!!"; } if (!(event.events & EPOLLIN)) { @@ -89,8 +92,7 @@ class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> { template <typename T, typename R> void expiresFromNowImpl(std::chrono::duration<T, R> &&duration) { std::memset(&new_value_, 0, sizeof(new_value_)); - new_value_.it_value = std::chrono::duration_cast<struct timespec>( - std::forward<std::chrono::duration<T, R>>(duration)); + new_value_.it_value = std::chrono::duration_cast<struct timespec>(duration); } template <typename TimePoint, diff --git a/libtransport/src/utils/log.cc b/libtransport/src/utils/log.cc index 27dd3f541..44acf4595 100644 --- a/libtransport/src/utils/log.cc +++ b/libtransport/src/utils/log.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,1391 +13,79 @@ * limitations under the License. */ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 wonder-mice - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* When defined, Android log (android/log.h) will be used by default instead of - * stderr (ignored on non-Android platforms). Date, time, pid and tid (context) - * will be provided by Android log. Android log features will be used to output - * log level and tag. - */ - -#if defined(__ANDROID__) -#define TRANSPORT_LOG_USE_ANDROID_LOG 1 -#define ANDROID_TAG "HicnTransport" -#else -#define TRANSPORT_LOG_USE_ANDROID_LOG 0 -#endif - -/* When defined, NSLog (uses Apple System Log) will be used instead of stderr - * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be - * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on - * non-public CFLog() function. Both use Apple System Log internally, but it's - * easier to call CFLog() from C than NSLog(). Current implementation doesn't - * support "%@" format specifier. - */ -#ifdef TRANSPORT_LOG_USE_NSLOG -#undef TRANSPORT_LOG_USE_NSLOG -#if defined(__APPLE__) && defined(__MACH__) -#define TRANSPORT_LOG_USE_NSLOG 1 -#else -#define TRANSPORT_LOG_USE_NSLOG 0 -#endif -#else -#define TRANSPORT_LOG_USE_NSLOG 0 -#endif -/* When defined, OutputDebugString() will be used instead of stderr (ignored on - * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with - * UTF-8 data. - */ -#ifdef TRANSPORT_LOG_USE_DEBUGSTRING -#undef TRANSPORT_LOG_USE_DEBUGSTRING -#if defined(_WIN32) || defined(_WIN64) -#define TRANSPORT_LOG_USE_DEBUGSTRING 1 -#else -#define TRANSPORT_LOG_USE_DEBUGSTRING 0 -#endif -#else -#define TRANSPORT_LOG_USE_DEBUGSTRING 0 -#endif -/* When defined, TRANSPORT_LOG library will not contain definition of tag prefix - * variable. In that case it must be defined elsewhere using - * TRANSPORT_LOG_DEFINE_TAG_PREFIX macro, for example: - * - * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "ProcessName"; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef TRANSPORT_LOG_EXTERN_TAG_PREFIX -#undef TRANSPORT_LOG_EXTERN_TAG_PREFIX -#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 1 -#else -#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 0 -#endif -/* When defined, TRANSPORT_LOG library will not contain definition of global - * format variable. In that case it must be defined elsewhere using - * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT macro, for example: - * - * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH}; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT -#undef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT -#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 1 -#else -#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 0 -#endif -/* When defined, transport_log library will not contain definition of global - * output variable. In that case it must be defined elsewhere using - * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example: - * - * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD, - * custom_output_callback}; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT -#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT -#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 1 -#else -#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 0 -#endif -/* When defined, transport_log library will not contain definition of global - * output level variable. In that case it must be defined elsewhere using - * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example: - * - * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = TRANSPORT_LOG_WARN; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL -#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL -#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1 -#else -#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0 -#endif -/* When defined, implementation will prefer smaller code size over speed. - * Very rough estimate is that code will be up to 2x smaller and up to 2x - * slower. Disabled by default. - */ -#ifdef TRANSPORT_LOG_OPTIMIZE_SIZE -#undef TRANSPORT_LOG_OPTIMIZE_SIZE -#define TRANSPORT_LOG_OPTIMIZE_SIZE 1 -#else -#define TRANSPORT_LOG_OPTIMIZE_SIZE 0 -#endif -/* Size of the log line buffer. The buffer is allocated on stack. It limits - * maximum length of a log line. - */ -#ifndef TRANSPORT_LOG_BUF_SZ -#define TRANSPORT_LOG_BUF_SZ 512 -#endif -/* Default number of bytes in one line of memory output. For large values - * TRANSPORT_LOG_BUF_SZ also must be increased. - */ -#ifndef TRANSPORT_LOG_MEM_WIDTH -#define TRANSPORT_LOG_MEM_WIDTH 32 -#endif -/* String to put in the end of each log line (can be empty). Its value used by - * stderr output callback. Its size used as a default value for - * TRANSPORT_LOG_EOL_SZ. - */ -#ifndef TRANSPORT_LOG_EOL -#define TRANSPORT_LOG_EOL "\n" -#endif -/* Default delimiter that separates parts of log message. Can NOT contain '%' - * or '\0'. - * - * Log message format specifications can override (or ignore) this value. For - * more details see TRANSPORT_LOG_MESSAGE_CTX_FORMAT, - * TRANSPORT_LOG_MESSAGE_SRC_FORMAT and TRANSPORT_LOG_MESSAGE_TAG_FORMAT. - */ -#ifndef TRANSPORT_LOG_DEF_DELIMITER -#define TRANSPORT_LOG_DEF_DELIMITER " " -#endif -/* Specifies log message context format. Log message context includes date, - * time, process id, thread id and message's log level. Custom information can - * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, - * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements), - * F_UINT(width, value). - * - * Must be defined as a tuple, for example: - * - * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, - * S(" > ")) - * - * In that case, resulting log message will be: - * - * 2016.12.22 > TAG function@filename.c:line Message text - * - * Note, that tag, source location and message text are not impacted by - * this setting. See TRANSPORT_LOG_MESSAGE_TAG_FORMAT and - * TRANSPORT_LOG_MESSAGE_SRC_FORMAT. - * - * If message context must be visually separated from the rest of the message, - * it must be reflected in context format (notice trailing S(" > ") in the - * example above). - * - * S(str) adds constant string str. String can NOT contain '%' or '\0'. - * - * F_INIT(statements) adds initialization statement(s) that will be evaluated - * once for each log message. All statements are evaluated in specified order. - * Several F_INIT() fields can be used in every log message format - * specification. Fields, like F_UINT(width, value), are allowed to use results - * of initialization statements. If statement introduces variables (or other - * names, like structures) they must be prefixed with "f_". Statements must be - * enclosed into additional "()". Example: - * - * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ - * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \ - * YEAR, S("."), MONTH, S("."), DAY, S(" "), \ - * F_UINT(5, f_ru.ru_nsignals), \ - * S(" ")) - * - * F_UINT(width, value) adds unsigned integer value extended with up to width - * spaces (for alignment purposes). Value can be any expression that evaluates - * to unsigned integer. If expression contains non-standard functions, they - * must be declared with F_INIT(). Example: - * - * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ - * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \ - * F_INIT(( unsigned tickcount(); )), \ - * F_UINT(5, tickcount()), \ - * S(" ")) - * - * Other log message format specifications follow same rules, but have a - * different set of supported fields. - */ -#ifndef TRANSPORT_LOG_MESSAGE_CTX_FORMAT -#define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ - (MONTH, S("-"), DAY, S(TRANSPORT_LOG_DEF_DELIMITER), HOUR, S(":"), MINUTE, \ - S(":"), SECOND, S("."), MILLISECOND, S(TRANSPORT_LOG_DEF_DELIMITER), PID, \ - S(TRANSPORT_LOG_DEF_DELIMITER), TID, S(TRANSPORT_LOG_DEF_DELIMITER), LEVEL, \ - S(TRANSPORT_LOG_DEF_DELIMITER)) -#endif -/* Example: - */ -/* Specifies log message tag format. It includes tag prefix and tag. Custom - * information can be added as well. Supported fields: - * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements), - * F_UINT(width, value). - * - * TAG(prefix_delimiter, tag_delimiter) adds following string to log message: - * - * PREFIX<prefix_delimiter>TAG<tag_delimiter> - * - * Prefix delimiter will be used only when prefix is not empty. Tag delimiter - * will be used only when prefixed tag is not empty. Example: - * - * #define TRANSPORT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] ")) - * - * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. - */ -#ifndef TRANSPORT_LOG_MESSAGE_TAG_FORMAT -#define TRANSPORT_LOG_MESSAGE_TAG_FORMAT (TAG(".", TRANSPORT_LOG_DEF_DELIMITER)) -#endif -/* Specifies log message source location format. It includes function name, - * file name and file line. Custom information can be added as well. Supported - * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements), - * F_UINT(width, value). - * - * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. - */ -#ifndef TRANSPORT_LOG_MESSAGE_SRC_FORMAT -#define TRANSPORT_LOG_MESSAGE_SRC_FORMAT \ - (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(TRANSPORT_LOG_DEF_DELIMITER)) -#endif -/* Fields that can be used in log message format specifications (see above). - * Mentioning them here explicitly, so we know that nobody else defined them - * before us. See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. - */ -#define YEAR YEAR -#define MONTH MONTH -#define DAY DAY -#define MINUTE MINUTE -#define SECOND SECOND -#define MILLISECOND MILLISECOND -#define PID PID -#define TID TID -#define LEVEL LEVEL -#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim) -#define FUNCTION FUNCTION -#define FILENAME FILENAME -#define FILELINE FILELINE -#define S(str) S(str) -#define F_INIT(statements) F_INIT(statements) -#define F_UINT(width, value) F_UINT(width, value) -/* Number of bytes to reserve for EOL in the log line buffer (must be >0). - * Must be larger than or equal to length of TRANSPORT_LOG_EOL with terminating - * null. - */ -#ifndef TRANSPORT_LOG_EOL_SZ -#define TRANSPORT_LOG_EOL_SZ sizeof(TRANSPORT_LOG_EOL) -#endif -/* Compile instrumented version of the library to facilitate unit testing. - */ -#ifndef TRANSPORT_LOG_INSTRUMENTED -#define TRANSPORT_LOG_INSTRUMENTED 0 -#endif +#define GLOG_CUSTOM_PREFIX_SUPPORT 1 +#include <glog/logging.h> +#undef GLOG_CUSTOM_PREFIX_SUPPORT -#if defined(__linux__) -#if !defined(__ANDROID__) && !defined(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#endif -#if defined(__MINGW32__) -#ifdef __STRICT_ANSI__ -#undef __STRICT_ANSI__ -#endif -#endif - -#include <assert.h> -#include <ctype.h> +#include <core/global_configuration.h> +#include <hicn/transport/config.h> #include <hicn/transport/utils/log.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#if defined(_WIN32) || defined(_WIN64) -#include <windows.h> -#else -#include <sys/time.h> -#include <unistd.h> -#if defined(__linux__) -#include <linux/limits.h> -#else -#include <sys/syslimits.h> -#endif -#endif - -#if defined(__linux__) -#include <sys/prctl.h> -#include <sys/types.h> -#if !defined(__ANDROID__) -#include <sys/syscall.h> -#endif -#endif -#if defined(__MACH__) -#include <pthread.h> -#endif - -#define INLINE _TRANSPORT_LOG_INLINE -#define VAR_UNUSED(var) (void)var -#define RETVAL_UNUSED(expr) \ - do { \ - while (expr) break; \ - } while (0) -#define STATIC_ASSERT(name, cond) typedef char assert_##name[(cond) ? 1 : -1] -#define ASSERT_UNREACHABLE(why) assert(!sizeof(why)) -#ifndef _countof -#define _countof(xs) (sizeof(xs) / sizeof((xs)[0])) -#endif - -#if TRANSPORT_LOG_INSTRUMENTED -#define INSTRUMENTED_CONST -#else -#define INSTRUMENTED_CONST const -#endif - -#define _PP_PASTE_2(a, b) a##b -#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b) - -#define _PP_PASTE_3(a, b, c) a##b##c -#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c) - -/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__ - * as a single token and requires additional expansion to realize that it's - * actually a list. If not for it, there would be no need in this extra - * expansion. - */ -#define _PP_ID(x) x -#define _PP_NARGS_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \ - _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, \ - _24, ...) \ - _24 -#define _PP_NARGS(...) \ - _PP_ID(_PP_NARGS_N(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, \ - 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) - -/* There is a more efficient way to implement this, but it requires - * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't - * have one. - */ -#define _PP_HEAD__(x, ...) x -#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~)) -#define _PP_HEAD(xs) _PP_HEAD_ xs -#define _PP_TAIL_(x, ...) (__VA_ARGS__) -#define _PP_TAIL(xs) _PP_TAIL_ xs -#define _PP_UNTUPLE_(...) __VA_ARGS__ -#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs - -/* Apply function macro to each element in tuple. Output is not - * enforced to be a tuple. - */ -#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs)) -#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs)) -#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs)) -#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs)) -#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs)) -#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs)) -#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs)) -#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs)) -#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs)) -#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs)) -#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs)) -#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs)) -#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs)) -#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs)) -#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs)) -#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs)) -#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs)) -#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs)) -#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs)) -#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs)) -#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs)) -#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs)) -#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs)) -#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs)) -#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs)(f, xs) - -/* Apply function macro to each element in tuple in reverse order. - * Output is not enforced to be a tuple. - */ -#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs)) -#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs)(f, xs) - -/* Used to implement _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All - * possible fields must be mentioned here. Not counting F_INIT() here because - * it's somewhat special and is handled spearatly (at least for now). - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__ (0 << 0) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__YEAR (1 << 1) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MONTH (1 << 2) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__DAY (1 << 3) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__HOUR (1 << 4) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1 << 5) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__SECOND (1 << 6) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1 << 7) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__PID (1 << 8) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TID (1 << 9) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1 << 10) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1 << 11) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1 << 12) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1 << 13) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1 << 14) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__S(s) (1 << 15) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0 << 16) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1 << 17) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) \ - _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_MASK_, _, field) - -/* Logical "or" of masks of fields used in specified format specification. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format) \ - (0 _PP_MAP(| _TRANSPORT_LOG_MESSAGE_FORMAT_MASK, format)) - -/* Expands to expressions that evaluates to true if field is used in - * specified format specification. Example: - * - * #if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, - * TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - * ... - * #endif - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \ - (_TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) & \ - _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format)) - -/* Same, but checks all supported format specifications. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \ - (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ - TRANSPORT_LOG_MESSAGE_TAG_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ - TRANSPORT_LOG_MESSAGE_SRC_FORMAT)) - -#define _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED \ - (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT)) - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(disable : 4204) /* nonstandard extension used: non-constant \ - aggregate initializer */ -#define memccpy _memccpy -#endif - -#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || defined(__MINGW64__) -#define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va) -static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) { - const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap); - return 0 < n ? n : (int)sz + 1; /* no need in _vscprintf() for now */ -} -#if TRANSPORT_LOG_OPTIMIZE_SIZE -#define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__) -static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) { - va_list va; - va_start(va, fmt); - const int n = fake_vsnprintf(s, sz, fmt, va); - va_end(va); - return n; -} -#endif -#endif - -typedef void (*time_cb)(struct tm *const tm, unsigned *const usec); -typedef void (*pid_cb)(int *const pid, int *const tid); -typedef void (*buffer_cb)(transport_log_message *msg, char *buf); - -typedef struct src_location { - const char *const func; - const char *const file; - const unsigned line; -} src_location; - -typedef struct mem_block { - const void *const d; - const unsigned d_sz; -} mem_block; - -static void time_callback(struct tm *const tm, unsigned *const usec); -static void pid_callback(int *const pid, int *const tid); -static void buffer_callback(transport_log_message *msg, char *buf); - -STATIC_ASSERT(eol_fits_eol_sz, - sizeof(TRANSPORT_LOG_EOL) <= TRANSPORT_LOG_EOL_SZ); -STATIC_ASSERT(eol_sz_greater_than_zero, 0 < TRANSPORT_LOG_EOL_SZ); -STATIC_ASSERT(eol_sz_less_than_buf_sz, - TRANSPORT_LOG_EOL_SZ < TRANSPORT_LOG_BUF_SZ); -#if !defined(_WIN32) && !defined(_WIN64) -STATIC_ASSERT(buf_sz_less_than_pipe_buf, TRANSPORT_LOG_BUF_SZ <= PIPE_BUF); -#endif -static const char c_hex[] = "0123456789abcdef"; - -static INSTRUMENTED_CONST unsigned g_buf_sz = - TRANSPORT_LOG_BUF_SZ - TRANSPORT_LOG_EOL_SZ; -static INSTRUMENTED_CONST time_cb g_time_cb = time_callback; -static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback; -static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback; -#if TRANSPORT_LOG_USE_ANDROID_LOG -#include <android/log.h> - -static INLINE int android_lvl(const int lvl) { - switch (lvl) { - case TRANSPORT_LOG_VERBOSE: - return ANDROID_LOG_VERBOSE; - case TRANSPORT_LOG_DEBUG: - return ANDROID_LOG_DEBUG; - case TRANSPORT_LOG_INFO: - return ANDROID_LOG_INFO; - case TRANSPORT_LOG_WARN: - return ANDROID_LOG_WARN; - case TRANSPORT_LOG_ERROR: - return ANDROID_LOG_ERROR; - case TRANSPORT_LOG_FATAL: - return ANDROID_LOG_FATAL; - default: - ASSERT_UNREACHABLE("Bad log level"); - return ANDROID_LOG_UNKNOWN; - } -} - -static void out_android_callback(const transport_log_message *const msg, - void *arg) { - VAR_UNUSED(arg); - *msg->p = 0; - const char *tag = msg->p; - if (msg->tag_e != msg->tag_b) { - tag = msg->tag_b; - *msg->tag_e = 0; +#include <iomanip> +#include <iostream> +#include <libconfig.h++> + +namespace utils { + +#define _(class_name, macro_name) \ + std::ostream &CLASS_NAME(class_name)::getStream() { return macro_name; } +foreach_log_level +#undef _ + + class LogConfiguration { + static constexpr char log_config_section[] = "log"; +#define LOG_NAME \ + "Libhicntransport-" HICNTRANSPORT_VERSION_MAJOR \ + "." HICNTRANSPORT_VERSION_MINOR "." HICNTRANSPORT_VERSION_PATCH + static constexpr char log_name[] = LOG_NAME; + +#define foreach_log_config \ + _(bool, logtostderr, true) \ + _(bool, alsologtostderr, false) \ + _(bool, colorlogtostderr, true) \ + _(int32_t, stderrthreshold, 2) \ + _(int32_t, minloglevel, 0) \ + _(bool, log_prefix, true) \ + _(std::string, log_dir, "") \ + _(int32_t, v, 1) \ + _(std::string, vmodule, "") \ + _(int32_t, max_log_size, 5) \ + _(bool, stop_logging_if_full_disk, true) + + public: + LogConfiguration() { + auto &conf = transport::core::GlobalConfiguration::getInstance(); + + using namespace std::placeholders; + conf.registerConfigurationParser( + log_config_section, + std::bind(&LogConfiguration::parseLogConfiguration, this, _1, _2)); + } + + private: + void parseLogConfiguration(const libconfig::Setting &log_config, + std::error_code &ec) { +#define _(type, name, default) \ + type _##name = default; \ + \ + if (log_config.exists(#name)) { \ + log_config.lookupValue(#name, _##name); \ + VLOG(2) << "Setting log config " << #name << " to " << _##name; \ + \ + FLAGS_##name = _##name; \ + } else { \ + VLOG(2) << "Log config " << #name << " do not exists"; \ + } + foreach_log_config +#undef _ + + google::InitGoogleLogging(log_name); } - __android_log_print(android_lvl(msg->lvl), ANDROID_TAG, "%s", msg->msg_b); -} - -enum { OUT_ANDROID_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX }; -#define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback -#endif - -#if TRANSPORT_LOG_USE_NSLOG -#include <CoreFoundation/CoreFoundation.h> -CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); - -static INLINE int apple_lvl(const int lvl) { - switch (lvl) { - case TRANSPORT_LOG_VERBOSE: - return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */ - ; - case TRANSPORT_LOG_DEBUG: - return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */ - ; - case TRANSPORT_LOG_INFO: - return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */ - ; - case TRANSPORT_LOG_WARN: - return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */ - ; - case TRANSPORT_LOG_ERROR: - return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */ - ; - case TRANSPORT_LOG_FATAL: - return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */ - ; - default: - ASSERT_UNREACHABLE("Bad log level"); - return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */ - ; - } -} - -static void out_nslog_callback(const transport_log_message *const msg, - void *arg) { - VAR_UNUSED(arg); - *msg->p = 0; - CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b); -} - -enum { OUT_NSLOG_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX }; -#define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback -#endif - -#if TRANSPORT_LOG_USE_DEBUGSTRING -#include <windows.h> - -static void out_debugstring_callback(const transport_log_message *const msg, - void *arg) { - VAR_UNUSED(arg); - msg->p[0] = '\n'; - msg->p[1] = '\0'; - OutputDebugStringA(msg->buf); -} - -enum { OUT_DEBUGSTRING_MASK = TRANSPORT_LOG_PUT_STD }; -#define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback -#endif - -void transport_log_out_stderr_callback(const transport_log_message *const msg, - void *arg) { - VAR_UNUSED(arg); - const size_t eol_len = sizeof(TRANSPORT_LOG_EOL) - 1; - memcpy(msg->p, TRANSPORT_LOG_EOL, eol_len); -#if defined(_WIN32) || defined(_WIN64) - /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and - without FILE_WRITE_DATA */ - DWORD written; - WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf, - (DWORD)(msg->p - msg->buf + eol_len), &written, 0); -#else - /* write() is atomic for buffers less than or equal to PIPE_BUF. */ - RETVAL_UNUSED( - write(STDERR_FILENO, msg->buf, (size_t)(msg->p - msg->buf) + eol_len)); -#endif -} - -static const transport_log_output out_stderr = {TRANSPORT_LOG_OUT_STDERR}; - -#if !TRANSPORT_LOG_EXTERN_TAG_PREFIX -TRANSPORT_LOG_DEFINE_TAG_PREFIX = 0; -#endif - -#if !TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT -TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {TRANSPORT_LOG_MEM_WIDTH}; -#endif - -#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT -#if TRANSPORT_LOG_USE_ANDROID_LOG -TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID}; -#elif TRANSPORT_LOG_USE_NSLOG -TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG}; -#elif TRANSPORT_LOG_USE_DEBUGSTRING -TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING}; -#else -TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR}; -#endif -#endif - -#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL -TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0; -#endif - -const transport_log_spec _transport_log_stderr_spec = { - TRANSPORT_LOG_GLOBAL_FORMAT, - &out_stderr, }; -static const transport_log_spec global_spec = { - TRANSPORT_LOG_GLOBAL_FORMAT, - TRANSPORT_LOG_GLOBAL_OUTPUT, -}; - -#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) -static char lvl_char(const int lvl) { - switch (lvl) { - case TRANSPORT_LOG_VERBOSE: - return 'V'; - case TRANSPORT_LOG_DEBUG: - return 'D'; - case TRANSPORT_LOG_INFO: - return 'I'; - case TRANSPORT_LOG_WARN: - return 'W'; - case TRANSPORT_LOG_ERROR: - return 'E'; - case TRANSPORT_LOG_FATAL: - return 'F'; - default: - ASSERT_UNREACHABLE("Bad log level"); - return '?'; - } -} -#endif - -#define GCCVER_LESS(MAJOR, MINOR, PATCH) \ - (__GNUC__ < MAJOR || (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \ - (__GNUC_MINOR__ == MINOR && \ - __GNUC_PATCHLEVEL__ < PATCH)))) - -#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4, 7, 0) -#define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0) -#define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n) -#define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n) -#define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n) -#define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n) -/* Note: will not store old value of *vp in *ep (non-standard behaviour) */ -#define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \ - __sync_bool_compare_and_swap(vp, *(ep), d) -#endif - -#if !TRANSPORT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64) -#define TCACHE -#define TCACHE_STALE (0x40000000) -#define TCACHE_FLUID (0x40000000 | 0x80000000) -static unsigned g_tcache_mode = TCACHE_STALE; -static struct timeval g_tcache_tv = {0, 0}; -static struct tm g_tcache_tm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -static INLINE int tcache_get(const struct timeval *const tv, - struct tm *const tm) { - unsigned mode; - mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED); - if (0 == (mode & TCACHE_FLUID)) { - mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE); - if (0 == (mode & TCACHE_FLUID)) { - if (g_tcache_tv.tv_sec == tv->tv_sec) { - *tm = g_tcache_tm; - __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); - return !0; - } - __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED); - } - __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); - } - return 0; -} - -static INLINE void tcache_set(const struct timeval *const tv, - struct tm *const tm) { - unsigned stale = TCACHE_STALE; - if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, 0, - __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { - g_tcache_tv = *tv; - g_tcache_tm = *tm; - __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE); - } -} -#endif - -static void time_callback(struct tm *const tm, unsigned *const msec) { -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED - VAR_UNUSED(tm); - VAR_UNUSED(msec); -#else -#if defined(_WIN32) || defined(_WIN64) - SYSTEMTIME st; - GetLocalTime(&st); - tm->tm_year = st.wYear; - tm->tm_mon = st.wMonth - 1; - tm->tm_mday = st.wDay; - tm->tm_wday = st.wDayOfWeek; - tm->tm_hour = st.wHour; - tm->tm_min = st.wMinute; - tm->tm_sec = st.wSecond; - *msec = st.wMilliseconds; -#else - struct timeval tv; - gettimeofday(&tv, 0); -#ifndef TCACHE - localtime_r(&tv.tv_sec, tm); -#else - if (!tcache_get(&tv, tm)) { - localtime_r(&tv.tv_sec, tm); - tcache_set(&tv, tm); - } -#endif - *msec = (unsigned)tv.tv_usec / 1000; -#endif -#endif -} - -static void pid_callback(int *const pid, int *const tid) { -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(PID, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - VAR_UNUSED(pid); -#else -#if defined(_WIN32) || defined(_WIN64) - *pid = GetCurrentProcessId(); -#else - *pid = getpid(); -#endif -#endif - -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - VAR_UNUSED(tid); -#else -#if defined(_WIN32) || defined(_WIN64) - *tid = GetCurrentThreadId(); -#elif defined(__ANDROID__) - *tid = gettid(); -#elif defined(__linux__) - *tid = syscall(SYS_gettid); -#elif defined(__MACH__) - *tid = (int)pthread_mach_thread_np(pthread_self()); -#else -#define Platform not supported -#endif -#endif -} - -static void buffer_callback(transport_log_message *msg, char *buf) { - msg->e = (msg->p = msg->buf = buf) + g_buf_sz; -} - -#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, \ - TRANSPORT_LOG_MESSAGE_SRC_FORMAT) -static const char *funcname(const char *func) { return func ? func : ""; } -#endif - -#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, \ - TRANSPORT_LOG_MESSAGE_SRC_FORMAT) -static const char *filename(const char *file) { - const char *f = file; - for (const char *p = file; 0 != *p; ++p) { - if ('/' == *p || '\\' == *p) { - f = p + 1; - } - } - return f; -} -#endif - -static INLINE size_t nprintf_size(transport_log_message *const msg) { - // *nprintf() always puts 0 in the end when input buffer is not empty. This - // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which - // leaves space for one more character. Some put_xxx() functions don't use - // *nprintf() and could use that last character. In that case log line will - // have multiple (two) half-written parts which is confusing. To workaround - // that we allow *nprintf() to write its 0 in the eol area (which is always - // not empty). - return (size_t)(msg->e - msg->p + 1); -} - -static INLINE void put_nprintf(transport_log_message *const msg, const int n) { - if (0 < n) { - msg->p = n < msg->e - msg->p ? msg->p + n : msg->e; - } -} - -static INLINE char *put_padding_r(const unsigned w, const char wc, char *p, - char *e) { - for (char *const b = e - w; b < p; *--p = wc) { - } - return p; -} - -static char *put_integer_r(unsigned v, const int sign, const unsigned w, - const char wc, char *const e) { - static const char _signs[] = {'-', '0', '+'}; - static const char *const signs = _signs + 1; - char *p = e; - do { - *--p = '0' + v % 10; - } while (0 != (v /= 10)); - if (0 == sign) return put_padding_r(w, wc, p, e); - if ('0' != wc) { - *--p = signs[sign]; - return put_padding_r(w, wc, p, e); - } - p = put_padding_r(w, wc, p, e + 1); - *--p = signs[sign]; - return p; -} - -static INLINE char *put_uint_r(const unsigned v, const unsigned w, - const char wc, char *const e) { - return put_integer_r(v, 0, w, wc, e); -} - -static INLINE char *put_int_r(const int v, const unsigned w, const char wc, - char *const e) { - return 0 <= v ? put_integer_r((unsigned)v, 0, w, wc, e) - : put_integer_r((unsigned)-v, -1, w, wc, e); -} - -static INLINE char *put_stringn(const char *const s_p, const char *const s_e, - char *const p, char *const e) { - const ptrdiff_t m = e - p; - ptrdiff_t n = s_e - s_p; - if (n > m) { - n = m; - } - memcpy(p, s_p, n); - return p + n; -} - -static INLINE char *put_string(const char *s, char *p, char *const e) { - const ptrdiff_t n = e - p; - char *const c = (char *)memccpy(p, s, '\0', n); - return 0 != c ? c - 1 : e; -} - -static INLINE char *put_uint(unsigned v, const unsigned w, const char wc, - char *const p, char *const e) { - char buf[16]; - char *const se = buf + _countof(buf); - char *sp = put_uint_r(v, w, wc, se); - return put_stringn(sp, se, p, e); -} - -#define PUT_CSTR_R(p, STR) \ - do { \ - for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \ - *--(p) = (STR)[i]; \ - } \ - } \ - _TRANSPORT_LOG_ONCE - -#define PUT_CSTR_CHECKED(p, e, STR) \ - do { \ - for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \ - *(p)++ = (STR)[i]; \ - } \ - } \ - _TRANSPORT_LOG_ONCE - -/* F_INIT field support. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__YEAR -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MONTH -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__DAY -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__HOUR -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MINUTE -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__SECOND -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__PID -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TID -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__LEVEL -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FUNCTION -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILENAME -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILELINE -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__S(s) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT(field) \ - _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT_, _, field) - -/* Implements generation of printf-like format string for log message - * format specification. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ "" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) "" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u" -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \ - _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field) - -/* Implements generation of printf-like format parameters for log message - * format specification. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR \ - , (unsigned)(tm.tm_year + 1900) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH \ - , (unsigned)(tm.tm_mon + 1) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY , (unsigned)tm.tm_mday -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR , (unsigned)tm.tm_hour -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE , (unsigned)tm.tm_min -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND , (unsigned)tm.tm_sec -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND , (unsigned)msec -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID , pid -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID , tid -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL \ - , (char)lvl_char(msg->lvl) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION , funcname(src->func) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME , filename(src->file) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE , src->line -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) , v -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \ - _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field) - -/* Implements generation of put_xxx_t statements for log message specification. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__YEAR \ - p = put_uint_r(tm.tm_year + 1900, 4, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MONTH \ - p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__DAY \ - p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__HOUR \ - p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE \ - p = put_uint_r((unsigned)tm.tm_min, 2, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__SECOND \ - p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND \ - p = put_uint_r(msec, 3, '0', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) \ - p = put_uint_r(v, w, ' ', p); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R(field) \ - _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R_, _, field) - -static void put_ctx(transport_log_message *const msg) { - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - VAR_UNUSED(msg); -#else -#if _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED - struct tm tm; - unsigned msec; - g_time_cb(&tm, &msec); -#endif -#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ - PID, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ - _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \ - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - int pid, tid; - g_pid_cb(&pid, &tid); -#endif - -#if TRANSPORT_LOG_OPTIMIZE_SIZE - int n; - n = snprintf(msg->p, nprintf_size(msg), - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT, - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL, - TRANSPORT_LOG_MESSAGE_CTX_FORMAT)); - put_nprintf(msg, n); -#else - char buf[64]; - char *const e = buf + sizeof(buf); - char *p = e; - _PP_RMAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R, - TRANSPORT_LOG_MESSAGE_CTX_FORMAT) - msg->p = put_stringn(p, e, msg->p, msg->e); -#endif -#endif -} - -#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \ - do { \ - const char *ch; \ - msg->tag_b = msg->p; \ - if (0 != (ch = _transport_log_tag_prefix)) { \ - for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \ - } \ - } \ - if (0 != (ch = tag) && 0 != tag[0]) { \ - if (msg->tag_b != msg->p) { \ - PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \ - } \ - for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \ - } \ - } \ - msg->tag_e = msg->p; \ - if (msg->tag_b != msg->p) { \ - PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \ - } \ - } \ - _TRANSPORT_LOG_ONCE - -/* Implements simple put statements for log message specification. - */ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__ -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) \ - PUT_TAG(msg, tag, pd, td); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FUNCTION \ - msg->p = put_string(funcname(src->func), msg->p, msg->e); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILENAME \ - msg->p = put_string(filename(src->file), msg->p, msg->e); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILELINE \ - msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__S(s) \ - PUT_CSTR_CHECKED(msg->p, msg->e, s); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr) -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) \ - msg->p = put_uint(v, w, ' ', msg->p, msg->e); -#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT(field) \ - _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_, _, field) - -static void put_tag(transport_log_message *const msg, const char *const tag) { - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT) -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, \ - TRANSPORT_LOG_MESSAGE_TAG_FORMAT) - VAR_UNUSED(tag); -#endif -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_TAG_FORMAT) - VAR_UNUSED(msg); -#else - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT) -#endif -} - -static void put_src(transport_log_message *const msg, - const src_location *const src) { - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ - FUNCTION, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \ - !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ - FILENAME, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \ - !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, \ - TRANSPORT_LOG_MESSAGE_SRC_FORMAT) - VAR_UNUSED(src); -#endif -#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_SRC_FORMAT) - VAR_UNUSED(msg); -#else -#if TRANSPORT_LOG_OPTIMIZE_SIZE - int n; - n = snprintf(msg->p, nprintf_size(msg), - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT, - TRANSPORT_LOG_MESSAGE_SRC_FORMAT) - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL, - TRANSPORT_LOG_MESSAGE_SRC_FORMAT)); - put_nprintf(msg, n); -#else - _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) -#endif -#endif -} - -static void put_msg(transport_log_message *const msg, const char *const fmt, - va_list va) { - int n; - msg->msg_b = msg->p; - n = vsnprintf(msg->p, nprintf_size(msg), fmt, va); - put_nprintf(msg, n); -} - -static void output_mem(const transport_log_spec *log, - transport_log_message *const msg, - const mem_block *const mem) { - if (0 == mem->d || 0 == mem->d_sz) { - return; - } - const unsigned char *mem_p = (const unsigned char *)mem->d; - const unsigned char *const mem_e = mem_p + mem->d_sz; - const unsigned char *mem_cut; - const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width; - char *const hex_b = msg->msg_b; - char *const ascii_b = hex_b + 2 * mem_width + 2; - char *const ascii_e = ascii_b + mem_width; - if (msg->e < ascii_e) { - return; - } - while (mem_p != mem_e) { - char *hex = hex_b; - char *ascii = ascii_b; - for (mem_cut = mem_width < mem_e - mem_p ? mem_p + mem_width : mem_e; - mem_cut != mem_p; ++mem_p) { - const unsigned char ch = *mem_p; - *hex++ = c_hex[(0xf0 & ch) >> 4]; - *hex++ = c_hex[(0x0f & ch)]; - *ascii++ = isprint(ch) ? (char)ch : '?'; - } - while (hex != ascii_b) { - *hex++ = ' '; - } - msg->p = ascii; - log->output->callback(msg, log->output->arg); - } -} - -void transport_log_set_tag_prefix(const char *const prefix) { - _transport_log_tag_prefix = prefix; -} - -void transport_log_set_mem_width(const unsigned w) { - _transport_log_global_format.mem_width = w; -} - -void transport_log_set_output_level(const int lvl) { - _transport_log_global_output_lvl = lvl; -} - -void transport_log_set_output_v(const unsigned mask, void *const arg, - const transport_log_output_cb callback) { - _transport_log_global_output.mask = mask; - _transport_log_global_output.arg = arg; - _transport_log_global_output.callback = callback; -} - -static void _transport_log_write_imp(const transport_log_spec *log, - const src_location *const src, - const mem_block *const mem, const int lvl, - const char *const tag, - const char *const fmt, va_list va) { - transport_log_message msg; - char buf[TRANSPORT_LOG_BUF_SZ]; - const unsigned mask = log->output->mask; - msg.lvl = lvl; - msg.tag = tag; - g_buffer_cb(&msg, buf); - if (TRANSPORT_LOG_PUT_CTX & mask) { - put_ctx(&msg); - } - if (TRANSPORT_LOG_PUT_TAG & mask) { - put_tag(&msg, tag); - } - if (0 != src && TRANSPORT_LOG_PUT_SRC & mask) { - put_src(&msg, src); - } - if (TRANSPORT_LOG_PUT_MSG & mask) { - put_msg(&msg, fmt, va); - } - log->output->callback(&msg, log->output->arg); - if (0 != mem && TRANSPORT_LOG_PUT_MSG & mask) { - output_mem(log, &msg, mem); - } -} - -void _transport_log_write_d(const char *const func, const char *const file, - const unsigned line, const int lvl, - const char *const tag, const char *const fmt, ...) { - const src_location src = {func, file, line}; - va_list va; - va_start(va, fmt); - _transport_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va); - va_end(va); -} - -void _transport_log_write_aux_d(const char *const func, const char *const file, - const unsigned line, - const transport_log_spec *const log, - const int lvl, const char *const tag, - const char *const fmt, ...) { - const src_location src = {func, file, line}; - va_list va; - va_start(va, fmt); - _transport_log_write_imp(log, &src, 0, lvl, tag, fmt, va); - va_end(va); -} - -void _transport_log_write(const int lvl, const char *const tag, - const char *const fmt, ...) { - va_list va; - va_start(va, fmt); - _transport_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va); - va_end(va); -} - -void _transport_log_write_aux(const transport_log_spec *const log, - const int lvl, const char *const tag, - const char *const fmt, ...) { - va_list va; - va_start(va, fmt); - _transport_log_write_imp(log, 0, 0, lvl, tag, fmt, va); - va_end(va); -} - -void _transport_log_write_mem_d(const char *const func, const char *const file, - const unsigned line, const int lvl, - const char *const tag, const void *const d, - const unsigned d_sz, const char *const fmt, - ...) { - const src_location src = {func, file, line}; - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _transport_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va); - va_end(va); -} - -void _transport_log_write_mem_aux_d(const char *const func, - const char *const file, const unsigned line, - const transport_log_spec *const log, - const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) { - const src_location src = {func, file, line}; - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _transport_log_write_imp(log, &src, &mem, lvl, tag, fmt, va); - va_end(va); -} +constexpr char LogConfiguration::log_config_section[]; +constexpr char LogConfiguration::log_name[]; -void _transport_log_write_mem(const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) { - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _transport_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va); - va_end(va); -} +LogConfiguration log_conf = LogConfiguration(); -void _transport_log_write_mem_aux(const transport_log_spec *const log, - const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) { - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _transport_log_write_imp(log, 0, &mem, lvl, tag, fmt, va); - va_end(va); -}
\ No newline at end of file +} // namespace utils diff --git a/libtransport/src/utils/max_filter.h b/libtransport/src/utils/max_filter.h new file mode 100644 index 000000000..db1a1a565 --- /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() const { 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() const { return *by_order_.crbegin(); } + + const T& rBegin() const { 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 94e5b13a1..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> @@ -145,6 +146,18 @@ void MemBuf::operator delete(void* /* ptr */, void* /* placement */) { // constructor. } +bool MemBuf::operator==(const MemBuf& other) { + if (length() != other.length()) { + return false; + } + + return (memcmp(data(), other.data(), length()) == 0); +} + +bool MemBuf::operator!=(const MemBuf& other) { + return !this->operator==(other); +} + void MemBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) { // Use relaxed memory order here. If we are unlucky and happen to get // out-of-date data the compare_exchange_weak() call below will catch @@ -197,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); } @@ -299,21 +312,23 @@ unique_ptr<MemBuf> MemBuf::takeOwnership(void* buf, std::size_t capacity, } } -MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept +MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t length, + std::size_t capacity) noexcept : MemBuf(InternalConstructor(), 0, // We cast away the const-ness of the buffer here. // This is okay since MemBuf users must use unshare() to create a // copy of this buffer before writing to the buffer. static_cast<uint8_t*>(const_cast<void*>(buf)), capacity, - static_cast<uint8_t*>(const_cast<void*>(buf)), capacity) {} + static_cast<uint8_t*>(const_cast<void*>(buf)), length) {} -unique_ptr<MemBuf> MemBuf::wrapBuffer(const void* buf, std::size_t capacity) { - return std::make_unique<MemBuf>(WRAP_BUFFER, buf, capacity); +unique_ptr<MemBuf> MemBuf::wrapBuffer(const void* buf, std::size_t length, + std::size_t capacity) { + return std::make_unique<MemBuf>(WRAP_BUFFER, buf, length, capacity); } -MemBuf MemBuf::wrapBufferAsValue(const void* buf, +MemBuf MemBuf::wrapBufferAsValue(const void* buf, std::size_t length, std::size_t capacity) noexcept { - return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity); + return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, length, capacity); } MemBuf::MemBuf() noexcept {} @@ -355,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() { @@ -545,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_); } @@ -562,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) { @@ -592,7 +607,7 @@ void MemBuf::markExternallyShared() { } void MemBuf::makeManagedChained() { - assert(isChained()); + DCHECK(isChained()); MemBuf* current = this; while (true) { @@ -663,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(); @@ -785,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()) { @@ -862,4 +877,22 @@ void MemBuf::initExtBuffer(uint8_t* buf, size_t mallocSize, *infoReturn = sharedInfo; } +bool MemBuf::ensureCapacity(std::size_t capacity) { + return !isChained() && std::size_t((bufferEnd() - data())) >= capacity; +} + +bool MemBuf::ensureCapacityAndFillUnused(std::size_t capacity, + uint8_t placeholder) { + auto ret = ensureCapacity(capacity); + if (!ret) { + return ret; + } + + if (length() < capacity) { + std::memset(writableTail(), placeholder, capacity - length()); + } + + return ret; +} + } // namespace utils
\ No newline at end of file diff --git a/libtransport/src/utils/memory_pool_allocator.h b/libtransport/src/utils/memory_pool_allocator.h index adc1443ad..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: @@ -149,4 +149,4 @@ class Allocator : private MemoryPool<T, growSize> { void destroy(pointer p) { p->~T(); } }; -}
\ No newline at end of file +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/utils/min_filter.h b/libtransport/src/utils/min_filter.h index dcfd5652d..4a3882601 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: @@ -15,9 +15,6 @@ #pragma once -#include <hicn/transport/portability/portability.h> -#include <hicn/transport/utils/log.h> - #include <deque> #include <iostream> #include <set> @@ -31,10 +28,10 @@ class MinFilter { public: MinFilter(std::size_t size) : size_(size) {} - std::size_t size() { return by_arrival_.size(); } + std::size_t size() const { return by_arrival_.size(); } template <typename R> - TRANSPORT_ALWAYS_INLINE void pushBack(R&& value) { + void pushBack(R&& value) { if (by_arrival_.size() >= size_) { by_order_.erase(by_arrival_.back()); by_arrival_.pop_back(); @@ -43,9 +40,14 @@ class MinFilter { by_arrival_.push_front(by_order_.insert(std::forward<R>(value))); } - TRANSPORT_ALWAYS_INLINE const T& begin() { return *by_order_.cbegin(); } + void clear() { + by_arrival_.clear(); + by_order_.clear(); + } + + const T& begin() const { return *by_order_.cbegin(); } - TRANSPORT_ALWAYS_INLINE const T& rBegin() { return *by_order_.crbegin(); } + const T& rBegin() const { return *by_order_.crbegin(); } private: std::multiset<T> by_order_; 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 6c4dd2785..4b3ddbc74 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,149 +15,128 @@ #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 MANIFEST_MAX_CAPACITY = + std::numeric_limits<uint8_t>::max(); - SuffixStrategy(NextSegmentCalculationStrategy strategy) + SuffixStrategy(NextSuffixStrategy strategy, uint32_t offset = 0, + uint32_t manifest_capacity = MANIFEST_MAX_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() 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() const = 0; virtual uint32_t getNextManifestSuffix() = 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) {} - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextSuffix() override { - incrementTotalCount(); - return next_suffix_++; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextContentSuffix() override { - return getNextSuffix(); - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override { - return getNextSuffix(); - } - - uint32_t getManifestCapacity() override { - throw errors::RuntimeException( - "No manifest capacity in IncrementalSuffixStrategy."); - } + : SuffixStrategy(NextSuffixStrategy::INCREMENTAL, start_offset) {} - void setManifestCapacity(uint32_t capacity) override { - throw errors::RuntimeException( - "No manifest capacity in IncrementalSuffixStrategy."); + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() const override { + return next_suffix_; } - 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 getNextSuffix() override { incrementTotalCount(); return next_suffix_++; } - 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 checkNextContentSuffix() + const override { + return checkNextSuffix(); } - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override { - incrementTotalCount(); - return (current_manifest_iteration_++) * (segments_in_manifest_ + 1); + TRANSPORT_ALWAYS_INLINE std::uint32_t getNextContentSuffix() override { + return getNextSuffix(); } - TRANSPORT_ALWAYS_INLINE uint32_t getManifestCapacity() override { - return segments_in_manifest_; + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() + const override { + return checkNextSuffix(); } - TRANSPORT_ALWAYS_INLINE void setManifestCapacity(uint32_t capacity) override { - segments_in_manifest_ = capacity; + TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override { + return getNextSuffix(); } 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::MANIFEST_MAX_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/traffic_generator.cc b/libtransport/src/utils/traffic_generator.cc new file mode 100644 index 000000000..a617e3dc9 --- /dev/null +++ b/libtransport/src/utils/traffic_generator.cc @@ -0,0 +1,87 @@ +/* + * 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 <hicn/transport/core/prefix.h> +#include <hicn/transport/utils/traffic_generator.h> + +#include <iostream> + +namespace transport { + +/* TrafficGenerator */ + +TrafficGenerator::TrafficGenerator(uint32_t count) : count_(count), sent_(0) {} + +bool TrafficGenerator::hasFinished() { return sent_ >= count_; } + +uint32_t TrafficGenerator::getSentCount() { return sent_; } + +std::pair<std::string, uint32_t> TrafficGenerator::getPrefixAndSuffix() { + return std::make_pair(getPrefix(), getSuffix()); +} + +void TrafficGenerator::reset() { sent_ = 0; }; + +void TrafficGenerator::onSuffixGenerated() { + if (hasFinished()) throw std::runtime_error("Too many pings"); + sent_++; +}; + +/* IncrSuffixTrafficGenerator */ + +IncrSuffixTrafficGenerator::IncrSuffixTrafficGenerator(std::string prefix, + uint32_t suffix, + uint32_t count) + : TrafficGenerator(count), + prefix_(prefix), + suffix_(suffix), + initial_suffix_(suffix) {} + +std::string IncrSuffixTrafficGenerator::getPrefix() { return prefix_; } + +uint32_t IncrSuffixTrafficGenerator::getSuffix() { + TrafficGenerator::onSuffixGenerated(); + return suffix_++; +} + +void IncrSuffixTrafficGenerator::reset() { + TrafficGenerator::reset(); + suffix_ = initial_suffix_; +}; + +/* RandomTrafficGenerator */ + +RandomTrafficGenerator::RandomTrafficGenerator(uint32_t count, + std::string net_prefix) + : TrafficGenerator(count), + net_prefix_(net_prefix), + rand_engine_((std::random_device())()), + uniform_distribution_(0, std::numeric_limits<uint32_t>::max()) {} + +std::string RandomTrafficGenerator::getPrefix() { + // Generate random prefix + core::Prefix prefix(net_prefix_); + core::Name name = prefix.makeRandomName(); + return name.getPrefix(); +} + +uint32_t RandomTrafficGenerator::getSuffix() { + TrafficGenerator::onSuffixGenerated(); + + // Generate random suffix + return uniform_distribution_(rand_engine_); +} + +} // namespace transport
\ No newline at end of file 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: |