diff options
author | Luca Muscariello <lumuscar@cisco.com> | 2022-03-30 22:29:28 +0200 |
---|---|---|
committer | Mauro Sardara <msardara@cisco.com> | 2022-03-31 19:51:47 +0200 |
commit | c46e5df56b67bb8ea7a068d39324c640084ead2b (patch) | |
tree | eddeb17785938e09bc42eec98ee09b8a28846de6 /libtransport/src | |
parent | 18fa668f25d3cc5463417ce7df6637e31578e898 (diff) |
feat: boostrap hicn 22.02
The current patch provides several new features, improvements,
bug fixes and also complete rewrite of entire components.
- lib
The hicn packet parser has been improved with a new packet
format fully based on UDP. The TCP header is still temporarily
supported but the UDP header will replace completely the new hicn
packet format. Improvements have been made to make sure every
packet parsing operation is made via this library. The current
new header can be used as header between the payload and the
UDP header or as trailer in the UDP surplus area to be tested
when UDP options will start to be used.
- hicn-light
The portable packet forwarder has been completely rewritten from
scratch with the twofold objective to improve performance and
code size but also to drop dependencies such as libparc which is
now removed by the current implementation.
- hicn control
the control library is the agent that is used to program the
packet forwarders via their binary API. This component has
benefited from significant improvements in terms of interaction
model which is now event driven and more robust to failures.
- VPP plugin has been updated to support VPP 22.02
- transport
Major improvement have been made to the RTC protocol, to the
support of IO modules and to the security sub system. Signed
manifests are the default data authenticity and integrity framework.
Confidentiality can be enabled by sharing the encryption key to the
prod/cons layer. The library has been tested with group key based
applications such as broadcast/multicast and real-time on-line
meetings with trusted server keys or MLS.
- testing
Unit testing has been introduced using GoogleTest. One third of
the code base is covered by unit testing with priority on
critical features. Functional testing has also been introduce
using Docker, linux bridging and Robot Framework to define
test with Less Code techniques to facilitate the extension
of the coverage.
Co-authored-by: Mauro Sardara <msardara@cisco.com>
Co-authored-by: Jordan Augé <jordan.auge+fdio@cisco.com>
Co-authored-by: Michele Papalini <micpapal@cisco.com>
Co-authored-by: Angelo Mantellini <manangel@cisco.com>
Co-authored-by: Jacques Samain <jsamain@cisco.com>
Co-authored-by: Olivier Roques <oroques+fdio@cisco.com>
Co-authored-by: Enrico Loparco <eloparco@cisco.com>
Co-authored-by: Giulio Grassi <gigrassi@cisco.com>
Change-Id: I75d0ef70f86d921e3ef503c99271216ff583c215
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Signed-off-by: Mauro Sardara <msardara@cisco.com>
Diffstat (limited to 'libtransport/src')
225 files changed, 12099 insertions, 6235 deletions
diff --git a/libtransport/src/CMakeLists.txt b/libtransport/src/CMakeLists.txt index 5e0cd38e7..477a21cb1 100644 --- a/libtransport/src/CMakeLists.txt +++ b/libtransport/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -11,10 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(GNUInstallDirs) - -set(ASIO_STANDALONE 1) - +############################################################## +# Source files +############################################################## add_subdirectory(core) add_subdirectory(interfaces) add_subdirectory(protocols) @@ -23,49 +22,131 @@ add_subdirectory(implementation) add_subdirectory(utils) add_subdirectory(http) -include(Packager) -extract_version() -configure_file("config.h.in" "hicn/transport/config.h" @ONLY) -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/hicn/transport/config.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hicn/transport - COMPONENT ${LIBTRANSPORT_COMPONENT}-dev + +############################################################## +# Libraries to link +############################################################## +set(LIBRARIES + PRIVATE ${HICN_LIBRARIES} + PRIVATE ${CMAKE_THREAD_LIBS_INIT} + PRIVATE ${CMAKE_DL_LIBS} + PRIVATE ${OPENSSL_LIBRARIES} + PRIVATE ${LIBCONFIG_CPP_LIBRARIES} + PRIVATE ${THIRD_PARTY_LIBRARIES} ) -install( - FILES "transport.config" - DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn - COMPONENT ${LIBTRANSPORT_COMPONENT} + +############################################################## +# Include dirs +############################################################## +list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS + PRIVATE + ${CMAKE_THREADS_INCLUDE_DIRS} + ${ASIO_INCLUDE_DIRS} + ${WINDOWS_INCLUDE_DIRS} + ${LIBCONFIG_CPP_INCLUDE_DIRS} + ${THIRD_PARTY_INCLUDE_DIRS} + PUBLIC + ${ASIO_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIR} ) -list(APPEND COMPILER_DEFINITIONS - "-DASIO_STANDALONE" +list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/ + PUBLIC + $<BUILD_INTERFACE:${Libhicntransport_INCLUDE_DIRS}> + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) -list(INSERT LIBTRANSPORT_INTERNAL_INCLUDE_DIRS 0 - ${CMAKE_CURRENT_SOURCE_DIR}/ - ${CMAKE_CURRENT_BINARY_DIR}/ + +############################################################## +# Dependencies +############################################################## +list(APPEND DEPENDENCIES + ${THIRD_PARTY_DEPENDENCIES} ) -set(LIBTRANSPORT_INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - "" CACHE INTERNAL - "" FORCE +############################################################## +# Compiler definitions +############################################################## +list(APPEND COMPILER_DEFINITIONS + PUBLIC "-DASIO_STANDALONE" +) + +if (ENABLE_RELY) + list(APPEND COMPILER_DEFINITIONS + PRIVATE "-DENABLE_RELY=1" + ) +endif() + + +############################################################## +# Compiler options +############################################################## +list(APPEND COMPILER_OPTIONS + ${DEFAULT_COMPILER_OPTIONS} ) if (NOT WIN32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + list(APPEND COMPILER_OPTIONS + PRIVATE "-pthread" + ) else () - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4200 -D_WIN32_WINDOWS=0x0400") + list(APPEND COMPILER_OPTIONS + PRIVATE "-/wd4200 -D_WIN32_WINDOWS=0x0400" + ) if (CMAKE_BUILD_TYPE EQUAL "RELEASE") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:\"MSVCRTD\"" ) + list(APPEND COMPILER_OPTIONS + PRIVATE "/NODEFAULTLIB:\"MSVCRTD\"" + ) endif () endif () if (${CMAKE_SYSTEM_NAME} MATCHES "Android") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -isystem -lm") + list(APPEND COMPILER_OPTIONS + PRIVATE "-stdlib=libc++" + PRIVATE "-isystem" + PRIVATE "-lm" + ) endif() + +############################################################## +# Configuration file +############################################################## +configure_file("config.h.in" "hicn/transport/config.h" @ONLY) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/hicn/transport/config.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hicn/transport + COMPONENT ${LIBTRANSPORT_COMPONENT}-dev +) + +if (${CMAKE_SYSTEM_NAME} MATCHES Darwin OR ${CMAKE_SYSTEM_NAME} MATCHES Linux) + install( + FILES "transport.config" + DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + COMPONENT ${LIBTRANSPORT_COMPONENT} + ) +else () + install( + FILES "transport.config" + DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/hicn + COMPONENT ${LIBTRANSPORT_COMPONENT} + ) +endif() + + +############################################################## +# IO Modules +############################################################## +add_subdirectory(io_modules) + + +############################################################## +# Build type +############################################################## set (BUILD_TYPES "STATIC") if (NOT DISABLE_SHARED_LIBRARIES) @@ -74,8 +155,10 @@ if (NOT DISABLE_SHARED_LIBRARIES) ) endif() -add_subdirectory(io_modules) +############################################################## +# Build library +############################################################## build_library(${LIBTRANSPORT} ${BUILD_TYPES} SOURCES ${SOURCE_FILES} ${HEADER_FILES} @@ -84,12 +167,29 @@ build_library(${LIBTRANSPORT} OBJECT_LIBRARIES ${THIRD_PARTY_OBJECT_LIBRARIES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - HEADER_ROOT_DIR hicn/transport + INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION} + VERSION ${CURRENT_VERSION} + EXPORT_NAME ${LIBTRANSPORT_COMPONENT} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ) + +############################################################## +# Unit tests +############################################################## if (${BUILD_TESTS}) add_subdirectory(test) endif() + + +############################################################## +# Cmake config files +############################################################## +create_cmake_config ( + ${LIBTRANSPORT_COMPONENT} + INCLUDE_DIRS ${Libhicntransport_INCLUDE_DIRS} + VERSION ${CURRENT_VERSION} + COMPONENT ${LIBTRANSPORT_COMPONENT} + NAMESPACE hicn +) diff --git a/libtransport/src/auth/CMakeLists.txt b/libtransport/src/auth/CMakeLists.txt index 699bc1050..8d3286338 100644 --- a/libtransport/src/auth/CMakeLists.txt +++ b/libtransport/src/auth/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -14,7 +14,6 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc - ${CMAKE_CURRENT_SOURCE_DIR}/identity.cc ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.cc ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.cc ) diff --git a/libtransport/src/auth/crypto_hash.cc b/libtransport/src/auth/crypto_hash.cc index b4b0a8b81..f60f46051 100644 --- a/libtransport/src/auth/crypto_hash.cc +++ b/libtransport/src/auth/crypto_hash.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,8 +15,6 @@ #include <hicn/transport/auth/crypto_hash.h> -using namespace std; - namespace transport { namespace auth { @@ -28,7 +26,7 @@ CryptoHash::CryptoHash(const CryptoHash &other) digest_size_(other.digest_size_) {} CryptoHash::CryptoHash(CryptoHash &&other) - : digest_type_(move(other.digest_type_)), + : digest_type_(std::move(other.digest_type_)), digest_(other.digest_), digest_size_(other.digest_size_) { other.reset(); @@ -43,13 +41,16 @@ CryptoHash::CryptoHash(const uint8_t *hash, size_t size, memcpy(digest_.data(), hash, size); } -CryptoHash::CryptoHash(const vector<uint8_t> &hash, CryptoHashType hash_type) +CryptoHash::CryptoHash(const std::vector<uint8_t> &hash, + CryptoHashType hash_type) : CryptoHash(hash.data(), hash.size(), hash_type) {} CryptoHash &CryptoHash::operator=(const CryptoHash &other) { - digest_type_ = other.digest_type_; - digest_ = other.digest_; - digest_size_ = other.digest_size_; + if (this != &other) { + digest_type_ = other.digest_type_; + digest_ = other.digest_; + digest_size_ = other.digest_size_; + } return *this; } @@ -68,7 +69,7 @@ void CryptoHash::computeDigest(const uint8_t *buffer, size_t len) { (*hash_evp)(), nullptr); } -void CryptoHash::computeDigest(const vector<uint8_t> &buffer) { +void CryptoHash::computeDigest(const std::vector<uint8_t> &buffer) { computeDigest(buffer.data(), buffer.size()); } @@ -102,15 +103,15 @@ void CryptoHash::computeDigest(const utils::MemBuf *buffer) { EVP_MD_CTX_free(mcdtx); } -vector<uint8_t> CryptoHash::getDigest() const { return digest_; } +std::vector<uint8_t> CryptoHash::getDigest() const { return digest_; } -string CryptoHash::getStringDigest() const { - stringstream string_digest; +std::string CryptoHash::getStringDigest() const { + std::stringstream string_digest; - string_digest << hex << setfill('0'); + string_digest << std::hex << std::setfill('0'); for (auto byte : digest_) { - string_digest << hex << setw(2) << static_cast<int>(byte); + string_digest << std::hex << std::setw(2) << static_cast<int>(byte); } return string_digest.str(); @@ -130,23 +131,23 @@ void CryptoHash::setType(CryptoHashType hash_type) { void CryptoHash::display() { switch (digest_type_) { case CryptoHashType::SHA256: - cout << "SHA256"; + std::cout << "SHA256"; break; case CryptoHashType::SHA512: - cout << "SHA512"; + std::cout << "SHA512"; break; case CryptoHashType::BLAKE2S256: - cout << "BLAKE2s256"; + std::cout << "BLAKE2s256"; break; case CryptoHashType::BLAKE2B512: - cout << "BLAKE2b512"; + std::cout << "BLAKE2b512"; break; default: - cout << "UNKNOWN"; + std::cout << "UNKNOWN"; break; } - cout << ": " << getStringDigest() << endl; + std::cout << ": " << getStringDigest() << std::endl; } void CryptoHash::reset() { @@ -159,19 +160,14 @@ CryptoHashEVP CryptoHash::getEVP(CryptoHashType hash_type) { switch (hash_type) { case CryptoHashType::SHA256: return &EVP_sha256; - break; case CryptoHashType::SHA512: return &EVP_sha512; - break; case CryptoHashType::BLAKE2S256: return &EVP_blake2s256; - break; case CryptoHashType::BLAKE2B512: return &EVP_blake2b512; - break; default: return nullptr; - break; } } diff --git a/libtransport/src/auth/crypto_suite.cc b/libtransport/src/auth/crypto_suite.cc index 7e898ef09..44de85212 100644 --- a/libtransport/src/auth/crypto_suite.cc +++ b/libtransport/src/auth/crypto_suite.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -22,31 +22,61 @@ CryptoSuite getSuite(int nid) { switch (nid) { case NID_ecdsa_with_SHA256: return CryptoSuite::ECDSA_SHA256; - break; case NID_ecdsa_with_SHA512: return CryptoSuite::ECDSA_SHA512; - break; case NID_sha256WithRSAEncryption: return CryptoSuite::RSA_SHA256; - break; case NID_sha512WithRSAEncryption: return CryptoSuite::RSA_SHA512; - break; case NID_hmacWithSHA256: return CryptoSuite::HMAC_SHA256; - break; case NID_hmacWithSHA512: return CryptoSuite::HMAC_SHA512; - break; case NID_dsa_with_SHA256: return CryptoSuite::DSA_SHA256; - break; case NID_dsa_with_SHA512: return CryptoSuite::DSA_SHA512; - break; default: return CryptoSuite::UNKNOWN; - break; + } +} + +std::string getStringSuite(CryptoSuite suite) { + switch (suite) { + case CryptoSuite::ECDSA_BLAKE2B512: + return "ECDSA_BLAKE2B512"; + case CryptoSuite::ECDSA_BLAKE2S256: + return "ECDSA_BLAKE2S256"; + case CryptoSuite::ECDSA_SHA256: + return "ECDSA_SHA256"; + case CryptoSuite::ECDSA_SHA512: + return "ECDSA_SHA512"; + case CryptoSuite::RSA_BLAKE2B512: + return "RSA_BLAKE2B512"; + case CryptoSuite::RSA_BLAKE2S256: + return "RSA_BLAKE2S256"; + case CryptoSuite::RSA_SHA256: + return "RSA_SHA256"; + case CryptoSuite::RSA_SHA512: + return "RSA_SHA512"; + case CryptoSuite::HMAC_BLAKE2B512: + return "HMAC_BLAKE2B512"; + case CryptoSuite::HMAC_BLAKE2S256: + return "HMAC_BLAKE2S256"; + case CryptoSuite::HMAC_SHA256: + return "HMAC_SHA256"; + case CryptoSuite::HMAC_SHA512: + return "HMAC_SHA512"; + case CryptoSuite::DSA_BLAKE2B512: + return "DSA_BLAKE2B512"; + case CryptoSuite::DSA_BLAKE2S256: + return "DSA_BLAKE2S256"; + case CryptoSuite::DSA_SHA256: + return "DSA_SHA256"; + case CryptoSuite::DSA_SHA512: + return "DSA_SHA512"; + default: + return "UNKNOWN"; } } diff --git a/libtransport/src/auth/identity.cc b/libtransport/src/auth/identity.cc deleted file mode 100644 index f56532033..000000000 --- a/libtransport/src/auth/identity.cc +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <hicn/transport/auth/identity.h> - -using namespace std; - -// function needed to create the a certificate -static bool _addRandomSerial(X509 *cert) { - unsigned long serial = 0; - unsigned char serial_bytes[sizeof(serial)]; - - // Construct random positive serial number. - RAND_bytes(serial_bytes, sizeof(serial_bytes)); - serial_bytes[0] &= 0x7F; - serial = 0; - for (size_t i = 0; i < sizeof(serial_bytes); i++) { - serial = (256 * serial) + serial_bytes[i]; - } - ASN1_INTEGER_set(X509_get_serialNumber(cert), serial); - return true; -} - -static bool _addValidityPeriod(X509 *cert, size_t validityDays) { - // Set the validity from now for the specified number of days. - X509_gmtime_adj(X509_get_notBefore(cert), (long)0); - X509_gmtime_adj(X509_get_notAfter(cert), (long)(60 * 60 * 24 * validityDays)); - return true; -} - -static bool _addSubjectName(X509 *cert, const char *subjectname) { - // Set up the simple subject name and issuer name for the certificate. - X509_NAME *name = X509_get_subject_name(cert); - - if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, - (unsigned char *)subjectname, -1, -1, 0)) { - if (X509_set_issuer_name(cert, name)) { - return true; - } - } - return false; -} -static bool _addCertificateExtensionWithContext(X509 *cert, int nid, - const char *value) { - X509_EXTENSION *extension; - X509V3_CTX context; - - X509V3_set_ctx_nodb(&context); - X509V3_set_ctx(&context, cert, cert, NULL, NULL, 0); - extension = X509V3_EXT_conf_nid(NULL, &context, nid, value); - if (extension == NULL) { - return false; - } - X509_add_ext(cert, extension, -1); - X509_EXTENSION_free(extension); - return true; -} -static bool _addCertificateExtension(X509 *cert, int nid, const char *value) { - X509_EXTENSION *extension = X509V3_EXT_conf_nid(NULL, NULL, nid, value); - if (extension == NULL) { - return false; - } - X509_add_ext(cert, extension, -1); - X509_EXTENSION_free(extension); - return true; -} - -static bool _addExtensions(X509 *cert) { - // Add the necessary extensions. - if (_addCertificateExtension(cert, NID_basic_constraints, - "critical,CA:FALSE") == true) { - if (_addCertificateExtension( - cert, NID_key_usage, - "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment," - "keyAgreement") == true) { - if (_addCertificateExtension(cert, NID_ext_key_usage, "clientAuth") == - true) { - return true; - } - } - } - return false; -} -static bool _addKeyIdentifier(X509 *cert) { - unsigned char spkid[SHA256_DIGEST_LENGTH]; - char spkid_hex[1 + 2 * SHA256_DIGEST_LENGTH]; - if (ASN1_item_digest(ASN1_ITEM_rptr(X509_PUBKEY), EVP_sha256(), - X509_get_X509_PUBKEY(cert), spkid, NULL)) { - for (int i = 0; i < 32; i++) { - snprintf(&spkid_hex[2 * i], 3, "%02X", (unsigned)spkid[i]); - } - if (_addCertificateExtension(cert, NID_subject_key_identifier, spkid_hex) == - true) { - if (_addCertificateExtensionWithContext( - cert, NID_authority_key_identifier, "keyid:always") == true) { - return true; - } - } - } - return false; -} - -namespace transport { -namespace auth { - -Identity::Identity(const string &keystore_path, const string &keystore_pwd, - CryptoSuite suite, unsigned int signature_len, - unsigned int validity_days, const string &subject_name) - : cert_(X509_new(), ::X509_free) { - // create the file and complete it. - // first we create the certificate - - // to create the cert we will need a private key - - std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); - - if (suite == CryptoSuite::RSA_SHA512 || suite == CryptoSuite::RSA_SHA256) { - RSA *rsa = RSA_new(); - BIGNUM *pub_exp; - - pub_exp = BN_new(); - - BN_set_word(pub_exp, RSA_F4); - if (1 != RSA_generate_key_ex(rsa, signature_len, pub_exp, NULL)) - throw errors::RuntimeException("can't generate the key"); - if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa)) - throw errors::RuntimeException("can't generate the key"); - } else if (suite == CryptoSuite::ECDSA_SHA256) { - int curve_params; - switch (signature_len) { - case 160u: - curve_params = NID_secp160k1; - break; - case 192u: - curve_params = NID_secp192k1; - break; - case 224u: - curve_params = NID_secp224k1; - break; - case 256u: - curve_params = NID_secp256k1; - break; - default: - curve_params = -1; - break; - } - if (curve_params == -1) - throw errors::RuntimeException("can't generate the key"); - EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve_params); - if (ec_key == NULL) - throw errors::RuntimeException("can't create ecdsa key from curve"); - EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE); - if (EC_KEY_generate_key(ec_key) == 0) - throw errors::RuntimeException("can't generate the ecdsa key"); - if (EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_key) == 0) - throw errors::RuntimeException("can't generate the ecdsa key"); - } else if (suite == CryptoSuite::DSA_SHA256) { - DSA *dsa = DSA_new(); - unsigned char buffer[32]; - if (RAND_bytes(buffer, sizeof(buffer)) != 1) { - throw errors::RuntimeException("can't generate the key"); - } - if (DSA_generate_parameters_ex(dsa, signature_len, buffer, sizeof(buffer), - NULL, NULL, NULL) != 1) - throw errors::RuntimeException("can't generate the key"); - if (DSA_generate_key(dsa) != 1) - throw errors::RuntimeException("can't generate the key"); - if (EVP_PKEY_set1_DSA(privateKey.get(), dsa) != 1) - throw errors::RuntimeException("can't generate the key"); - } - bool success = true; - success = success && (X509_set_version(cert_.get(), 2) == 1); // 2 => X509v3 - success = success && _addRandomSerial(cert_.get()); - success = success && _addValidityPeriod(cert_.get(), validity_days); - success = success && (X509_set_pubkey(cert_.get(), privateKey.get()) == 1); - success = success && _addSubjectName(cert_.get(), subject_name.c_str()); - success = success && _addExtensions(cert_.get()); - success = - success && (X509_sign(cert_.get(), privateKey.get(), EVP_sha256()) != 0); - success = success && _addKeyIdentifier(cert_.get()); - if (!success) { - throw errors::RuntimeException("error while creating the certificate"); - } - // the certificate is created. We create the pkcs12 object to write the p12 - // file - PKCS12 *p12 = PKCS12_create( - keystore_pwd.c_str(), "ccnxuser", privateKey.get(), cert_.get(), NULL, 0, - 0, 0 /*default iter*/, PKCS12_DEFAULT_ITER /*mac_iter*/, 0); - filename_ = keystore_path; - int fp = open(filename_.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0600); - if (fp == -1) throw errors::RuntimeException("impossible to create the file"); - FILE *fp_f = fdopen(fp, "wb"); - if (fp_f == NULL) - throw errors::RuntimeException("impossible to create the file"); - i2d_PKCS12_fp(fp_f, p12); - fclose(fp_f); - close(fp); - std::shared_ptr<EVP_PKEY> publickey(X509_get_pubkey(cert_.get()), - EVP_PKEY_free); - signer_ = std::shared_ptr<AsymmetricSigner>( - new AsymmetricSigner(suite, privateKey, publickey)); - signer_->signature_len_ = signature_len; -} - -Identity::Identity(string &keystore_path, string &keystore_pwd, - CryptoHashType hash_type) - : cert_(X509_new(), ::X509_free) { - filename_ = keystore_path; - pwd_ = keystore_path; - // get the key and certificate by first opening the keystore file - FILE *p12file = fopen(keystore_path.c_str(), "r"); - if (p12file == NULL) - throw errors::RuntimeException("impossible open keystore"); - PKCS12 *p12 = d2i_PKCS12_fp(p12file, NULL); - EVP_PKEY *privatekey; - EVP_PKEY *publickey; - X509 *cert = cert_.get(); - // now we parse the file to get the first key and certificate - if (1 != PKCS12_parse(p12, keystore_pwd.c_str(), &privatekey, &cert, NULL)) - throw errors::RuntimeException("impossible to get the private key"); - publickey = X509_get_pubkey(cert); - // to have the cryptosuite we use the nid number that is used to identify the - // suite. - CryptoSuite suite = getSuite(X509_get_signature_nid(cert)); - signer_ = std::shared_ptr<AsymmetricSigner>(new AsymmetricSigner( - suite, std::shared_ptr<EVP_PKEY>(privatekey, EVP_PKEY_free), - std::shared_ptr<EVP_PKEY>(publickey, EVP_PKEY_free))); - PKCS12_free(p12); -} - -Identity::Identity(const Identity &other) { - pwd_ = other.pwd_; - filename_ = other.filename_; - signer_ = other.signer_; - cert_ = other.cert_; -} - -Identity::Identity(Identity &&other) { - signer_ = std::move(other.signer_); - other.signer_.reset(); - cert_ = std::move(other.cert_); - other.cert_.reset(); - pwd_ = other.pwd_; - other.pwd_ = ""; - filename_ = other.filename_; - other.filename_ = ""; - signer_ = other.signer_; - other.signer_ = nullptr; -} - -Identity::~Identity() {} - -shared_ptr<AsymmetricSigner> Identity::getSigner() const { return signer_; } - -string Identity::getFilename() const { return filename_; } - -std::shared_ptr<X509> Identity::getCertificate() const { return cert_; } -std::shared_ptr<EVP_PKEY> Identity::getPrivateKey() const { - return signer_->key_; -} - -string Identity::getPassword() const { return pwd_; } - -Identity Identity::generateIdentity(const string &subject_name) { - string keystore_name = "keystore"; - string keystore_password = "password"; - size_t key_length = 1024; - unsigned int validity_days = 30; - CryptoSuite suite = CryptoSuite::RSA_SHA256; - - return Identity(keystore_name, keystore_password, suite, - (unsigned int)key_length, validity_days, subject_name); -} - -} // namespace auth -} // namespace transport diff --git a/libtransport/src/auth/signer.cc b/libtransport/src/auth/signer.cc index 884e850ca..e74e2f1b8 100644 --- a/libtransport/src/auth/signer.cc +++ b/libtransport/src/auth/signer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,9 +13,9 @@ * limitations under the License. */ +#include <glog/logging.h> #include <hicn/transport/auth/signer.h> - -using namespace std; +#include <hicn/transport/utils/chrono_typedefs.h> namespace transport { namespace auth { @@ -29,31 +29,29 @@ Signer::Signer() Signer::~Signer() {} void Signer::signPacket(PacketPtr packet) { - assert(key_ != nullptr); + DCHECK(key_ != nullptr); core::Packet::Format format = packet->getFormat(); - if (!packet->authenticationHeader()) { + if (!packet->hasAH()) { throw errors::MalformedAHPacketException(); } // Set signature size size_t signature_field_len = getSignatureFieldSize(); - packet->setSignatureSize(signature_field_len); - packet->setSignatureSizeGap(0u); + packet->setSignatureFieldSize(signature_field_len); + packet->updateLength(); // update IP payload length // Copy IP+TCP / ICMP header before zeroing them hicn_header_t header_copy; hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); // Fill in the hICN AH header - auto now = chrono::duration_cast<chrono::milliseconds>( - chrono::system_clock::now().time_since_epoch()) - .count(); + auto now = utils::SteadyTime::nowMs().count(); packet->setSignatureTimestamp(now); packet->setValidationAlgorithm(suite_); // Set key ID - vector<uint8_t> key_id = key_id_.getDigest(); + std::vector<uint8_t> key_id = key_id_.getDigest(); packet->setKeyId({key_id.data(), key_id.size()}); // Reset fields to compute the packet hash @@ -61,22 +59,22 @@ void Signer::signPacket(PacketPtr packet) { // Compute the signature and put it in the packet signBuffer(packet); - hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); + packet->setSignature(signature_); + packet->setSignatureSize(signature_len_); - // Set the gap between the signature field size and the signature real size. - packet->setSignatureSizeGap(signature_field_len - signature_len_); - memcpy(packet->getSignature(), signature_.data(), signature_len_); + // Restore header + hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); } void Signer::signBuffer(const std::vector<uint8_t> &buffer) { - assert(key_ != nullptr); + DCHECK(key_ != nullptr); CryptoHashEVP hash_evp = CryptoHash::getEVP(getHashType()); if (hash_evp == nullptr) { throw errors::RuntimeException("Unknown hash type"); } - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -106,7 +104,7 @@ void Signer::signBuffer(const std::vector<uint8_t> &buffer) { } void Signer::signBuffer(const utils::MemBuf *buffer) { - assert(key_ != nullptr); + DCHECK(key_ != nullptr); CryptoHashEVP hash_evp = CryptoHash::getEVP(getHashType()); if (hash_evp == nullptr) { @@ -114,7 +112,7 @@ void Signer::signBuffer(const utils::MemBuf *buffer) { } const utils::MemBuf *p = buffer; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -147,7 +145,18 @@ void Signer::signBuffer(const utils::MemBuf *buffer) { signature_.resize(signature_len_); } -vector<uint8_t> Signer::getSignature() const { return signature_; } +std::vector<uint8_t> Signer::getSignature() const { return signature_; } + +std::string Signer::getStringSignature() const { + std::stringstream string_sig; + string_sig << std::hex << std::setfill('0'); + + for (auto byte : signature_) { + string_sig << std::hex << std::setw(2) << static_cast<int>(byte); + } + + return string_sig.str(); +} size_t Signer::getSignatureSize() const { return signature_len_; } @@ -159,36 +168,75 @@ size_t Signer::getSignatureFieldSize() const { return (signature_len_ + 4) - (signature_len_ % 4); } +CryptoSuite Signer::getSuite() const { return suite_; } + CryptoHashType Signer::getHashType() const { return ::transport::auth::getHashType(suite_); } -CryptoSuite Signer::getSuite() const { return suite_; } +void Signer::display() { + std::cout << getStringSuite(suite_) << ": " << getStringSignature() + << std::endl; +} // --------------------------------------------------------- // Void Signer // --------------------------------------------------------- -void VoidSigner::signPacket(PacketPtr packet){}; +void VoidSigner::signPacket(PacketPtr packet) {} -void VoidSigner::signBuffer(const std::vector<uint8_t> &buffer){}; +void VoidSigner::signBuffer(const std::vector<uint8_t> &buffer) {} -void VoidSigner::signBuffer(const utils::MemBuf *buffer){}; +void VoidSigner::signBuffer(const utils::MemBuf *buffer) {} // --------------------------------------------------------- // Asymmetric Signer // --------------------------------------------------------- -AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, shared_ptr<EVP_PKEY> key, - shared_ptr<EVP_PKEY> pub_key) { +AsymmetricSigner::AsymmetricSigner(CryptoSuite suite, + std::shared_ptr<EVP_PKEY> key, + std::shared_ptr<EVP_PKEY> pub_key) { + setKey(suite, key, pub_key); +} + +AsymmetricSigner::AsymmetricSigner(std::string keystore_path, + std::string password) { + FILE *p12file = fopen(keystore_path.c_str(), "r"); + + if (p12file == nullptr) { + throw errors::RuntimeException("failed to read keystore"); + } + + std::unique_ptr<PKCS12, decltype(&PKCS12_free)> p12( + d2i_PKCS12_fp(p12file, nullptr), &PKCS12_free); + X509 *cert_raw; + EVP_PKEY *key_raw; + + if (PKCS12_parse(p12.get(), password.c_str(), &key_raw, &cert_raw, nullptr) != + 1) { + fclose(p12file); + throw errors::RuntimeException("failed to parse keystore"); + } + + std::shared_ptr<EVP_PKEY> key(key_raw, EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pub_key(X509_get_pubkey(cert_raw), EVP_PKEY_free); + + setKey(transport::auth::getSuite(X509_get_signature_nid(cert_raw)), key, + pub_key); + + fclose(p12file); +} + +void AsymmetricSigner::setKey(CryptoSuite suite, std::shared_ptr<EVP_PKEY> key, + std::shared_ptr<EVP_PKEY> pub_key) { suite_ = suite; key_ = key; - key_id_ = CryptoHash(getHashType()); + signature_len_ = EVP_PKEY_size(key.get()); + signature_.resize(signature_len_); - vector<uint8_t> pbk(i2d_PublicKey(pub_key.get(), nullptr)); + std::vector<uint8_t> pbk(i2d_PublicKey(pub_key.get(), nullptr)); uint8_t *pbk_ptr = pbk.data(); int len = i2d_PublicKey(pub_key.get(), &pbk_ptr); - signature_len_ = EVP_PKEY_size(key.get()); - signature_.resize(signature_len_); + key_id_ = CryptoHash(getHashType()); key_id_.computeDigest(pbk_ptr, len); } @@ -205,9 +253,10 @@ size_t AsymmetricSigner::getSignatureFieldSize() const { // --------------------------------------------------------- // Symmetric Signer // --------------------------------------------------------- -SymmetricSigner::SymmetricSigner(CryptoSuite suite, const string &passphrase) { +SymmetricSigner::SymmetricSigner(CryptoSuite suite, + const std::string &passphrase) { suite_ = suite; - key_ = shared_ptr<EVP_PKEY>( + key_ = std::shared_ptr<EVP_PKEY>( EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, nullptr, (const unsigned char *)passphrase.c_str(), passphrase.size()), diff --git a/libtransport/src/auth/verifier.cc b/libtransport/src/auth/verifier.cc index c9ac1650f..0c35437f3 100644 --- a/libtransport/src/auth/verifier.cc +++ b/libtransport/src/auth/verifier.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,12 +16,10 @@ #include <hicn/transport/auth/verifier.h> #include <protocols/errors.h> -using namespace std; - namespace transport { namespace auth { -const vector<VerificationPolicy> Verifier::DEFAULT_FAILED_POLICIES = { +const std::vector<VerificationPolicy> Verifier::DEFAULT_FAILED_POLICIES = { VerificationPolicy::DROP, VerificationPolicy::ABORT, }; @@ -38,24 +36,21 @@ Verifier::~Verifier() {} bool Verifier::verifyPacket(PacketPtr packet) { core::Packet::Format format = packet->getFormat(); - if (!packet->authenticationHeader()) { + if (!packet->hasAH()) { throw errors::MalformedAHPacketException(); } // Get crypto suite, hash type, signature length CryptoSuite suite = packet->getValidationAlgorithm(); CryptoHashType hash_type = getHashType(suite); - size_t signature_len = packet->getSignatureSizeReal(); // Copy IP+TCP / ICMP header before zeroing them hicn_header_t header_copy; hicn_packet_copy_header(format, packet->packet_start_, &header_copy, false); - packet->setSignatureSizeGap(0u); // Retrieve packet signature - uint8_t *packet_signature = packet->getSignature(); - vector<uint8_t> signature_raw(packet_signature, - packet_signature + signature_len); + std::vector<uint8_t> signature_raw = packet->getSignature(); + signature_raw.resize(packet->getSignatureSize()); // Reset fields that are not used to compute signature packet->resetForHash(); @@ -66,12 +61,14 @@ bool Verifier::verifyPacket(PacketPtr packet) { // Restore header hicn_packet_copy_header(format, &header_copy, packet->packet_start_, false); - packet->setSignatureSizeGap(packet->getSignatureSize() - signature_len); + packet->setSignature(signature_raw); + packet->setSignatureSize(signature_raw.size()); return valid_packet; } -Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets) { +Verifier::PolicyMap Verifier::verifyPackets( + const std::vector<PacketPtr> &packets) { PolicyMap policies; for (const auto &packet : packets) { @@ -82,8 +79,8 @@ Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets) { policy = VerificationPolicy::ACCEPT; } + callVerificationFailedCallback(suffix, policy); policies[suffix] = policy; - callVerificationFailedCallback(packet, policy); } return policies; @@ -105,14 +102,15 @@ Verifier::PolicyMap Verifier::verifyHashes(const SuffixMap &packet_map, } } + callVerificationFailedCallback(packet_hash.first, policy); policies[packet_hash.first] = policy; } return policies; } -Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets, - const SuffixMap &suffix_map) { +Verifier::PolicyMap Verifier::verifyPackets( + const std::vector<PacketPtr> &packets, const SuffixMap &suffix_map) { PolicyMap policies; for (const auto &packet : packets) { @@ -130,8 +128,8 @@ Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets, } } + callVerificationFailedCallback(suffix, policy); policies[suffix] = policy; - callVerificationFailedCallback(packet, policy); } return policies; @@ -139,7 +137,7 @@ Verifier::PolicyMap Verifier::verifyPackets(const vector<PacketPtr> &packets, void Verifier::setVerificationFailedCallback( VerificationFailedCallback verfication_failed_cb, - const vector<VerificationPolicy> &failed_policies) { + const std::vector<VerificationPolicy> &failed_policies) { verification_failed_cb_ = verfication_failed_cb; failed_policies_ = failed_policies; } @@ -149,7 +147,7 @@ void Verifier::getVerificationFailedCallback( *verfication_failed_cb = &verification_failed_cb_; } -void Verifier::callVerificationFailedCallback(PacketPtr packet, +void Verifier::callVerificationFailedCallback(Suffix suffix, VerificationPolicy &policy) { if (verification_failed_cb_ == interface::VOID_HANDLER) { return; @@ -157,10 +155,7 @@ void Verifier::callVerificationFailedCallback(PacketPtr packet, if (find(failed_policies_.begin(), failed_policies_.end(), policy) != failed_policies_.end()) { - policy = verification_failed_cb_( - static_cast<const core::ContentObject &>(*packet), - make_error_code( - protocol::protocol_error::signature_verification_failed)); + policy = verification_failed_cb_(suffix, policy); } } @@ -169,71 +164,75 @@ void Verifier::callVerificationFailedCallback(PacketPtr packet, // --------------------------------------------------------- bool VoidVerifier::verifyPacket(PacketPtr packet) { return true; } -bool VoidVerifier::verifyBuffer(const vector<uint8_t> &buffer, - const vector<uint8_t> &signature, +bool VoidVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { return true; } bool VoidVerifier::verifyBuffer(const utils::MemBuf *buffer, - const vector<uint8_t> &signature, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { return true; } Verifier::PolicyMap VoidVerifier::verifyPackets( - const vector<PacketPtr> &packets) { + const std::vector<PacketPtr> &packets) { PolicyMap policies; for (const auto &packet : packets) { - policies[packet->getName().getSuffix()] = VerificationPolicy::ACCEPT; + auth::Suffix suffix = packet->getName().getSuffix(); + VerificationPolicy policy = VerificationPolicy::ACCEPT; + callVerificationFailedCallback(suffix, policy); + policies[suffix] = policy; } return policies; } Verifier::PolicyMap VoidVerifier::verifyPackets( - const vector<PacketPtr> &packets, const SuffixMap &suffix_map) { + const std::vector<PacketPtr> &packets, const SuffixMap &suffix_map) { return verifyPackets(packets); } // --------------------------------------------------------- // Asymmetric Verifier // --------------------------------------------------------- -AsymmetricVerifier::AsymmetricVerifier(shared_ptr<EVP_PKEY> key) { +AsymmetricVerifier::AsymmetricVerifier(std::shared_ptr<EVP_PKEY> key) { setKey(key); } -AsymmetricVerifier::AsymmetricVerifier(const string &cert_path) { +AsymmetricVerifier::AsymmetricVerifier(const std::string &cert_path) { useCertificate(cert_path); } -AsymmetricVerifier::AsymmetricVerifier(shared_ptr<X509> cert) { +AsymmetricVerifier::AsymmetricVerifier(std::shared_ptr<X509> cert) { useCertificate(cert); } -void AsymmetricVerifier::setKey(shared_ptr<EVP_PKEY> key) { key_ = key; }; +void AsymmetricVerifier::setKey(std::shared_ptr<EVP_PKEY> key) { key_ = key; }; -void AsymmetricVerifier::useCertificate(const string &cert_path) { +void AsymmetricVerifier::useCertificate(const std::string &cert_path) { FILE *certf = fopen(cert_path.c_str(), "rb"); if (certf == nullptr) { throw errors::RuntimeException("Certificate not found"); } - shared_ptr<X509> cert = shared_ptr<X509>( + std::shared_ptr<X509> cert = std::shared_ptr<X509>( PEM_read_X509(certf, nullptr, nullptr, nullptr), ::X509_free); useCertificate(cert); fclose(certf); } -void AsymmetricVerifier::useCertificate(shared_ptr<X509> cert) { - key_ = shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), ::EVP_PKEY_free); +void AsymmetricVerifier::useCertificate(std::shared_ptr<X509> cert) { + key_ = + std::shared_ptr<EVP_PKEY>(X509_get_pubkey(cert.get()), ::EVP_PKEY_free); } -bool AsymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, - const vector<uint8_t> &signature, +bool AsymmetricVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -241,7 +240,7 @@ bool AsymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, throw errors::RuntimeException("Unknown hash type"); } - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -261,7 +260,7 @@ bool AsymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, } bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, - const vector<uint8_t> &signature, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -270,7 +269,7 @@ bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, } const utils::MemBuf *p = buffer; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -296,21 +295,21 @@ bool AsymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, // --------------------------------------------------------- // Symmetric Verifier // --------------------------------------------------------- -SymmetricVerifier::SymmetricVerifier(const string &passphrase) { +SymmetricVerifier::SymmetricVerifier(const std::string &passphrase) { setPassphrase(passphrase); } // Create and set a symmetric key from a passphrase. -void SymmetricVerifier::setPassphrase(const string &passphrase) { - key_ = shared_ptr<EVP_PKEY>( +void SymmetricVerifier::setPassphrase(const std::string &passphrase) { + key_ = std::shared_ptr<EVP_PKEY>( EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, nullptr, (const unsigned char *)passphrase.c_str(), passphrase.size()), EVP_PKEY_free); } -bool SymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, - const vector<uint8_t> &signature, +bool SymmetricVerifier::verifyBuffer(const std::vector<uint8_t> &buffer, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -318,9 +317,9 @@ bool SymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, throw errors::RuntimeException("Unknown hash type"); } - vector<uint8_t> signature_bis(signature.size()); + std::vector<uint8_t> signature_bis(signature.size()); size_t signature_bis_len; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); @@ -344,7 +343,7 @@ bool SymmetricVerifier::verifyBuffer(const vector<uint8_t> &buffer, } bool SymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, - const vector<uint8_t> &signature, + const std::vector<uint8_t> &signature, CryptoHashType hash_type) { CryptoHashEVP hash_evp = CryptoHash::getEVP(hash_type); @@ -353,9 +352,9 @@ bool SymmetricVerifier::verifyBuffer(const utils::MemBuf *buffer, } const utils::MemBuf *p = buffer; - vector<uint8_t> signature_bis(signature.size()); + std::vector<uint8_t> signature_bis(signature.size()); size_t signature_bis_len; - shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); + std::shared_ptr<EVP_MD_CTX> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free); if (mdctx == nullptr) { throw errors::RuntimeException("Digest context allocation failed"); diff --git a/libtransport/src/config.h.in b/libtransport/src/config.h.in index 73a326a84..231a370aa 100644 --- a/libtransport/src/config.h.in +++ b/libtransport/src/config.h.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -19,13 +19,12 @@ #define HICNTRANSPORT_VERSION_MAJOR "@VERSION_MAJOR@" #define HICNTRANSPORT_VERSION_MINOR "@VERSION_MINOR@" -#define HICNTRANSPORT_VERSION_REVISION "@VERSION_REVISION@" +#define HICNTRANSPORT_VERSION_PATCH "@VERSION_PATCH@" #ifndef ASIO_STANDALONE #cmakedefine ASIO_STANDALONE #endif #define RAAQM_CONFIG_PATH "@raaqm_config_path@" -#define ENABLE_RELY @ENABLE_RELY@ #cmakedefine __vpp__ diff --git a/libtransport/src/core/CMakeLists.txt b/libtransport/src/core/CMakeLists.txt index e442bb863..b9b024d60 100644 --- a/libtransport/src/core/CMakeLists.txt +++ b/libtransport/src/core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -22,6 +22,9 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.h ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.h ${CMAKE_CURRENT_SOURCE_DIR}/local_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/global_workers.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/udp_listener.h ) list(APPEND SOURCE_FILES @@ -36,7 +39,21 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/global_configuration.cc ${CMAKE_CURRENT_SOURCE_DIR}/io_module.cc ${CMAKE_CURRENT_SOURCE_DIR}/local_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/udp_listener.cc ) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES Android) + if (UNIX AND NOT APPLE) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc + ) + + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ) + endif() +endif() + set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/core/content_object.cc b/libtransport/src/core/content_object.cc index 411494fdf..643e0388e 100644 --- a/libtransport/src/core/content_object.cc +++ b/libtransport/src/core/content_object.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -35,6 +35,11 @@ namespace core { ContentObject::ContentObject(const Name &name, Packet::Format format, std::size_t additional_header_size) : Packet(format, additional_header_size) { + if (TRANSPORT_EXPECT_FALSE(hicn_packet_set_data(format_, packet_start_) < + 0)) { + throw errors::MalformedPacketException(); + } + if (TRANSPORT_EXPECT_FALSE( hicn_data_set_name(format, packet_start_, &name.name_) < 0)) { throw errors::RuntimeException("Error filling the packet name."); @@ -47,15 +52,20 @@ ContentObject::ContentObject(const Name &name, Packet::Format format, } } -#ifdef __ANDROID__ ContentObject::ContentObject(hicn_format_t format, std::size_t additional_header_size) - : ContentObject(Name("0::0|0"), format, additional_header_size) {} + : ContentObject( +#ifdef __ANDROID__ + Name("0::0|0"), #else -ContentObject::ContentObject(hicn_format_t format, - std::size_t additional_header_size) - : ContentObject(Packet::base_name, format, additional_header_size) {} + Packet::base_name, #endif + format, additional_header_size) { + if (TRANSPORT_EXPECT_FALSE(hicn_packet_set_data(format_, packet_start_) < + 0)) { + throw errors::MalformedPacketException(); + } +} ContentObject::ContentObject(const Name &name, hicn_format_t format, std::size_t additional_header_size, @@ -167,6 +177,23 @@ void ContentObject::resetForHash() { } } +bool ContentObject::isLast() const { + int is_last = 0; + if (hicn_data_is_last(format_, packet_start_, &is_last) < 0) { + throw errors::RuntimeException( + "Impossible to get last data flag from packet header."); + } + + return is_last; +} + +void ContentObject::setLast() { + if (hicn_data_set_last(format_, packet_start_) < 0) { + throw errors::RuntimeException( + "Impossible to set last data flag to packet header."); + } +} + } // end namespace core } // end namespace transport diff --git a/libtransport/src/core/errors.cc b/libtransport/src/core/errors.cc index 82647a60b..68fd7bf38 100644 --- a/libtransport/src/core/errors.cc +++ b/libtransport/src/core/errors.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -39,6 +39,15 @@ std::string core_category_impl::message(int ev) const { case core_error::configuration_not_applied: { return "Configuration was not applied due to wrong parameters."; } + case core_error::send_failed: { + return "Error sending data to socket."; + } + case core_error::send_buffer_allocation_failed: { + return "Error allocating buffers to send data."; + } + case core_error::receive_failed: { + return "Error receiving data from socket."; + } default: { return "Unknown core error"; } diff --git a/libtransport/src/core/errors.h b/libtransport/src/core/errors.h index a46f1dbcd..4532e6dc5 100644 --- a/libtransport/src/core/errors.h +++ b/libtransport/src/core/errors.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -36,7 +36,10 @@ const std::error_category& core_category(); enum class core_error { success = 0, configuration_parse_failed, - configuration_not_applied + configuration_not_applied, + send_failed, + send_buffer_allocation_failed, + receive_failed }; /** diff --git a/libtransport/src/core/facade.h b/libtransport/src/core/facade.h index 199081271..1ad4437e2 100644 --- a/libtransport/src/core/facade.h +++ b/libtransport/src/core/facade.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/fec_base.cc b/libtransport/src/core/global_workers.h index 9252bc473..1ac254188 100644 --- a/libtransport/src/protocols/fec_base.cc +++ b/libtransport/src/core/global_workers.h @@ -13,12 +13,31 @@ * limitations under the License. */ -#include <protocols/fec/rs.h> -#include <protocols/fec_base.h> +#pragma once + +#include <hicn/transport/utils/singleton.h> +#include <hicn/transport/utils/thread_pool.h> + +#include <atomic> +#include <mutex> namespace transport { -namespace protocol { +namespace core { + +class GlobalWorkers : public utils::Singleton<GlobalWorkers> { + public: + friend class utils::Singleton<GlobalWorkers>; + + ::utils::EventThread& getWorker() { + return thread_pool_.getWorker(counter_++ % thread_pool_.getNThreads()); + } + + private: + GlobalWorkers() : counter_(0), thread_pool_() {} + + std::atomic_uint16_t counter_; + ::utils::ThreadPool thread_pool_; +}; -namespace fec {} // namespace fec -} // namespace protocol +} // namespace core } // namespace transport
\ No newline at end of file diff --git a/libtransport/src/core/interest.cc b/libtransport/src/core/interest.cc index 9d868ced0..b7719b3ed 100644 --- a/libtransport/src/core/interest.cc +++ b/libtransport/src/core/interest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -34,6 +34,10 @@ namespace core { Interest::Interest(const Name &interest_name, Packet::Format format, std::size_t additional_header_size) : Packet(format, additional_header_size) { + if (hicn_packet_set_interest(format_, packet_start_) < 0) { + throw errors::MalformedPacketException(); + } + if (hicn_interest_set_name(format_, packet_start_, interest_name.getConstStructReference()) < 0) { throw errors::MalformedPacketException(); @@ -45,13 +49,18 @@ Interest::Interest(const Name &interest_name, Packet::Format format, } } -#ifdef __ANDROID__ Interest::Interest(hicn_format_t format, std::size_t additional_header_size) - : Interest(Name("0::0|0"), format, additional_header_size) {} + : Interest( +#ifdef __ANDROID__ + Name("0::0|0"), #else -Interest::Interest(hicn_format_t format, std::size_t additional_header_size) - : Interest(base_name, format, additional_header_size) {} + base_name, #endif + format, additional_header_size) { + if (hicn_packet_set_interest(format_, packet_start_) < 0) { + throw errors::MalformedPacketException(); + } +} Interest::Interest(MemBuf &&buffer) : Packet(std::move(buffer)) { if (hicn_interest_get_name(format_, packet_start_, diff --git a/libtransport/src/core/io_module.cc b/libtransport/src/core/io_module.cc index a751eabf5..69e4e8bcf 100644 --- a/libtransport/src/core/io_module.cc +++ b/libtransport/src/core/io_module.cc @@ -19,8 +19,10 @@ #include <glog/logging.h> #include <hicn/transport/core/io_module.h> +#include <iostream> + #ifdef ANDROID -#include <io_modules/udp/hicn_forwarder_module.h> +#include <io_modules/hicn-light-ng/hicn_forwarder_module.h> #elif _WIN32 #include <hicn/util/windows/windows_utils.h> #endif @@ -55,8 +57,9 @@ IoModule *IoModule::load(const char *module_name) { if (!creator) { if ((error = dlerror()) != 0) { LOG(ERROR) << error; - return 0; } + + return 0; } // create object and return it @@ -85,4 +88,4 @@ bool IoModule::unload(IoModule *module) { } } // namespace core -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/core/local_connector.cc b/libtransport/src/core/local_connector.cc index 50dadc677..f27be2e5c 100644 --- a/libtransport/src/core/local_connector.cc +++ b/libtransport/src/core/local_connector.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -32,13 +32,17 @@ void LocalConnector::send(Packet &packet) { return; } + auto buffer = + std::static_pointer_cast<utils::MemBuf>(packet.shared_from_this()); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending packet to local socket."; - io_service_.get().post([this, p{packet.shared_from_this()}]() mutable { - receive_callback_(this, *p, std::make_error_code(std::errc(0))); + io_service_.get().post([this, buffer]() mutable { + std::vector<utils::MemBuf::Ptr> v{std::move(buffer)}; + receive_callback_(this, v, std::make_error_code(std::errc(0))); }); } -void LocalConnector::send(const uint8_t *packet, std::size_t len) { +void LocalConnector::send(const utils::MemBuf::Ptr &buffer) { throw errors::NotImplementedException(); } diff --git a/libtransport/src/core/local_connector.h b/libtransport/src/core/local_connector.h index 0e2d8f676..eede89e74 100644 --- a/libtransport/src/core/local_connector.h +++ b/libtransport/src/core/local_connector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -42,7 +42,7 @@ class LocalConnector : public Connector { void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; void close() override; diff --git a/libtransport/src/core/manifest.cc b/libtransport/src/core/manifest.cc index 3f890f3d0..da2689426 100644 --- a/libtransport/src/core/manifest.cc +++ b/libtransport/src/core/manifest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/core/manifest.h b/libtransport/src/core/manifest.h index 9b25ebd67..5bdbfc6ff 100644 --- a/libtransport/src/core/manifest.h +++ b/libtransport/src/core/manifest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,6 +16,7 @@ #pragma once #include <core/manifest_format.h> +#include <glog/logging.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/name.h> @@ -40,17 +41,20 @@ class Manifest : public Base { using Encoder = typename FormatTraits::Encoder; using Decoder = typename FormatTraits::Decoder; - Manifest(std::size_t signature_size = 0) - : Base(HF_INET6_TCP_AH, signature_size), + Manifest(Packet::Format format, std::size_t signature_size = 0) + : Base(format, signature_size), encoder_(*this, signature_size), decoder_(*this) { + DCHECK(_is_ah(format)); Base::setPayloadType(PayloadType::MANIFEST); } - Manifest(const core::Name &name, std::size_t signature_size = 0) - : Base(name, HF_INET6_TCP_AH, signature_size), + Manifest(Packet::Format format, const core::Name &name, + std::size_t signature_size = 0) + : Base(name, format, signature_size), encoder_(*this, signature_size), decoder_(*this) { + DCHECK(_is_ah(format)); Base::setPayloadType(PayloadType::MANIFEST); } @@ -62,6 +66,11 @@ class Manifest : public Base { Base::setPayloadType(PayloadType::MANIFEST); } + // Useful for decoding manifests while avoiding packet copy + template <typename T> + Manifest(T &base) + : Base(base.getFormat()), encoder_(base, 0, false), decoder_(base) {} + virtual ~Manifest() = default; std::size_t estimateManifestSize(std::size_t additional_entries = 0) { @@ -78,24 +87,27 @@ class Manifest : public Base { Manifest &decode() { Manifest::decoder_.decode(); - manifest_type_ = decoder_.getManifestType(); + manifest_type_ = decoder_.getType(); + manifest_transport_type_ = decoder_.getTransportType(); hash_algorithm_ = decoder_.getHashAlgorithm(); - is_last_ = decoder_.getIsFinalManifest(); + is_last_ = decoder_.getIsLast(); return static_cast<ManifestImpl &>(*this).decodeImpl(); } - static std::size_t getManifestHeaderSize() { - return Encoder::getManifestHeaderSize(); + static std::size_t manifestHeaderSize( + interface::ProductionProtocolAlgorithms transport_type = + interface::ProductionProtocolAlgorithms::UNKNOWN) { + return Encoder::manifestHeaderSize(transport_type); } - static std::size_t getManifestEntrySize() { - return Encoder::getManifestEntrySize(); + static std::size_t manifestEntrySize() { + return Encoder::manifestEntrySize(); } - Manifest &setManifestType(ManifestType type) { + Manifest &setType(ManifestType type) { manifest_type_ = type; - encoder_.setManifestType(manifest_type_); + encoder_.setType(manifest_type_); return *this; } @@ -105,31 +117,46 @@ class Manifest : public Base { return *this; } - auth::CryptoHashType getHashAlgorithm() { return hash_algorithm_; } + auth::CryptoHashType getHashAlgorithm() const { return hash_algorithm_; } - ManifestType getManifestType() const { return manifest_type_; } + ManifestType getType() const { return manifest_type_; } + + interface::ProductionProtocolAlgorithms getTransportType() const { + return manifest_transport_type_; + } - bool isFinalManifest() const { return is_last_; } + bool getIsLast() const { return is_last_; } Manifest &setVersion(ManifestVersion version) { encoder_.setVersion(version); return *this; } - Manifest &setFinalBlockNumber(std::uint32_t final_block_number) { - encoder_.setFinalBlockNumber(final_block_number); + Manifest &setParamsBytestream(const ParamsBytestream ¶ms) { + manifest_transport_type_ = + interface::ProductionProtocolAlgorithms::BYTE_STREAM; + encoder_.setParamsBytestream(params); return *this; } - uint32_t getFinalBlockNumber() const { - return decoder_.getFinalBlockNumber(); + Manifest &setParamsRTC(const ParamsRTC ¶ms) { + manifest_transport_type_ = + interface::ProductionProtocolAlgorithms::RTC_PROD; + encoder_.setParamsRTC(params); + return *this; + } + + ParamsBytestream getParamsBytestream() const { + return decoder_.getParamsBytestream(); } + ParamsRTC getParamsRTC() const { return decoder_.getParamsRTC(); } + ManifestVersion getVersion() const { return decoder_.getVersion(); } - Manifest &setFinalManifest(bool is_final_manifest) { - encoder_.setIsFinalManifest(is_final_manifest); - is_last_ = is_final_manifest; + Manifest &setIsLast(bool is_last) { + encoder_.setIsLast(is_last); + is_last_ = is_last; return *this; } @@ -141,6 +168,7 @@ class Manifest : public Base { protected: ManifestType manifest_type_; + interface::ProductionProtocolAlgorithms manifest_transport_type_; auth::CryptoHashType hash_algorithm_; bool is_last_; diff --git a/libtransport/src/core/manifest_format.h b/libtransport/src/core/manifest_format.h index 90d221f5e..38f26067e 100644 --- a/libtransport/src/core/manifest_format.h +++ b/libtransport/src/core/manifest_format.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -17,6 +17,7 @@ #include <hicn/transport/auth/crypto_hash.h> #include <hicn/transport/core/name.h> +#include <hicn/transport/interfaces/socket_options_keys.h> #include <cinttypes> #include <type_traits> @@ -26,15 +27,6 @@ namespace transport { namespace core { -enum class ManifestFields : uint8_t { - VERSION, - HASH_ALGORITHM, - SEGMENT_CALCULATION_STRATEGY, - FINAL_MANIFEST, - NAME_HASH_LIST, - BASE_NAME -}; - enum class ManifestVersion : uint8_t { VERSION_1 = 1, }; @@ -45,18 +37,24 @@ enum class ManifestType : uint8_t { FLIC_MANIFEST = 3, }; -/** - * INCREMENTAL: Manifests will be received inline with the data with no specific - * assumption regarding the manifest capacity. Consumers can send interests - * using a +1 heuristic. - * - * MANIFEST_CAPACITY_BASED: manifests with capacity N have a suffix multiple of - * N+1: 0, N+1, 2(N+1) etc. Contents have a suffix incremented by 1 except when - * it conflicts with a manifest: 1, 2, ..., N, N+2, N+3, ..., 2N+1, 2N+3 - */ -enum class NextSegmentCalculationStrategy : uint8_t { - INCREMENTAL = 1, - MANIFEST_CAPACITY_BASED = 2, +struct ParamsRTC { + std::uint64_t timestamp; + std::uint32_t prod_rate; + std::uint32_t prod_seg; + std::uint32_t support_fec; + + bool operator==(const ParamsRTC &other) const { + return (timestamp == other.timestamp && prod_rate == other.prod_rate && + prod_seg == other.prod_seg && support_fec == other.support_fec); + } +}; + +struct ParamsBytestream { + std::uint32_t final_segment; + + bool operator==(const ParamsBytestream &other) const { + return (final_segment == other.final_segment); + } }; template <typename T> @@ -84,24 +82,14 @@ class ManifestEncoder { return static_cast<Implementation &>(*this).clearImpl(); } - ManifestEncoder &setManifestType(ManifestType type) { - return static_cast<Implementation &>(*this).setManifestTypeImpl(type); + ManifestEncoder &setType(ManifestType type) { + return static_cast<Implementation &>(*this).setTypeImpl(type); } ManifestEncoder &setHashAlgorithm(auth::CryptoHashType hash) { return static_cast<Implementation &>(*this).setHashAlgorithmImpl(hash); } - ManifestEncoder &setFinalChunkNumber(uint32_t final_chunk) { - return static_cast<Implementation &>(*this).setFinalChunkImpl(final_chunk); - } - - ManifestEncoder &setNextSegmentCalculationStrategy( - NextSegmentCalculationStrategy strategy) { - return static_cast<Implementation &>(*this) - .setNextSegmentCalculationStrategyImpl(strategy); - } - template < typename T, typename = std::enable_if_t<std::is_same< @@ -116,8 +104,8 @@ class ManifestEncoder { suffix, std::forward<Hash &&>(hash)); } - ManifestEncoder &setIsFinalManifest(bool is_last) { - return static_cast<Implementation &>(*this).setIsFinalManifestImpl(is_last); + ManifestEncoder &setIsLast(bool is_last) { + return static_cast<Implementation &>(*this).setIsLastImpl(is_last); } ManifestEncoder &setVersion(ManifestVersion version) { @@ -133,17 +121,22 @@ class ManifestEncoder { return static_cast<Implementation &>(*this).updateImpl(); } - ManifestEncoder &setFinalBlockNumber(std::uint32_t final_block_number) { - return static_cast<Implementation &>(*this).setFinalBlockNumberImpl( - final_block_number); + ManifestEncoder &setParamsBytestream(const ParamsBytestream ¶ms) { + return static_cast<Implementation &>(*this).setParamsBytestreamImpl(params); + } + + ManifestEncoder &setParamsRTC(const ParamsRTC ¶ms) { + return static_cast<Implementation &>(*this).setParamsRTCImpl(params); } - static std::size_t getManifestHeaderSize() { - return Implementation::getManifestHeaderSizeImpl(); + static std::size_t manifestHeaderSize( + interface::ProductionProtocolAlgorithms transport_type = + interface::ProductionProtocolAlgorithms::UNKNOWN) { + return Implementation::manifestHeaderSizeImpl(transport_type); } - static std::size_t getManifestEntrySize() { - return Implementation::getManifestEntrySizeImpl(); + static std::size_t manifestEntrySize() { + return Implementation::manifestEntrySizeImpl(); } }; @@ -158,21 +151,16 @@ class ManifestDecoder { void decode() { static_cast<Implementation &>(*this).decodeImpl(); } - ManifestType getManifestType() const { - return static_cast<const Implementation &>(*this).getManifestTypeImpl(); - } - - auth::CryptoHashType getHashAlgorithm() const { - return static_cast<const Implementation &>(*this).getHashAlgorithmImpl(); + ManifestType getType() const { + return static_cast<const Implementation &>(*this).getTypeImpl(); } - uint32_t getFinalChunkNumber() const { - return static_cast<const Implementation &>(*this).getFinalChunkImpl(); + interface::ProductionProtocolAlgorithms getTransportType() const { + return static_cast<const Implementation &>(*this).getTransportTypeImpl(); } - NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() const { - return static_cast<const Implementation &>(*this) - .getNextSegmentCalculationStrategyImpl(); + auth::CryptoHashType getHashAlgorithm() const { + return static_cast<const Implementation &>(*this).getHashAlgorithmImpl(); } core::Name getBaseName() const { @@ -183,8 +171,8 @@ class ManifestDecoder { return static_cast<Implementation &>(*this).getSuffixHashListImpl(); } - bool getIsFinalManifest() const { - return static_cast<const Implementation &>(*this).getIsFinalManifestImpl(); + bool getIsLast() const { + return static_cast<const Implementation &>(*this).getIsLastImpl(); } ManifestVersion getVersion() const { @@ -196,8 +184,12 @@ class ManifestDecoder { .estimateSerializedLengthImpl(number_of_entries); } - uint32_t getFinalBlockNumber() const { - return static_cast<const Implementation &>(*this).getFinalBlockNumberImpl(); + ParamsBytestream getParamsBytestream() const { + return static_cast<const Implementation &>(*this).getParamsBytestreamImpl(); + } + + ParamsRTC getParamsRTC() const { + return static_cast<const Implementation &>(*this).getParamsRTCImpl(); } }; diff --git a/libtransport/src/core/manifest_format_fixed.cc b/libtransport/src/core/manifest_format_fixed.cc index 11d4a56cb..4c8a5e031 100644 --- a/libtransport/src/core/manifest_format_fixed.cc +++ b/libtransport/src/core/manifest_format_fixed.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -27,164 +27,288 @@ FixedManifestEncoder::FixedManifestEncoder(Packet &packet, bool clear) : packet_(packet), max_size_(Packet::default_mtu - packet_.headerSize()), - manifest_header_(reinterpret_cast<ManifestHeader *>( - packet_.writableData() + packet_.headerSize())), - manifest_entries_( - reinterpret_cast<ManifestEntry *>(manifest_header_ + 1)), - current_entry_(0), - signature_size_(signature_size) { + signature_size_(signature_size), + transport_type_(interface::ProductionProtocolAlgorithms::UNKNOWN), + encoded_(false), + params_bytestream_({0}), + params_rtc_({0}) { + manifest_meta_ = reinterpret_cast<ManifestMeta *>(packet_.writableData() + + packet_.headerSize()); + manifest_entry_meta_ = + reinterpret_cast<ManifestEntryMeta *>(manifest_meta_ + 1); + if (clear) { - *manifest_header_ = {0}; + *manifest_meta_ = {0}; + *manifest_entry_meta_ = {0}; } } FixedManifestEncoder::~FixedManifestEncoder() {} FixedManifestEncoder &FixedManifestEncoder::encodeImpl() { - packet_.append(sizeof(ManifestHeader) + - manifest_header_->number_of_entries * sizeof(ManifestEntry)); + if (encoded_) { + return *this; + } + + manifest_meta_->transport_type = static_cast<uint8_t>(transport_type_); + manifest_entry_meta_->nb_entries = manifest_entries_.size(); + + packet_.append(FixedManifestEncoder::manifestHeaderSizeImpl()); packet_.updateLength(); + + switch (transport_type_) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: + packet_.appendPayload( + reinterpret_cast<const uint8_t *>(¶ms_bytestream_), + MANIFEST_PARAMS_BYTESTREAM_SIZE); + break; + case interface::ProductionProtocolAlgorithms::RTC_PROD: + packet_.appendPayload(reinterpret_cast<const uint8_t *>(¶ms_rtc_), + MANIFEST_PARAMS_RTC_SIZE); + break; + default: + break; + } + + packet_.appendPayload( + reinterpret_cast<const uint8_t *>(manifest_entries_.data()), + manifest_entries_.size() * FixedManifestEncoder::manifestEntrySizeImpl()); + + if (TRANSPORT_EXPECT_FALSE(packet_.payloadSize() < + estimateSerializedLengthImpl())) { + throw errors::RuntimeException("Error encoding the manifest"); + } + + encoded_ = true; return *this; } FixedManifestEncoder &FixedManifestEncoder::clearImpl() { - packet_.trimEnd(sizeof(ManifestHeader) + - manifest_header_->number_of_entries * sizeof(ManifestEntry)); - current_entry_ = 0; - *manifest_header_ = {0}; + if (encoded_) { + packet_.trimEnd(FixedManifestEncoder::manifestHeaderSizeImpl() + + manifest_entries_.size() * + FixedManifestEncoder::manifestEntrySizeImpl()); + } + + transport_type_ = interface::ProductionProtocolAlgorithms::UNKNOWN; + encoded_ = false; + params_bytestream_ = {0}; + params_rtc_ = {0}; + *manifest_meta_ = {0}; + *manifest_entry_meta_ = {0}; + manifest_entries_.clear(); + return *this; } -FixedManifestEncoder &FixedManifestEncoder::setHashAlgorithmImpl( - auth::CryptoHashType algorithm) { - manifest_header_->hash_algorithm = static_cast<uint8_t>(algorithm); +FixedManifestEncoder &FixedManifestEncoder::updateImpl() { + max_size_ = Packet::default_mtu - packet_.headerSize() - signature_size_; + return *this; +} + +FixedManifestEncoder &FixedManifestEncoder::setVersionImpl( + ManifestVersion version) { + manifest_meta_->version = static_cast<uint8_t>(version); return *this; } -FixedManifestEncoder &FixedManifestEncoder::setManifestTypeImpl( +FixedManifestEncoder &FixedManifestEncoder::setTypeImpl( ManifestType manifest_type) { - manifest_header_->manifest_type = static_cast<uint8_t>(manifest_type); + manifest_meta_->type = static_cast<uint8_t>(manifest_type); + return *this; +} + +FixedManifestEncoder &FixedManifestEncoder::setHashAlgorithmImpl( + auth::CryptoHashType algorithm) { + manifest_meta_->hash_algorithm = static_cast<uint8_t>(algorithm); return *this; } -FixedManifestEncoder & -FixedManifestEncoder::setNextSegmentCalculationStrategyImpl( - NextSegmentCalculationStrategy strategy) { - manifest_header_->next_segment_strategy = static_cast<uint8_t>(strategy); +FixedManifestEncoder &FixedManifestEncoder::setIsLastImpl(bool is_last) { + manifest_meta_->is_last = static_cast<uint8_t>(is_last); return *this; } FixedManifestEncoder &FixedManifestEncoder::setBaseNameImpl( const core::Name &base_name) { - base_name.copyToDestination( - reinterpret_cast<uint8_t *>(&manifest_header_->prefix[0]), false); - manifest_header_->flags.ipv6 = + manifest_entry_meta_->is_ipv6 = base_name.getAddressFamily() == AF_INET6 ? 1_U8 : 0_U8; + base_name.copyPrefixToDestination( + reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix[0])); return *this; } -FixedManifestEncoder &FixedManifestEncoder::addSuffixAndHashImpl( - uint32_t suffix, const auth::CryptoHash &hash) { - auto _hash = hash.getDigest(); - addSuffixHashBytes(suffix, _hash.data(), _hash.size()); +FixedManifestEncoder &FixedManifestEncoder::setParamsBytestreamImpl( + const ParamsBytestream ¶ms) { + transport_type_ = interface::ProductionProtocolAlgorithms::BYTE_STREAM; + params_bytestream_ = TransportParamsBytestream{ + .final_segment = params.final_segment, + }; return *this; } -void FixedManifestEncoder::addSuffixHashBytes(uint32_t suffix, - const uint8_t *hash, - std::size_t length) { - manifest_entries_[current_entry_].suffix = htonl(suffix); - // std::copy(hash, hash + length, - // manifest_entries_[current_entry_].hash); - std::memcpy( - reinterpret_cast<uint8_t *>(manifest_entries_[current_entry_].hash), hash, - length); +FixedManifestEncoder &FixedManifestEncoder::setParamsRTCImpl( + const ParamsRTC ¶ms) { + transport_type_ = interface::ProductionProtocolAlgorithms::RTC_PROD; + params_rtc_ = TransportParamsRTC{ + .timestamp = params.timestamp, + .prod_rate = params.prod_rate, + .prod_seg = params.prod_seg, + .support_fec = params.support_fec, + }; + return *this; +} - manifest_header_->number_of_entries++; - current_entry_++; +FixedManifestEncoder &FixedManifestEncoder::addSuffixAndHashImpl( + uint32_t suffix, const auth::CryptoHash &hash) { + std::vector<uint8_t> _hash = hash.getDigest(); + + manifest_entries_.push_back(ManifestEntry{ + .suffix = htonl(suffix), + .hash = {0}, + }); + + std::memcpy(reinterpret_cast<uint8_t *>(manifest_entries_.back().hash), + _hash.data(), _hash.size()); if (TRANSPORT_EXPECT_FALSE(estimateSerializedLengthImpl() > max_size_)) { throw errors::RuntimeException("Manifest size exceeded the packet MTU!"); } -} - -FixedManifestEncoder &FixedManifestEncoder::setIsFinalManifestImpl( - bool is_last) { - manifest_header_->flags.is_last = static_cast<uint8_t>(is_last); - return *this; -} -FixedManifestEncoder &FixedManifestEncoder::setVersionImpl( - ManifestVersion version) { - manifest_header_->version = static_cast<uint8_t>(version); return *this; } std::size_t FixedManifestEncoder::estimateSerializedLengthImpl( std::size_t additional_entries) { - return sizeof(ManifestHeader) + - (manifest_header_->number_of_entries + additional_entries) * - sizeof(ManifestEntry); + return FixedManifestEncoder::manifestHeaderSizeImpl(transport_type_) + + (manifest_entries_.size() + additional_entries) * + FixedManifestEncoder::manifestEntrySizeImpl(); } -FixedManifestEncoder &FixedManifestEncoder::updateImpl() { - max_size_ = Packet::default_mtu - packet_.headerSize() - signature_size_; - return *this; -} - -FixedManifestEncoder &FixedManifestEncoder::setFinalBlockNumberImpl( - std::uint32_t final_block_number) { - manifest_header_->final_block_number = htonl(final_block_number); - return *this; -} +std::size_t FixedManifestEncoder::manifestHeaderSizeImpl( + interface::ProductionProtocolAlgorithms transport_type) { + uint32_t params_size = 0; + + switch (transport_type) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: + params_size = MANIFEST_PARAMS_BYTESTREAM_SIZE; + break; + case interface::ProductionProtocolAlgorithms::RTC_PROD: + params_size = MANIFEST_PARAMS_RTC_SIZE; + break; + default: + break; + } -std::size_t FixedManifestEncoder::getManifestHeaderSizeImpl() { - return sizeof(ManifestHeader); + return MANIFEST_META_SIZE + MANIFEST_ENTRY_META_SIZE + params_size; } -std::size_t FixedManifestEncoder::getManifestEntrySizeImpl() { - return sizeof(ManifestEntry); +std::size_t FixedManifestEncoder::manifestEntrySizeImpl() { + return MANIFEST_ENTRY_SIZE; } FixedManifestDecoder::FixedManifestDecoder(Packet &packet) - : packet_(packet), - manifest_header_(reinterpret_cast<ManifestHeader *>( - packet_.getPayload()->writableData())), - manifest_entries_(reinterpret_cast<ManifestEntry *>( - packet_.getPayload()->writableData() + sizeof(ManifestHeader))) {} + : packet_(packet), decoded_(false) { + manifest_meta_ = + reinterpret_cast<ManifestMeta *>(packet_.getPayload()->writableData()); + manifest_entry_meta_ = + reinterpret_cast<ManifestEntryMeta *>(manifest_meta_ + 1); + transport_type_ = getTransportTypeImpl(); + + switch (transport_type_) { + case interface::ProductionProtocolAlgorithms::BYTE_STREAM: + params_bytestream_ = reinterpret_cast<TransportParamsBytestream *>( + manifest_entry_meta_ + 1); + manifest_entries_ = + reinterpret_cast<ManifestEntry *>(params_bytestream_ + 1); + break; + case interface::ProductionProtocolAlgorithms::RTC_PROD: + params_rtc_ = + reinterpret_cast<TransportParamsRTC *>(manifest_entry_meta_ + 1); + manifest_entries_ = reinterpret_cast<ManifestEntry *>(params_rtc_ + 1); + break; + default: + manifest_entries_ = + reinterpret_cast<ManifestEntry *>(manifest_entry_meta_ + 1); + break; + } +} FixedManifestDecoder::~FixedManifestDecoder() {} void FixedManifestDecoder::decodeImpl() { + if (decoded_) { + return; + } + std::size_t packet_size = packet_.payloadSize(); - if (packet_size < sizeof(ManifestHeader) || + if (packet_size < + FixedManifestEncoder::manifestHeaderSizeImpl(transport_type_) || packet_size < estimateSerializedLengthImpl()) { throw errors::RuntimeException( "The packet does not match expected manifest size."); } + + decoded_ = true; +} + +FixedManifestDecoder &FixedManifestDecoder::clearImpl() { + decoded_ = false; + return *this; } -FixedManifestDecoder &FixedManifestDecoder::clearImpl() { return *this; } +ManifestType FixedManifestDecoder::getTypeImpl() const { + return static_cast<ManifestType>(manifest_meta_->type); +} -ManifestType FixedManifestDecoder::getManifestTypeImpl() const { - return static_cast<ManifestType>(manifest_header_->manifest_type); +ManifestVersion FixedManifestDecoder::getVersionImpl() const { + return static_cast<ManifestVersion>(manifest_meta_->version); +} + +interface::ProductionProtocolAlgorithms +FixedManifestDecoder::getTransportTypeImpl() const { + return static_cast<interface::ProductionProtocolAlgorithms>( + manifest_meta_->transport_type); } auth::CryptoHashType FixedManifestDecoder::getHashAlgorithmImpl() const { - return static_cast<auth::CryptoHashType>(manifest_header_->hash_algorithm); + return static_cast<auth::CryptoHashType>(manifest_meta_->hash_algorithm); } -NextSegmentCalculationStrategy -FixedManifestDecoder::getNextSegmentCalculationStrategyImpl() const { - return static_cast<NextSegmentCalculationStrategy>( - manifest_header_->next_segment_strategy); +bool FixedManifestDecoder::getIsLastImpl() const { + return static_cast<bool>(manifest_meta_->is_last); +} + +core::Name FixedManifestDecoder::getBaseNameImpl() const { + if (static_cast<bool>(manifest_entry_meta_->is_ipv6)) { + return core::Name( + AF_INET6, reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix)); + } else { + return core::Name( + AF_INET, reinterpret_cast<uint8_t *>(&manifest_entry_meta_->prefix)); + } +} + +ParamsBytestream FixedManifestDecoder::getParamsBytestreamImpl() const { + return ParamsBytestream{ + .final_segment = params_bytestream_->final_segment, + }; +} + +ParamsRTC FixedManifestDecoder::getParamsRTCImpl() const { + return ParamsRTC{ + .timestamp = params_rtc_->timestamp, + .prod_rate = params_rtc_->prod_rate, + .prod_seg = params_rtc_->prod_seg, + .support_fec = params_rtc_->support_fec, + }; } typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { typename Fixed::SuffixList hash_list; - for (int i = 0; i < manifest_header_->number_of_entries; i++) { + for (int i = 0; i < manifest_entry_meta_->nb_entries; i++) { hash_list.insert(hash_list.end(), std::make_pair(ntohl(manifest_entries_[i].suffix), reinterpret_cast<uint8_t *>( @@ -194,33 +318,11 @@ typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { return hash_list; } -core::Name FixedManifestDecoder::getBaseNameImpl() const { - if (static_cast<bool>(manifest_header_->flags.ipv6)) { - return core::Name(AF_INET6, - reinterpret_cast<uint8_t *>(&manifest_header_->prefix)); - } else { - return core::Name(AF_INET, - reinterpret_cast<uint8_t *>(&manifest_header_->prefix)); - } -} - -bool FixedManifestDecoder::getIsFinalManifestImpl() const { - return static_cast<bool>(manifest_header_->flags.is_last); -} - -ManifestVersion FixedManifestDecoder::getVersionImpl() const { - return static_cast<ManifestVersion>(manifest_header_->version); -} - std::size_t FixedManifestDecoder::estimateSerializedLengthImpl( std::size_t additional_entries) const { - return sizeof(ManifestHeader) + - (additional_entries + manifest_header_->number_of_entries) * - sizeof(ManifestEntry); -} - -uint32_t FixedManifestDecoder::getFinalBlockNumberImpl() const { - return ntohl(manifest_header_->final_block_number); + return FixedManifestEncoder::manifestHeaderSizeImpl(transport_type_) + + (manifest_entry_meta_->nb_entries + additional_entries) * + FixedManifestEncoder::manifestEntrySizeImpl(); } } // end namespace core diff --git a/libtransport/src/core/manifest_format_fixed.h b/libtransport/src/core/manifest_format_fixed.h index 56ad4ef6d..ade4bf02c 100644 --- a/libtransport/src/core/manifest_format_fixed.h +++ b/libtransport/src/core/manifest_format_fixed.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -24,26 +24,74 @@ namespace transport { namespace core { -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// |Version| MType |HashAlg|NextStr| Flags |NumberOfEntries| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Final Block Number | -// +---------------------------------------------------------------| -// | | -// + + -// | | -// + Prefix + -// | | -// + + -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Suffix | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Hash Value | -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// Manifest Metadata: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Version| Type | Transport Type| Hash Algorithm|L| Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Entry Metadata: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Nb entries |I| Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + + +// | | +// + Prefix + +// | | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Transport Parameters - Bytestream: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Final Segment | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Transport Parameters - RTC: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Timestamp + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Production Rate | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Current Segment | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |F| | +// + Reserved for future parameters + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// Manifest Entry: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Packet Suffix | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + + +// | | +// + + +// | | +// + + +// | | +// + Packet Digest + +// | | +// + + +// | | +// + + +// | | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ class FixedManifestEncoder; class FixedManifestDecoder; @@ -58,28 +106,47 @@ struct Fixed { using SuffixList = std::list<std::pair<uint32_t, uint8_t *>>; }; -struct Flags { - std::uint8_t ipv6 : 1; - std::uint8_t is_last : 1; - std::uint8_t unused : 6; +const size_t MANIFEST_META_SIZE = 4; +struct __attribute__((__packed__)) ManifestMeta { + std::uint8_t version : 4; + std::uint8_t type : 4; + std::uint8_t transport_type; + std::uint8_t hash_algorithm; + std::uint8_t is_last; }; +static_assert(sizeof(ManifestMeta) == MANIFEST_META_SIZE); -struct ManifestEntry { - std::uint32_t suffix; - std::uint32_t hash[8]; +const size_t MANIFEST_ENTRY_META_SIZE = 20; +struct __attribute__((__packed__)) ManifestEntryMeta { + std::uint8_t nb_entries; + std::uint8_t is_ipv6; + std::uint16_t unused; + std::uint32_t prefix[4]; }; +static_assert(sizeof(ManifestEntryMeta) == MANIFEST_ENTRY_META_SIZE); -struct ManifestHeader { - std::uint8_t version : 4; - std::uint8_t manifest_type : 4; - std::uint8_t hash_algorithm : 4; - std::uint8_t next_segment_strategy : 4; - Flags flags; - std::uint8_t number_of_entries; - std::uint32_t final_block_number; - std::uint32_t prefix[4]; - ManifestEntry entries[0]; +const size_t MANIFEST_PARAMS_BYTESTREAM_SIZE = 4; +struct __attribute__((__packed__)) TransportParamsBytestream { + std::uint32_t final_segment; +}; +static_assert(sizeof(TransportParamsBytestream) == + MANIFEST_PARAMS_BYTESTREAM_SIZE); + +const size_t MANIFEST_PARAMS_RTC_SIZE = 20; +struct __attribute__((__packed__)) TransportParamsRTC { + std::uint64_t timestamp; + std::uint32_t prod_rate; + std::uint32_t prod_seg; + std::uint32_t support_fec; +}; +static_assert(sizeof(TransportParamsRTC) == MANIFEST_PARAMS_RTC_SIZE); + +const size_t MANIFEST_ENTRY_SIZE = 36; +struct __attribute__((__packed__)) ManifestEntry { + std::uint32_t suffix; + std::uint32_t hash[8]; }; +static_assert(sizeof(ManifestEntry) == MANIFEST_ENTRY_SIZE); static const constexpr std::uint8_t manifest_version = 1; @@ -91,46 +158,48 @@ class FixedManifestEncoder : public ManifestEncoder<FixedManifestEncoder> { ~FixedManifestEncoder(); FixedManifestEncoder &encodeImpl(); - FixedManifestEncoder &clearImpl(); + FixedManifestEncoder &updateImpl(); - FixedManifestEncoder &setManifestTypeImpl(ManifestType manifest_type); - + // ManifestMeta + FixedManifestEncoder &setVersionImpl(ManifestVersion version); + FixedManifestEncoder &setTypeImpl(ManifestType manifest_type); FixedManifestEncoder &setHashAlgorithmImpl(Fixed::HashType algorithm); + FixedManifestEncoder &setIsLastImpl(bool is_last); - FixedManifestEncoder &setNextSegmentCalculationStrategyImpl( - NextSegmentCalculationStrategy strategy); - + // ManifestEntryMeta FixedManifestEncoder &setBaseNameImpl(const core::Name &base_name); + // TransportParams + FixedManifestEncoder &setParamsBytestreamImpl(const ParamsBytestream ¶ms); + FixedManifestEncoder &setParamsRTCImpl(const ParamsRTC ¶ms); + + // ManifestEntry FixedManifestEncoder &addSuffixAndHashImpl(uint32_t suffix, const Fixed::Hash &hash); - FixedManifestEncoder &setIsFinalManifestImpl(bool is_last); - - FixedManifestEncoder &setVersionImpl(ManifestVersion version); - std::size_t estimateSerializedLengthImpl(std::size_t additional_entries = 0); - FixedManifestEncoder &updateImpl(); - - FixedManifestEncoder &setFinalBlockNumberImpl( - std::uint32_t final_block_number); - - static std::size_t getManifestHeaderSizeImpl(); - - static std::size_t getManifestEntrySizeImpl(); + static std::size_t manifestHeaderSizeImpl( + interface::ProductionProtocolAlgorithms transport_type = + interface::ProductionProtocolAlgorithms::UNKNOWN); + static std::size_t manifestEntrySizeImpl(); private: - void addSuffixHashBytes(uint32_t suffix, const uint8_t *hash, - std::size_t length); - Packet &packet_; std::size_t max_size_; - ManifestHeader *manifest_header_; - ManifestEntry *manifest_entries_; - std::size_t current_entry_; std::size_t signature_size_; + interface::ProductionProtocolAlgorithms transport_type_; + bool encoded_; + + // Manifest Header + ManifestMeta *manifest_meta_; + ManifestEntryMeta *manifest_entry_meta_; + TransportParamsBytestream params_bytestream_; + TransportParamsRTC params_rtc_; + + // Manifest Entries + std::vector<ManifestEntry> manifest_entries_; }; class FixedManifestDecoder : public ManifestDecoder<FixedManifestDecoder> { @@ -140,31 +209,40 @@ class FixedManifestDecoder : public ManifestDecoder<FixedManifestDecoder> { ~FixedManifestDecoder(); void decodeImpl(); - FixedManifestDecoder &clearImpl(); - ManifestType getManifestTypeImpl() const; - + // ManifestMeta + ManifestVersion getVersionImpl() const; + ManifestType getTypeImpl() const; + interface::ProductionProtocolAlgorithms getTransportTypeImpl() const; Fixed::HashType getHashAlgorithmImpl() const; + bool getIsLastImpl() const; - NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; - - typename Fixed::SuffixList getSuffixHashListImpl(); - + // ManifestEntryMeta core::Name getBaseNameImpl() const; - bool getIsFinalManifestImpl() const; + // TransportParams + ParamsBytestream getParamsBytestreamImpl() const; + ParamsRTC getParamsRTCImpl() const; + + // ManifestEntry + typename Fixed::SuffixList getSuffixHashListImpl(); std::size_t estimateSerializedLengthImpl( std::size_t additional_entries = 0) const; - ManifestVersion getVersionImpl() const; - - uint32_t getFinalBlockNumberImpl() const; - private: Packet &packet_; - ManifestHeader *manifest_header_; + interface::ProductionProtocolAlgorithms transport_type_; + bool decoded_; + + // Manifest Header + ManifestMeta *manifest_meta_; + ManifestEntryMeta *manifest_entry_meta_; + TransportParamsBytestream *params_bytestream_; + TransportParamsRTC *params_rtc_; + + // Manifest Entries ManifestEntry *manifest_entries_; }; diff --git a/libtransport/src/core/manifest_inline.h b/libtransport/src/core/manifest_inline.h index a487ccfe3..ca48a4a79 100644 --- a/libtransport/src/core/manifest_inline.h +++ b/libtransport/src/core/manifest_inline.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -40,25 +40,26 @@ class ManifestInline public: ManifestInline() : ManifestBase() {} - ManifestInline(const core::Name &name, std::size_t signature_size = 0) - : ManifestBase(name, signature_size) {} + ManifestInline(Packet::Format format, const core::Name &name, + std::size_t signature_size = 0) + : ManifestBase(format, name, signature_size) {} template <typename T> ManifestInline(T &&base) : ManifestBase(std::forward<T &&>(base)) {} + template <typename T> + ManifestInline(T &base) : ManifestBase(base) {} + static TRANSPORT_ALWAYS_INLINE ManifestInline *createManifest( - const core::Name &manifest_name, ManifestVersion version, - ManifestType type, HashType algorithm, bool is_last, - const Name &base_name, NextSegmentCalculationStrategy strategy, - std::size_t signature_size) { - auto manifest = new ManifestInline(manifest_name, signature_size); + Packet::Format format, const core::Name &manifest_name, + ManifestVersion version, ManifestType type, bool is_last, + const Name &base_name, HashType hash_algo, std::size_t signature_size) { + auto manifest = new ManifestInline(format, manifest_name, signature_size); manifest->setVersion(version); - manifest->setManifestType(type); - manifest->setHashAlgorithm(algorithm); - manifest->setFinalManifest(is_last); + manifest->setType(type); + manifest->setHashAlgorithm(hash_algo); + manifest->setIsLast(is_last); manifest->setBaseName(base_name); - manifest->setNextSegmentCalculationStrategy(strategy); - return manifest; } @@ -69,8 +70,6 @@ class ManifestInline ManifestInline &decodeImpl() { base_name_ = ManifestBase::decoder_.getBaseName(); - next_segment_strategy_ = - ManifestBase::decoder_.getNextSegmentCalculationStrategy(); suffix_hash_map_ = ManifestBase::decoder_.getSuffixHashList(); return *this; @@ -96,18 +95,6 @@ class ManifestInline // Call this function only after the decode function! const SuffixList &getSuffixList() { return suffix_hash_map_; } - ManifestInline &setNextSegmentCalculationStrategy( - NextSegmentCalculationStrategy strategy) { - next_segment_strategy_ = strategy; - ManifestBase::encoder_.setNextSegmentCalculationStrategy( - next_segment_strategy_); - return *this; - } - - NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() { - return next_segment_strategy_; - } - // Convert several manifests into a single map from suffixes to packet hashes. // All manifests must have been decoded beforehand. static std::unordered_map<Suffix, Hash> getSuffixMap( @@ -134,7 +121,6 @@ class ManifestInline private: core::Name base_name_; - NextSegmentCalculationStrategy next_segment_strategy_; SuffixList suffix_hash_map_; }; diff --git a/libtransport/src/core/memif_connector.cc b/libtransport/src/core/memif_connector.cc new file mode 100644 index 000000000..fb38a6e23 --- /dev/null +++ b/libtransport/src/core/memif_connector.cc @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <core/errors.h> +#include <core/memif_connector.h> +#include <glog/logging.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <sys/epoll.h> + +#include <cstdlib> + +/* sstrncpy */ +#include <hicn/util/sstrncpy.h> + +#define CANCEL_TIMER 1 + +namespace transport { + +namespace core { + +MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, + PacketSentCallback &&packet_sent, + OnCloseCallback &&close_callback, + OnReconnectCallback &&on_reconnect, + asio::io_service &io_service, + std::string app_name) + : Connector(std::move(receive_callback), std::move(packet_sent), + std::move(close_callback), std::move(on_reconnect)), + event_reactor_(), + memif_worker_(std::bind(&MemifConnector::threadMain, this)), + timer_set_(false), + send_timer_(event_reactor_), + disconnect_timer_(event_reactor_), + io_service_(io_service), + work_(asio::make_work_guard(io_service_)), + memif_connection_({0}), + tx_buf_counter_(0), + is_reconnection_(false), + data_available_(false), + app_name_(app_name), + socket_filename_(""), + buffer_size_(kbuf_size), + log2_ring_size_(klog2_ring_size), + max_memif_bufs_(1 << klog2_ring_size) {} + +MemifConnector::~MemifConnector() { close(); } + +void MemifConnector::connect(uint32_t memif_id, long memif_mode, + const std::string &socket_filename, + std::size_t buffer_size, + std::size_t log2_ring_size) { + state_ = State::CONNECTING; + + memif_id_ = memif_id; + socket_filename_ = socket_filename; + buffer_size_ = buffer_size; + log2_ring_size_ = log2_ring_size; + max_memif_bufs_ = 1 << log2_ring_size; + createMemif(memif_id, memif_mode); +} + +int MemifConnector::createMemif(uint32_t index, uint8_t is_master) { + int err = MEMIF_ERR_SUCCESS; + + memif_socket_args_t socket_args; + memif_conn_args_t args; + memset(&socket_args, 0, sizeof(memif_socket_args_t)); + memset(&args, 0, sizeof(memif_conn_args_t)); + + // Setup memif socket first + + int rc = strcpy_s(socket_args.path, sizeof(socket_args.path) - 1, + socket_filename_.c_str()); + if (rc != EOK) { + std::string error = "Provided socket path is larger than " + + std::to_string(sizeof(socket_args.path)) + " bytes."; + throw errors::RuntimeException(error); + } + + rc = strcpy_s(socket_args.app_name, sizeof(socket_args.app_name) - 1, + app_name_.c_str()); + if (rc != EOK) { + std::string error = "Provided app_name is larger than " + + std::to_string(sizeof(socket_args.app_name)) + + " bytes."; + throw errors::RuntimeException(error); + } + + socket_args.on_control_fd_update = controlFdUpdate; + socket_args.alloc = nullptr; + socket_args.realloc = nullptr; + socket_args.free = nullptr; + + err = memif_create_socket(&args.socket, &socket_args, this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + + // Setup memif connection using provided memif_socket_handle_t + args.is_master = is_master; + args.log2_ring_size = log2_ring_size_; + args.buffer_size = buffer_size_; + args.num_s2m_rings = 1; + args.num_m2s_rings = 1; + strcpy_s((char *)args.interface_name, sizeof(args.interface_name), IF_NAME); + args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + args.interface_id = index; + err = memif_create(&memif_connection_.conn, &args, onConnect, onDisconnect, + onInterrupt, this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + + memif_connection_.index = (uint16_t)index; + memif_connection_.tx_qid = 0; + /* alloc memif buffers */ + memif_connection_.rx_buf_num = 0; + memif_connection_.rx_bufs = static_cast<memif_buffer_t *>( + malloc(sizeof(memif_buffer_t) * max_memif_bufs_)); + memif_connection_.tx_buf_num = 0; + memif_connection_.tx_bufs = static_cast<memif_buffer_t *>( + malloc(sizeof(memif_buffer_t) * max_memif_bufs_)); + + return 0; +} + +int MemifConnector::deleteMemif() { + if (memif_connection_.rx_bufs) { + free(memif_connection_.rx_bufs); + } + + memif_connection_.rx_bufs = nullptr; + memif_connection_.rx_buf_num = 0; + + if (memif_connection_.tx_bufs) { + free(memif_connection_.tx_bufs); + } + + memif_connection_.tx_bufs = nullptr; + memif_connection_.tx_buf_num = 0; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete(&memif_connection_.conn); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_delete: " << memif_strerror(err); + } + + if (TRANSPORT_EXPECT_FALSE(memif_connection_.conn != nullptr)) { + LOG(ERROR) << "memif delete fail"; + } + + state_ = State::CLOSED; + + return 0; +} + +int MemifConnector::controlFdUpdate(memif_fd_event_t fde, void *private_ctx) { + auto self = reinterpret_cast<MemifConnector *>(private_ctx); + uint32_t evt = 0; + + /* convert memif event definitions to epoll events */ + auto events = fde.type; + auto fd = fde.fd; + + if (events & MEMIF_FD_EVENT_ERROR) { + LOG(ERROR) << "memif fd event: Error"; + return -1; + } + + if (events & MEMIF_FD_EVENT_DEL) { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: DEL fd " << fd; + return self->event_reactor_.delFileDescriptor(fd); + } + + if (events & MEMIF_FD_EVENT_MOD) { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: MOD fd " << fd; + return self->event_reactor_.modFileDescriptor(fd, evt); + } + + if (events & MEMIF_FD_EVENT_READ) { + evt |= EPOLLIN; + } + + if (events & MEMIF_FD_EVENT_WRITE) { + evt |= EPOLLOUT; + } + + DLOG_IF(INFO, VLOG_IS_ON(4)) << "memif fd event: ADD fd " << fd; + return self->event_reactor_.addFileDescriptor( + fd, evt, [fde](const utils::Event &evt) -> int { + int event = 0; + int memif_err = 0; + + if (evt.events & EPOLLIN) { + event |= MEMIF_FD_EVENT_READ; + } + + if (evt.events & EPOLLOUT) { + event |= MEMIF_FD_EVENT_WRITE; + } + + if (evt.events & EPOLLERR) { + event |= MEMIF_FD_EVENT_ERROR; + } + + memif_err = memif_control_fd_handler(fde.private_ctx, + memif_fd_event_type_t(event)); + + if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_control_fd_handler: " + << memif_strerror(memif_err); + } + + return 0; + }); +} + +uint16_t MemifConnector::bufferAlloc(long n, uint16_t qid, + std::error_code &ec) { + int err; + uint16_t r = 0; + /* set data pointer to shared memory and set buffer_len to shared mmeory + * buffer len */ + err = memif_buffer_alloc(memif_connection_.conn, qid, + memif_connection_.tx_bufs, n, &r, buffer_size_); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + ec = make_error_code(core_error::send_buffer_allocation_failed); + } + + memif_connection_.tx_buf_num += r; + return r; +} + +uint16_t MemifConnector::txBurst(uint16_t qid, std::error_code &ec) { + int err = MEMIF_ERR_SUCCESS; + ec = make_error_code(core_error::success); + uint16_t tx = 0; + + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst(memif_connection_.conn, qid, memif_connection_.tx_bufs, + memif_connection_.tx_buf_num, &tx); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + ec = make_error_code(core_error::send_failed); + } + + memif_connection_.tx_buf_num -= tx; + return tx; +} + +void MemifConnector::scheduleSend(std::uint64_t delay) { + if (!timer_set_) { + timer_set_ = true; + send_timer_.expiresFromNow(std::chrono::microseconds(delay)); + send_timer_.asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } +} + +void MemifConnector::sendCallback(const std::error_code &ec) { + timer_set_ = false; + + if (TRANSPORT_EXPECT_TRUE(!ec && state_ == State::CONNECTED)) { + doSend(); + } +} + +/* informs user about connected status. private_ctx is used by user to identify + connection (multiple connections WIP) */ +int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { + auto self = reinterpret_cast<MemifConnector *>(private_ctx); + self->state_ = State::CONNECTED; + memif_refill_queue(conn, 0, -1, 0); + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Memif " << self->app_name_ << " connected"; + + // We are connected. Notify higher layers. + self->io_service_.post([self]() { + self->on_reconnect_callback_(self, make_error_code(core_error::success)); + }); + + self->doSend(); + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + identify connection (multiple connections WIP) */ +int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { + MemifConnector *connector = (MemifConnector *)private_ctx; + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Memif " << connector->app_name_ << " disconnected"; + return 0; +} + +void MemifConnector::threadMain() { event_reactor_.runEventLoop(200); } + +int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid) { + MemifConnector *connector = (MemifConnector *)private_ctx; + + Details &c = connector->memif_connection_; + std::weak_ptr<MemifConnector> self = connector->shared_from_this(); + std::vector<::utils::MemBuf::Ptr> v; + std::error_code ec = make_error_code(core_error::success); + + int err = MEMIF_ERR_SUCCESS, ret_val; + uint16_t rx = 0; + + do { + err = memif_rx_burst(conn, qid, c.rx_bufs, max_burst, &rx); + ret_val = err; + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && + err != MEMIF_ERR_NOBUF)) { + ec = make_error_code(core_error::receive_failed); + LOG(ERROR) << "memif_rx_burst: " << memif_strerror(err); + goto error; + } + + c.rx_buf_num += rx; + + if (TRANSPORT_EXPECT_FALSE(connector->io_service_.stopped())) { + LOG(ERROR) << "socket stopped: ignoring " << rx << " packets"; + goto error; + } + + std::size_t packet_length; + v.reserve(rx); + for (int i = 0; i < rx; i++) { + auto buffer = connector->getRawBuffer(); + packet_length = (c.rx_bufs + i)->len; + std::memcpy(buffer.first, (c.rx_bufs + i)->data, packet_length); + auto packet = connector->getPacketFromBuffer(buffer.first, packet_length); + v.emplace_back(std::move(packet)); + } + + /* mark memif buffers and shared memory buffers as free */ + /* free processed buffers */ + + err = memif_refill_queue(conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); + } + + c.rx_buf_num -= rx; + + } while (ret_val == MEMIF_ERR_NOBUF); + + connector->io_service_.post([self, buffers = std::move(v)]() { + if (auto c = self.lock()) { + c->receive_callback_(c.get(), buffers, + std::make_error_code(std::errc(0))); + } + }); + + return 0; + +error: + err = memif_refill_queue(c.conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); + } + c.rx_buf_num -= rx; + + connector->io_service_.post([self, ec]() { + if (auto c = self.lock()) { + c->receive_callback_(c.get(), {}, ec); + } + }); + + return 0; +} + +void MemifConnector::close() { + if (state_ != State::CLOSED) { + disconnect_timer_.expiresFromNow(std::chrono::microseconds(50)); + disconnect_timer_.asyncWait([this](const std::error_code &ec) { + deleteMemif(); + event_reactor_.stop(); + }); + } + + if (memif_worker_.joinable()) { + memif_worker_.join(); + } +} + +void MemifConnector::send(Packet &packet) { send(packet.shared_from_this()); } + +void MemifConnector::send(const utils::MemBuf::Ptr &buffer) { + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + output_buffer_.push_back(buffer); + } +#if CANCEL_TIMER + scheduleSend(50); +#endif +} + +int MemifConnector::doSend() { + std::size_t max = 0; + std::size_t size = 0; + std::error_code ec = make_error_code(core_error::success); + int ret = 0; + uint64_t delay = 50; // microseconds + + utils::SpinLock::Acquire locked(write_msgs_lock_); + + // Check if there are pending buffers to send + if (memif_connection_.tx_buf_num > 0) { + ret = txBurst(memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + delay = 200; + goto done; + } + } + + // Continue trying to send buffers in output_buffer_ + size = output_buffer_.size(); + max = size < max_burst ? size : max_burst; + + ret = bufferAlloc(max, memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool() && ret == 0)) { + delay = 200; + goto done; + } + + // Fill allocated buffers and remove them from output_buffer_ + for (uint16_t i = 0; i < ret; i++) { + auto packet = output_buffer_.front().get(); + const utils::MemBuf *current = packet; + std::size_t offset = 0; + uint8_t *shared_buffer = + reinterpret_cast<uint8_t *>(memif_connection_.tx_bufs[i].data); + do { + std::memcpy(shared_buffer + offset, current->data(), current->length()); + offset += current->length(); + current = current->next(); + } while (current != packet); + + memif_connection_.tx_bufs[i].len = uint32_t(offset); + output_buffer_.pop_front(); + } + + // Try to send them + ret = txBurst(memif_connection_.tx_qid, ec); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + LOG(ERROR) << "Tx burst failed " << ec.message(); + delay = 200; + goto done; + } + +done: + memif_refill_queue(memif_connection_.conn, memif_connection_.tx_qid, ret, 0); + + // If there are still packets to send, schedule another send + if (memif_connection_.tx_buf_num > 0 || !output_buffer_.empty()) { + scheduleSend(delay); + } + + // If error, signal to upper layers + if (ec.operator bool()) { + std::weak_ptr<MemifConnector> self = shared_from_this(); + io_service_.post([self, ec]() { + if (auto c = self.lock()) { + c->sent_callback_(c.get(), ec); + } + }); + } + + return 0; +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/io_modules/memif/memif_connector.h b/libtransport/src/core/memif_connector.h index 0a189f893..d36be4616 100644 --- a/libtransport/src/io_modules/memif/memif_connector.h +++ b/libtransport/src/core/memif_connector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -25,27 +25,48 @@ #include <utils/fd_deadline_timer.h> #include <deque> +#include <future> #include <mutex> #include <thread> +extern "C" { +#include <libmemif.h> +}; + #define _Static_assert static_assert namespace transport { namespace core { -typedef struct memif_connection memif_connection_t; - #define APP_NAME "libtransport" #define IF_NAME "vpp_connection" -#define MEMIF_BUF_SIZE 2048 -#define MEMIF_LOG2_RING_SIZE 13 -#define MAX_MEMIF_BUFS (1 << MEMIF_LOG2_RING_SIZE) - class MemifConnector : public Connector { - using memif_conn_handle_t = void *; + static inline std::size_t kbuf_size = 2048; + static inline std::size_t klog2_ring_size = 13; + using PacketRing = utils::CircularFifo<utils::MemBuf::Ptr, queue_size>; + struct Details { + // index + uint16_t index; + // memif conenction handle + memif_conn_handle_t conn; + // transmit queue id + uint16_t tx_qid; + // tx buffers + memif_buffer_t *tx_bufs; + // allocated tx buffers counter + // number of tx buffers pointing to shared memory + uint16_t tx_buf_num; + // rx buffers + memif_buffer_t *rx_bufs; + // allocated rx buffers counter + // number of rx buffers pointing to shared memory + uint16_t rx_buf_num; + // interface ip address + uint8_t ip_addr[4]; + }; public: MemifConnector(PacketReceivedCallback &&receive_callback, @@ -59,11 +80,14 @@ class MemifConnector : public Connector { void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; void close() override; - void connect(uint32_t memif_id, long memif_mode); + void connect(uint32_t memif_id, long memif_mode, + const std::string &socket_filename, + std::size_t buffer_size = kbuf_size, + std::size_t log2_ring_size = klog2_ring_size); TRANSPORT_ALWAYS_INLINE uint32_t getMemifId() { return memif_id_; }; @@ -72,13 +96,13 @@ class MemifConnector : public Connector { int doSend(); - int createMemif(uint32_t index, uint8_t mode, char *s); + int createMemif(uint32_t index, uint8_t is_master); uint32_t getMemifConfiguration(); int deleteMemif(); - static int controlFdUpdate(int fd, uint8_t events, void *private_ctx); + static int controlFdUpdate(memif_fd_event_t fde, void *private_ctx); static int onConnect(memif_conn_handle_t conn, void *private_ctx); @@ -89,27 +113,26 @@ class MemifConnector : public Connector { void threadMain(); - int txBurst(uint16_t qid); + uint16_t txBurst(uint16_t qid, std::error_code &ec); + + uint16_t bufferAlloc(long n, uint16_t qid, std::error_code &ec); - int bufferAlloc(long n, uint16_t qid); + void scheduleSend(std::uint64_t delay); void sendCallback(const std::error_code &ec); - void processInputBuffer(std::uint16_t total_packets); + auto shared_from_this() { return utils::shared_from(this); } private: - static utils::EpollEventReactor main_event_reactor_; - static std::unique_ptr<std::thread> main_worker_; - int epfd; - std::unique_ptr<std::thread> memif_worker_; utils::EpollEventReactor event_reactor_; + std::thread memif_worker_; std::atomic_bool timer_set_; - std::unique_ptr<utils::FdDeadlineTimer> send_timer_; - std::unique_ptr<utils::FdDeadlineTimer> disconnect_timer_; + utils::FdDeadlineTimer send_timer_; + utils::FdDeadlineTimer disconnect_timer_; asio::io_service &io_service_; asio::executor_work_guard<asio::io_context::executor_type> work_; - std::unique_ptr<memif_connection_t> memif_connection_; + Details memif_connection_; uint16_t tx_buf_counter_; PacketRing input_buffer_; @@ -121,8 +144,9 @@ class MemifConnector : public Connector { uint16_t transmission_index_; utils::SpinLock write_msgs_lock_; std::string socket_filename_; - - static std::once_flag flag_; + std::size_t buffer_size_; + std::size_t log2_ring_size_; + std::size_t max_memif_bufs_; }; } // end namespace core diff --git a/libtransport/src/core/name.cc b/libtransport/src/core/name.cc index 795c8a697..98091eea5 100644 --- a/libtransport/src/core/name.cc +++ b/libtransport/src/core/name.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -26,30 +26,28 @@ namespace core { Name::Name() { name_ = {}; } +/** + * XXX This function does not use the name API provided by libhicn + */ Name::Name(int family, const uint8_t *ip_address, std::uint32_t suffix) : name_({}) { - name_.type = HNT_UNSPEC; std::size_t length; uint8_t *dst = NULL; if (family == AF_INET) { - dst = name_.ip4.prefix_as_u8; + dst = name_.prefix.v4.as_u8; length = IPV4_ADDR_LEN; - name_.type = HNT_CONTIGUOUS_V4; } else if (family == AF_INET6) { - dst = name_.ip6.prefix_as_u8; + dst = name_.prefix.v6.as_u8; length = IPV6_ADDR_LEN; - name_.type = HNT_CONTIGUOUS_V6; } else { throw errors::RuntimeException("Specified name family does not exist."); } std::memcpy(dst, ip_address, length); - *reinterpret_cast<std::uint32_t *>(dst + length) = suffix; + name_.suffix = suffix; } - Name::Name(const char *name, uint32_t segment) { - name_.type = HNT_UNSPEC; if (hicn_name_create(name, segment, &name_) < 0) { throw errors::InvalidIpAddressException(); } @@ -59,7 +57,6 @@ Name::Name(const std::string &uri, uint32_t segment) : Name(uri.c_str(), segment) {} Name::Name(const std::string &uri) { - name_.type = HNT_UNSPEC; utils::StringTokenizer tokenizer(uri, "|"); std::string ip_address; std::string seq_number; @@ -80,9 +77,13 @@ Name::Name(const std::string &uri) { Name::Name(const Name &name) { this->name_ = name.name_; } +Name::~Name() {} + Name &Name::operator=(const Name &name) { - if (hicn_name_copy(&this->name_, &name.name_) < 0) { - throw errors::MalformedNameException(); + if (this != &name) { + if (hicn_name_copy(&this->name_, &name.name_) < 0) { + throw errors::MalformedNameException(); + } } return *this; @@ -129,9 +130,13 @@ uint32_t Name::getHash32(bool consider_suffix) const { return hash; } -void Name::clear() { name_.type = HNT_UNSPEC; }; +void Name::clear() { std::memset(&name_, 0, sizeof(name_)); }; -Name::Type Name::getType() const { return name_.type; } +Name::Type Name::getType() const { + int family; + hicn_name_get_family(&name_, &family); + return family == AF_INET ? Name::Type::V4 : Name::Type::V6; +} uint32_t Name::getSuffix() const { uint32_t ret = 0; @@ -151,29 +156,6 @@ Name &Name::setSuffix(uint32_t seq_number) { return *this; } -std::shared_ptr<Sockaddr> Name::getAddress() const { - Sockaddr *ret = nullptr; - - switch (name_.type) { - case HNT_CONTIGUOUS_V4: - case HNT_IOV_V4: - ret = (Sockaddr *)new Sockaddr4; - break; - case HNT_CONTIGUOUS_V6: - case HNT_IOV_V6: - ret = (Sockaddr *)new Sockaddr6; - break; - default: - throw errors::MalformedNameException(); - } - - if (hicn_name_to_sockaddr_address((hicn_name_t *)&name_, ret) < 0) { - throw errors::MalformedNameException(); - } - - return std::shared_ptr<Sockaddr>(ret); -} - ip_prefix_t Name::toIpAddress() const { ip_prefix_t ret; std::memset(&ret, 0, sizeof(ret)); @@ -195,8 +177,8 @@ int Name::getAddressFamily() const { return ret; } -void Name::copyToDestination(uint8_t *destination, bool include_suffix) const { - if (hicn_name_copy_to_destination(destination, &name_, include_suffix) < 0) { +void Name::copyPrefixToDestination(uint8_t *destination) const { + if (hicn_name_copy_prefix_to_destination(destination, &name_) < 0) { throw errors::RuntimeException( "Impossibe to copy the name into the " "provided destination"); diff --git a/libtransport/src/core/packet.cc b/libtransport/src/core/packet.cc index 51337201f..df27444af 100644 --- a/libtransport/src/core/packet.cc +++ b/libtransport/src/core/packet.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -41,13 +41,6 @@ Packet::Packet(Format format, std::size_t additional_header_size) setFormat(format_, additional_header_size); } -Packet::Packet(MemBuf &&buffer) - : utils::MemBuf(std::move(buffer)), - packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), - header_offset_(0), - format_(getFormatFromBuffer(data(), length())), - payload_type_(PayloadType::UNSPECIFIED) {} - Packet::Packet(CopyBufferOp, const uint8_t *buffer, std::size_t size) : utils::MemBuf(COPY_BUFFER, buffer, size), packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), @@ -74,11 +67,11 @@ Packet::Packet(CreateOp, uint8_t *buffer, std::size_t length, std::size_t size, setFormat(format_, additional_header_size); } -Packet::Packet(const Packet &other) - : utils::MemBuf(other), +Packet::Packet(MemBuf &&buffer) + : utils::MemBuf(std::move(buffer)), packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), - header_offset_(other.header_offset_), - format_(other.format_), + header_offset_(0), + format_(getFormatFromBuffer(data(), length())), payload_type_(PayloadType::UNSPECIFIED) {} Packet::Packet(Packet &&other) @@ -92,6 +85,13 @@ Packet::Packet(Packet &&other) other.header_offset_ = 0; } +Packet::Packet(const Packet &other) + : utils::MemBuf(other), + packet_start_(reinterpret_cast<hicn_header_t *>(writableData())), + header_offset_(other.header_offset_), + format_(other.format_), + payload_type_(PayloadType::UNSPECIFIED) {} + Packet::~Packet() {} Packet &Packet::operator=(const Packet &other) { @@ -103,29 +103,19 @@ Packet &Packet::operator=(const Packet &other) { return *this; } -std::size_t Packet::getHeaderSizeFromBuffer(Format format, - const uint8_t *buffer) { - size_t header_length; - - if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, - &header_length) < 0) { - throw errors::MalformedPacketException(); - } - - return header_length; +std::shared_ptr<utils::MemBuf> Packet::acquireMemBufReference() { + return std::static_pointer_cast<utils::MemBuf>(shared_from_this()); } -bool Packet::isInterest(const uint8_t *buffer) { - bool is_interest = false; - - if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece(HF_INET6_TCP, - (const hicn_header_t *)buffer, - &is_interest) < 0)) { - throw errors::RuntimeException( - "Impossible to retrieve ece flag from packet"); +Packet::Format Packet::getFormat() const { + // We check packet start because after a movement it will result in a nullptr + if (format_ == HF_UNSPEC && length()) { + if (hicn_packet_get_format(packet_start_, &format_) < 0) { + LOG(ERROR) << "Unexpected packet format HF_UNSPEC."; + } } - return !is_interest; + return format_; } void Packet::setFormat(Packet::Format format, @@ -136,47 +126,43 @@ void Packet::setFormat(Packet::Format format, } auto header_size = getHeaderSizeFromFormat(format_); - assert(header_size <= tailroom()); + DCHECK(header_size <= tailroom()); append(header_size); - assert(additional_header_size <= tailroom()); + DCHECK(additional_header_size <= tailroom()); append(additional_header_size); header_offset_ = length(); } -bool Packet::isInterest() { return Packet::isInterest(data()); } +PayloadType Packet::getPayloadType() const { + if (payload_type_ == PayloadType::UNSPECIFIED) { + hicn_payload_type_t ret; -std::size_t Packet::getPayloadSizeFromBuffer(Format format, - const uint8_t *buffer) { - std::size_t payload_length; - if (TRANSPORT_EXPECT_FALSE( - hicn_packet_get_payload_length(format, (hicn_header_t *)buffer, - &payload_length) < 0)) { - throw errors::MalformedPacketException(); + if (hicn_packet_get_payload_type(format_, packet_start_, &ret) < 0) { + throw errors::RuntimeException("Impossible to retrieve payload type."); + } + + payload_type_ = (PayloadType)ret; } - return payload_length; + return payload_type_; } -std::size_t Packet::payloadSize() const { - std::size_t ret = 0; - - if (length()) { - ret = getPayloadSizeFromBuffer(format_, - reinterpret_cast<uint8_t *>(packet_start_)); +Packet &Packet::setPayloadType(PayloadType payload_type) { + if (hicn_packet_set_payload_type(format_, packet_start_, + hicn_payload_type_t(payload_type)) < 0) { + throw errors::RuntimeException("Error setting payload type of the packet."); } - return ret; + payload_type_ = payload_type; + return *this; } -std::size_t Packet::headerSize() const { - if (header_offset_ == 0 && length()) { - const_cast<Packet *>(this)->header_offset_ = getHeaderSizeFromBuffer( - format_, reinterpret_cast<uint8_t *>(packet_start_)); - } - - return header_offset_; +std::unique_ptr<utils::MemBuf> Packet::getPayload() const { + auto ret = clone(); + ret->trimStart(headerSize()); + return ret; } Packet &Packet::appendPayload(std::unique_ptr<utils::MemBuf> &&payload) { @@ -196,12 +182,56 @@ Packet &Packet::appendPayload(const uint8_t *buffer, std::size_t length) { return *this; } -std::unique_ptr<utils::MemBuf> Packet::getPayload() const { - auto ret = clone(); - ret->trimStart(headerSize()); +std::size_t Packet::headerSize() const { + if (header_offset_ == 0 && length()) { + const_cast<Packet *>(this)->header_offset_ = getHeaderSizeFromBuffer( + format_, reinterpret_cast<uint8_t *>(packet_start_)); + } + + return header_offset_; +} + +std::size_t Packet::payloadSize() const { + std::size_t ret = 0; + + if (length()) { + ret = getPayloadSizeFromBuffer(format_, + reinterpret_cast<uint8_t *>(packet_start_)); + } + return ret; } +auth::CryptoHash Packet::computeDigest(auth::CryptoHashType algorithm) const { + auth::CryptoHash hash; + hash.setType(algorithm); + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + hicn_packet_copy_header(format_, packet_start_, &header_copy, false); + const_cast<Packet *>(this)->resetForHash(); + + hash.computeDigest(this); + hicn_packet_copy_header(format_, &header_copy, packet_start_, false); + + return hash; +} + +void Packet::reset() { + clear(); + packet_start_ = reinterpret_cast<hicn_header_t *>(writableData()); + header_offset_ = 0; + format_ = HF_UNSPEC; + payload_type_ = PayloadType::UNSPECIFIED; + name_.clear(); + + if (isChained()) { + separateChain(next(), prev()); + } +} + +bool Packet::isInterest() { return Packet::isInterest(data(), format_); } + Packet &Packet::updateLength(std::size_t length) { std::size_t total_length = length; @@ -221,47 +251,6 @@ Packet &Packet::updateLength(std::size_t length) { return *this; } -PayloadType Packet::getPayloadType() const { - if (payload_type_ == PayloadType::UNSPECIFIED) { - hicn_payload_type_t ret; - if (hicn_packet_get_payload_type(packet_start_, &ret) < 0) { - throw errors::RuntimeException("Impossible to retrieve payload type."); - } - - payload_type_ = (PayloadType)ret; - } - - return payload_type_; -} - -Packet &Packet::setPayloadType(PayloadType payload_type) { - if (hicn_packet_set_payload_type(packet_start_, - hicn_payload_type_t(payload_type)) < 0) { - throw errors::RuntimeException("Error setting payload type of the packet."); - } - - payload_type_ = payload_type; - - return *this; -} - -Packet::Format Packet::getFormat() const { - /** - * We check packet start because after a movement it will result in a nullptr - */ - if (format_ == HF_UNSPEC && length()) { - if (hicn_packet_get_format(packet_start_, &format_) < 0) { - LOG(ERROR) << "Unexpected packet format HF_UNSPEC."; - } - } - - return format_; -} - -std::shared_ptr<utils::MemBuf> Packet::acquireMemBufReference() { - return std::static_pointer_cast<utils::MemBuf>(shared_from_this()); -} - void Packet::dump() const { LOG(INFO) << "HEADER -- Length: " << headerSize(); LOG(INFO) << "PAYLOAD -- Length: " << payloadSize(); @@ -274,180 +263,42 @@ void Packet::dump() const { } while (current != this); } -void Packet::dump(uint8_t *buffer, std::size_t length) { - hicn_packet_dump(buffer, length); -} - -void Packet::setSignatureSize(std::size_t size_bytes) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_signature_size(format_, packet_start_, size_bytes); - - if (ret < 0) { - throw errors::RuntimeException("Error setting signature size."); - } -} - -void Packet::setSignatureSizeGap(std::size_t size_bytes) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_signature_gap(format_, packet_start_, - (uint8_t)size_bytes); - - if (ret < 0) { - throw errors::RuntimeException("Error setting signature size."); - } -} - -uint8_t *Packet::getSignature() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - uint8_t *signature; - int ret = hicn_packet_get_signature(format_, packet_start_, &signature); - - if (ret < 0) { - throw errors::RuntimeException("Error getting signature."); - } - - return signature; -} - -void Packet::setSignatureTimestamp(const uint64_t ×tamp) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = - hicn_packet_set_signature_timestamp(format_, packet_start_, timestamp); - - if (ret < 0) { - throw errors::RuntimeException("Error setting the signature timestamp."); - } -} - -uint64_t Packet::getSignatureTimestamp() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - uint64_t return_value; - int ret = hicn_packet_get_signature_timestamp(format_, packet_start_, - &return_value); - - if (ret < 0) { - throw errors::RuntimeException("Error getting the signature timestamp."); - } - - return return_value; -} - -void Packet::setValidationAlgorithm( - const auth::CryptoSuite &validation_algorithm) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_validation_algorithm(format_, packet_start_, - uint8_t(validation_algorithm)); - - if (ret < 0) { - throw errors::RuntimeException("Error setting the validation algorithm."); - } -} - -auth::CryptoSuite Packet::getValidationAlgorithm() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - uint8_t return_value; - int ret = hicn_packet_get_validation_algorithm(format_, packet_start_, - &return_value); - - if (ret < 0) { - throw errors::RuntimeException("Error getting the validation algorithm."); - } - - return auth::CryptoSuite(return_value); -} - -void Packet::setKeyId(const auth::KeyId &key_id) { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } - - int ret = hicn_packet_set_key_id(format_, packet_start_, key_id.first); - - if (ret < 0) { - throw errors::RuntimeException("Error setting the key id."); - } -} - -auth::KeyId Packet::getKeyId() const { - if (!authenticationHeader()) { - throw errors::RuntimeException("Packet without Authentication Header."); - } +void Packet::setChecksum() { + if (_is_tcp(format_)) { + uint16_t partial_csum = + csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); - auth::KeyId return_value; - int ret = hicn_packet_get_key_id(format_, packet_start_, &return_value.first, - &return_value.second); + for (utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); + } - if (ret < 0) { - throw errors::RuntimeException("Error getting the validation algorithm."); + if (hicn_packet_compute_header_checksum(format_, packet_start_, + partial_csum) < 0) { + throw errors::MalformedPacketException(); + } } - - return return_value; -} - -auth::CryptoHash Packet::computeDigest(auth::CryptoHashType algorithm) const { - auth::CryptoHash hash; - hash.setType(algorithm); - - // Copy IP+TCP/ICMP header before zeroing them - hicn_header_t header_copy; - - hicn_packet_copy_header(format_, packet_start_, &header_copy, false); - - const_cast<Packet *>(this)->resetForHash(); - - hash.computeDigest(this); - hicn_packet_copy_header(format_, &header_copy, packet_start_, false); - - return hash; } bool Packet::checkIntegrity() const { - uint16_t partial_csum = - csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); + if (_is_tcp(format_)) { + uint16_t partial_csum = + csum(data() + HICN_V6_TCP_HDRLEN, length() - HICN_V6_TCP_HDRLEN, 0); - for (const utils::MemBuf *current = next(); current != this; - current = current->next()) { - partial_csum = csum(current->data(), current->length(), ~partial_csum); - } + for (const utils::MemBuf *current = next(); current != this; + current = current->next()) { + partial_csum = csum(current->data(), current->length(), ~partial_csum); + } - if (hicn_packet_check_integrity_no_payload(format_, packet_start_, - partial_csum) < 0) { - return false; + if (hicn_packet_check_integrity_no_payload(format_, packet_start_, + partial_csum) < 0) { + return false; + } } return true; } -void Packet::prependPayload(const uint8_t **buffer, std::size_t *size) { - auto last = prev(); - auto to_copy = std::min(*size, last->tailroom()); - std::memcpy(last->writableTail(), *buffer, to_copy); - last->append(to_copy); - *size -= to_copy; - *buffer += to_copy; -} - Packet &Packet::setSyn() { if (hicn_packet_set_syn(format_, packet_start_) < 0) { throw errors::RuntimeException("Error setting syn bit in the packet."); @@ -553,29 +404,23 @@ Packet &Packet::resetFlags() { resetAck(); resetRst(); resetFin(); - return *this; } std::string Packet::printFlags() const { std::string flags; - if (testSyn()) { flags += "S"; } - if (testAck()) { flags += "A"; } - if (testRst()) { flags += "R"; } - if (testFin()) { flags += "F"; } - return flags; } @@ -634,6 +479,245 @@ uint8_t Packet::getTTL() const { return hops; } +bool Packet::hasAH() const { return _is_ah(format_); } + +std::vector<uint8_t> Packet::getSignature() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint8_t *signature; + int ret = hicn_packet_get_signature(format_, packet_start_, &signature); + + if (ret < 0) { + throw errors::RuntimeException("Error getting signature."); + } + + return std::vector<uint8_t>(signature, signature + getSignatureFieldSize()); +} + +std::size_t Packet::getSignatureFieldSize() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + size_t field_size; + int ret = hicn_packet_get_signature_size(format_, packet_start_, &field_size); + if (ret < 0) { + throw errors::RuntimeException("Error reading signature field size"); + } + return field_size; +} + +std::size_t Packet::getSignatureSize() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + size_t padding; + int ret = hicn_packet_get_signature_padding(format_, packet_start_, &padding); + if (ret < 0) { + throw errors::RuntimeException("Error reading signature padding"); + } + + size_t size = getSignatureFieldSize() - padding; + if (size < 0) { + throw errors::RuntimeException("Error reading signature size"); + } + + return size; +} + +uint64_t Packet::getSignatureTimestamp() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint64_t timestamp; + int ret = + hicn_packet_get_signature_timestamp(format_, packet_start_, ×tamp); + if (ret < 0) { + throw errors::RuntimeException("Error getting the signature timestamp."); + } + return timestamp; +} + +auth::KeyId Packet::getKeyId() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + auth::KeyId key_id; + int ret = hicn_packet_get_key_id(format_, packet_start_, &key_id.first, + &key_id.second); + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + return key_id; +} + +auth::CryptoSuite Packet::getValidationAlgorithm() const { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint8_t return_value; + int ret = hicn_packet_get_validation_algorithm(format_, packet_start_, + &return_value); + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + return auth::CryptoSuite(return_value); +} + +void Packet::setSignature(const std::vector<uint8_t> &signature) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + uint8_t *signature_field; + int ret = hicn_packet_get_signature(format_, packet_start_, &signature_field); + if (ret < 0) { + throw errors::RuntimeException("Error getting signature."); + } + memcpy(signature_field, signature.data(), signature.size()); +} + +void Packet::setSignatureFieldSize(std::size_t size) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = hicn_packet_set_signature_size(format_, packet_start_, size); + if (ret < 0) { + throw errors::RuntimeException("Error setting signature size."); + } +} + +void Packet::setSignatureSize(std::size_t size) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + size_t padding = getSignatureFieldSize() - size; + if (padding < 0) { + throw errors::RuntimeException("Error setting signature padding."); + } + + int ret = hicn_packet_set_signature_padding(format_, packet_start_, padding); + if (ret < 0) { + throw errors::RuntimeException("Error setting signature padding."); + } +} + +void Packet::setSignatureTimestamp(const uint64_t ×tamp) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = + hicn_packet_set_signature_timestamp(format_, packet_start_, timestamp); + if (ret < 0) { + throw errors::RuntimeException("Error setting the signature timestamp."); + } +} + +void Packet::setKeyId(const auth::KeyId &key_id) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = hicn_packet_set_key_id(format_, packet_start_, key_id.first); + if (ret < 0) { + throw errors::RuntimeException("Error setting the key id."); + } +} + +void Packet::setValidationAlgorithm( + const auth::CryptoSuite &validation_algorithm) { + if (!hasAH()) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + int ret = hicn_packet_set_validation_algorithm(format_, packet_start_, + uint8_t(validation_algorithm)); + if (ret < 0) { + throw errors::RuntimeException("Error setting the validation algorithm."); + } +} + +Packet::Format Packet::toAHFormat(const Format &format) { + return hicn_get_ah_format(format); +} + +Packet::Format Packet::getFormatFromBuffer(const uint8_t *buffer, + std::size_t /* length */) { + Packet::Format format = HF_UNSPEC; + hicn_packet_get_format((const hicn_header_t *)buffer, &format); + return format; +} + +std::size_t Packet::getHeaderSizeFromFormat(Format format, + std::size_t signature_size) { + std::size_t header_length; + hicn_packet_get_header_length_from_format(format, &header_length); + int is_ah = _is_ah(format); + return is_ah * (header_length + signature_size) + (!is_ah) * header_length; +} + +std::size_t Packet::getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer) { + size_t header_length; + + if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, + &header_length) < 0) { + throw errors::MalformedPacketException(); + } + + return header_length; +} + +std::size_t Packet::getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer) { + std::size_t payload_length; + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_get_payload_length(format, (hicn_header_t *)buffer, + &payload_length) < 0)) { + throw errors::MalformedPacketException(); + } + + return payload_length; +} + +bool Packet::isInterest(const uint8_t *buffer, Format format) { + int is_interest = 0; + + if (TRANSPORT_EXPECT_FALSE(format == Format::HF_UNSPEC)) { + format = getFormatFromBuffer(buffer, /* Unused length */ 128); + } + + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_is_interest(format, (const hicn_header_t *)buffer, + &is_interest) < 0)) { + throw errors::RuntimeException("Error reading ece flag from packet"); + } + + return is_interest; +} + +void Packet::dump(uint8_t *buffer, std::size_t length) { + hicn_packet_dump(buffer, length); +} + +void Packet::prependPayload(const uint8_t **buffer, std::size_t *size) { + auto last = prev(); + auto to_copy = std::min(*size, last->tailroom()); + std::memcpy(last->writableTail(), *buffer, to_copy); + last->append(to_copy); + *size -= to_copy; + *buffer += to_copy; +} + } // end namespace core } // end namespace transport diff --git a/libtransport/src/core/pending_interest.cc b/libtransport/src/core/pending_interest.cc deleted file mode 100644 index fbe98cab5..000000000 --- a/libtransport/src/core/pending_interest.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <core/pending_interest.h> - -namespace transport { - -namespace core { - -PendingInterest::PendingInterest() - : interest_(nullptr, nullptr), - timer_(), - on_content_object_callback_(), - on_interest_timeout_callback_() {} - -PendingInterest::PendingInterest(Interest::Ptr &&interest, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), - on_content_object_callback_(), - on_interest_timeout_callback_() {} - -PendingInterest::PendingInterest( - Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object, - OnInterestTimeoutCallback &&on_interest_timeout, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), - on_content_object_callback_(std::move(on_content_object)), - on_interest_timeout_callback_(std::move(on_interest_timeout)) {} - -PendingInterest::~PendingInterest() {} - -void PendingInterest::cancelTimer() { timer_->cancel(); } - -void PendingInterest::setInterest(Interest::Ptr &&interest) { - interest_ = std::move(interest); -} - -Interest::Ptr &&PendingInterest::getInterest() { return std::move(interest_); } - -const OnContentObjectCallback &PendingInterest::getOnDataCallback() const { - return on_content_object_callback_; -} - -void PendingInterest::setOnContentObjectCallback( - OnContentObjectCallback &&on_content_object) { - PendingInterest::on_content_object_callback_ = on_content_object; -} - -const OnInterestTimeoutCallback &PendingInterest::getOnTimeoutCallback() const { - return on_interest_timeout_callback_; -} - -void PendingInterest::setOnTimeoutCallback( - OnInterestTimeoutCallback &&on_interest_timeout) { - PendingInterest::on_interest_timeout_callback_ = on_interest_timeout; -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/core/pending_interest.h b/libtransport/src/core/pending_interest.h index 99a8bd327..f8a4ba10e 100644 --- a/libtransport/src/core/pending_interest.h +++ b/libtransport/src/core/pending_interest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -48,19 +48,17 @@ class PendingInterest { // on_content_object_callback_(), // on_interest_timeout_callback_() {} - PendingInterest(Interest::Ptr &&interest, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), + PendingInterest(asio::io_service &io_service, const Interest::Ptr &interest) + : interest_(interest), + timer_(io_service), on_content_object_callback_(), on_interest_timeout_callback_() {} - PendingInterest(Interest::Ptr &&interest, + PendingInterest(asio::io_service &io_service, const Interest::Ptr &interest, OnContentObjectCallback &&on_content_object, - OnInterestTimeoutCallback &&on_interest_timeout, - std::unique_ptr<asio::steady_timer> &&timer) - : interest_(std::move(interest)), - timer_(std::move(timer)), + OnInterestTimeoutCallback &&on_interest_timeout) + : interest_(interest), + timer_(io_service), on_content_object_callback_(std::move(on_content_object)), on_interest_timeout_callback_(std::move(on_interest_timeout)) {} @@ -68,12 +66,12 @@ class PendingInterest { template <typename Handler> TRANSPORT_ALWAYS_INLINE void startCountdown(Handler &&cb) { - timer_->expires_from_now( + timer_.expires_from_now( std::chrono::milliseconds(interest_->getLifetime())); - timer_->async_wait(std::forward<Handler &&>(cb)); + timer_.async_wait(std::forward<Handler &&>(cb)); } - TRANSPORT_ALWAYS_INLINE void cancelTimer() { timer_->cancel(); } + TRANSPORT_ALWAYS_INLINE void cancelTimer() { timer_.cancel(); } TRANSPORT_ALWAYS_INLINE Interest::Ptr &&getInterest() { return std::move(interest_); @@ -105,7 +103,7 @@ class PendingInterest { private: Interest::Ptr interest_; - std::unique_ptr<asio::steady_timer> timer_; + asio::steady_timer timer_; OnContentObjectCallback on_content_object_callback_; OnInterestTimeoutCallback on_interest_timeout_callback_; }; diff --git a/libtransport/src/core/portal.cc b/libtransport/src/core/portal.cc index c4c0cf8ba..d8e8d78ea 100644 --- a/libtransport/src/core/portal.cc +++ b/libtransport/src/core/portal.cc @@ -30,11 +30,11 @@ namespace core { #ifdef ANDROID static const constexpr char default_module[] = ""; #elif defined(MACINTOSH) -static const constexpr char default_module[] = "hicnlight_module.dylib"; +static const constexpr char default_module[] = "hicnlightng_module.dylib"; #elif defined(LINUX) -static const constexpr char default_module[] = "hicnlight_module.so"; +static const constexpr char default_module[] = "hicnlightng_module.so"; #elif defined(WINDOWS) -static const constexpr char default_module[] = "hicnlight_module.lib"; +static const constexpr char default_module[] = "hicnlightng_module.lib"; #endif IoModuleConfiguration Portal::conf_; @@ -57,7 +57,7 @@ std::string Portal::defaultIoModule() { void Portal::getModuleConfiguration(ConfigurationObject& object, std::error_code& ec) { - assert(object.getKey() == io_module_section); + DCHECK(object.getKey() == io_module_section); auto conf = dynamic_cast<const IoModuleConfiguration&>(object); conf = conf_; @@ -103,7 +103,7 @@ std::string getIoModulePath(const std::string& name, void Portal::setModuleConfiguration(const ConfigurationObject& object, std::error_code& ec) { - assert(object.getKey() == io_module_section); + DCHECK(object.getKey() == io_module_section); const IoModuleConfiguration& conf = dynamic_cast<const IoModuleConfiguration&>(object); diff --git a/libtransport/src/core/portal.h b/libtransport/src/core/portal.h index f6a9ce85b..aae4c573e 100644 --- a/libtransport/src/core/portal.h +++ b/libtransport/src/core/portal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,6 +15,7 @@ #pragma once +#include <core/global_workers.h> #include <core/pending_interest.h> #include <glog/logging.h> #include <hicn/transport/config.h> @@ -28,6 +29,7 @@ #include <hicn/transport/interfaces/global_conf_interface.h> #include <hicn/transport/interfaces/portal.h> #include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/event_thread.h> #include <hicn/transport/utils/fixed_block_allocator.h> #include <future> @@ -54,11 +56,11 @@ class HandlerMemory { HandlerMemory(const HandlerMemory &) = delete; HandlerMemory &operator=(const HandlerMemory &) = delete; - TRANSPORT_ALWAYS_INLINE void *allocate(std::size_t size) { + void *allocate(std::size_t size) { return utils::FixedBlockAllocator<128, 8192>::getInstance().allocateBlock(); } - TRANSPORT_ALWAYS_INLINE void deallocate(void *pointer) { + void deallocate(void *pointer) { utils::FixedBlockAllocator<128, 8192>::getInstance().deallocateBlock( pointer); } @@ -69,13 +71,9 @@ class HandlerMemory { HandlerMemory(const HandlerMemory &) = delete; HandlerMemory &operator=(const HandlerMemory &) = delete; - TRANSPORT_ALWAYS_INLINE void *allocate(std::size_t size) { - return ::operator new(size); - } + void *allocate(std::size_t size) { return ::operator new(size); } - TRANSPORT_ALWAYS_INLINE void deallocate(void *pointer) { - ::operator delete(pointer); - } + void deallocate(void *pointer) { ::operator delete(pointer); } #endif }; @@ -92,21 +90,19 @@ class HandlerAllocator { HandlerAllocator(const HandlerAllocator<U> &other) noexcept : memory_(other.memory_) {} - TRANSPORT_ALWAYS_INLINE bool operator==( - const HandlerAllocator &other) const noexcept { + bool operator==(const HandlerAllocator &other) const noexcept { return &memory_ == &other.memory_; } - TRANSPORT_ALWAYS_INLINE bool operator!=( - const HandlerAllocator &other) const noexcept { + bool operator!=(const HandlerAllocator &other) const noexcept { return &memory_ != &other.memory_; } - TRANSPORT_ALWAYS_INLINE T *allocate(std::size_t n) const { + T *allocate(std::size_t n) const { return static_cast<T *>(memory_.allocate(sizeof(T) * n)); } - TRANSPORT_ALWAYS_INLINE void deallocate(T *p, std::size_t /*n*/) const { + void deallocate(T *p, std::size_t /*n*/) const { return memory_.deallocate(p); } @@ -135,7 +131,7 @@ class CustomAllocatorHandler { } template <typename... Args> - void operator()(Args &&... args) { + void operator()(Args &&...args) { handler_(std::forward<Args>(args)...); } @@ -151,49 +147,16 @@ inline CustomAllocatorHandler<Handler> makeCustomAllocatorHandler( return CustomAllocatorHandler<Handler>(m, h); } -class Pool { - public: - Pool(asio::io_service &io_service) : io_service_(io_service) { - increasePendingInterestPool(); - } - - TRANSPORT_ALWAYS_INLINE void increasePendingInterestPool() { - // Create pool of pending interests to reuse - for (uint32_t i = 0; i < pit_size; i++) { - pending_interests_pool_.add(new PendingInterest( - Interest::Ptr(nullptr), - std::make_unique<asio::steady_timer>(io_service_))); - } - } - PendingInterest::Ptr getPendingInterest() { - auto res = pending_interests_pool_.get(); - while (TRANSPORT_EXPECT_FALSE(!res.first)) { - increasePendingInterestPool(); - res = pending_interests_pool_.get(); - } - - return std::move(res.second); - } - - private: - utils::ObjectPool<PendingInterest> pending_interests_pool_; - asio::io_service &io_service_; -}; - } // namespace portal_details class PortalConfiguration; -using PendingInterestHashTable = - std::unordered_map<uint32_t, PendingInterest::Ptr>; - -using interface::BindConfig; +using PendingInterestHashTable = std::unordered_map<uint32_t, PendingInterest>; /** * Portal is a opaque class which is used for sending/receiving interest/data - * packets over multiple kind of connector. The connector itself is defined by - * the template ForwarderInt, which is resolved at compile time. It is then not - * possible to decide at runtime what the connector will be. + * packets over multiple kind of io_modules. The io_module itself is an external + * module loaded at runtime. * * The tasks performed by portal are the following: * - Sending/Receiving Interest packets @@ -210,22 +173,16 @@ using interface::BindConfig; * users of this class. */ -class Portal { - public: - using ConsumerCallback = interface::Portal::ConsumerCallback; - using ProducerCallback = interface::Portal::ProducerCallback; - - friend class PortalConfiguration; - - Portal() : Portal(internal_io_service_) {} +class Portal : public ::utils::NonCopyable, + public std::enable_shared_from_this<Portal> { + private: + Portal() : Portal(GlobalWorkers::getInstance().getWorker()) {} - Portal(asio::io_service &io_service) + Portal(::utils::EventThread &worker) : io_module_(nullptr, [](IoModule *module) { IoModule::unload(module); }), - io_service_(io_service), - packet_pool_(io_service), + worker_(worker), app_name_("libtransport_application"), - consumer_callback_(nullptr), - producer_callback_(nullptr), + transport_callback_(nullptr), is_consumer_(false) { /** * This workaroung allows to initialize memory for packet buffers *before* @@ -235,58 +192,105 @@ class Portal { */ PacketManager<>::getInstance(); } + + public: + using TransportCallback = interface::Portal::TransportCallback; + friend class PortalConfiguration; + + static std::shared_ptr<Portal> createShared() { + return std::shared_ptr<Portal>(new Portal()); + } + + static std::shared_ptr<Portal> createShared(::utils::EventThread &worker) { + return std::shared_ptr<Portal>(new Portal(worker)); + } + + bool isConnected() const { return io_module_.get() != nullptr; } + /** - * Set the consumer callback. + * Set the transport callback. Must be called from the same worker thread. * - * @param consumer_callback - The pointer to the ConsumerCallback object. + * @param consumer_callback - The pointer to the TransportCallback object. */ - void setConsumerCallback(ConsumerCallback *consumer_callback) { - consumer_callback_ = consumer_callback; + void registerTransportCallback(TransportCallback *transport_callback) { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + transport_callback_ = transport_callback; } /** - * Set the producer callback. - * - * @param producer_callback - The pointer to the ProducerCallback object. + * Unset the consumer callback. Must be called from the same worker thread. */ - void setProducerCallback(ProducerCallback *producer_callback) { - producer_callback_ = producer_callback; + void unregisterTransportCallback() { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + transport_callback_ = nullptr; } /** - * Specify the output interface to use. This method will be useful in a future - * scenario where the library will be able to forward packets without + * Specify the output interface to use. This method will be useful in a + * future scenario where the library will be able to forward packets without * connecting to a local forwarder. Now it is not used. * * @param output_interface - The output interface to use for * forwarding/receiving packets. */ - TRANSPORT_ALWAYS_INLINE void setOutputInterface( - const std::string &output_interface) { - io_module_->setOutputInterface(output_interface); + void setOutputInterface(const std::string &output_interface) { + if (io_module_) { + io_module_->setOutputInterface(output_interface); + } } /** * Connect the transport to the local hicn forwarder. * - * @param is_consumer - Boolean specifying if the application on top of portal - * is a consumer or a producer. + * @param is_consumer - Boolean specifying if the application on top of + * portal is a consumer or a producer. */ - TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { - if (!io_module_) { - pending_interest_hash_table_.reserve(portal_details::pit_size); - io_module_.reset(IoModule::load(io_module_path_.c_str())); - - CHECK(io_module_); - - io_module_->init(std::bind(&Portal::processIncomingMessages, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - std::bind(&Portal::setLocalRoutes, this), io_service_, - app_name_); - io_module_->connect(is_consumer); - is_consumer_ = is_consumer; + void connect(bool is_consumer = true) { + if (isConnected()) { + return; } + + worker_.addAndWaitForExecution([this, is_consumer]() { + if (!io_module_) { + pending_interest_hash_table_.reserve(portal_details::pit_size); + io_module_.reset(IoModule::load(io_module_path_.c_str())); + + CHECK(io_module_); + + std::weak_ptr<Portal> self(shared_from_this()); + + io_module_->init( + [self](Connector *c, const std::vector<utils::MemBuf::Ptr> &buffers, + const std::error_code &ec) { + if (auto ptr = self.lock()) { + ptr->processIncomingMessages(c, buffers, ec); + } + }, + [self](Connector *c, const std::error_code &ec) { + if (!ec) { + return; + } + auto ptr = self.lock(); + if (ptr && ptr->transport_callback_) { + ptr->transport_callback_->onError(ec); + } + }, + [self](Connector *c, const std::error_code &ec) { + auto ptr = self.lock(); + if (ptr) { + if (ec && ptr->transport_callback_) { + ptr->transport_callback_->onError(ec); + return; + } + ptr->setLocalRoutes(); + } + }, + worker_.getIoService(), app_name_); + + io_module_->connect(is_consumer); + is_consumer_ = is_consumer; + } + }); } /** @@ -295,18 +299,13 @@ class Portal { ~Portal() { killConnection(); } /** - * Compute name hash - */ - TRANSPORT_ALWAYS_INLINE uint32_t getHash(const Name &name) { - return name.getHash32(false) + name.getSuffix(); - } - - /** * Check if there is already a pending interest for a given name. * * @param name - The interest name. */ - TRANSPORT_ALWAYS_INLINE bool interestIsPending(const Name &name) { + bool interestIsPending(const Name &name) { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + auto it = pending_interest_hash_table_.find(getHash(name)); if (it != pending_interest_hash_table_.end()) { return true; @@ -321,19 +320,21 @@ class Portal { * @param interest - The pointer to the interest. The ownership of the * interest is transferred by the caller to portal. * - * @param on_content_object_callback - If the caller wishes to use a different - * callback to be called for this interest, it can set this parameter. - * Otherwise ConsumerCallback::onContentObject will be used. + * @param on_content_object_callback - If the caller wishes to use a + * different callback to be called for this interest, it can set this + * parameter. Otherwise ConsumerCallback::onContentObject will be used. * * @param on_interest_timeout_callback - If the caller wishes to use a * different callback to be called for this interest, it can set this * parameter. Otherwise ConsumerCallback::onTimeout will be used. */ - TRANSPORT_ALWAYS_INLINE void sendInterest( + void sendInterest( Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object_callback = UNSET_CALLBACK, OnInterestTimeoutCallback &&on_interest_timeout_callback = UNSET_CALLBACK) { + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + // Send it interest->encodeSuffixes(); io_module_->send(*interest); @@ -346,35 +347,42 @@ class Portal { uint32_t counter = 0; // Set timers do { - auto pending_interest = packet_pool_.getPendingInterest(); - pending_interest->setInterest(interest); + if (suffix) { + hash = initial_hash + *suffix; + seq = *suffix; + suffix++; + } + + auto it = pending_interest_hash_table_.find(hash); + PendingInterest *pending_interest = nullptr; + if (it != pending_interest_hash_table_.end()) { + it->second.cancelTimer(); + pending_interest = &it->second; + pending_interest->setInterest(interest); + } else { + auto pend_int = pending_interest_hash_table_.try_emplace( + hash, worker_.getIoService(), interest); + pending_interest = &pend_int.first->second; + } + pending_interest->setOnContentObjectCallback( std::move(on_content_object_callback)); pending_interest->setOnTimeoutCallback( std::move(on_interest_timeout_callback)); + auto self = weak_from_this(); pending_interest->startCountdown( portal_details::makeCustomAllocatorHandler( async_callback_memory_, - std::bind(&Portal::timerHandler, this, std::placeholders::_1, - hash, seq))); + [self, hash, seq](const std::error_code &ec) { + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + return; + } - auto it = pending_interest_hash_table_.find(hash); - if (it != pending_interest_hash_table_.end()) { - it->second->cancelTimer(); - - // Get reference to interest packet in order to have it destroyed. - auto _int = it->second->getInterest(); - it->second = std::move(pending_interest); - } else { - pending_interest_hash_table_[hash] = std::move(pending_interest); - } - - if (suffix) { - hash = initial_hash + *suffix; - seq = *suffix; - suffix++; - } + if (auto ptr = self.lock()) { + ptr->timerHandler(hash, seq); + } + })); } while (counter++ < n_suffixes); } @@ -385,214 +393,174 @@ class Portal { * @param ec - Error code which says whether the timer expired or has been * canceled upon data packet reception. * - * @param hash - The index of the interest in the pending interest hash table. + * @param hash - The index of the interest in the pending interest hash + * table. */ - TRANSPORT_ALWAYS_INLINE void timerHandler(const std::error_code &ec, - uint32_t hash, uint32_t seq) { - bool is_stopped = io_service_.stopped(); - if (TRANSPORT_EXPECT_FALSE(is_stopped)) { - return; - } + void timerHandler(uint32_t hash, uint32_t seq) { + PendingInterestHashTable::iterator it = + pending_interest_hash_table_.find(hash); + if (it != pending_interest_hash_table_.end()) { + PendingInterest &pend_interest = it->second; + auto _int = pend_interest.getInterest(); + auto callback = pend_interest.getOnTimeoutCallback(); + pending_interest_hash_table_.erase(it); + Name &name = const_cast<Name &>(_int->getName()); + name.setSuffix(seq); - if (TRANSPORT_EXPECT_TRUE(!ec)) { - PendingInterestHashTable::iterator it = - pending_interest_hash_table_.find(hash); - if (it != pending_interest_hash_table_.end()) { - PendingInterest::Ptr ptr = std::move(it->second); - pending_interest_hash_table_.erase(it); - auto _int = ptr->getInterest(); - Name &name = const_cast<Name &>(_int->getName()); - name.setSuffix(seq); - - if (ptr->getOnTimeoutCallback() != UNSET_CALLBACK) { - ptr->on_interest_timeout_callback_(_int, name); - } else if (consumer_callback_) { - consumer_callback_->onTimeout(_int, name); - } + if (callback != UNSET_CALLBACK) { + callback(_int, name); + } else if (transport_callback_) { + transport_callback_->onTimeout(_int, name); } } } /** - * Register a producer name to the local forwarder and optionally set the - * content store size in a per-face manner. - * - * @param config - The configuration for the local forwarder binding. - */ - TRANSPORT_ALWAYS_INLINE void bind(const BindConfig &config) { - assert(io_module_); - io_module_->setContentStoreSize(config.csReserved()); - served_namespaces_.push_back(config.prefix()); - setLocalRoutes(); - } - - /** - * Start the event loop. This function blocks here and calls the callback set - * by the application upon interest/data received or timeout. - */ - TRANSPORT_ALWAYS_INLINE void runEventsLoop() { - if (io_service_.stopped()) { - io_service_.reset(); // ensure that run()/poll() will do some work - } - - io_service_.run(); - } - - /** - * Run one event and return. - */ - TRANSPORT_ALWAYS_INLINE void runOneEvent() { - if (io_service_.stopped()) { - io_service_.reset(); // ensure that run()/poll() will do some work - } - - io_service_.run_one(); - } - - /** - * Send a data packet to the local forwarder. As opposite to sendInterest, the - * ownership of the content object is not transferred to the portal. + * Send a data packet to the local forwarder. * * @param content_object - The data packet. */ - TRANSPORT_ALWAYS_INLINE void sendContentObject( - ContentObject &content_object) { + void sendContentObject(ContentObject &content_object) { + DCHECK(io_module_); + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + io_module_->send(content_object); } /** - * Stop the event loop, canceling all the pending events in the event queue. - * - * Beware that stopping the event loop DOES NOT disconnect the transport from - * the local forwarder, the connector underneath will stay connected. + * Disconnect the transport from the local forwarder. */ - TRANSPORT_ALWAYS_INLINE void stopEventsLoop() { - if (!io_service_.stopped()) { - io_service_.dispatch([this]() { - clear(); - io_service_.stop(); - }); + void killConnection() { + if (TRANSPORT_EXPECT_TRUE(io_module_ != nullptr)) { + io_module_->closeConnection(); } } /** - * Disconnect the transport from the local forwarder. + * Clear the pending interest hash table. */ - TRANSPORT_ALWAYS_INLINE void killConnection() { - io_module_->closeConnection(); + void clear() { + worker_.tryRunHandlerNow([self{shared_from_this()}]() { self->doClear(); }); } /** - * Clear the pending interest hash table. + * Get a reference to the io_service object. */ - TRANSPORT_ALWAYS_INLINE void clear() { - if (!io_service_.stopped()) { - io_service_.dispatch(std::bind(&Portal::doClear, this)); - } else { - doClear(); - } - } + utils::EventThread &getThread() { return worker_; } /** - * Remove one pending interest. + * Register a route to the local forwarder. */ - TRANSPORT_ALWAYS_INLINE void clearOne(const Name &name) { - if (!io_service_.stopped()) { - io_service_.dispatch(std::bind(&Portal::doClearOne, this, name)); - } else { - doClearOne(name); - } + void registerRoute(const Prefix &prefix) { + std::weak_ptr<Portal> self = shared_from_this(); + worker_.tryRunHandlerNow([self, prefix]() { + if (auto ptr = self.lock()) { + auto ret = ptr->served_namespaces_.insert(prefix); + if (ret.second && ptr->io_module_ && ptr->io_module_->isConnected()) { + ptr->io_module_->registerRoute(prefix); + } + } + }); } /** - * Get a reference to the io_service object. + * Send a MAP-Me update to traverse NATs. */ - TRANSPORT_ALWAYS_INLINE asio::io_service &getIoService() { - return io_service_; + TRANSPORT_ALWAYS_INLINE void sendMapme() { + if (io_module_->isConnected()) { + io_module_->sendMapme(); + } } /** - * Register a route to the local forwarder. + * set forwarding strategy */ - TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { - served_namespaces_.push_back(prefix); + TRANSPORT_ALWAYS_INLINE void setForwardingStrategy(Prefix &prefix, + std::string &strategy) { if (io_module_->isConnected()) { - io_module_->registerRoute(prefix); + io_module_->setForwardingStrategy(prefix, strategy); } } /** * Check if the transport is connected to a forwarder or not */ - TRANSPORT_ALWAYS_INLINE bool isConnectedToFwd() { + bool isConnectedToFwd() { std::string mod = io_module_path_.substr(0, io_module_path_.find(".")); if (mod == "forwarder_module") return false; return true; } + auto &getServedNamespaces() { return served_namespaces_; } + private: /** - * Clear the pending interest hash table. + * Compute name hash */ - TRANSPORT_ALWAYS_INLINE void doClear() { - for (auto &pend_interest : pending_interest_hash_table_) { - pend_interest.second->cancelTimer(); - - // Get interest packet from pending interest and do nothing with it. It - // will get destroyed as it goes out of scope. - auto _int = pend_interest.second->getInterest(); - } - - pending_interest_hash_table_.clear(); + uint32_t getHash(const Name &name) { + return name.getHash32(false) + name.getSuffix(); } /** - * Remove one pending interest. + * Clear the pending interest hash table. */ - TRANSPORT_ALWAYS_INLINE void doClearOne(const Name &name) { - auto it = pending_interest_hash_table_.find(getHash(name)); - - if (it != pending_interest_hash_table_.end()) { - it->second->cancelTimer(); - - // Get interest packet from pending interest and do nothing with it. It - // will get destroyed as it goes out of scope. - auto _int = it->second->getInterest(); - - pending_interest_hash_table_.erase(it); + void doClear() { + for (auto &pend_interest : pending_interest_hash_table_) { + pend_interest.second.cancelTimer(); } + + pending_interest_hash_table_.clear(); } /** - * Callback called by the underlying connector upon reception of a packet from - * the local forwarder. + * Callback called by the underlying connector upon reception of a packet + * from the local forwarder. * * @param packet_buffer - The bytes of the packet. */ - TRANSPORT_ALWAYS_INLINE void processIncomingMessages( - Connector *c, utils::MemBuf &buffer, const std::error_code &ec) { - bool is_stopped = io_service_.stopped(); - if (TRANSPORT_EXPECT_FALSE(is_stopped)) { + void processIncomingMessages(Connector *c, + const std::vector<utils::MemBuf::Ptr> &buffers, + const std::error_code &ec) { + if (!transport_callback_) { return; } - if (TRANSPORT_EXPECT_FALSE(io_module_->isControlMessage(buffer.data()))) { - processControlMessage(buffer); + if (TRANSPORT_EXPECT_FALSE(ec.operator bool())) { + // Error receiving from underlying infra. + if (transport_callback_) { + transport_callback_->onError(ec); + } + return; } - // The buffer is a base class for an interest or a content object - Packet &packet_buffer = static_cast<Packet &>(buffer); + for (auto &buffer_ptr : buffers) { + auto &buffer = *buffer_ptr; + + if (TRANSPORT_EXPECT_FALSE(io_module_->isControlMessage(buffer))) { + processControlMessage(buffer); + return; + } - auto format = packet_buffer.getFormat(); - if (TRANSPORT_EXPECT_TRUE(_is_tcp(format))) { - if (is_consumer_) { - processContentObject(static_cast<ContentObject &>(packet_buffer)); + auto format = Packet::getFormatFromBuffer(buffer.data(), buffer.length()); + if (TRANSPORT_EXPECT_TRUE(_is_cmpr(format) || _is_tcp(format))) { + // The buffer is a base class for an interest or a content object + Packet &packet_buffer = static_cast<Packet &>(buffer); + if (is_consumer_ && !packet_buffer.isInterest()) { + processContentObject(static_cast<ContentObject &>(packet_buffer)); + } else if (!is_consumer_ && packet_buffer.isInterest()) { + processInterest(static_cast<Interest &>(packet_buffer)); + } else { + auto packet_type = + packet_buffer.isInterest() ? "Interest" : "ContentObject"; + auto socket_type = is_consumer_ ? "consumer " : "producer "; + LOG(ERROR) << "Received a " << packet_type << " packet with name " + << packet_buffer.getName() << " in a " << socket_type + << " transport. Ignoring it."; + } } else { - processInterest(static_cast<Interest &>(packet_buffer)); + LOG(ERROR) << "Received not supported packet. Ignoring it."; } - } else { - LOG(ERROR) << "Received not supported packet. Ignoring it."; } } @@ -601,19 +569,21 @@ class Portal { * It register the prefixes in the served_namespaces_ list to the local * forwarder. */ - TRANSPORT_ALWAYS_INLINE void setLocalRoutes() { + void setLocalRoutes() { + DCHECK(io_module_); + DCHECK(io_module_->isConnected()); + DCHECK(std::this_thread::get_id() == worker_.getThreadId()); + for (auto &prefix : served_namespaces_) { - if (io_module_->isConnected()) { - io_module_->registerRoute(prefix); - } + io_module_->registerRoute(prefix); } } - TRANSPORT_ALWAYS_INLINE void processInterest(Interest &interest) { + void processInterest(Interest &interest) { // Interest for a producer DLOG_IF(INFO, VLOG_IS_ON(3)) << "processInterest " << interest.getName(); - if (TRANSPORT_EXPECT_TRUE(producer_callback_ != nullptr)) { - producer_callback_->onInterest(interest); + if (TRANSPORT_EXPECT_TRUE(transport_callback_ != nullptr)) { + transport_callback_->onInterest(interest); } } @@ -625,8 +595,7 @@ class Portal { * * @param content_object - The data packet */ - TRANSPORT_ALWAYS_INLINE void processContentObject( - ContentObject &content_object) { + void processContentObject(ContentObject &content_object) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "processContentObject " << content_object.getName(); uint32_t hash = getHash(content_object.getName()); @@ -635,15 +604,16 @@ class Portal { if (it != pending_interest_hash_table_.end()) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Found pending interest."; - PendingInterest::Ptr interest_ptr = std::move(it->second); + PendingInterest &pend_interest = it->second; + pend_interest.cancelTimer(); + auto _int = pend_interest.getInterest(); + auto callback = pend_interest.getOnDataCallback(); pending_interest_hash_table_.erase(it); - interest_ptr->cancelTimer(); - auto _int = interest_ptr->getInterest(); - if (interest_ptr->getOnDataCallback() != UNSET_CALLBACK) { - interest_ptr->on_content_object_callback_(*_int, content_object); - } else if (consumer_callback_) { - consumer_callback_->onContentObject(*_int, content_object); + if (callback != UNSET_CALLBACK) { + callback(*_int, content_object); + } else if (transport_callback_) { + transport_callback_->onContentObject(*_int, content_object); } } else { DLOG_IF(INFO, VLOG_IS_ON(3)) @@ -652,12 +622,11 @@ class Portal { } /** - * Process a control message. Control messages are different depending on the - * connector, then the forwarder_interface will do the job of understanding - * them. + * Process a control message. Control messages are different depending on + * the connector, then the forwarder_interface will do the job of + * understanding them. */ - TRANSPORT_ALWAYS_INLINE void processControlMessage( - utils::MemBuf &packet_buffer) { + void processControlMessage(utils::MemBuf &packet_buffer) { io_module_->processControlMessageReply(packet_buffer); } @@ -665,17 +634,14 @@ class Portal { portal_details::HandlerMemory async_callback_memory_; std::unique_ptr<IoModule, void (*)(IoModule *)> io_module_; - asio::io_service &io_service_; - asio::io_service internal_io_service_; - portal_details::Pool packet_pool_; + ::utils::EventThread &worker_; std::string app_name_; PendingInterestHashTable pending_interest_hash_table_; - std::list<Prefix> served_namespaces_; + std::set<Prefix> served_namespaces_; - ConsumerCallback *consumer_callback_; - ProducerCallback *producer_callback_; + TransportCallback *transport_callback_; bool is_consumer_; diff --git a/libtransport/src/core/prefix.cc b/libtransport/src/core/prefix.cc index d598cff75..4c1e191e9 100644 --- a/libtransport/src/core/prefix.cc +++ b/libtransport/src/core/prefix.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -99,6 +99,14 @@ void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, ip_prefix_.family = family; } +bool Prefix::operator<(const Prefix &other) const { + return ip_prefix_cmp(&ip_prefix_, &other.ip_prefix_) < 0; +} + +bool Prefix::operator==(const Prefix &other) const { + return ip_prefix_cmp(&ip_prefix_, &other.ip_prefix_) == 0; +} + std::unique_ptr<Sockaddr> Prefix::toSockaddr() const { Sockaddr *ret = nullptr; @@ -211,8 +219,6 @@ Name Prefix::getName(const core::Name &mask, const core::Name &components, } } - // if (this->contains(name_ip)) - // throw errors::RuntimeException("Mask overrides the prefix"); return Name(ip_prefix_.family, (uint8_t *)&name_ip); } @@ -279,8 +285,6 @@ Prefix &Prefix::setNetwork(std::string &network) { } Name Prefix::makeRandomName() const { - srand((unsigned int)time(nullptr)); - if (ip_prefix_.family == AF_INET6) { std::default_random_engine eng((std::random_device())()); std::uniform_int_distribution<uint32_t> idis( diff --git a/libtransport/src/core/tcp_socket_connector.cc b/libtransport/src/core/tcp_socket_connector.cc index a30264271..7758e2cf2 100644 --- a/libtransport/src/core/tcp_socket_connector.cc +++ b/libtransport/src/core/tcp_socket_connector.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -85,8 +85,10 @@ void TcpSocketConnector::send(const uint8_t *packet, std::size_t len, const PacketSentCallback &packet_sent) { if (packet_sent != 0) { asio::async_write(socket_, asio::buffer(packet, len), - [packet_sent](std::error_code ec, - std::size_t /*length*/) { packet_sent(); }); + [packet_sent](const std::error_code &ec) + std::size_t /*length*/) { + packet_sent(); + }); } else { if (state_ == ConnectorState::CONNECTED) { asio::write(socket_, asio::buffer(packet, len)); @@ -151,7 +153,7 @@ void TcpSocketConnector::doWrite() { asio::async_write( socket_, std::move(array), - [this, packet_store = std::move(packet_store)](std::error_code ec, + [this, packet_store = std::move(packet_store)](const std::error_code &ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { if (!output_buffer_.empty()) { @@ -172,7 +174,7 @@ void TcpSocketConnector::doReadBody(std::size_t body_length) { asio::async_read( socket_, asio::buffer(read_msg_->writableTail(), body_length), asio::transfer_exactly(body_length), - [this](std::error_code ec, std::size_t length) { + [this](const std::error_code &ec, std::size_t length) { read_msg_->append(length); if (TRANSPORT_EXPECT_TRUE(!ec)) { receive_callback_(std::move(read_msg_)); @@ -195,7 +197,7 @@ void TcpSocketConnector::doReadHeader() { asio::buffer(read_msg_->writableData(), NetworkMessage::fixed_header_length), asio::transfer_exactly(NetworkMessage::fixed_header_length), - [this](std::error_code ec, std::size_t length) { + [this](const std::error_code &ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { read_msg_->append(NetworkMessage::fixed_header_length); std::size_t body_length = 0; @@ -235,7 +237,7 @@ void TcpSocketConnector::tryReconnect() { void TcpSocketConnector::doConnect() { asio::async_connect( socket_, endpoint_iterator_, - [this](std::error_code ec, tcp::resolver::iterator) { + [this](const std::error_code &ec, tcp::resolver::iterator) { if (!ec) { timer_.cancel(); state_ = ConnectorState::CONNECTED; diff --git a/libtransport/src/core/tcp_socket_connector.h b/libtransport/src/core/tcp_socket_connector.h index 21db8301e..2901a5c9d 100644 --- a/libtransport/src/core/tcp_socket_connector.h +++ b/libtransport/src/core/tcp_socket_connector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/core/udp_connector.cc b/libtransport/src/core/udp_connector.cc new file mode 100644 index 000000000..ee0c7ea9c --- /dev/null +++ b/libtransport/src/core/udp_connector.cc @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#include <core/errors.h> +#include <core/udp_connector.h> +#include <glog/logging.h> +#include <hicn/transport/utils/branch_prediction.h> + +#include <iostream> +#include <thread> +#include <vector> + +namespace transport { +namespace core { + +UdpTunnelConnector::~UdpTunnelConnector() {} + +void UdpTunnelConnector::connect(const std::string &hostname, uint16_t port, + const std::string &bind_address, + uint16_t bind_port) { + if (state_ == State::CLOSED) { + state_ = State::CONNECTING; + + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), hostname, + std::to_string(port)); + + endpoint_iterator_ = resolver_.resolve(query); + remote_endpoint_send_ = *endpoint_iterator_; + socket_->open(remote_endpoint_send_.protocol()); + + if (!bind_address.empty() && bind_port != 0) { + using namespace asio::ip; + + auto address = address::from_string(bind_address); + if (address.is_v6()) { + std::error_code ec; + socket_->set_option(asio::ip::v6_only(false), ec); + // Call succeeds only on dual stack systems. + } + + socket_->bind(udp::endpoint(address, bind_port)); + } + + remote_endpoint_ = Endpoint(remote_endpoint_send_); + local_endpoint_ = Endpoint(socket_->local_endpoint()); + + auto self = shared_from_this(); + doConnect(self); + } +} + +void UdpTunnelConnector::send(Packet &packet) { + send(packet.shared_from_this()); +} + +void UdpTunnelConnector::send(const utils::MemBuf::Ptr &buffer) { + auto self = shared_from_this(); + io_service_.post([self, pkt{buffer}]() { + bool write_in_progress = !self->output_buffer_.empty(); + self->output_buffer_.push_back(std::move(pkt)); + if (TRANSPORT_EXPECT_TRUE(self->state_ == State::CONNECTED)) { + if (!write_in_progress) { + self->doSendPacket(self); + } + } else { + self->data_available_ = true; + } + }); +} + +void UdpTunnelConnector::close() { + DLOG_IF(INFO, VLOG_IS_ON(2)) << "UDPTunnelConnector::close"; + state_ = State::CLOSED; + bool is_socket_owned = socket_.use_count() == 1; + if (is_socket_owned) { + // Here we use a shared ptr to keep the object alive until we call the close + // function + auto self = shared_from_this(); + io_service_.dispatch([this, self]() { + socket_->close(); + // on_close_callback_(shared_from_this()); + }); + } +} + +void UdpTunnelConnector::doSendPacket( + const std::shared_ptr<UdpTunnelConnector> &self) { +#ifdef LINUX + send_timer_.expires_from_now(std::chrono::microseconds(50)); + send_timer_.async_wait([self](const std::error_code &ec) { + if (ec) { + return; + } + + self->writeHandler(); + }); +#else + auto packet = output_buffer_.front().get(); + auto array = std::vector<asio::const_buffer>(); + + const ::utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_->async_send_to( + std::move(array), remote_endpoint_send_, + [this, self](const std::error_code &ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + sent_callback_(this, make_error_code(core_error::success)); + } else if (ec.value() == + static_cast<int>(std::errc::operation_canceled)) { + // The connection has been closed by the application. + return; + } else { + sendFailed(); + sent_callback_(this, ec); + } + + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doSendPacket(self); + } + }); +#endif +} + +void UdpTunnelConnector::retryConnection() { + // The connection was refused. In this case let's retry to reconnect. + connection_reattempts_++; + LOG(ERROR) << "Error in UDP: Connection refused. Retrying..."; + state_ = State::CONNECTING; + timer_.expires_from_now(std::chrono::milliseconds(500)); + std::weak_ptr<UdpTunnelConnector> self = shared_from_this(); + timer_.async_wait([self, this](const std::error_code &ec) { + if (ec) { + } + if (auto ptr = self.lock()) { + doConnect(ptr); + } + }); + return; +} + +#ifdef LINUX +void UdpTunnelConnector::writeHandler() { + if (TRANSPORT_EXPECT_FALSE(state_ != State::CONNECTED)) { + return; + } + + auto len = std::min(output_buffer_.size(), std::size_t(Connector::max_burst)); + + if (len) { + int m = 0; + for (auto &p : output_buffer_) { + auto packet = p.get(); + ::utils::MemBuf *current = packet; + int b = 0; + do { + // array.push_back(asio::const_buffer(current->data(), + // current->length())); + tx_iovecs_[m][b].iov_base = current->writableData(); + tx_iovecs_[m][b].iov_len = current->length(); + current = current->next(); + b++; + } while (current != packet); + + tx_msgs_[m].msg_hdr.msg_iov = tx_iovecs_[m]; + tx_msgs_[m].msg_hdr.msg_iovlen = b; + tx_msgs_[m].msg_hdr.msg_name = remote_endpoint_send_.data(); + tx_msgs_[m].msg_hdr.msg_namelen = remote_endpoint_send_.size(); + m++; + + if (--len == 0) { + break; + } + } + + int retval = sendmmsg(socket_->native_handle(), tx_msgs_, m, MSG_DONTWAIT); + if (retval > 0) { + while (retval--) { + output_buffer_.pop_front(); + } + } else if (errno != EWOULDBLOCK && errno != EAGAIN) { + LOG(ERROR) << "Error sending messages: " << strerror(errno); + sent_callback_(this, make_error_code(core_error::send_failed)); + return; + } + } + + if (!output_buffer_.empty()) { + send_timer_.expires_from_now(std::chrono::microseconds(50)); + std::weak_ptr<UdpTunnelConnector> self = shared_from_this(); + send_timer_.async_wait([self](const std::error_code &ec) { + if (ec) { + return; + } + if (auto ptr = self.lock()) { + ptr->writeHandler(); + } + }); + } +} + +void UdpTunnelConnector::readHandler(const std::error_code &ec) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; + + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + if (current_position_ == 0) { + for (int i = 0; i < max_burst; i++) { + auto read_buffer = getRawBuffer(); + rx_iovecs_[i][0].iov_base = read_buffer.first; + rx_iovecs_[i][0].iov_len = read_buffer.second; + rx_msgs_[i].msg_hdr.msg_iov = rx_iovecs_[i]; + rx_msgs_[i].msg_hdr.msg_iovlen = 1; + } + } + + int res = recvmmsg(socket_->native_handle(), rx_msgs_ + current_position_, + max_burst - current_position_, MSG_DONTWAIT, nullptr); + if (res < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + // Try again later + return; + } + + if (errno == ECONNREFUSED && + connection_reattempts_ < max_reconnection_reattempts) { + retryConnection(); + return; + } + + LOG(ERROR) << "Error receiving messages! " << strerror(errno) << " " + << res; + std::vector<utils::MemBuf::Ptr> v; + auto ec = make_error_code(core_error::receive_failed); + + receive_callback_(this, v, ec); + return; + } + + std::vector<utils::MemBuf::Ptr> v; + v.reserve(res); + for (int i = 0; i < res; i++) { + auto packet = getPacketFromBuffer( + reinterpret_cast<uint8_t *>( + rx_msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), + rx_msgs_[current_position_].msg_len); + receiveSuccess(*packet); + v.push_back(std::move(packet)); + ++current_position_; + } + + receive_callback_(this, v, make_error_code(core_error::success)); + + doRecvPacket(); + } else { + LOG(ERROR) << "Error in UDP: Receiving packets from a not " + "connected socket."; + } + } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { + LOG(ERROR) << "The connection has been closed by the application."; + return; + } else { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + // receive_callback_(this, *read_msg_, ec); + LOG(ERROR) << "Error in UDP connector: " << ec.value() << " " + << ec.message(); + } else { + LOG(ERROR) << "Error in connector while not connected. " << ec.value() + << " " << ec.message(); + } + } +} +#endif + +void UdpTunnelConnector::doRecvPacket() { + std::weak_ptr<UdpTunnelConnector> self = shared_from_this(); +#ifdef LINUX + if (state_ == State::CONNECTED) { +#if ((ASIO_VERSION / 100 % 1000) < 11) + socket_->async_receive(asio::null_buffers(), +#else + socket_->async_wait(asio::ip::tcp::socket::wait_read, +#endif + [self](const std::error_code &ec) { + if (ec) { + LOG(ERROR) + << "Error in UDP connector: " << ec.value() + << " " << ec.message(); + return; + } + if (auto ptr = self.lock()) { + ptr->readHandler(ec); + } + }); + } +#else + DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; + read_msg_ = getRawBuffer(); + socket_->async_receive_from( + asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_recv_, + [this, self](const std::error_code &ec, std::size_t length) { + if (auto ptr = self.lock()) { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "UdpTunnelConnector received packet length=" << length; + if (TRANSPORT_EXPECT_TRUE(!ec)) { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + auto packet = getPacketFromBuffer(read_msg_.first, length); + receiveSuccess(*packet); + std::vector<utils::MemBuf::Ptr> v{std::move(packet)}; + receive_callback_(this, v, make_error_code(core_error::success)); + doRecvPacket(); + } else { + LOG(ERROR) << "Error in UDP: Receiving packets from a not " + "connected socket."; + } + } else if (ec.value() == + static_cast<int>(std::errc::operation_canceled)) { + LOG(ERROR) << "The connection has been closed by the application."; + return; + } else if (ec.value() == + static_cast<int>(std::errc::connection_refused)) { + if (connection_reattempts_ < max_reconnection_reattempts) { + retryConnection(); + } + } else { + if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { + LOG(ERROR) << "Error in UDP connector: " << ec.value() + << ec.message(); + } else { + LOG(ERROR) << "Error while not connected"; + } + } + } + }); +#endif +} + +void UdpTunnelConnector::doConnect( + std::shared_ptr<UdpTunnelConnector> &self_shared) { + std::weak_ptr<UdpTunnelConnector> self = self_shared; + asio::async_connect(*socket_, endpoint_iterator_, + [this, self](const std::error_code &ec, + asio::ip::udp::resolver::iterator) { + if (auto ptr = self.lock()) { + if (!ec) { + state_ = State::CONNECTED; + doRecvPacket(); + + if (data_available_) { + data_available_ = false; + doSendPacket(ptr); + } + + on_reconnect_callback_( + this, make_error_code(core_error::success)); + } else { + LOG(ERROR) << "UDP Connection failed!!!"; + retryConnection(); + } + } + }); +} + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel.h b/libtransport/src/core/udp_connector.h index 4f044f93f..65821852d 100644 --- a/libtransport/src/io_modules/forwarder/udp_tunnel.h +++ b/libtransport/src/core/udp_connector.h @@ -1,13 +1,13 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #pragma once +#include <core/errors.h> #include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/connector.h> #include <hicn/transport/portability/platform.h> -#include <io_modules/forwarder/errors.h> #include <iostream> #include <memory> @@ -30,16 +30,15 @@ class UdpTunnelConnector : public Connector { : Connector(receive_callback, packet_sent, on_close_callback, on_reconnect), io_service_(io_service), - strand_(std::make_shared<asio::io_service::strand>(io_service_)), socket_(std::make_shared<asio::ip::udp::socket>(io_service_)), resolver_(io_service_), timer_(io_service_), #ifdef LINUX send_timer_(io_service_), - tx_iovecs_{0}, - tx_msgs_{0}, - rx_iovecs_{0}, - rx_msgs_{0}, + tx_iovecs_{}, + tx_msgs_{}, + rx_iovecs_{}, + rx_msgs_{}, current_position_(0), #else read_msg_(nullptr, 0), @@ -61,17 +60,16 @@ class UdpTunnelConnector : public Connector { #else io_service_((asio::io_context &)(socket->get_executor().context())), #endif - strand_(strand), socket_(socket), resolver_(io_service_), remote_endpoint_send_(std::forward<EndpointType &&>(remote_endpoint)), timer_(io_service_), #ifdef LINUX send_timer_(io_service_), - tx_iovecs_{0}, - tx_msgs_{0}, - rx_iovecs_{0}, - rx_msgs_{0}, + tx_iovecs_{}, + tx_msgs_{}, + rx_iovecs_{}, + rx_msgs_{}, current_position_(0), #else read_msg_(nullptr, 0), @@ -88,7 +86,7 @@ class UdpTunnelConnector : public Connector { void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; void close() override; @@ -99,26 +97,27 @@ class UdpTunnelConnector : public Connector { auto shared_from_this() { return utils::shared_from(this); } private: - void doConnect(); + void retryConnection(); + void doConnect(std::shared_ptr<UdpTunnelConnector> &self); void doRecvPacket(); - void doRecvPacket(utils::MemBuf &buffer) { - receive_callback_(this, buffer, make_error_code(forwarder_error::success)); + void doRecvPacket(utils::MemBuf::Ptr &buffer) { + std::vector<utils::MemBuf::Ptr> v{std::move(buffer)}; + receive_callback_(this, v, make_error_code(core_error::success)); } #ifdef LINUX - void readHandler(std::error_code ec); - void writeHandler(std::error_code ec); + void readHandler(const std::error_code &ec); + void writeHandler(); #endif void setConnected() { state_ = State::CONNECTED; } - void doSendPacket(); + void doSendPacket(const std::shared_ptr<UdpTunnelConnector> &self); void doClose(); private: asio::io_service &io_service_; - std::shared_ptr<asio::io_service::strand> strand_; std::shared_ptr<asio::ip::udp::socket> socket_; asio::ip::udp::resolver resolver_; asio::ip::udp::resolver::iterator endpoint_iterator_; diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc b/libtransport/src/core/udp_listener.cc index d047cc568..c67673392 100644 --- a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.cc +++ b/libtransport/src/core/udp_listener.cc @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ +#include <core/udp_connector.h> +#include <core/udp_listener.h> #include <glog/logging.h> #include <hicn/transport/utils/hash.h> -#include <io_modules/forwarder/udp_tunnel.h> -#include <io_modules/forwarder/udp_tunnel_listener.h> #ifndef LINUX namespace std { @@ -35,7 +35,7 @@ void UdpTunnelListener::close() { } #ifdef LINUX -void UdpTunnelListener::readHandler(std::error_code ec) { +void UdpTunnelListener::readHandler(const std::error_code &ec) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; if (TRANSPORT_EXPECT_TRUE(!ec)) { @@ -102,7 +102,8 @@ void UdpTunnelListener::readHandler(std::error_code ec) { std::make_shared<UdpTunnelConnector>( socket_, strand_, receive_callback_, [](Connector *, const std::error_code &) {}, [](Connector *) {}, - [](Connector *) {}, std::move(remote_endpoint_))); + [](Connector *, const std::error_code &) {}, + std::move(remote_endpoint_))); connector = ret.first; connector->second->setConnectorId(connector_id); } @@ -112,7 +113,7 @@ void UdpTunnelListener::readHandler(std::error_code ec) { */ UdpTunnelConnector *c = dynamic_cast<UdpTunnelConnector *>(connector->second.get()); - c->doRecvPacket(*packet); + c->doRecvPacket(packet); ++current_position_; } @@ -141,7 +142,7 @@ void UdpTunnelListener::doRecvPacket() { read_msg_ = Connector::getRawBuffer(); socket_->async_receive_from( asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_, - [this](std::error_code ec, std::size_t length) { + [this](const std::error_code &ec, std::size_t length) { if (TRANSPORT_EXPECT_TRUE(!ec)) { auto packet = Connector::getPacketFromBuffer(read_msg_.first, length); auto connector_id = @@ -153,7 +154,8 @@ void UdpTunnelListener::doRecvPacket() { connector_id, std::make_shared<UdpTunnelConnector>( socket_, strand_, receive_callback_, [](Connector *, const std::error_code &) {}, - [](Connector *) {}, [](Connector *) {}, + [](Connector *) {}, + [](Connector *, const std::error_code &) {}, std::move(remote_endpoint_))); connector = ret.first; connector->second->setConnectorId(connector_id); @@ -161,7 +163,7 @@ void UdpTunnelListener::doRecvPacket() { UdpTunnelConnector *c = dynamic_cast<UdpTunnelConnector *>(connector->second.get()); - c->doRecvPacket(*packet); + c->doRecvPacket(packet); doRecvPacket(); } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h b/libtransport/src/core/udp_listener.h index 5d197dcb0..813520309 100644 --- a/libtransport/src/io_modules/forwarder/udp_tunnel_listener.h +++ b/libtransport/src/core/udp_listener.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #pragma once @@ -44,8 +44,8 @@ class UdpTunnelListener #ifndef LINUX read_msg_(nullptr, 0) #else - iovecs_{0}, - msgs_{0}, + iovecs_{}, + msgs_{}, current_position_(0) #endif { @@ -83,7 +83,7 @@ class UdpTunnelListener private: void doRecvPacket(); - void readHandler(std::error_code ec); + void readHandler(const std::error_code &ec); asio::io_service &io_service_; std::shared_ptr<asio::io_service::strand> strand_; diff --git a/libtransport/src/http/CMakeLists.txt b/libtransport/src/http/CMakeLists.txt index 2407faea3..0060f8714 100644 --- a/libtransport/src/http/CMakeLists.txt +++ b/libtransport/src/http/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: diff --git a/libtransport/src/http/client_connection.cc b/libtransport/src/http/client_connection.cc index b4ab7cbc3..05fa3e335 100644 --- a/libtransport/src/http/client_connection.cc +++ b/libtransport/src/http/client_connection.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -77,9 +77,7 @@ class HTTPClientConnection::Implementation auto end = std::chrono::steady_clock::now(); LOG(INFO) << method_map[method].c_str() << " " << url.c_str() << " [" << name_.str() << "] duration: " - << std::chrono::duration_cast<std::chrono::microseconds>(end - - start) - .count() + << utils::SteadyTime::getDurationUs(start, end).count() << " [usec] " << size << " [bytes]"; }; @@ -106,7 +104,7 @@ class HTTPClientConnection::Implementation HTTPClientConnection &setTimeout(const std::chrono::seconds &timeout) { timer_->cancel(); timer_->expires_from_now(timeout); - timer_->async_wait([this](std::error_code ec) { + timer_->async_wait([this](const std::error_code &ec) { if (!ec) { consumer_.stop(); } @@ -147,18 +145,14 @@ class HTTPClientConnection::Implementation name_ << ipv6_first_word << ":"; - for (uint16_t *word = (uint16_t *)&locator_hash; - std::size_t(word) < - (std::size_t(&locator_hash) + sizeof(locator_hash)); - word++) { - name_ << ":" << std::hex << *word; + uint16_t *word = (uint16_t *)(&locator_hash); + for (std::size_t i = 0; i < sizeof(locator_hash) / 2; i++) { + name_ << ":" << std::hex << word[i]; } - for (uint16_t *word = (uint16_t *)&request_hash; - std::size_t(word) < - (std::size_t(&request_hash) + sizeof(request_hash)); - word++) { - name_ << ":" << std::hex << *word; + word = (uint16_t *)(&request_hash); + for (std::size_t i = 0; i < sizeof(request_hash) / 2; i++) { + name_ << ":" << std::hex << word[i]; } name_ << "|0"; @@ -199,7 +193,7 @@ class HTTPClientConnection::Implementation } } - void readError(const std::error_code ec) noexcept override { + void readError(const std::error_code &ec) noexcept override { LOG(ERROR) << "Error " << ec.message() << " during download of " << current_url_.c_str(); if (read_bytes_callback_) { diff --git a/libtransport/src/http/request.cc b/libtransport/src/http/request.cc index 29118fd88..ad726741a 100644 --- a/libtransport/src/http/request.cc +++ b/libtransport/src/http/request.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/http/response.cc b/libtransport/src/http/response.cc index c665fbc5f..727e2c755 100644 --- a/libtransport/src/http/response.cc +++ b/libtransport/src/http/response.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/implementation/CMakeLists.txt b/libtransport/src/implementation/CMakeLists.txt index daf899d06..1f2a33a4c 100644 --- a/libtransport/src/implementation/CMakeLists.txt +++ b/libtransport/src/implementation/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.cc b/libtransport/src/implementation/p2psecure_socket_consumer.cc index 4b14da5d2..6b67a5487 100644 --- a/libtransport/src/implementation/p2psecure_socket_consumer.cc +++ b/libtransport/src/implementation/p2psecure_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -44,7 +44,7 @@ int readOld(BIO *b, char *buf, int size) { if (!socket->something_to_read_) { if (!socket->transport_protocol_->isRunning()) { socket->network_name_.setSuffix(socket->random_suffix_); - socket->ConsumerSocket::asyncConsume(socket->network_name_); + socket->ConsumerSocket::consume(socket->network_name_); } if (!socket->something_to_read_) socket->cv_.wait(lck); @@ -312,36 +312,6 @@ int P2PSecureConsumerSocket::consume(const Name &name) { return tls_consumer_->consume((prefix->mapName(name))); } -int P2PSecureConsumerSocket::asyncConsume(const Name &name) { - if (transport_protocol_->isRunning()) { - return CONSUMER_BUSY; - } - - if (handshake() != 1) { - throw errors::RuntimeException("Unable to perform client handshake"); - } else { - DLOG_IF(INFO, VLOG_IS_ON(2)) << "Handshake performed!"; - } - - initSessionSocket(); - - if (tls_consumer_ == nullptr) { - throw errors::RuntimeException("TLS socket does not exist"); - } - - std::shared_ptr<Name> prefix_name = std::make_shared<Name>( - secure_prefix_.family, - ip_address_get_buffer(&(secure_prefix_.address), secure_prefix_.family)); - std::shared_ptr<Prefix> prefix = - std::make_shared<Prefix>(*prefix_name, secure_prefix_.len); - - if (payload_ != NULL) - return tls_consumer_->asyncConsume((prefix->mapName(name)), - std::move(payload_)); - else - return tls_consumer_->asyncConsume((prefix->mapName(name))); -} - void P2PSecureConsumerSocket::registerPrefix(const Prefix &producer_namespace) { producer_namespace_ = producer_namespace; } @@ -385,7 +355,7 @@ void P2PSecureConsumerSocket::readBufferAvailable( cv_.notify_one(); } -void P2PSecureConsumerSocket::readError(const std::error_code ec) noexcept {}; +void P2PSecureConsumerSocket::readError(const std::error_code &ec) noexcept {}; void P2PSecureConsumerSocket::readSuccess(std::size_t total_size) noexcept { std::unique_lock<std::mutex> lck(this->mtx_); diff --git a/libtransport/src/implementation/p2psecure_socket_consumer.h b/libtransport/src/implementation/p2psecure_socket_consumer.h index a35a50352..a5e69f611 100644 --- a/libtransport/src/implementation/p2psecure_socket_consumer.h +++ b/libtransport/src/implementation/p2psecure_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -48,8 +48,6 @@ class P2PSecureConsumerSocket : public ConsumerSocket, int consume(const Name &name) override; - int asyncConsume(const Name &name) override; - void registerPrefix(const Prefix &producer_namespace); int setSocketOption( @@ -120,7 +118,7 @@ class P2PSecureConsumerSocket : public ConsumerSocket, virtual void readBufferAvailable( std::unique_ptr<utils::MemBuf> &&buffer) noexcept override; - virtual void readError(const std::error_code ec) noexcept override; + virtual void readError(const std::error_code &ec) noexcept override; virtual void readSuccess(std::size_t total_size) noexcept override; diff --git a/libtransport/src/implementation/p2psecure_socket_producer.cc b/libtransport/src/implementation/p2psecure_socket_producer.cc index 3748001fc..ee78ea53b 100644 --- a/libtransport/src/implementation/p2psecure_socket_producer.cc +++ b/libtransport/src/implementation/p2psecure_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -20,6 +20,7 @@ #include <interfaces/tls_rtc_socket_producer.h> #include <interfaces/tls_socket_producer.h> #include <openssl/bio.h> +#include <openssl/pkcs12.h> #include <openssl/rand.h> #include <openssl/ssl.h> @@ -41,7 +42,7 @@ P2PSecureProducerSocket::P2PSecureProducerSocket( P2PSecureProducerSocket::P2PSecureProducerSocket( interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr<auth::Identity> &identity) + std::string &keystore_path, std::string &keystore_pwd) : ProducerSocket(producer_socket, ProductionProtocolAlgorithms::BYTE_STREAM), rtc_(rtc), @@ -50,8 +51,16 @@ P2PSecureProducerSocket::P2PSecureProducerSocket( map_producers(), list_producers() { /* Setup SSL context (identity and parameter to use TLS 1.3) */ - cert_509_ = identity->getCertificate().get(); - pkey_rsa_ = identity->getPrivateKey().get(); + FILE *p12file = fopen(keystore_path.c_str(), "r"); + if (p12file == NULL) + throw errors::RuntimeException("impossible open keystore"); + std::unique_ptr<PKCS12, decltype(&::PKCS12_free)> p12( + d2i_PKCS12_fp(p12file, NULL), ::PKCS12_free); + // now we parse the file to get the first key and certificate + if (1 != PKCS12_parse(p12.get(), keystore_pwd.c_str(), &pkey_rsa_, &cert_509_, + NULL)) + throw errors::RuntimeException("impossible to get the private key"); + fclose(p12file); /* Set the callback so that when an interest is received we catch it and we * decrypt the payload before passing it to the application. */ @@ -133,15 +142,15 @@ uint32_t P2PSecureProducerSocket::produceDatagram( // TODO throw errors::NotImplementedException(); - if (!rtc_) { - throw errors::RuntimeException( - "RTC must be the transport protocol to start the production of current " - "data. Aborting."); - } + // if (!rtc_) { + // throw errors::RuntimeException( + // "RTC must be the transport protocol to start the production of + // current " "data. Aborting."); + // } - std::unique_lock<std::mutex> lck(mtx_); + // std::unique_lock<std::mutex> lck(mtx_); - if (list_producers.empty()) cv_.wait(lck); + // if (list_producers.empty()) cv_.wait(lck); // TODO // for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) @@ -151,7 +160,7 @@ uint32_t P2PSecureProducerSocket::produceDatagram( // rtc_producer->produce(utils::MemBuf::copyBuffer(buffer, buffer_size)); // } - return 0; + // return 0; } uint32_t P2PSecureProducerSocket::produceStream( @@ -197,44 +206,6 @@ uint32_t P2PSecureProducerSocket::produceStream(const Name &content_name, return segments; } -// void P2PSecureProducerSocket::asyncProduce(const Name &content_name, -// const uint8_t *buf, -// size_t buffer_size, bool is_last, -// uint32_t *start_offset) { -// if (rtc_) { -// throw errors::RuntimeException( -// "RTC transport protocol is not compatible with the production of " -// "current data. Aborting."); -// } - -// std::unique_lock<std::mutex> lck(mtx_); -// if (list_producers.empty()) cv_.wait(lck); - -// for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) -// { -// (*it)->asyncProduce(content_name, buf, buffer_size, is_last, -// start_offset); -// } -// } - -void P2PSecureProducerSocket::asyncProduce( - Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, bool is_last, - uint32_t offset, uint32_t **last_segment) { - if (rtc_) { - throw errors::RuntimeException( - "RTC transport protocol is not compatible with the production of " - "current data. Aborting."); - } - - std::unique_lock<std::mutex> lck(mtx_); - if (list_producers.empty()) cv_.wait(lck); - - for (auto it = list_producers.cbegin(); it != list_producers.cend(); it++) { - (*it)->asyncProduce(content_name, buffer->clone(), is_last, offset, - last_segment); - } -} - /* Redefinition of socket options to avoid name hiding */ int P2PSecureProducerSocket::setSocketOption( int socket_option_key, ProducerInterestCallback socket_option_value) { diff --git a/libtransport/src/implementation/p2psecure_socket_producer.h b/libtransport/src/implementation/p2psecure_socket_producer.h index f94347258..00f407a75 100644 --- a/libtransport/src/implementation/p2psecure_socket_producer.h +++ b/libtransport/src/implementation/p2psecure_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,7 +15,6 @@ #pragma once -#include <hicn/transport/auth/identity.h> #include <hicn/transport/auth/signer.h> #include <implementation/socket_producer.h> // #include <implementation/tls_rtc_socket_producer.h> @@ -38,9 +37,9 @@ class P2PSecureProducerSocket : public ProducerSocket { public: explicit P2PSecureProducerSocket(interface::ProducerSocket *producer_socket); - explicit P2PSecureProducerSocket( - interface::ProducerSocket *producer_socket, bool rtc, - const std::shared_ptr<auth::Identity> &identity); + explicit P2PSecureProducerSocket(interface::ProducerSocket *producer_socket, + bool rtc, std::string &keystore_path, + std::string &keystore_pwd); ~P2PSecureProducerSocket(); @@ -56,10 +55,6 @@ class P2PSecureProducerSocket : public ProducerSocket { bool is_last = true, uint32_t start_offset = 0) override; - void asyncProduce(Name content_name, std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr) override; - int setSocketOption(int socket_option_key, ProducerInterestCallback socket_option_value) override; diff --git a/libtransport/src/implementation/socket.cc b/libtransport/src/implementation/socket.cc index 2e21f2bc3..95941da07 100644 --- a/libtransport/src/implementation/socket.cc +++ b/libtransport/src/implementation/socket.cc @@ -14,13 +14,42 @@ */ #include <core/global_configuration.h> +#include <hicn/transport/interfaces/socket_options_default_values.h> #include <implementation/socket.h> namespace transport { namespace implementation { Socket::Socket(std::shared_ptr<core::Portal> &&portal) - : portal_(std::move(portal)), is_async_(false) {} + : portal_(std::move(portal)), + is_async_(false), + packet_format_(interface::default_values::packet_format) {} + +int Socket::setSocketOption(int socket_option_key, + hicn_format_t packet_format) { + switch (socket_option_key) { + case interface::GeneralTransportOptions::PACKET_FORMAT: + packet_format_ = packet_format; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; +} + +int Socket::getSocketOption(int socket_option_key, + hicn_format_t &packet_format) { + switch (socket_option_key) { + case interface::GeneralTransportOptions::PACKET_FORMAT: + packet_format = packet_format_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; +} } // namespace implementation } // namespace transport
\ No newline at end of file diff --git a/libtransport/src/implementation/socket.h b/libtransport/src/implementation/socket.h index cf22c03e1..11c9a704d 100644 --- a/libtransport/src/implementation/socket.h +++ b/libtransport/src/implementation/socket.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -38,7 +38,26 @@ class Socket { virtual void connect() = 0; virtual bool isRunning() = 0; - virtual asio::io_service &getIoService() { return portal_->getIoService(); } + virtual asio::io_service &getIoService() { + return portal_->getThread().getIoService(); + } + + int setSocketOption(int socket_option_key, hicn_format_t packet_format); + int getSocketOption(int socket_option_key, hicn_format_t &packet_format); + + int getSocketOption(int socket_option_key, + std::shared_ptr<core::Portal> &socket_option_value) { + switch (socket_option_key) { + case interface::GeneralTransportOptions::PORTAL: + socket_option_value = portal_; + break; + default: + return SOCKET_OPTION_NOT_GET; + ; + } + + return SOCKET_OPTION_GET; + } protected: Socket(std::shared_ptr<core::Portal> &&portal); @@ -48,6 +67,7 @@ class Socket { protected: std::shared_ptr<core::Portal> portal_; bool is_async_; + hicn_format_t packet_format_; }; } // namespace implementation diff --git a/libtransport/src/implementation/socket_consumer.h b/libtransport/src/implementation/socket_consumer.h index e0981af7f..ebdac7f93 100644 --- a/libtransport/src/implementation/socket_consumer.h +++ b/libtransport/src/implementation/socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -20,6 +20,7 @@ #include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/interfaces/statistics.h> #include <hicn/transport/utils/event_thread.h> +#include <implementation/socket.h> #include <protocols/cbr.h> #include <protocols/raaqm.h> #include <protocols/rtc/rtc.h> @@ -38,7 +39,6 @@ class ConsumerSocket : public Socket { std::shared_ptr<core::Portal> &&portal) : Socket(std::move(portal)), consumer_interface_(consumer), - async_downloader_(), interest_lifetime_(default_values::interest_lifetime), min_window_size_(default_values::min_window_size), max_window_size_(default_values::max_window_size), @@ -56,6 +56,7 @@ class ConsumerSocket : public Socket { rate_estimation_observer_(nullptr), rate_estimation_batching_parameter_(default_values::batch), rate_estimation_choice_(0), + max_unverified_delay_(default_values::max_unverified_delay), verifier_(std::make_shared<auth::VoidVerifier>()), verify_signature_(false), reset_window_(false), @@ -64,41 +65,42 @@ class ConsumerSocket : public Socket { on_interest_satisfied_(VOID_HANDLER), on_content_object_input_(VOID_HANDLER), stats_summary_(VOID_HANDLER), + on_fwd_strategy_(VOID_HANDLER), + on_rec_strategy_(VOID_HANDLER), read_callback_(nullptr), timer_interval_milliseconds_(0), + recovery_strategy_(RtcTransportRecoveryStrategies::RTX_ONLY), + aggregated_data_(false), + fec_setting_(""), guard_raaqm_params_() { switch (protocol) { case TransportProtocolAlgorithms::CBR: transport_protocol_ = - std::make_unique<protocol::CbrTransportProtocol>(this); + std::make_shared<protocol::CbrTransportProtocol>(this); break; case TransportProtocolAlgorithms::RTC: transport_protocol_ = - std::make_unique<protocol::rtc::RTCTransportProtocol>(this); + std::make_shared<protocol::rtc::RTCTransportProtocol>(this); break; case TransportProtocolAlgorithms::RAAQM: default: transport_protocol_ = - std::make_unique<protocol::RaaqmTransportProtocol>(this); + std::make_shared<protocol::RaaqmTransportProtocol>(this); break; } } public: ConsumerSocket(interface::ConsumerSocket *consumer, int protocol) - : ConsumerSocket(consumer, protocol, std::make_shared<core::Portal>()) {} + : ConsumerSocket(consumer, protocol, core::Portal::createShared()) {} ConsumerSocket(interface::ConsumerSocket *consumer, int protocol, - asio::io_service &io_service) - : ConsumerSocket(consumer, protocol, - std::make_shared<core::Portal>(io_service)) { + ::utils::EventThread &worker) + : ConsumerSocket(consumer, protocol, core::Portal::createShared(worker)) { is_async_ = true; } - ~ConsumerSocket() { - stop(); - async_downloader_.stop(); - } + ~ConsumerSocket() { stop(); } interface::ConsumerSocket *getInterface() { return consumer_interface_; @@ -122,18 +124,6 @@ class ConsumerSocket : public Socket { transport_protocol_->start(); - return is_async_ ? CONSUMER_RUNNING : CONSUMER_FINISHED; - } - - virtual int asyncConsume(const Name &name) { - if (!async_downloader_.stopped()) { - async_downloader_.add([this, name]() { - network_name_ = std::move(name); - network_name_.setSuffix(0); - transport_protocol_->start(); - }); - } - return CONSUMER_RUNNING; } @@ -149,6 +139,9 @@ class ConsumerSocket : public Socket { } } + using Socket::getSocketOption; + using Socket::setSocketOption; + virtual int setSocketOption(int socket_option_key, ReadCallback *socket_option_value) { // Reschedule the function on the io_service to avoid race condition in @@ -245,6 +238,10 @@ class ConsumerSocket : public Socket { interest_lifetime_ = socket_option_value; break; + case GeneralTransportOptions::MAX_UNVERIFIED_TIME: + max_unverified_delay_ = socket_option_value; + break; + case RateEstimationOptions::RATE_ESTIMATION_BATCH_PARAMETER: if (socket_option_value > 0) { rate_estimation_batching_parameter_ = socket_option_value; @@ -265,6 +262,11 @@ class ConsumerSocket : public Socket { timer_interval_milliseconds_ = socket_option_value; break; + case RtcTransportOptions::RECOVERY_STRATEGY: + recovery_strategy_ = + (RtcTransportRecoveryStrategies)socket_option_value; + break; + default: return SOCKET_OPTION_NOT_SET; } @@ -328,6 +330,11 @@ class ConsumerSocket : public Socket { result = SOCKET_OPTION_SET; break; + case RtcTransportOptions::AGGREGATED_DATA: + aggregated_data_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; + default: return result; } @@ -406,36 +413,37 @@ class ConsumerSocket : public Socket { int setSocketOption( int socket_option_key, const std::shared_ptr<auth::Verifier> &socket_option_value) { - int result = SOCKET_OPTION_NOT_SET; if (!transport_protocol_->isRunning()) { switch (socket_option_key) { case GeneralTransportOptions::VERIFIER: verifier_.reset(); verifier_ = socket_option_value; - result = SOCKET_OPTION_SET; break; default: - return result; + return SOCKET_OPTION_NOT_SET; } } - - return result; + return SOCKET_OPTION_SET; } int setSocketOption(int socket_option_key, const std::string &socket_option_value) { int result = SOCKET_OPTION_NOT_SET; - if (!transport_protocol_->isRunning()) { - switch (socket_option_key) { - case DataLinkOptions::OUTPUT_INTERFACE: + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + if (!transport_protocol_->isRunning()) { output_interface_ = socket_option_value; portal_->setOutputInterface(output_interface_); result = SOCKET_OPTION_SET; - break; + } + break; + case GeneralTransportOptions::FEC_TYPE: + fec_setting_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; - default: - return result; - } + default: + return result; } return result; } @@ -461,6 +469,29 @@ class ConsumerSocket : public Socket { }); } + int setSocketOption(int socket_option_key, + StrategyCallback socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + StrategyCallback socket_option_value) -> int { + switch (socket_option_key) { + case ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE: + on_fwd_strategy_ = socket_option_value; + break; + case ConsumerCallbacksOptions::REC_STRATEGY_CHANGE: + on_rec_strategy_ = socket_option_value; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; + }); + } + int getSocketOption(int socket_option_key, double &socket_option_value) { utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { @@ -516,6 +547,10 @@ class ConsumerSocket : public Socket { socket_option_value = interest_lifetime_; break; + case GeneralTransportOptions::MAX_UNVERIFIED_TIME: + socket_option_value = max_unverified_delay_; + break; + case RaaqmTransportOptions::SAMPLE_NUMBER: socket_option_value = sample_number_; break; @@ -532,6 +567,10 @@ class ConsumerSocket : public Socket { socket_option_value = timer_interval_milliseconds_; break; + case RtcTransportOptions::RECOVERY_STRATEGY: + socket_option_value = recovery_strategy_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -553,6 +592,10 @@ class ConsumerSocket : public Socket { socket_option_value = reset_window_; break; + case RtcTransportOptions::AGGREGATED_DATA: + socket_option_value = aggregated_data_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -628,20 +671,6 @@ class ConsumerSocket : public Socket { } int getSocketOption(int socket_option_key, - std::shared_ptr<core::Portal> &socket_option_value) { - switch (socket_option_key) { - case PORTAL: - socket_option_value = portal_; - break; - - default: - return SOCKET_OPTION_NOT_GET; - } - - return SOCKET_OPTION_GET; - } - - int getSocketOption(int socket_option_key, IcnObserver **socket_option_value) { utils::SpinLock::Acquire locked(guard_raaqm_params_); switch (socket_option_key) { @@ -665,7 +694,6 @@ class ConsumerSocket : public Socket { default: return SOCKET_OPTION_NOT_GET; } - return SOCKET_OPTION_GET; } @@ -674,6 +702,9 @@ class ConsumerSocket : public Socket { case DataLinkOptions::OUTPUT_INTERFACE: socket_option_value = output_interface_; break; + case GeneralTransportOptions::FEC_TYPE: + socket_option_value = fec_setting_; + break; default: return SOCKET_OPTION_NOT_GET; } @@ -714,6 +745,29 @@ class ConsumerSocket : public Socket { }); } + int getSocketOption(int socket_option_key, + StrategyCallback **socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + StrategyCallback **socket_option_value) -> int { + switch (socket_option_key) { + case ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE: + *socket_option_value = &on_fwd_strategy_; + break; + case ConsumerCallbacksOptions::REC_STRATEGY_CHANGE: + *socket_option_value = &on_rec_strategy_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; + }); + } + protected: template <typename Lambda, typename arg2> int rescheduleOnIOService(int socket_option_key, arg2 socket_option_value, @@ -726,9 +780,9 @@ class ConsumerSocket : public Socket { /* Condition variable for the wait */ std::condition_variable cv; bool done = false; - portal_->getIoService().dispatch([&socket_option_key, - &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getThread().tryRunHandlerNow([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock<std::mutex> lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -748,8 +802,6 @@ class ConsumerSocket : public Socket { protected: interface::ConsumerSocket *consumer_interface_; - utils::EventThread async_downloader_; - // No need to protect from multiple accesses in the async consumer // The parameter is accessible only with a getSocketOption and // set from the consume @@ -776,6 +828,7 @@ class ConsumerSocket : public Socket { int rate_estimation_choice_; // Verification parameters + int max_unverified_delay_; std::shared_ptr<auth::Verifier> verifier_; transport::auth::KeyId *key_id_; std::atomic_bool verify_signature_; @@ -787,17 +840,26 @@ class ConsumerSocket : public Socket { ConsumerInterestCallback on_interest_satisfied_; ConsumerContentObjectCallback on_content_object_input_; ConsumerTimerCallback stats_summary_; + StrategyCallback on_fwd_strategy_; + StrategyCallback on_rec_strategy_; ReadCallback *read_callback_; uint32_t timer_interval_milliseconds_; // Transport protocol - std::unique_ptr<protocol::TransportProtocol> transport_protocol_; + std::shared_ptr<protocol::TransportProtocol> transport_protocol_; // Statistic TransportStatistics stats_; + // RTC protocol + RtcTransportRecoveryStrategies recovery_strategy_; + bool aggregated_data_; + + // FEC setting + std::string fec_setting_; + utils::SpinLock guard_raaqm_params_; std::string output_interface_; }; diff --git a/libtransport/src/implementation/socket_producer.h b/libtransport/src/implementation/socket_producer.h index 9daf79b9d..37151d497 100644 --- a/libtransport/src/implementation/socket_producer.h +++ b/libtransport/src/implementation/socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -40,6 +40,7 @@ namespace implementation { using namespace core; using namespace interface; +using ProducerCallback = interface::ProducerSocket::Callback; class ProducerSocket : public Socket { private: @@ -48,11 +49,14 @@ class ProducerSocket : public Socket { : Socket(std::move(portal)), producer_interface_(producer_socket), data_packet_size_(default_values::content_object_packet_size), + max_segment_size_(default_values::content_object_packet_size), content_object_expiry_time_(default_values::content_object_expiry_time), - async_thread_(), - making_manifest_(false), + making_manifest_(default_values::manifest_capacity), hash_algorithm_(auth::CryptoHashType::SHA256), - suffix_strategy_(core::NextSegmentCalculationStrategy::INCREMENTAL), + signer_(std::make_shared<auth::VoidSigner>()), + suffix_strategy_(std::make_shared<utils::IncrementalSuffixStrategy>(0)), + aggregated_data_(false), + fec_setting_(""), on_interest_input_(VOID_HANDLER), on_interest_dropped_input_buffer_(VOID_HANDLER), on_interest_inserted_input_buffer_(VOID_HANDLER), @@ -63,29 +67,30 @@ class ProducerSocket : public Socket { on_content_object_in_output_buffer_(VOID_HANDLER), on_content_object_output_(VOID_HANDLER), on_content_object_evicted_from_output_buffer_(VOID_HANDLER), - on_content_produced_(VOID_HANDLER) { + on_content_produced_(VOID_HANDLER), + application_callback_(nullptr) { switch (protocol) { case ProductionProtocolAlgorithms::RTC_PROD: production_protocol_ = - std::make_unique<protocol::RTCProductionProtocol>(this); + std::make_shared<protocol::RTCProductionProtocol>(this); break; case ProductionProtocolAlgorithms::BYTE_STREAM: default: production_protocol_ = - std::make_unique<protocol::ByteStreamProductionProtocol>(this); + std::make_shared<protocol::ByteStreamProductionProtocol>(this); break; } } public: ProducerSocket(interface::ProducerSocket *producer, int protocol) - : ProducerSocket(producer, protocol, std::make_shared<core::Portal>()) {} + : ProducerSocket(producer, protocol, core::Portal::createShared()) { + is_async_ = true; + } ProducerSocket(interface::ProducerSocket *producer, int protocol, - asio::io_service &io_service) - : ProducerSocket(producer, protocol, - std::make_shared<core::Portal>(io_service)) { - is_async_ = true; + ::utils::EventThread &worker) + : ProducerSocket(producer, protocol, core::Portal::createShared(worker)) { } virtual ~ProducerSocket() {} @@ -98,31 +103,9 @@ class ProducerSocket : public Socket { producer_interface_ = producer_socket; } - void connect() override { - portal_->connect(false); - production_protocol_->start(); - } - - bool isRunning() override { return !production_protocol_->isRunning(); }; + void connect() override { portal_->connect(false); } - virtual void asyncProduce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment = nullptr) { - if (!async_thread_.stopped()) { - auto a = buffer.release(); - async_thread_.add([this, content_name, a, is_last, offset, - last_segment]() { - auto buf = std::unique_ptr<utils::MemBuf>(a); - if (last_segment != NULL) { - **last_segment = offset + produceStream(content_name, std::move(buf), - is_last, offset); - } else { - produceStream(content_name, std::move(buf), is_last, offset); - } - }); - } - } + bool isRunning() override { return production_protocol_->isRunning(); }; virtual uint32_t produceStream(const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer, @@ -156,12 +139,38 @@ class ProducerSocket : public Socket { production_protocol_->produce(content_object); } + void sendMapme() { production_protocol_->sendMapme(); } + void registerPrefix(const Prefix &producer_namespace) { - production_protocol_->registerNamespaceWithNetwork(producer_namespace); + portal_->registerRoute(producer_namespace); } + void start() { production_protocol_->start(); } void stop() { production_protocol_->stop(); } + using Socket::getSocketOption; + using Socket::setSocketOption; + + virtual int setSocketOption(int socket_option_key, + ProducerCallback *socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + ProducerCallback *socket_option_value) -> int { + switch (socket_option_key) { + case ProducerCallbacksOptions::PRODUCER_CALLBACK: + application_callback_ = socket_option_value; + break; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; + }); + } + virtual int setSocketOption(int socket_option_key, uint32_t socket_option_value) { switch (socket_option_key) { @@ -172,6 +181,17 @@ class ProducerSocket : public Socket { } break; + case GeneralTransportOptions::MAKE_MANIFEST: + making_manifest_ = socket_option_value; + break; + + case GeneralTransportOptions::MAX_SEGMENT_SIZE: + if (socket_option_value <= default_values::max_content_object_size && + socket_option_value > 0) { + max_segment_size_ = socket_option_value; + } + break; + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: production_protocol_->setOutputBufferSize(socket_option_value); break; @@ -260,8 +280,8 @@ class ProducerSocket : public Socket { virtual int setSocketOption(int socket_option_key, bool socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::MAKE_MANIFEST: - making_manifest_ = socket_option_value; + case RtcTransportOptions::AGGREGATED_DATA: + aggregated_data_ = socket_option_value; break; default: @@ -385,7 +405,7 @@ class ProducerSocket : public Socket { virtual int setSocketOption( int socket_option_key, - core::NextSegmentCalculationStrategy socket_option_value) { + const std::shared_ptr<utils::SuffixStrategy> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SUFFIX_STRATEGY: suffix_strategy_ = socket_option_value; @@ -413,9 +433,33 @@ class ProducerSocket : public Socket { return SOCKET_OPTION_SET; } + int getSocketOption(int socket_option_key, + ProducerCallback **socket_option_value) { + // Reschedule the function on the io_service to avoid race condition in + // case setSocketOption is called while the io_service is running. + return rescheduleOnIOService( + socket_option_key, socket_option_value, + [this](int socket_option_key, + ProducerCallback **socket_option_value) -> int { + switch (socket_option_key) { + case ProducerCallbacksOptions::PRODUCER_CALLBACK: + *socket_option_value = application_callback_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; + }); + } + virtual int getSocketOption(int socket_option_key, uint32_t &socket_option_value) { switch (socket_option_key) { + case GeneralTransportOptions::MAKE_MANIFEST: + socket_option_value = making_manifest_; + break; + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: socket_option_value = production_protocol_->getOutputBufferSize(); break; @@ -424,6 +468,10 @@ class ProducerSocket : public Socket { socket_option_value = (uint32_t)data_packet_size_; break; + case GeneralTransportOptions::MAX_SEGMENT_SIZE: + socket_option_value = (uint32_t)max_segment_size_; + break; + case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: socket_option_value = content_object_expiry_time_; break; @@ -438,14 +486,14 @@ class ProducerSocket : public Socket { virtual int getSocketOption(int socket_option_key, bool &socket_option_value) { switch (socket_option_key) { - case GeneralTransportOptions::MAKE_MANIFEST: - socket_option_value = making_manifest_; - break; - case GeneralTransportOptions::ASYNC_MODE: socket_option_value = is_async_; break; + case RtcTransportOptions::AGGREGATED_DATA: + socket_option_value = aggregated_data_; + break; + default: return SOCKET_OPTION_NOT_GET; } @@ -547,21 +595,6 @@ class ProducerSocket : public Socket { }); } - virtual int getSocketOption( - int socket_option_key, - std::shared_ptr<core::Portal> &socket_option_value) { - switch (socket_option_key) { - case PORTAL: - socket_option_value = portal_; - break; - default: - return SOCKET_OPTION_NOT_GET; - ; - } - - return SOCKET_OPTION_GET; - } - virtual int getSocketOption(int socket_option_key, auth::CryptoHashType &socket_option_value) { switch (socket_option_key) { @@ -577,7 +610,7 @@ class ProducerSocket : public Socket { virtual int getSocketOption( int socket_option_key, - core::NextSegmentCalculationStrategy &socket_option_value) { + std::shared_ptr<utils::SuffixStrategy> &socket_option_value) { switch (socket_option_key) { case GeneralTransportOptions::SUFFIX_STRATEGY: socket_option_value = suffix_strategy_; @@ -603,9 +636,31 @@ class ProducerSocket : public Socket { return SOCKET_OPTION_GET; } + int getSocketOption(int socket_option_key, std::string &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::FEC_TYPE: + socket_option_value = fec_setting_; + break; + default: + return SOCKET_OPTION_NOT_GET; + } + + return SOCKET_OPTION_GET; + } + virtual int setSocketOption(int socket_option_key, const std::string &socket_option_value) { - return SOCKET_OPTION_NOT_SET; + int result = SOCKET_OPTION_NOT_SET; + switch (socket_option_key) { + case GeneralTransportOptions::FEC_TYPE: + fec_setting_ = socket_option_value; + result = SOCKET_OPTION_SET; + break; + + default: + return result; + } + return result; } // If the thread calling lambda_func is not the same of io_service, this @@ -623,9 +678,9 @@ class ProducerSocket : public Socket { std::condition_variable cv; bool done = false; - portal_->getIoService().dispatch([&socket_option_key, - &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getThread().tryRunHandlerNow([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock<std::mutex> lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -655,9 +710,9 @@ class ProducerSocket : public Socket { /* Condition variable for the wait */ std::condition_variable cv; bool done = false; - portal_->getIoService().dispatch([&socket_option_key, - &socket_option_value, &mtx, &cv, - &result, &done, &func]() { + portal_->getThread().tryRunHandlerNow([&socket_option_key, + &socket_option_value, &mtx, &cv, + &result, &done, &func]() { std::unique_lock<std::mutex> lck(mtx); done = true; result = func(socket_option_key, socket_option_value); @@ -677,20 +732,24 @@ class ProducerSocket : public Socket { // Threads protected: interface::ProducerSocket *producer_interface_; - asio::io_service io_service_; std::atomic<size_t> data_packet_size_; + std::atomic<size_t> max_segment_size_; std::atomic<uint32_t> content_object_expiry_time_; - utils::EventThread async_thread_; - - std::atomic<bool> making_manifest_; + std::atomic<uint32_t> making_manifest_; std::atomic<auth::CryptoHashType> hash_algorithm_; std::atomic<auth::CryptoSuite> crypto_suite_; utils::SpinLock signer_lock_; std::shared_ptr<auth::Signer> signer_; - core::NextSegmentCalculationStrategy suffix_strategy_; + std::shared_ptr<utils::SuffixStrategy> suffix_strategy_; + + std::shared_ptr<protocol::ProductionProtocol> production_protocol_; - std::unique_ptr<protocol::ProductionProtocol> production_protocol_; + // RTC transport + bool aggregated_data_; + + // FEC setting + std::string fec_setting_; // callbacks ProducerInterestCallback on_interest_input_; @@ -706,6 +765,8 @@ class ProducerSocket : public Socket { ProducerContentObjectCallback on_content_object_evicted_from_output_buffer_; ProducerContentCallback on_content_produced_; + + ProducerCallback *application_callback_; }; } // namespace implementation diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.cc b/libtransport/src/implementation/tls_rtc_socket_producer.cc index db62b10c1..06d613ef0 100644 --- a/libtransport/src/implementation/tls_rtc_socket_producer.cc +++ b/libtransport/src/implementation/tls_rtc_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -90,11 +90,11 @@ int TLSRTCProducerSocket::writeOld(BIO *b, const char *buf, int num) { socket = (TLSRTCProducerSocket *)BIO_get_data(b); if (socket->getHandshakeState() != SERVER_FINISHED && socket->first_) { - bool making_manifest = socket->parent_->making_manifest_; + uint32_t making_manifest = socket->parent_->making_manifest_; socket->tls_chunks_--; socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - false); + 0U); socket->parent_->ProducerSocket::produce( socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, 0); socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, diff --git a/libtransport/src/implementation/tls_rtc_socket_producer.h b/libtransport/src/implementation/tls_rtc_socket_producer.h index 92c657afc..f6dc425e4 100644 --- a/libtransport/src/implementation/tls_rtc_socket_producer.h +++ b/libtransport/src/implementation/tls_rtc_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/implementation/tls_socket_consumer.cc b/libtransport/src/implementation/tls_socket_consumer.cc index 65472b41d..b368c4b88 100644 --- a/libtransport/src/implementation/tls_socket_consumer.cc +++ b/libtransport/src/implementation/tls_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -43,7 +43,7 @@ int readOldTLS(BIO *b, char *buf, int size) { if (!socket->something_to_read_) { if (!socket->transport_protocol_->isRunning()) { socket->network_name_.setSuffix(socket->random_suffix_); - socket->ConsumerSocket::asyncConsume(socket->network_name_); + socket->ConsumerSocket::consume(socket->network_name_); } if (!socket->something_to_read_) socket->cv_.wait(lck); @@ -284,31 +284,6 @@ int TLSConsumerSocket::download_content(const Name &name) { return CONSUMER_FINISHED; } -int TLSConsumerSocket::asyncConsume(const Name &name, - std::unique_ptr<utils::MemBuf> &&buffer) { - this->payload_ = std::move(buffer); - - this->ConsumerSocket::setSocketOption( - ConsumerCallbacksOptions::INTEREST_OUTPUT, - (ConsumerInterestCallback)std::bind( - &TLSConsumerSocket::setInterestPayload, this, std::placeholders::_1, - std::placeholders::_2)); - - return asyncConsume(name); -} - -int TLSConsumerSocket::asyncConsume(const Name &name) { - if ((SSL_in_before(this->ssl_) || SSL_in_init(this->ssl_))) { - throw errors::RuntimeException("Handshake not performed"); - } - - if (!async_downloader_tls_.stopped()) { - async_downloader_tls_.add([this, name]() { download_content(name); }); - } - - return CONSUMER_RUNNING; -} - void TLSConsumerSocket::registerPrefix(const Prefix &producer_namespace) { producer_namespace_ = producer_namespace; } @@ -353,7 +328,7 @@ void TLSConsumerSocket::readBufferAvailable( cv_.notify_one(); } -void TLSConsumerSocket::readError(const std::error_code ec) noexcept {} +void TLSConsumerSocket::readError(const std::error_code &ec) noexcept {} void TLSConsumerSocket::readSuccess(std::size_t total_size) noexcept { std::unique_lock<std::mutex> lck(this->mtx_); diff --git a/libtransport/src/implementation/tls_socket_consumer.h b/libtransport/src/implementation/tls_socket_consumer.h index be08ec47d..a74f1ee10 100644 --- a/libtransport/src/implementation/tls_socket_consumer.h +++ b/libtransport/src/implementation/tls_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -47,9 +47,6 @@ class TLSConsumerSocket : public ConsumerSocket, int consume(const Name &name, std::unique_ptr<utils::MemBuf> &&buffer); int consume(const Name &name) override; - int asyncConsume(const Name &name, std::unique_ptr<utils::MemBuf> &&buffer); - int asyncConsume(const Name &name) override; - void registerPrefix(const Prefix &producer_namespace); int setSocketOption( @@ -100,7 +97,7 @@ class TLSConsumerSocket : public ConsumerSocket, virtual void readBufferAvailable( std::unique_ptr<utils::MemBuf> &&buffer) noexcept override; - virtual void readError(const std::error_code ec) noexcept override; + virtual void readError(const std::error_code &ec) noexcept override; virtual void readSuccess(std::size_t total_size) noexcept override; diff --git a/libtransport/src/implementation/tls_socket_producer.cc b/libtransport/src/implementation/tls_socket_producer.cc index 3992ca45c..47f3b43a6 100644 --- a/libtransport/src/implementation/tls_socket_producer.cc +++ b/libtransport/src/implementation/tls_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -99,12 +99,12 @@ int TLSProducerSocket::writeOld(BIO *b, const char *buf, int num) { socket = (TLSProducerSocket *)BIO_get_data(b); if (socket->getHandshakeState() != SERVER_FINISHED && socket->first_) { - bool making_manifest = socket->parent_->making_manifest_; + uint32_t making_manifest = socket->parent_->making_manifest_; //! socket->tls_chunks_ corresponds to is_last socket->tls_chunks_--; socket->parent_->setSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - false); + 0U); socket->parent_->ProducerSocket::produceStream( socket->name_, (const uint8_t *)buf, num, socket->tls_chunks_ == 0, socket->last_segment_); @@ -358,7 +358,7 @@ uint32_t TLSProducerSocket::produceStream( } size_t buf_size = buffer->length(); - name_ = production_protocol_->getNamespaces().front().mapName(content_name); + name_ = portal_->getServedNamespaces().begin()->mapName(content_name); tls_chunks_ = to_call_oncontentproduced_ = (int)ceil((float)buf_size / (float)SSL3_RT_MAX_PLAIN_LENGTH); @@ -394,8 +394,7 @@ int TLSProducerSocket::addHicnKeyIdCb(SSL *s, unsigned int ext_type, << "On addHicnKeyIdCb, for the prefix registration."; if (ext_type == 100) { - auto &prefix = - socket->parent_->production_protocol_->getNamespaces().front(); + auto &prefix = *socket->parent_->portal_->getServedNamespaces().begin(); const ip_prefix_t &ip_prefix = prefix.toIpPrefixStruct(); int inet_family = prefix.getAddressFamily(); uint16_t prefix_len_bits = prefix.getPrefixLength(); diff --git a/libtransport/src/implementation/tls_socket_producer.h b/libtransport/src/implementation/tls_socket_producer.h index a542a4d9f..0e958b321 100644 --- a/libtransport/src/implementation/tls_socket_producer.h +++ b/libtransport/src/implementation/tls_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/CMakeLists.txt b/libtransport/src/interfaces/CMakeLists.txt index 7ec024fec..0a0603ac8 100644 --- a/libtransport/src/interfaces/CMakeLists.txt +++ b/libtransport/src/interfaces/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/callbacks.cc b/libtransport/src/interfaces/callbacks.cc index 6869ac3f7..776b4cbe7 100644 --- a/libtransport/src/interfaces/callbacks.cc +++ b/libtransport/src/interfaces/callbacks.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/p2psecure_socket_consumer.cc b/libtransport/src/interfaces/p2psecure_socket_consumer.cc index e473a1e2e..e329a50f1 100644 --- a/libtransport/src/interfaces/p2psecure_socket_consumer.cc +++ b/libtransport/src/interfaces/p2psecure_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/p2psecure_socket_producer.cc b/libtransport/src/interfaces/p2psecure_socket_producer.cc index 10d8a1367..5f98302d0 100644 --- a/libtransport/src/interfaces/p2psecure_socket_producer.cc +++ b/libtransport/src/interfaces/p2psecure_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -23,10 +23,11 @@ P2PSecureProducerSocket::P2PSecureProducerSocket() { socket_ = std::make_unique<implementation::P2PSecureProducerSocket>(this); } -P2PSecureProducerSocket::P2PSecureProducerSocket( - bool rtc, const std::shared_ptr<auth::Identity> &identity) { - socket_ = std::make_unique<implementation::P2PSecureProducerSocket>(this, rtc, - identity); +P2PSecureProducerSocket::P2PSecureProducerSocket(bool rtc, + std::string &keystore_path, + std::string &keystore_pwd) { + socket_ = std::make_unique<implementation::P2PSecureProducerSocket>( + this, rtc, keystore_path, keystore_pwd); } } // namespace interface diff --git a/libtransport/src/interfaces/portal.cc b/libtransport/src/interfaces/portal.cc index 9db0621f6..84634a282 100644 --- a/libtransport/src/interfaces/portal.cc +++ b/libtransport/src/interfaces/portal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,83 +13,106 @@ * limitations under the License. */ +#include <core/portal.h> #include <hicn/transport/interfaces/portal.h> -#include <implementation/socket.h> namespace transport { namespace interface { -Portal::Portal() { implementation_ = new core::Portal(); } +class Portal::Impl { + public: + Impl() : portal_(core::Portal::createShared()) {} + Impl(::utils::EventThread &worker) + : portal_(core::Portal::createShared(worker)) {} -Portal::Portal(asio::io_service &io_service) { - implementation_ = new core::Portal(io_service); -} + void registerTransportCallback(TransportCallback *transport_callback) { + portal_->registerTransportCallback(transport_callback); + } + + void connect(bool is_consumer) { portal_->connect(is_consumer); } + + bool interestIsPending(const core::Name &name) { + return portal_->interestIsPending(name); + } + + void sendInterest(core::Interest::Ptr &&interest, + OnContentObjectCallback &&on_content_object_callback, + OnInterestTimeoutCallback &&on_interest_timeout_callback) { + portal_->sendInterest(std::move(interest), + std::move(on_content_object_callback), + std::move(on_interest_timeout_callback)); + } + + void sendContentObject(core::ContentObject &content_object) { + portal_->sendContentObject(content_object); + } + + void killConnection() { portal_->killConnection(); } + + void clear() { portal_->clear(); } -Portal::~Portal() { delete reinterpret_cast<core::Portal *>(implementation_); } + utils::EventThread &getThread() { return portal_->getThread(); } -void Portal::setConsumerCallback(ConsumerCallback *consumer_callback) { - reinterpret_cast<core::Portal *>(implementation_) - ->setConsumerCallback(consumer_callback); + void registerRoute(core::Prefix &prefix) { portal_->registerRoute(prefix); } + + void sendMapme() { portal_->sendMapme(); } + + void setForwardingStrategy(core::Prefix &prefix, std::string &strategy) { + portal_->setForwardingStrategy(prefix, strategy); + } + + private: + std::shared_ptr<core::Portal> portal_; +}; + +Portal::Portal() { implementation_ = new Impl(); } + +Portal::Portal(::utils::EventThread &worker) { + implementation_ = new Impl(worker); } -void Portal::setProducerCallback(ProducerCallback *producer_callback) { - reinterpret_cast<core::Portal *>(implementation_) - ->setProducerCallback(producer_callback); +Portal::~Portal() { delete implementation_; } + +void Portal::registerTransportCallback(TransportCallback *transport_callback) { + implementation_->registerTransportCallback(transport_callback); } void Portal::connect(bool is_consumer) { - reinterpret_cast<core::Portal *>(implementation_)->connect(is_consumer); + implementation_->connect(is_consumer); } bool Portal::interestIsPending(const core::Name &name) { - return reinterpret_cast<core::Portal *>(implementation_) - ->interestIsPending(name); + return implementation_->interestIsPending(name); } void Portal::sendInterest( core::Interest::Ptr &&interest, OnContentObjectCallback &&on_content_object_callback, OnInterestTimeoutCallback &&on_interest_timeout_callback) { - reinterpret_cast<core::Portal *>(implementation_) - ->sendInterest(std::move(interest), std::move(on_content_object_callback), - std::move(on_interest_timeout_callback)); -} - -void Portal::bind(const BindConfig &config) { - reinterpret_cast<core::Portal *>(implementation_)->bind(config); -} - -void Portal::runEventsLoop() { - reinterpret_cast<core::Portal *>(implementation_)->runEventsLoop(); -} - -void Portal::runOneEvent() { - reinterpret_cast<core::Portal *>(implementation_)->runOneEvent(); + implementation_->sendInterest(std::move(interest), + std::move(on_content_object_callback), + std::move(on_interest_timeout_callback)); } void Portal::sendContentObject(core::ContentObject &content_object) { - reinterpret_cast<core::Portal *>(implementation_) - ->sendContentObject(content_object); + implementation_->sendContentObject(content_object); } -void Portal::stopEventsLoop() { - reinterpret_cast<core::Portal *>(implementation_)->stopEventsLoop(); -} +void Portal::killConnection() { implementation_->killConnection(); } -void Portal::killConnection() { - reinterpret_cast<core::Portal *>(implementation_)->killConnection(); -} +void Portal::clear() { implementation_->clear(); } -void Portal::clear() { - reinterpret_cast<core::Portal *>(implementation_)->clear(); -} +utils::EventThread &Portal::getThread() { return implementation_->getThread(); } -asio::io_service &Portal::getIoService() { - return reinterpret_cast<core::Portal *>(implementation_)->getIoService(); +void Portal::registerRoute(core::Prefix &prefix) { + implementation_->registerRoute(prefix); } -void Portal::registerRoute(core::Prefix &prefix) { - reinterpret_cast<core::Portal *>(implementation_)->registerRoute(prefix); +void Portal::sendMapme() { implementation_->sendMapme(); } + +void Portal::setForwardingStrategy(core::Prefix &prefix, + std::string &strategy) { + implementation_->setForwardingStrategy(prefix, strategy); } } // namespace interface diff --git a/libtransport/src/interfaces/socket_consumer.cc b/libtransport/src/interfaces/socket_consumer.cc index 4eee73cab..747dc0974 100644 --- a/libtransport/src/interfaces/socket_consumer.cc +++ b/libtransport/src/interfaces/socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -23,23 +23,28 @@ ConsumerSocket::ConsumerSocket(int protocol) { socket_ = std::make_unique<implementation::ConsumerSocket>(this, protocol); } -ConsumerSocket::ConsumerSocket(int protocol, asio::io_service &io_service) { - socket_ = std::make_unique<implementation::ConsumerSocket>(this, protocol, - io_service); +ConsumerSocket::ConsumerSocket(int protocol, ::utils::EventThread &worker) { + socket_ = + std::make_unique<implementation::ConsumerSocket>(this, protocol, worker); } ConsumerSocket::ConsumerSocket() {} -ConsumerSocket::~ConsumerSocket() { socket_->stop(); } +ConsumerSocket::ConsumerSocket(ConsumerSocket &&other) noexcept + : socket_(std::move(other.socket_)) {} + +ConsumerSocket::~ConsumerSocket() { + if (socket_) { + socket_->stop(); + } +} void ConsumerSocket::connect() { socket_->connect(); } bool ConsumerSocket::isRunning() { return socket_->isRunning(); } -int ConsumerSocket::consume(const Name &name) { return socket_->consume(name); } - -int ConsumerSocket::asyncConsume(const Name &name) { - return socket_->asyncConsume(name); +int ConsumerSocket::consume(const Name &name, bool blocking) { + return socket_->consume(name); } void ConsumerSocket::stop() { socket_->stop(); } @@ -111,6 +116,16 @@ int ConsumerSocket::setSocketOption(int socket_option_key, return socket_->setSocketOption(socket_option_key, socket_option_value); } +int ConsumerSocket::setSocketOption(int socket_option_key, + StrategyCallback socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + Packet::Format socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + int ConsumerSocket::getSocketOption(int socket_option_key, double &socket_option_value) { return socket_->getSocketOption(socket_option_key, socket_option_value); @@ -169,6 +184,16 @@ int ConsumerSocket::getSocketOption( return socket_->getSocketOption(socket_option_key, socket_option_value); } +int ConsumerSocket::getSocketOption(int socket_option_key, + StrategyCallback **socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + Packet::Format &socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + } // namespace interface } // namespace transport diff --git a/libtransport/src/interfaces/socket_producer.cc b/libtransport/src/interfaces/socket_producer.cc index b04947dfd..10613c0e1 100644 --- a/libtransport/src/interfaces/socket_producer.cc +++ b/libtransport/src/interfaces/socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -33,14 +33,21 @@ ProducerSocket::ProducerSocket(int protocol) { socket_ = std::make_unique<implementation::ProducerSocket>(this, protocol); } -ProducerSocket::ProducerSocket(int protocol, asio::io_service &io_service) { - socket_ = std::make_unique<implementation::ProducerSocket>(this, protocol, - io_service); +ProducerSocket::ProducerSocket(int protocol, ::utils::EventThread &worker) { + socket_ = + std::make_unique<implementation::ProducerSocket>(this, protocol, worker); } +ProducerSocket::ProducerSocket(ProducerSocket &&other) noexcept + : socket_(std::move(other.socket_)) {} + ProducerSocket::ProducerSocket(bool) {} -ProducerSocket::~ProducerSocket() { socket_->stop(); } +ProducerSocket::~ProducerSocket() { + if (socket_) { + socket_->stop(); + } +} void ProducerSocket::connect() { socket_->connect(); } @@ -76,18 +83,14 @@ void ProducerSocket::produce(ContentObject &content_object) { return socket_->produce(content_object); } -void ProducerSocket::asyncProduce(Name content_name, - std::unique_ptr<utils::MemBuf> &&buffer, - bool is_last, uint32_t offset, - uint32_t **last_segment) { - return socket_->asyncProduce(content_name, std::move(buffer), is_last, offset, - last_segment); -} +void ProducerSocket::sendMapme() { return socket_->sendMapme(); } void ProducerSocket::registerPrefix(const Prefix &producer_namespace) { return socket_->registerPrefix(producer_namespace); } +void ProducerSocket::start() { return socket_->start(); } + void ProducerSocket::stop() { return socket_->stop(); } asio::io_service &ProducerSocket::getIoService() { @@ -95,6 +98,11 @@ asio::io_service &ProducerSocket::getIoService() { }; int ProducerSocket::setSocketOption(int socket_option_key, + Callback *socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + +int ProducerSocket::setSocketOption(int socket_option_key, uint32_t socket_option_value) { return socket_->setSocketOption(socket_option_key, socket_option_value); } @@ -140,9 +148,14 @@ int ProducerSocket::setSocketOption( return socket_->setSocketOption(socket_option_key, socket_option_value); } +int ProducerSocket::setSocketOption(int socket_option_key, + Packet::Format socket_option_value) { + return socket_->setSocketOption(socket_option_key, socket_option_value); +} + int ProducerSocket::getSocketOption(int socket_option_key, uint32_t &socket_option_value) { - return socket_->setSocketOption(socket_option_key, socket_option_value); + return socket_->getSocketOption(socket_option_key, socket_option_value); } int ProducerSocket::setSocketOption(int socket_option_key, @@ -181,6 +194,11 @@ int ProducerSocket::getSocketOption( return socket_->getSocketOption(socket_option_key, socket_option_value); } +int ProducerSocket::getSocketOption(int socket_option_key, + Packet::Format &socket_option_value) { + return socket_->getSocketOption(socket_option_key, socket_option_value); +} + } // namespace interface } // namespace transport diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.cc b/libtransport/src/interfaces/tls_rtc_socket_producer.cc index 7326fcbcb..6bf1b011c 100644 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.cc +++ b/libtransport/src/interfaces/tls_rtc_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/tls_rtc_socket_producer.h b/libtransport/src/interfaces/tls_rtc_socket_producer.h index 3ea84095b..b8b6ec298 100644 --- a/libtransport/src/interfaces/tls_rtc_socket_producer.h +++ b/libtransport/src/interfaces/tls_rtc_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/tls_socket_consumer.cc b/libtransport/src/interfaces/tls_socket_consumer.cc index 6c1c535b5..24060d1d8 100644 --- a/libtransport/src/interfaces/tls_socket_consumer.cc +++ b/libtransport/src/interfaces/tls_socket_consumer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/tls_socket_consumer.h b/libtransport/src/interfaces/tls_socket_consumer.h index 845a9181f..242dc91a5 100644 --- a/libtransport/src/interfaces/tls_socket_consumer.h +++ b/libtransport/src/interfaces/tls_socket_consumer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/tls_socket_producer.cc b/libtransport/src/interfaces/tls_socket_producer.cc index 037702f72..b2b9e723a 100644 --- a/libtransport/src/interfaces/tls_socket_producer.cc +++ b/libtransport/src/interfaces/tls_socket_producer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/interfaces/tls_socket_producer.h b/libtransport/src/interfaces/tls_socket_producer.h index 3c662176a..9b31cb483 100644 --- a/libtransport/src/interfaces/tls_socket_producer.h +++ b/libtransport/src/interfaces/tls_socket_producer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/io_modules/CMakeLists.txt b/libtransport/src/io_modules/CMakeLists.txt index 29aec236a..f4143de04 100644 --- a/libtransport/src/io_modules/CMakeLists.txt +++ b/libtransport/src/io_modules/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -11,25 +11,69 @@ # See the License for the specific language governing permissions and # limitations under the License. + +############################################################## +# Android case: no submodules +############################################################## if (${CMAKE_SYSTEM_NAME} MATCHES Android) list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/udp/hicn_forwarder_module.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp/udp_socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hicn-light-ng/hicn_forwarder_module.cc ) list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/udp/hicn_forwarder_module.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp/udp_socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn-light-ng/hicn_forwarder_module.h + ) + + if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(Libhicnctrl ${CURRENT_VERSION} REQUIRED NO_MODULE) + + if (DISABLE_SHARED_LIBRARIES) + set(LIBTYPE static) + else() + set(LIBTYPE shared) + endif() + + list(APPEND LIBHICNCTRL_LIBRARIES hicn::hicnctrl.${LIBTYPE}) + else() + if (DISABLE_SHARED_LIBRARIES) + if (WIN32) + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC}) + else () + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC} log) + endif () + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_STATIC} + ) + else() + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_SHARED}) + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_SHARED} + ) + endif() + endif() + + list(APPEND LIBRARIES + PRIVATE ${LIBHICNCTRL_LIBRARIES} + ) + + list(APPEND LIBTRANSPORT_INTERNAL_INCLUDE_DIRS + PUBLIC + $<BUILD_INTERFACE:${LIBHICNCTRL_INCLUDE_DIRS}> ) set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) + set(LIBRARIES ${LIBRARIES} PARENT_SCOPE) + set(LIBTRANSPORT_INTERNAL_INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} PARENT_SCOPE) else() - add_subdirectory(udp) +############################################################## +# Compile submodules +############################################################## + add_subdirectory(hicn-light-ng) add_subdirectory(loopback) add_subdirectory(forwarder) if (__vpp__) add_subdirectory(memif) endif() -endif()
\ No newline at end of file +endif() diff --git a/libtransport/src/io_modules/forwarder/CMakeLists.txt b/libtransport/src/io_modules/forwarder/CMakeLists.txt index a1d0c5db5..3922316d3 100644 --- a/libtransport/src/io_modules/forwarder/CMakeLists.txt +++ b/libtransport/src/io_modules/forwarder/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -17,8 +17,6 @@ list(APPEND MODULE_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.h ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.h ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel_listener.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel.h ${CMAKE_CURRENT_SOURCE_DIR}/global_counter.h ) @@ -26,16 +24,13 @@ list(APPEND MODULE_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_module.cc ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel_listener.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_tunnel.cc ) build_module(forwarder_module - SHARED SOURCES ${MODULE_SOURCE_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT}-io-modules - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + INCLUDE_DIRS ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} ${Libhicn_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ) diff --git a/libtransport/src/io_modules/forwarder/errors.cc b/libtransport/src/io_modules/forwarder/errors.cc index b5f131499..6e93d0453 100644 --- a/libtransport/src/io_modules/forwarder/errors.cc +++ b/libtransport/src/io_modules/forwarder/errors.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. */ #include <io_modules/forwarder/errors.h> diff --git a/libtransport/src/io_modules/forwarder/forwarder.cc b/libtransport/src/io_modules/forwarder/forwarder.cc index 0546cb8b3..3ae5bf397 100644 --- a/libtransport/src/io_modules/forwarder/forwarder.cc +++ b/libtransport/src/io_modules/forwarder/forwarder.cc @@ -15,11 +15,11 @@ #include <core/global_configuration.h> #include <core/local_connector.h> +#include <core/udp_connector.h> +#include <core/udp_listener.h> #include <glog/logging.h> #include <io_modules/forwarder/forwarder.h> #include <io_modules/forwarder/global_id_counter.h> -#include <io_modules/forwarder/udp_tunnel.h> -#include <io_modules/forwarder/udp_tunnel_listener.h> namespace transport { @@ -89,11 +89,12 @@ void Forwarder::initConnectors() { Connector::Id Forwarder::registerLocalConnector( asio::io_service &io_service, Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback) { utils::SpinLock::Acquire locked(connector_lock_); auto id = GlobalCounter<Connector::Id>::getInstance().getNext(); auto connector = std::make_shared<LocalConnector>( - io_service, receive_callback, nullptr, nullptr, reconnect_callback); + io_service, receive_callback, sent_callback, nullptr, reconnect_callback); connector->setConnectorId(id); local_connectors_.emplace(id, std::move(connector)); return id; @@ -105,6 +106,7 @@ Forwarder &Forwarder::deleteConnector(Connector::Id id) { if (it != local_connectors_.end()) { it->second->close(); local_connectors_.erase(it); + } else { } return *this; @@ -120,9 +122,9 @@ Connector::Ptr Forwarder::getConnector(Connector::Id id) { return nullptr; } -void Forwarder::onPacketFromListener(Connector *connector, - utils::MemBuf &packet_buffer, - const std::error_code &ec) { +void Forwarder::onPacketFromListener( + Connector *connector, const std::vector<utils::MemBuf::Ptr> &packets, + const std::error_code &ec) { // Create connector connector->setReceiveCallback( std::bind(&Forwarder::onPacketReceived, this, std::placeholders::_1, @@ -135,37 +137,47 @@ void Forwarder::onPacketFromListener(Connector *connector, remote_connectors_.emplace(connector->getConnectorId(), connector->shared_from_this()); } + // TODO Check if control packet or not. For the moment it is not. - onPacketReceived(connector, packet_buffer, ec); + onPacketReceived(connector, packets, ec); } void Forwarder::onPacketReceived(Connector *connector, - utils::MemBuf &packet_buffer, + const std::vector<utils::MemBuf::Ptr> &packets, const std::error_code &ec) { - // Figure out the type of packet we received - bool is_interest = Packet::isInterest(packet_buffer.data()); - - Packet *packet = nullptr; - if (is_interest) { - packet = static_cast<Interest *>(&packet_buffer); - } else { - packet = static_cast<ContentObject *>(&packet_buffer); + if (ec) { + LOG(ERROR) << "Error receiving packet: " << ec.message(); + return; } - for (auto &c : local_connectors_) { - auto role = c.second->getRole(); - auto is_producer = role == Connector::Role::PRODUCER; - if ((is_producer && is_interest) || (!is_producer && !is_interest)) { - c.second->send(*packet); + for (auto &packet_buffer_ptr : packets) { + auto &packet_buffer = *packet_buffer_ptr; + + // Figure out the type of packet we received + bool is_interest = Packet::isInterest(packet_buffer.data()); + + Packet *packet = nullptr; + if (is_interest) { + packet = static_cast<Interest *>(&packet_buffer); } else { - LOG(ERROR) << "Error sending packet to local connector. is_interest = " - << is_interest << " - is_producer = " << is_producer; + packet = static_cast<ContentObject *>(&packet_buffer); + } + + for (auto &c : local_connectors_) { + auto role = c.second->getRole(); + auto is_producer = role == Connector::Role::PRODUCER; + if ((is_producer && is_interest) || (!is_producer && !is_interest)) { + c.second->send(*packet); + } else { + LOG(ERROR) << "Error sending packet to local connector. is_interest = " + << is_interest << " - is_producer = " << is_producer; + } } - } - // PCS Lookup + FIB lookup. Skip for now + // PCS Lookup + FIB lookup. Skip for now - // Forward packet to local connectors + // Forward packet to local connectors + } } void Forwarder::send(Packet &packet) { diff --git a/libtransport/src/io_modules/forwarder/forwarder.h b/libtransport/src/io_modules/forwarder/forwarder.h index 5b564bb5e..38b4260b3 100644 --- a/libtransport/src/io_modules/forwarder/forwarder.h +++ b/libtransport/src/io_modules/forwarder/forwarder.h @@ -15,13 +15,13 @@ #pragma once +#include <core/udp_listener.h> #include <hicn/transport/core/io_module.h> #include <hicn/transport/core/prefix.h> #include <hicn/transport/utils/event_thread.h> #include <hicn/transport/utils/singleton.h> #include <hicn/transport/utils/spinlock.h> #include <io_modules/forwarder/configuration.h> -#include <io_modules/forwarder/udp_tunnel_listener.h> #include <atomic> #include <libconfig.h++> @@ -31,9 +31,8 @@ namespace transport { namespace core { -class Forwarder : public utils::Singleton<Forwarder> { +class Forwarder { static constexpr char forwarder_config_section[] = "forwarder"; - friend class utils::Singleton<Forwarder>; public: Forwarder(); @@ -47,6 +46,7 @@ class Forwarder : public utils::Singleton<Forwarder> { Connector::Id registerLocalConnector( asio::io_service &io_service, Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback); Forwarder &deleteConnector(Connector::Id id); @@ -58,9 +58,11 @@ class Forwarder : public utils::Singleton<Forwarder> { void stop(); private: - void onPacketFromListener(Connector *connector, utils::MemBuf &packet_buffer, + void onPacketFromListener(Connector *connector, + const std::vector<utils::MemBuf::Ptr> &packets, const std::error_code &ec); - void onPacketReceived(Connector *connector, utils::MemBuf &packet_buffer, + void onPacketReceived(Connector *connector, + const std::vector<utils::MemBuf::Ptr> &packets, const std::error_code &ec); void onPacketSent(Connector *connector, const std::error_code &ec); void onConnectorClosed(Connector *connector); @@ -86,5 +88,20 @@ class Forwarder : public utils::Singleton<Forwarder> { Configuration config_; }; +class ForwarderGlobal : public ::utils::Singleton<ForwarderGlobal> { + friend class utils::Singleton<ForwarderGlobal>; + + public: + ~ForwarderGlobal() {} + std::shared_ptr<Forwarder> &getReference() { return forwarder_; } + + private: + ForwarderGlobal() : forwarder_(std::make_shared<Forwarder>()) {} + + private: + std::shared_ptr<Forwarder> forwarder_; +}; + } // namespace core -} // namespace transport
\ No newline at end of file + +} // namespace transport diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.cc b/libtransport/src/io_modules/forwarder/forwarder_module.cc index 4f95b9ca0..0ced84ab4 100644 --- a/libtransport/src/io_modules/forwarder/forwarder_module.cc +++ b/libtransport/src/io_modules/forwarder/forwarder_module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -25,11 +25,10 @@ ForwarderModule::ForwarderModule() : IoModule(), name_(""), connector_id_(Connector::invalid_connector), - forwarder_(Forwarder::getInstance()) {} + forwarder_ptr_(ForwarderGlobal::getInstance().getReference()), + forwarder_(*forwarder_ptr_) {} -ForwarderModule::~ForwarderModule() { - forwarder_.deleteConnector(connector_id_); -} +ForwarderModule::~ForwarderModule() {} bool ForwarderModule::isConnected() { return true; } @@ -42,7 +41,7 @@ void ForwarderModule::send(Packet &packet) { // local_faces_.at(1 - local_id_).onPacket(packet); } -void ForwarderModule::send(const uint8_t *packet, std::size_t len) { +void ForwarderModule::send(const utils::MemBuf::Ptr &buffer) { // not supported throw errors::NotImplementedException(); } @@ -58,11 +57,13 @@ void ForwarderModule::closeConnection() { } void ForwarderModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name) { connector_id_ = forwarder_.registerLocalConnector( - io_service, std::move(receive_callback), std::move(reconnect_callback)); + io_service, std::move(receive_callback), std::move(sent_callback), + std::move(reconnect_callback)); name_ = app_name; } @@ -78,7 +79,9 @@ void ForwarderModule::connect(bool is_consumer) { std::uint32_t ForwarderModule::getMtu() { return interface_mtu; } -bool ForwarderModule::isControlMessage(const uint8_t *message) { return false; } +bool ForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { + return false; +} extern "C" IoModule *create_module(void) { return new ForwarderModule(); } diff --git a/libtransport/src/io_modules/forwarder/forwarder_module.h b/libtransport/src/io_modules/forwarder/forwarder_module.h index 58bfb7996..52a12b67e 100644 --- a/libtransport/src/io_modules/forwarder/forwarder_module.h +++ b/libtransport/src/io_modules/forwarder/forwarder_module.h @@ -38,11 +38,12 @@ class ForwarderModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; @@ -51,15 +52,19 @@ class ForwarderModule : public IoModule { std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; void closeConnection() override; private: + static void initForwarder(); + + private: std::string name_; Connector::Id connector_id_; + std::shared_ptr<Forwarder> forwarder_ptr_; Forwarder &forwarder_; }; diff --git a/libtransport/src/io_modules/forwarder/udp_tunnel.cc b/libtransport/src/io_modules/forwarder/udp_tunnel.cc deleted file mode 100644 index bf6a69b92..000000000 --- a/libtransport/src/io_modules/forwarder/udp_tunnel.cc +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - */ - -#include <glog/logging.h> -#include <hicn/transport/utils/branch_prediction.h> -#include <io_modules/forwarder/errors.h> -#include <io_modules/forwarder/udp_tunnel.h> - -#include <iostream> -#include <thread> -#include <vector> - -namespace transport { -namespace core { - -UdpTunnelConnector::~UdpTunnelConnector() {} - -void UdpTunnelConnector::connect(const std::string &hostname, uint16_t port, - const std::string &bind_address, - uint16_t bind_port) { - if (state_ == State::CLOSED) { - state_ = State::CONNECTING; - endpoint_iterator_ = resolver_.resolve({hostname, std::to_string(port)}); - remote_endpoint_send_ = *endpoint_iterator_; - socket_->open(remote_endpoint_send_.protocol()); - - if (!bind_address.empty() && bind_port != 0) { - using namespace asio::ip; - socket_->bind( - udp::endpoint(address::from_string(bind_address), bind_port)); - } - - state_ = State::CONNECTED; - - remote_endpoint_ = Endpoint(remote_endpoint_send_); - local_endpoint_ = Endpoint(socket_->local_endpoint()); - - doRecvPacket(); - -#ifdef LINUX - send_timer_.expires_from_now(std::chrono::microseconds(50)); - send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, - std::placeholders::_1)); -#endif - } -} - -void UdpTunnelConnector::send(Packet &packet) { - strand_->post([this, pkt{packet.shared_from_this()}]() { - bool write_in_progress = !output_buffer_.empty(); - output_buffer_.push_back(std::move(pkt)); - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - if (!write_in_progress) { - doSendPacket(); - } - } else { - data_available_ = true; - } - }); -} - -void UdpTunnelConnector::send(const uint8_t *packet, std::size_t len) {} - -void UdpTunnelConnector::close() { - DLOG_IF(INFO, VLOG_IS_ON(2)) << "UDPTunnelConnector::close"; - state_ = State::CLOSED; - bool is_socket_owned = socket_.use_count() == 1; - if (is_socket_owned) { - io_service_.dispatch([this]() { - this->socket_->close(); - // on_close_callback_(shared_from_this()); - }); - } -} - -void UdpTunnelConnector::doSendPacket() { -#ifdef LINUX - send_timer_.expires_from_now(std::chrono::microseconds(50)); - send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, - std::placeholders::_1)); -#else - auto packet = output_buffer_.front().get(); - auto array = std::vector<asio::const_buffer>(); - - const ::utils::MemBuf *current = packet; - do { - array.push_back(asio::const_buffer(current->data(), current->length())); - current = current->next(); - } while (current != packet); - - socket_->async_send_to( - std::move(array), remote_endpoint_send_, - strand_->wrap([this](std::error_code ec, std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - sent_callback_(this, make_error_code(forwarder_error::success)); - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - sendFailed(); - sent_callback_(this, ec); - } - - output_buffer_.pop_front(); - if (!output_buffer_.empty()) { - doSendPacket(); - } - })); -#endif -} - -#ifdef LINUX -void UdpTunnelConnector::writeHandler(std::error_code ec) { - if (TRANSPORT_EXPECT_FALSE(state_ != State::CONNECTED)) { - return; - } - - auto len = std::min(output_buffer_.size(), std::size_t(Connector::max_burst)); - - if (len) { - int m = 0; - for (auto &p : output_buffer_) { - auto packet = p.get(); - ::utils::MemBuf *current = packet; - int b = 0; - do { - // array.push_back(asio::const_buffer(current->data(), - // current->length())); - tx_iovecs_[m][b].iov_base = current->writableData(); - tx_iovecs_[m][b].iov_len = current->length(); - current = current->next(); - b++; - } while (current != packet); - - tx_msgs_[m].msg_hdr.msg_iov = tx_iovecs_[m]; - tx_msgs_[m].msg_hdr.msg_iovlen = b; - tx_msgs_[m].msg_hdr.msg_name = remote_endpoint_send_.data(); - tx_msgs_[m].msg_hdr.msg_namelen = remote_endpoint_send_.size(); - m++; - - if (--len == 0) { - break; - } - } - - int retval = sendmmsg(socket_->native_handle(), tx_msgs_, m, MSG_DONTWAIT); - if (retval > 0) { - while (retval--) { - output_buffer_.pop_front(); - } - } else if (retval != EWOULDBLOCK && retval != EAGAIN) { - LOG(ERROR) << "Error sending messages! " << strerror(errno) - << " << retval"; - return; - } - } - - if (!output_buffer_.empty()) { - send_timer_.expires_from_now(std::chrono::microseconds(50)); - send_timer_.async_wait(std::bind(&UdpTunnelConnector::writeHandler, this, - std::placeholders::_1)); - } -} - -void UdpTunnelConnector::readHandler(std::error_code ec) { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; - - if (TRANSPORT_EXPECT_TRUE(!ec)) { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - if (current_position_ == 0) { - for (int i = 0; i < max_burst; i++) { - auto read_buffer = getRawBuffer(); - rx_iovecs_[i][0].iov_base = read_buffer.first; - rx_iovecs_[i][0].iov_len = read_buffer.second; - rx_msgs_[i].msg_hdr.msg_iov = rx_iovecs_[i]; - rx_msgs_[i].msg_hdr.msg_iovlen = 1; - } - } - - int res = recvmmsg(socket_->native_handle(), rx_msgs_ + current_position_, - max_burst - current_position_, MSG_DONTWAIT, nullptr); - if (res < 0) { - LOG(ERROR) << "Error receiving messages! " << strerror(errno) << " " - << res; - return; - } - - for (int i = 0; i < res; i++) { - auto packet = getPacketFromBuffer( - reinterpret_cast<uint8_t *>( - rx_msgs_[current_position_].msg_hdr.msg_iov[0].iov_base), - rx_msgs_[current_position_].msg_len); - receiveSuccess(*packet); - receive_callback_(this, *packet, - make_error_code(forwarder_error::success)); - ++current_position_; - } - - doRecvPacket(); - } else { - LOG(ERROR) - << "Error in UDP: Receiving packets from a not connected socket."; - } - } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { - LOG(ERROR) << "The connection has been closed by the application."; - return; - } else { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - // receive_callback_(this, *read_msg_, ec); - LOG(ERROR) << "Error in UDP connector: " << ec.value() << " " - << ec.message(); - } else { - LOG(ERROR) << "Error in connector while not connected. " << ec.value() - << " " << ec.message(); - } - } -} -#endif - -void UdpTunnelConnector::doRecvPacket() { -#ifdef LINUX - if (state_ == State::CONNECTED) { -#if ((ASIO_VERSION / 100 % 1000) < 11) - socket_->async_receive(asio::null_buffers(), -#else - socket_->async_wait(asio::ip::tcp::socket::wait_read, -#endif - std::bind(&UdpTunnelConnector::readHandler, this, - std::placeholders::_1)); - } -#else - DLOG_IF(INFO, VLOG_IS_ON(3)) << "UdpTunnelConnector receive packet"; - read_msg_ = getRawBuffer(); - socket_->async_receive_from( - asio::buffer(read_msg_.first, read_msg_.second), remote_endpoint_recv_, - [this](std::error_code ec, std::size_t length) { - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "UdpTunnelConnector received packet length=" << length; - if (TRANSPORT_EXPECT_TRUE(!ec)) { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - auto packet = getPacketFromBuffer(read_msg_.first, length); - receiveSuccess(*packet); - receive_callback_(this, *packet, - make_error_code(forwarder_error::success)); - doRecvPacket(); - } else { - LOG(ERROR) << "Error in UDP: Receiving packets from a not " - "connected socket."; - } - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - LOG(ERROR) << "The connection has been closed by the application."; - return; - } else { - if (TRANSPORT_EXPECT_TRUE(state_ == State::CONNECTED)) { - LOG(ERROR) << "Error in UDP connector: " << ec.value() - << ec.message(); - } else { - LOG(ERROR) << "Error while not connected"; - } - } - }); -#endif -} - -void UdpTunnelConnector::doConnect() { - asio::async_connect( - *socket_, endpoint_iterator_, - [this](std::error_code ec, asio::ip::udp::resolver::iterator) { - if (!ec) { - state_ = State::CONNECTED; - doRecvPacket(); - - if (data_available_) { - data_available_ = false; - doSendPacket(); - } - } else { - LOG(ERROR) << "UDP Connection failed!!!"; - timer_.expires_from_now(std::chrono::milliseconds(500)); - timer_.async_wait(std::bind(&UdpTunnelConnector::doConnect, this)); - } - }); -} - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/hicn-light-ng/CMakeLists.txt b/libtransport/src/io_modules/hicn-light-ng/CMakeLists.txt new file mode 100644 index 000000000..325a8bd1d --- /dev/null +++ b/libtransport/src/io_modules/hicn-light-ng/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (c) 2021-2022 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(Libhicnctrl ${CURRENT_VERSION} REQUIRED NO_MODULE) + + if (DISABLE_SHARED_LIBRARIES) + set(LIBTYPE static) + else() + set(LIBTYPE shared) + endif() + + list(APPEND LIBHICNCTRL_LIBRARIES hicn::hicnctrl.${LIBTYPE}) +else() + if (DISABLE_SHARED_LIBRARIES) + if (WIN32) + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC}) + else () + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_STATIC} log) + endif () + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_STATIC} + ) + else() + set(LIBHICNCTRL_LIBRARIES ${LIBHICNCTRL_SHARED}) + list(APPEND DEPENDENCIES + ${LIBHICNCTRL_SHARED} + ) + endif() +endif() + +list(APPEND MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.h +) + +list(APPEND MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.cc +) + +build_module(hicnlightng_module + SHARED + SOURCES ${MODULE_SOURCE_FILES} + DEPENDS ${DEPENDENCIES} + COMPONENT ${LIBTRANSPORT_COMPONENT} + LINK_LIBRARIES PRIVATE ${LIBHICNCTRL_LIBRARIES} + INCLUDE_DIRS + PRIVATE + ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + ${Libhicnctrl_INCLUDE_DIRS} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} +) diff --git a/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.cc b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.cc new file mode 100644 index 000000000..f67bd9447 --- /dev/null +++ b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.cc @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <core/udp_connector.h> +#include <io_modules/hicn-light-ng/hicn_forwarder_module.h> + +extern "C" { +#include <hicn/ctrl/hicn-light-ng.h> +} + +namespace transport { + +namespace core { + +HicnForwarderModule::HicnForwarderModule() + : IoModule(), connector_(nullptr), seq_(0) {} + +HicnForwarderModule::~HicnForwarderModule() {} + +void HicnForwarderModule::connect(bool is_consumer) { + connector_->connect("localhost", 9695); + connector_->setRole(is_consumer ? Connector::Role::CONSUMER + : Connector::Role::PRODUCER); +} + +bool HicnForwarderModule::isConnected() { return connector_->isConnected(); } + +void HicnForwarderModule::send(Packet &packet) { + IoModule::send(packet); + packet.setChecksum(); + connector_->send(packet); +} + +void HicnForwarderModule::send(const utils::MemBuf::Ptr &packet) { + counters_.tx_packets++; + counters_.tx_bytes += packet->length(); + + // Perfect forwarding + connector_->send(packet); +} + +void HicnForwarderModule::registerRoute(const Prefix &prefix) { + auto command = createCommandRoute(prefix.toSockaddr(), + (uint8_t)prefix.getPrefixLength()); + if (!command) { + // TODO error + return; + } + send(command); +} + +void HicnForwarderModule::sendMapme() { + auto command = createCommandMapmeSendUpdate(); + if (!command) { + // TODO error + return; + } + send(command); +} + +void HicnForwarderModule::setForwardingStrategy(const Prefix &prefix, + std::string &strategy) { + auto command = createCommandSetForwardingStrategy( + prefix.toSockaddr(), (uint8_t)prefix.getPrefixLength(), strategy); + if (!command) { + // TODO error + return; + } + send(command); +} + +void HicnForwarderModule::closeConnection() { + auto command = createCommandDeleteConnection(); + if (!command) { + // TODO error + return; + } + + connector_->setSentCallback([](Connector *c, const std::error_code &ec) { + if (!ec) { + c->close(); + } + }); + + send(command); +} + +void HicnForwarderModule::init( + Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, + Connector::OnReconnectCallback &&reconnect_callback, + asio::io_service &io_service, const std::string &app_name) { + if (!connector_) { + connector_.reset(new UdpTunnelConnector( + io_service, std::move(receive_callback), std::move(sent_callback), + nullptr, std::move(reconnect_callback))); + } +} + +void HicnForwarderModule::processControlMessageReply( + utils::MemBuf &packet_buffer) { + if (packet_buffer.data()[0] == NACK_LIGHT) { + throw errors::RuntimeException( + "Received Nack message from hicn light forwarder."); + } +} + +std::uint32_t HicnForwarderModule::getMtu() { return interface_mtu; } + +bool HicnForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { + return packet_buffer.data()[0] == ACK_LIGHT || + packet_buffer.data()[0] == NACK_LIGHT; +} + +/** + * @return A valid msg_route_add_t structure if the command was successful, or + * with .command_id == COMMAND_TYPE_UNDEFINED in case of error. + */ +utils::MemBuf::Ptr HicnForwarderModule::createCommandRoute( + std::unique_ptr<sockaddr> &&addr, uint8_t prefix_length) { + utils::MemBuf::Ptr ret = utils::MemBuf::create(sizeof(msg_route_add_t)); + auto command = reinterpret_cast<msg_route_add_t *>(ret->writableData()); + ret->append(sizeof(msg_route_add_t)); + std::memset(command, 0, sizeof(*command)); + + if (!IS_VALID_FAMILY(addr->sa_family)) return nullptr; + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .cost = 1, + .family = (uint8_t)addr->sa_family, + .len = prefix_length, + }, + }; + + switch (addr->sa_family) { + case AF_INET: + command->payload.address.v4.as_inaddr = + ((sockaddr_in *)addr.get())->sin_addr; + break; + case AF_INET6: + command->payload.address.v6.as_in6addr = + ((sockaddr_in6 *)addr.get())->sin6_addr; + break; + } + snprintf(command->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + "SELF"); + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandDeleteConnection() { + utils::MemBuf::Ptr ret = + utils::MemBuf::create(sizeof(msg_connection_remove_t)); + auto command = + reinterpret_cast<msg_connection_remove_t *>(ret->writableData()); + ret->append(sizeof(msg_connection_remove_t)); + std::memset(command, 0, sizeof(*command)); + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_REMOVE, + .length = 1, + .seq_num = 0, + }, + }; + + snprintf(command->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + "SELF"); + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandMapmeSendUpdate() { + utils::MemBuf::Ptr ret = + utils::MemBuf::create(sizeof(msg_mapme_send_update_t)); + auto command = + reinterpret_cast<msg_mapme_send_update_t *>(ret->writableData()); + ret->append(sizeof(msg_mapme_send_update_t)); + std::memset(command, 0, sizeof(*command)); + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SEND_UPDATE, + .length = 1, + .seq_num = seq_++, + }, + }; + + return ret; +} + +utils::MemBuf::Ptr HicnForwarderModule::createCommandSetForwardingStrategy( + std::unique_ptr<sockaddr> &&addr, uint32_t prefix_len, + std::string strategy) { + utils::MemBuf::Ptr ret = utils::MemBuf::create(sizeof(msg_strategy_set_t)); + auto command = reinterpret_cast<msg_strategy_set_t *>(ret->writableData()); + ret->append(sizeof(msg_strategy_set_t)); + std::memset(command, 0, sizeof(*command)); + + if (!IS_VALID_FAMILY(addr->sa_family)) return nullptr; + + strategy_type_t strategy_type = strategy_type_from_str(strategy.c_str()); + if (strategy_type == STRATEGY_TYPE_UNDEFINED) return nullptr; + + *command = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_STRATEGY_SET, + .length = 1, + .seq_num = seq_++, + }, + .payload = + { + .family = (uint8_t)addr->sa_family, + .len = (uint8_t)prefix_len, + .type = (uint8_t)strategy_type, + }, + }; + + switch (addr->sa_family) { + case AF_INET: + command->payload.address.v4.as_inaddr = + ((sockaddr_in *)addr.get())->sin_addr; + break; + case AF_INET6: + command->payload.address.v6.as_in6addr = + ((sockaddr_in6 *)addr.get())->sin6_addr; + break; + } + + return ret; +} + +extern "C" IoModule *create_module(void) { return new HicnForwarderModule(); } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/io_modules/udp/hicn_forwarder_module.h b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.h index 845db73bf..0bf82757d 100644 --- a/libtransport/src/io_modules/udp/hicn_forwarder_module.h +++ b/libtransport/src/io_modules/hicn-light-ng/hicn_forwarder_module.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -18,15 +18,17 @@ #include <hicn/transport/core/io_module.h> #include <hicn/transport/core/prefix.h> +extern "C" { +#include <hicn/ctrl/hicn-light-ng.h> +} + namespace transport { namespace core { -class UdpSocketConnector; +class UdpTunnelConnector; class HicnForwarderModule : public IoModule { - static constexpr uint8_t ack_code = 0xc2; - static constexpr uint8_t nack_code = 0xc3; static constexpr std::uint16_t interface_mtu = 1500; public: @@ -56,27 +58,45 @@ class HicnForwarderModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; void registerRoute(const Prefix &prefix) override; + void sendMapme() override; + + void setForwardingStrategy(const Prefix &prefix, + std::string &strategy) override; + std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; void closeConnection() override; private: - UdpSocketConnector *connector_; + utils::MemBuf::Ptr createCommandRoute(std::unique_ptr<sockaddr> &&addr, + uint8_t prefix_length); + utils::MemBuf::Ptr createCommandDeleteConnection(); + utils::MemBuf::Ptr createCommandMapmeSendUpdate(); + utils::MemBuf::Ptr createCommandSetForwardingStrategy( + std::unique_ptr<sockaddr> &&addr, uint32_t prefix_len, + std::string strategy); + + private: + std::shared_ptr<UdpTunnelConnector> connector_; + + /* Sequence number used for sending control messages */ + uint32_t seq_; }; extern "C" IoModule *create_module(void); diff --git a/libtransport/src/io_modules/loopback/CMakeLists.txt b/libtransport/src/io_modules/loopback/CMakeLists.txt index b5ae0b7f7..817effb3b 100644 --- a/libtransport/src/io_modules/loopback/CMakeLists.txt +++ b/libtransport/src/io_modules/loopback/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -20,11 +20,11 @@ list(APPEND MODULE_SOURCE_FILES ) build_module(loopback_module - SHARED SOURCES ${MODULE_SOURCE_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + INCLUDE_DIRS + PRIVATE ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} ${Libhicn_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ) diff --git a/libtransport/src/io_modules/loopback/local_face.cc b/libtransport/src/io_modules/loopback/local_face.cc index b73444330..7ef3f1a59 100644 --- a/libtransport/src/io_modules/loopback/local_face.cc +++ b/libtransport/src/io_modules/loopback/local_face.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/io_modules/loopback/local_face.h b/libtransport/src/io_modules/loopback/local_face.h index 1f4101447..f54f38afa 100644 --- a/libtransport/src/io_modules/loopback/local_face.h +++ b/libtransport/src/io_modules/loopback/local_face.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/io_modules/loopback/loopback_module.cc b/libtransport/src/io_modules/loopback/loopback_module.cc index f7dd5e7b0..5b7ed5f61 100644 --- a/libtransport/src/io_modules/loopback/loopback_module.cc +++ b/libtransport/src/io_modules/loopback/loopback_module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -41,7 +41,7 @@ void LoopbackModule::send(Packet &packet) { local_faces_.at(1 - local_id_)->send(packet); } -void LoopbackModule::send(const uint8_t *packet, std::size_t len) { +void LoopbackModule::send(const utils::MemBuf::Ptr &buffer) { // not supported throw errors::NotImplementedException(); } @@ -57,6 +57,7 @@ void LoopbackModule::closeConnection() { } void LoopbackModule::init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name) { @@ -64,8 +65,9 @@ void LoopbackModule::init(Connector::PacketReceivedCallback &&receive_callback, local_id_ = global_counter_++; local_faces_.emplace( local_faces_.begin() + local_id_, - new LocalConnector(io_service, std::move(receive_callback), nullptr, - nullptr, std::move(reconnect_callback))); + new LocalConnector(io_service, std::move(receive_callback), + std::move(sent_callback), nullptr, + std::move(reconnect_callback))); } } @@ -75,7 +77,9 @@ void LoopbackModule::processControlMessageReply(utils::MemBuf &packet_buffer) { std::uint32_t LoopbackModule::getMtu() { return interface_mtu; } -bool LoopbackModule::isControlMessage(const uint8_t *message) { return false; } +bool LoopbackModule::isControlMessage(utils::MemBuf &packet_buffer) { + return false; +} extern "C" IoModule *create_module(void) { return new LoopbackModule(); } diff --git a/libtransport/src/io_modules/loopback/loopback_module.h b/libtransport/src/io_modules/loopback/loopback_module.h index 219fa8841..2779ae7e3 100644 --- a/libtransport/src/io_modules/loopback/loopback_module.h +++ b/libtransport/src/io_modules/loopback/loopback_module.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -36,11 +36,12 @@ class LoopbackModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; @@ -49,7 +50,7 @@ class LoopbackModule : public IoModule { std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; diff --git a/libtransport/src/io_modules/memif/CMakeLists.txt b/libtransport/src/io_modules/memif/CMakeLists.txt index fc1c1f135..134ac1db6 100644 --- a/libtransport/src/io_modules/memif/CMakeLists.txt +++ b/libtransport/src/io_modules/memif/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -11,18 +11,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -find_package(Vpp REQUIRED) -find_package(Libmemif REQUIRED) + +############################################################## +# Dependencies and third party libs +############################################################## +find_package(Vpp ${VPP_DEFAULT_VERSION} EXACT REQUIRED) if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - find_package(HicnPlugin REQUIRED) - find_package(SafeVapi REQUIRED) + find_package(HicnPlugin ${CURRENT_VERSION} REQUIRED) + find_package(SafeVapi ${CURRENT_VERSION} REQUIRED) else() list(APPEND DEPENDENCIES ${SAFE_VAPI_SHARED} ) endif() +list(APPEND DEPENDENCIES + ${MEMIF_THIRD_PARTY_DEPENDENCIES} +) + +############################################################## +# Sources +############################################################## list(APPEND MODULE_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.h ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h @@ -32,23 +42,23 @@ list(APPEND MODULE_HEADER_FILES list(APPEND MODULE_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/hicn_vapi.c - ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc ${CMAKE_CURRENT_SOURCE_DIR}/memif_vapi.c ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_module.cc ) build_module(memif_module - SHARED SOURCES ${MODULE_SOURCE_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBTRANSPORT_COMPONENT}-io-modules - LINK_LIBRARIES ${LIBMEMIF_LIBRARIES} ${SAFE_VAPI_LIBRARIES} + OBJECT_LIBRARIES ${MEMIF_THIRD_PARTY_OBJECT_LIBRARIES} + LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} ${SAFE_VAPI_LIBRARIES} INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - ${VPP_INCLUDE_DIRS} - ${LIBMEMIF_INCLUDE_DIRS} - ${SAFE_VAPI_INCLUDE_DIRS} + PUBLIC + ${MEMIF_THIRD_PARTY_INCLUDE_DIRS} + ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + ${VPP_INCLUDE_DIRS} + ${LIBMEMIF_INCLUDE_DIRS} + ${SAFE_VAPI_INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} ) diff --git a/libtransport/src/io_modules/memif/hicn_vapi.c b/libtransport/src/io_modules/memif/hicn_vapi.c index 6d78026ab..753679f54 100644 --- a/libtransport/src/io_modules/memif/hicn_vapi.c +++ b/libtransport/src/io_modules/memif/hicn_vapi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -27,8 +27,6 @@ #include <vlibapi/api.h> #include <vlibmemory/api.h> #include <vnet/ip/format.h> -#include <vnet/ip/ip4_packet.h> -#include <vnet/ip/ip6_packet.h> #include <vpp_plugins/hicn/error.h> #include <vppinfra/error.h> @@ -46,9 +44,6 @@ u8 *format_vl_api_address_union(u8 *s, va_list *args) { return NULL; } /*********************************************************************************/ -DEFINE_VAPI_MSG_IDS_HICN_API_JSON -DEFINE_VAPI_MSG_IDS_IP_API_JSON - static vapi_error_e register_prod_app_cb( vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, vapi_payload_hicn_api_register_prod_app_reply *reply) { @@ -78,7 +73,7 @@ int hicn_vapi_register_prod_app(vapi_ctx_t ctx, vapi_msg_hicn_api_register_prod_app *msg = vapi_alloc_hicn_api_register_prod_app(ctx); - if (ip46_address_is_ip4((ip46_address_t *)&input_params->prefix->address)) { + if (ip_address_is_v4((ip_address_t *)&input_params->prefix->address)) { memcpy(&msg->payload.prefix.address.un.ip4, &input_params->prefix->address, sizeof(ip4_address_t)); msg->payload.prefix.address.af = ADDRESS_IP4; @@ -190,7 +185,7 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, vapi_msg_ip_route_add_del *msg = vapi_alloc_ip_route_add_del(ctx, 1); msg->payload.is_add = 1; - if (ip46_address_is_ip4((ip46_address_t *)(input_params->prod_addr))) { + if (ip_address_is_v4((ip_address_t *)(input_params->prod_addr))) { memcpy(&msg->payload.route.prefix.address.un.ip4, &input_params->prefix->address.v4, sizeof(ip4_address_t)); msg->payload.route.prefix.address.af = ADDRESS_IP4; @@ -204,7 +199,7 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, msg->payload.route.paths[0].sw_if_index = ~0; msg->payload.route.paths[0].table_id = 0; - if (ip46_address_is_ip4((ip46_address_t *)(input_params->prod_addr))) { + if (ip_address_is_v4((ip_address_t *)(input_params->prod_addr))) { memcpy(&(msg->payload.route.paths[0].nh.address.ip4), input_params->prod_addr->v4.as_u8, sizeof(ip4_address_t)); msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; @@ -214,7 +209,7 @@ int hicn_vapi_register_route(vapi_ctx_t ctx, msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; } - msg->payload.route.paths[0].type = FIB_API_PATH_FLAG_NONE; + msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; int ret = vapi_ip_route_add_del(ctx, msg, reigster_route_cb, NULL); diff --git a/libtransport/src/io_modules/memif/hicn_vapi.h b/libtransport/src/io_modules/memif/hicn_vapi.h index e94c97749..967179f68 100644 --- a/libtransport/src/io_modules/memif/hicn_vapi.h +++ b/libtransport/src/io_modules/memif/hicn_vapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/io_modules/memif/memif_connector.cc b/libtransport/src/io_modules/memif/memif_connector.cc deleted file mode 100644 index 68ad52b63..000000000 --- a/libtransport/src/io_modules/memif/memif_connector.cc +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <glog/logging.h> -#include <hicn/transport/errors/not_implemented_exception.h> -#include <io_modules/memif/memif_connector.h> -#include <sys/epoll.h> - -#include <cstdlib> - -extern "C" { -#include <memif/libmemif.h> -}; - -#define CANCEL_TIMER 1 - -namespace transport { - -namespace core { - -struct memif_connection { - uint16_t index; - /* memif conenction handle */ - memif_conn_handle_t conn; - /* transmit queue id */ - uint16_t tx_qid; - /* tx buffers */ - memif_buffer_t *tx_bufs; - /* allocated tx buffers counter */ - /* number of tx buffers pointing to shared memory */ - uint16_t tx_buf_num; - /* rx buffers */ - memif_buffer_t *rx_bufs; - /* allcoated rx buffers counter */ - /* number of rx buffers pointing to shared memory */ - uint16_t rx_buf_num; - /* interface ip address */ - uint8_t ip_addr[4]; -}; - -std::once_flag MemifConnector::flag_; -utils::EpollEventReactor MemifConnector::main_event_reactor_; - -MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, - PacketSentCallback &&packet_sent, - OnCloseCallback &&close_callback, - OnReconnectCallback &&on_reconnect, - asio::io_service &io_service, - std::string app_name) - : Connector(std::move(receive_callback), std::move(packet_sent), - std::move(close_callback), std::move(on_reconnect)), - memif_worker_(nullptr), - timer_set_(false), - send_timer_(std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), - disconnect_timer_( - std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), - io_service_(io_service), - work_(asio::make_work_guard(io_service_)), - memif_connection_(std::make_unique<memif_connection_t>()), - tx_buf_counter_(0), - is_reconnection_(false), - data_available_(false), - app_name_(app_name), - socket_filename_("") { - std::call_once(MemifConnector::flag_, &MemifConnector::init, this); -} - -MemifConnector::~MemifConnector() { close(); } - -void MemifConnector::init() { - /* initialize memory interface */ - int err = memif_init(controlFdUpdate, const_cast<char *>(app_name_.c_str()), - nullptr, nullptr, nullptr); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_init: " << memif_strerror(err); - } -} - -void MemifConnector::connect(uint32_t memif_id, long memif_mode) { - state_ = State::CONNECTING; - - memif_id_ = memif_id; - socket_filename_ = "/run/vpp/memif.sock"; - - createMemif(memif_id, memif_mode, nullptr); - - while (state_ != State::CONNECTED) { - MemifConnector::main_event_reactor_.runOneEvent(); - } - - int err; - - /* get interrupt queue id */ - int fd = -1; - err = memif_get_queue_efd(memif_connection_->conn, 0, &fd); - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_get_queue_efd: " << memif_strerror(err); - return; - } - - // Remove fd from main epoll - main_event_reactor_.delFileDescriptor(fd); - - // Add fd to epoll of instance - event_reactor_.addFileDescriptor( - fd, EPOLLIN, [this](const utils::Event &evt) -> int { - return onInterrupt(memif_connection_->conn, this, 0); - }); - - memif_worker_ = std::make_unique<std::thread>( - std::bind(&MemifConnector::threadMain, this)); -} - -int MemifConnector::createMemif(uint32_t index, uint8_t mode, char *s) { - memif_connection_t *c = memif_connection_.get(); - - /* setting memif connection arguments */ - memif_conn_args_t args; - memset(&args, 0, sizeof(args)); - - args.is_master = mode; - args.log2_ring_size = MEMIF_LOG2_RING_SIZE; - args.buffer_size = MEMIF_BUF_SIZE; - args.num_s2m_rings = 1; - args.num_m2s_rings = 1; - strncpy((char *)args.interface_name, IF_NAME, strlen(IF_NAME) + 1); - args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; - - int err; - - err = memif_create_socket(&args.socket, socket_filename_.c_str(), nullptr); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - throw errors::RuntimeException(memif_strerror(err)); - } - - args.interface_id = index; - /* last argument for memif_create (void * private_ctx) is used by user - to identify connection. this context is returned with callbacks */ - - /* default interrupt */ - if (s == nullptr) { - err = memif_create(&c->conn, &args, onConnect, onDisconnect, onInterrupt, - this); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - throw errors::RuntimeException(memif_strerror(err)); - } - } - - c->index = (uint16_t)index; - c->tx_qid = 0; - /* alloc memif buffers */ - c->rx_buf_num = 0; - c->rx_bufs = static_cast<memif_buffer_t *>( - malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); - c->tx_buf_num = 0; - c->tx_bufs = static_cast<memif_buffer_t *>( - malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); - - // memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, 0); - - return 0; -} - -int MemifConnector::deleteMemif() { - memif_connection_t *c = memif_connection_.get(); - - if (c->rx_bufs) { - free(c->rx_bufs); - } - - c->rx_bufs = nullptr; - c->rx_buf_num = 0; - - if (c->tx_bufs) { - free(c->tx_bufs); - } - - c->tx_bufs = nullptr; - c->tx_buf_num = 0; - - int err; - /* disconenct then delete memif connection */ - err = memif_delete(&c->conn); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_delete: " << memif_strerror(err); - } - - if (TRANSPORT_EXPECT_FALSE(c->conn != nullptr)) { - LOG(ERROR) << "memif delete fail"; - } - - return 0; -} - -int MemifConnector::controlFdUpdate(int fd, uint8_t events, void *private_ctx) { - /* convert memif event definitions to epoll events */ - if (events & MEMIF_FD_EVENT_DEL) { - return MemifConnector::main_event_reactor_.delFileDescriptor(fd); - } - - uint32_t evt = 0; - - if (events & MEMIF_FD_EVENT_READ) { - evt |= EPOLLIN; - } - - if (events & MEMIF_FD_EVENT_WRITE) { - evt |= EPOLLOUT; - } - - if (events & MEMIF_FD_EVENT_MOD) { - return MemifConnector::main_event_reactor_.modFileDescriptor(fd, evt); - } - - return MemifConnector::main_event_reactor_.addFileDescriptor( - fd, evt, [](const utils::Event &evt) -> int { - uint32_t event = 0; - int memif_err = 0; - - if (evt.events & EPOLLIN) { - event |= MEMIF_FD_EVENT_READ; - } - - if (evt.events & EPOLLOUT) { - event |= MEMIF_FD_EVENT_WRITE; - } - - if (evt.events & EPOLLERR) { - event |= MEMIF_FD_EVENT_ERROR; - } - - memif_err = memif_control_fd_handler(evt.data.fd, event); - - if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_control_fd_handler: " - << memif_strerror(memif_err); - } - - return 0; - }); -} - -int MemifConnector::bufferAlloc(long n, uint16_t qid) { - memif_connection_t *c = memif_connection_.get(); - int err; - uint16_t r; - /* set data pointer to shared memory and set buffer_len to shared mmeory - * buffer len */ - err = memif_buffer_alloc(c->conn, qid, c->tx_bufs, n, &r, 2000); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_buffer_alloc: " << memif_strerror(err); - return -1; - } - - c->tx_buf_num += r; - return r; -} - -int MemifConnector::txBurst(uint16_t qid) { - memif_connection_t *c = memif_connection_.get(); - int err; - uint16_t r; - /* inform peer memif interface about data in shared memory buffers */ - /* mark memif buffers as free */ - err = memif_tx_burst(c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_tx_burst: " << memif_strerror(err); - } - - // err = memif_refill_queue(c->conn, qid, r, 0); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_tx_burst: " << memif_strerror(err); - c->tx_buf_num -= r; - return -1; - } - - c->tx_buf_num -= r; - return 0; -} - -void MemifConnector::sendCallback(const std::error_code &ec) { - timer_set_ = false; - - if (TRANSPORT_EXPECT_TRUE(!ec && state_ == State::CONNECTED)) { - doSend(); - } -} - -void MemifConnector::processInputBuffer(std::uint16_t total_packets) { - utils::MemBuf::Ptr ptr; - - for (; total_packets > 0; total_packets--) { - if (input_buffer_.pop(ptr)) { - receive_callback_(this, *ptr, std::make_error_code(std::errc(0))); - } - } -} - -/* informs user about connected status. private_ctx is used by user to identify - connection (multiple connections WIP) */ -int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { - MemifConnector *connector = (MemifConnector *)private_ctx; - connector->state_ = State::CONNECTED; - memif_refill_queue(conn, 0, -1, 0); - - return 0; -} - -/* informs user about disconnected status. private_ctx is used by user to - identify connection (multiple connections WIP) */ -int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { - MemifConnector *connector = (MemifConnector *)private_ctx; - connector->state_ = State::CLOSED; - return 0; -} - -void MemifConnector::threadMain() { event_reactor_.runEventLoop(1000); } - -int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, - uint16_t qid) { - MemifConnector *connector = (MemifConnector *)private_ctx; - - memif_connection_t *c = connector->memif_connection_.get(); - int err = MEMIF_ERR_SUCCESS, ret_val; - uint16_t total_packets = 0; - uint16_t rx; - - do { - err = memif_rx_burst(conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); - ret_val = err; - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && - err != MEMIF_ERR_NOBUF)) { - LOG(ERROR) << "memif_rx_burst: " << memif_strerror(err); - goto error; - } - - c->rx_buf_num += rx; - - if (TRANSPORT_EXPECT_FALSE(connector->io_service_.stopped())) { - LOG(ERROR) << "socket stopped: ignoring " << rx << " packets"; - goto error; - } - - std::size_t packet_length; - for (int i = 0; i < rx; i++) { - auto buffer = connector->getRawBuffer(); - packet_length = (c->rx_bufs + i)->len; - std::memcpy(buffer.first, (c->rx_bufs + i)->data, packet_length); - auto packet = connector->getPacketFromBuffer(buffer.first, packet_length); - - if (!connector->input_buffer_.push(std::move(packet))) { - LOG(ERROR) << "Error pushing packet. Ring buffer full."; - - // TODO Here we should consider the possibility to signal the congestion - // to the application, that would react properly (e.g. slow down - // message) - } - } - - /* mark memif buffers and shared memory buffers as free */ - /* free processed buffers */ - - err = memif_refill_queue(conn, qid, rx, 0); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); - } - - c->rx_buf_num -= rx; - total_packets += rx; - - } while (ret_val == MEMIF_ERR_NOBUF); - - connector->io_service_.post( - std::bind(&MemifConnector::processInputBuffer, connector, total_packets)); - - return 0; - -error: - err = memif_refill_queue(c->conn, qid, rx, 0); - - if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { - LOG(ERROR) << "memif_buffer_free: " << memif_strerror(err); - } - c->rx_buf_num -= rx; - - return 0; -} - -void MemifConnector::close() { - if (state_ != State::CLOSED) { - disconnect_timer_->expiresFromNow(std::chrono::microseconds(50)); - disconnect_timer_->asyncWait([this](const std::error_code &ec) { - deleteMemif(); - event_reactor_.stop(); - }); - - if (memif_worker_ && memif_worker_->joinable()) { - memif_worker_->join(); - } - } -} - -void MemifConnector::send(Packet &packet) { - { - utils::SpinLock::Acquire locked(write_msgs_lock_); - output_buffer_.push_back(packet.shared_from_this()); - } -#if CANCEL_TIMER - if (!timer_set_) { - timer_set_ = true; - send_timer_->expiresFromNow(std::chrono::microseconds(50)); - send_timer_->asyncWait( - std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); - } -#endif -} - -int MemifConnector::doSend() { - std::size_t max = 0; - int32_t n = 0; - std::size_t size = 0; - - { - utils::SpinLock::Acquire locked(write_msgs_lock_); - size = output_buffer_.size(); - } - - do { - max = size < MAX_MEMIF_BUFS ? size : MAX_MEMIF_BUFS; - n = bufferAlloc(max, memif_connection_->tx_qid); - - if (TRANSPORT_EXPECT_FALSE(n < 0)) { - LOG(ERROR) << "Error allocating buffers."; - return -1; - } - - for (uint16_t i = 0; i < n; i++) { - utils::SpinLock::Acquire locked(write_msgs_lock_); - - auto packet = output_buffer_.front().get(); - const utils::MemBuf *current = packet; - std::size_t offset = 0; - uint8_t *shared_buffer = - reinterpret_cast<uint8_t *>(memif_connection_->tx_bufs[i].data); - do { - std::memcpy(shared_buffer + offset, current->data(), current->length()); - offset += current->length(); - current = current->next(); - } while (current != packet); - - memif_connection_->tx_bufs[i].len = uint32_t(offset); - - output_buffer_.pop_front(); - } - - txBurst(memif_connection_->tx_qid); - - utils::SpinLock::Acquire locked(write_msgs_lock_); - size = output_buffer_.size(); - } while (size > 0); - - return 0; -} - -void MemifConnector::send(const uint8_t *packet, std::size_t len) { - throw errors::NotImplementedException(); -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/memif/memif_vapi.c b/libtransport/src/io_modules/memif/memif_vapi.c index b3da2b012..54e2c3134 100644 --- a/libtransport/src/io_modules/memif/memif_vapi.c +++ b/libtransport/src/io_modules/memif/memif_vapi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -22,8 +22,6 @@ #include <vapi/vapi_safe.h> #include <vppinfra/clib.h> -DEFINE_VAPI_MSG_IDS_MEMIF_API_JSON - static vapi_error_e memif_details_cb(vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, vapi_payload_memif_details *reply) { @@ -45,6 +43,9 @@ static vapi_error_e memif_details_cb(vapi_ctx_t ctx, void *callback_ctx, int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id) { vapi_lock(); vapi_msg_memif_dump *msg = vapi_alloc_memif_dump(ctx); + + // Initialize memif id to 0 + *memif_id = 0; int ret = vapi_memif_dump(ctx, msg, memif_details_cb, memif_id); vapi_unlock(); return ret; diff --git a/libtransport/src/io_modules/memif/memif_vapi.h b/libtransport/src/io_modules/memif/memif_vapi.h index bcf06ed43..f5f0639e7 100644 --- a/libtransport/src/io_modules/memif/memif_vapi.h +++ b/libtransport/src/io_modules/memif/memif_vapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -44,6 +44,10 @@ typedef struct memif_output_params_s { int memif_vapi_get_next_memif_id(vapi_ctx_t ctx, uint32_t *memif_id); +int memif_vapi_create_socket(vapi_ctx_t ctx, + memif_create_params_t *input_params, + memif_output_params_t *output_params); + int memif_vapi_create_memif(vapi_ctx_t ctx, memif_create_params_t *input_params, memif_output_params_t *output_params); diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.cc b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc index 44c8376df..65260077a 100644 --- a/libtransport/src/io_modules/memif/vpp_forwarder_module.cc +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,16 +13,16 @@ * limitations under the License. */ +#include <core/memif_connector.h> #include <glog/logging.h> #include <hicn/transport/config.h> #include <hicn/transport/errors/not_implemented_exception.h> #include <io_modules/memif/hicn_vapi.h> -#include <io_modules/memif/memif_connector.h> #include <io_modules/memif/memif_vapi.h> #include <io_modules/memif/vpp_forwarder_module.h> extern "C" { -#include <memif/libmemif.h> +#include <libmemif.h> }; typedef enum { MASTER = 0, SLAVE = 1 } memif_role_t; @@ -39,21 +39,24 @@ namespace core { VPPForwarderModule::VPPForwarderModule() : IoModule(), connector_(nullptr), + memif_id_(0), sw_if_index_(~0), face_id1_(~0), face_id2_(~0), is_consumer_(false) {} -VPPForwarderModule::~VPPForwarderModule() { delete connector_; } +VPPForwarderModule::~VPPForwarderModule() {} void VPPForwarderModule::init( Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name) { if (!connector_) { - connector_ = - new MemifConnector(std::move(receive_callback), 0, 0, - std::move(reconnect_callback), io_service, app_name); + connector_ = std::make_unique<MemifConnector>( + std::move(receive_callback), std::move(sent_callback), + Connector::OnCloseCallback(0), std::move(reconnect_callback), + io_service, app_name); } } @@ -62,7 +65,7 @@ void VPPForwarderModule::processControlMessageReply( throw errors::NotImplementedException(); } -bool VPPForwarderModule::isControlMessage(const uint8_t *message) { +bool VPPForwarderModule::isControlMessage(utils::MemBuf &packet_buffer) { return false; } @@ -73,12 +76,12 @@ void VPPForwarderModule::send(Packet &packet) { connector_->send(packet); } -void VPPForwarderModule::send(const uint8_t *packet, std::size_t len) { +void VPPForwarderModule::send(const utils::MemBuf::Ptr &buffer) { counters_.tx_packets++; - counters_.tx_bytes += len; + counters_.tx_bytes += buffer->length(); // Perfect forwarding - connector_->send(packet, len); + connector_->send(buffer); } std::uint32_t VPPForwarderModule::getMtu() { return interface_mtu; } @@ -170,7 +173,8 @@ void VPPForwarderModule::connect(bool is_consumer) { consumerConnection(); } - connector_->connect(memif_id_, 0); + connector_->connect(memif_id_, 0 /* is_master = false */, + memif_socket_filename); connector_->setRole(is_consumer_ ? Connector::Role::CONSUMER : Connector::Role::PRODUCER); } @@ -207,7 +211,8 @@ void VPPForwarderModule::registerRoute(const Prefix &prefix) { throw errors::RuntimeException(hicn_vapi_get_error_string(ret)); } - inet6_address_ = *output.prod_addr; + std::memcpy(inet6_address_.v6.as_u8, output.prod_addr->v6.as_u8, + sizeof(inet6_address_)); face_id1_ = output.face_id; } else { @@ -228,8 +233,6 @@ void VPPForwarderModule::registerRoute(const Prefix &prefix) { void VPPForwarderModule::closeConnection() { if (VPPForwarderModule::sock_) { - connector_->close(); - if (is_consumer_) { hicn_del_face_app_input_params params; params.face_id = face_id1_; @@ -242,6 +245,8 @@ void VPPForwarderModule::closeConnection() { hicn_vapi_face_prod_del(VPPForwarderModule::sock_, ¶ms); } + connector_->close(); + if (sw_if_index_ != uint32_t(~0)) { int ret = memif_vapi_delete_memif(VPPForwarderModule::sock_, sw_if_index_); diff --git a/libtransport/src/io_modules/memif/vpp_forwarder_module.h b/libtransport/src/io_modules/memif/vpp_forwarder_module.h index 8c4114fed..162ee0ca5 100644 --- a/libtransport/src/io_modules/memif/vpp_forwarder_module.h +++ b/libtransport/src/io_modules/memif/vpp_forwarder_module.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -32,7 +32,8 @@ namespace core { class MemifConnector; class VPPForwarderModule : public IoModule { - static constexpr std::uint16_t interface_mtu = 1500; + static inline std::uint16_t interface_mtu = 1500; + static inline std::string const memif_socket_filename = "/run/vpp/memif.sock"; public: VPPForwarderModule(); @@ -41,11 +42,12 @@ class VPPForwarderModule : public IoModule { void connect(bool is_consumer) override; void send(Packet &packet) override; - void send(const uint8_t *packet, std::size_t len) override; + void send(const utils::MemBuf::Ptr &buffer) override; bool isConnected() override; void init(Connector::PacketReceivedCallback &&receive_callback, + Connector::PacketSentCallback &&sent_callback, Connector::OnReconnectCallback &&reconnect_callback, asio::io_service &io_service, const std::string &app_name = "Libtransport") override; @@ -54,7 +56,7 @@ class VPPForwarderModule : public IoModule { std::uint32_t getMtu() override; - bool isControlMessage(const uint8_t *message) override; + bool isControlMessage(utils::MemBuf &packet_buffer) override; void processControlMessageReply(utils::MemBuf &packet_buffer) override; @@ -66,7 +68,7 @@ class VPPForwarderModule : public IoModule { void producerConnection(); private: - MemifConnector *connector_; + std::shared_ptr<MemifConnector> connector_; uint32_t memif_id_; uint32_t sw_if_index_; // A consumer socket in vpp has two faces (ipv4 and ipv6) diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc b/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc deleted file mode 100644 index 62efdc3a5..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_connector.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <core/raw_socket_connector.h> -#include <hicn/transport/utils/conversions.h> -#include <net/if.h> -#include <netdb.h> -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#define MY_DEST_MAC0 0x0a -#define MY_DEST_MAC1 0x7b -#define MY_DEST_MAC2 0x7c -#define MY_DEST_MAC3 0x1c -#define MY_DEST_MAC4 0x4a -#define MY_DEST_MAC5 0x14 - -namespace transport { - -namespace core { - -RawSocketConnector::RawSocketConnector( - PacketReceivedCallback &&receive_callback, - OnReconnect &&on_reconnect_callback, asio::io_service &io_service, - std::string app_name) - : Connector(std::move(receive_callback), std::move(on_reconnect_callback)), - io_service_(io_service), - socket_(io_service_, raw_protocol(PF_PACKET, SOCK_RAW)), - // resolver_(io_service_), - timer_(io_service_), - read_msg_(packet_pool_.makePtr(nullptr)), - data_available_(false), - app_name_(app_name) { - memset(&link_layer_address_, 0, sizeof(link_layer_address_)); -} - -RawSocketConnector::~RawSocketConnector() {} - -void RawSocketConnector::connect(const std::string &interface_name, - const std::string &mac_address_str) { - state_ = ConnectorState::CONNECTING; - memset(ðernet_header_, 0, sizeof(ethernet_header_)); - struct ifreq ifr; - struct ifreq if_mac; - uint8_t mac_address[6]; - - utils::convertStringToMacAddress(mac_address_str, mac_address); - - // Get interface mac address - int fd = static_cast<int>(socket_.native_handle()); - - /* Get the index of the interface to send on */ - memset(&ifr, 0, sizeof(struct ifreq)); - strncpy(ifr.ifr_name, interface_name.c_str(), interface_name.size()); - - // if (ioctl(fd, SIOCGIFINDEX, &if_idx) < 0) { - // perror("SIOCGIFINDEX"); - // } - - /* Get the MAC address of the interface to send on */ - memset(&if_mac, 0, sizeof(struct ifreq)); - strncpy(if_mac.ifr_name, interface_name.c_str(), interface_name.size()); - if (ioctl(fd, SIOCGIFHWADDR, &if_mac) < 0) { - perror("SIOCGIFHWADDR"); - throw errors::RuntimeException("Interface does not exist"); - } - - /* Ethernet header */ - for (int i = 0; i < 6; i++) { - ethernet_header_.ether_shost[i] = - ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[i]; - ethernet_header_.ether_dhost[i] = mac_address[i]; - } - - /* Ethertype field */ - ethernet_header_.ether_type = htons(ETH_P_IPV6); - - strcpy(ifr.ifr_name, interface_name.c_str()); - - if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) { - memcpy(link_layer_address_.sll_addr, ifr.ifr_hwaddr.sa_data, 6); - } - - // memset(&ifr, 0, sizeof(ifr)); - // ioctl(fd, SIOCGIFFLAGS, &ifr); - // ifr.ifr_flags |= IFF_PROMISC; - // ioctl(fd, SIOCSIFFLAGS, &ifr); - - link_layer_address_.sll_family = AF_PACKET; - link_layer_address_.sll_protocol = htons(ETH_P_ALL); - link_layer_address_.sll_ifindex = if_nametoindex(interface_name.c_str()); - link_layer_address_.sll_hatype = 1; - link_layer_address_.sll_halen = 6; - - // startConnectionTimer(); - doConnect(); - doRecvPacket(); -} - -void RawSocketConnector::send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent) { - if (packet_sent != 0) { - socket_.async_send( - asio::buffer(packet, len), - [packet_sent](std::error_code ec, std::size_t /*length*/) { - packet_sent(); - }); - } else { - if (state_ == ConnectorState::CONNECTED) { - socket_.send(asio::buffer(packet, len)); - } - } -} - -void RawSocketConnector::send(const Packet::MemBufPtr &packet) { - io_service_.post([this, packet]() { - bool write_in_progress = !output_buffer_.empty(); - output_buffer_.push_back(std::move(packet)); - if (TRANSPORT_EXPECT_TRUE(state_ == ConnectorState::CONNECTED)) { - if (!write_in_progress) { - doSendPacket(); - } else { - // Tell the handle connect it has data to write - data_available_ = true; - } - } - }); -} - -void RawSocketConnector::close() { - io_service_.post([this]() { socket_.close(); }); -} - -void RawSocketConnector::doSendPacket() { - auto packet = output_buffer_.front().get(); - auto array = std::vector<asio::const_buffer>(); - - const utils::MemBuf *current = packet; - do { - array.push_back(asio::const_buffer(current->data(), current->length())); - current = current->next(); - } while (current != packet); - - socket_.async_send( - std::move(array), - [this /*, packet*/](std::error_code ec, std::size_t bytes_transferred) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - output_buffer_.pop_front(); - if (!output_buffer_.empty()) { - doSendPacket(); - } - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - } - }); -} - -void RawSocketConnector::doRecvPacket() { - read_msg_ = getPacket(); - socket_.async_receive( - asio::buffer(read_msg_->writableData(), packet_size), - [this](std::error_code ec, std::size_t bytes_transferred) mutable { - if (!ec) { - // Ignore packets that are not for us - uint8_t *dst_mac_address = const_cast<uint8_t *>(read_msg_->data()); - if (!std::memcmp(dst_mac_address, ethernet_header_.ether_shost, - ETHER_ADDR_LEN)) { - read_msg_->append(bytes_transferred); - read_msg_->trimStart(sizeof(struct ether_header)); - receive_callback_(std::move(read_msg_)); - } - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - } - doRecvPacket(); - }); -} - -void RawSocketConnector::doConnect() { - state_ = ConnectorState::CONNECTED; - socket_.bind(raw_endpoint(&link_layer_address_, sizeof(link_layer_address_))); -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_connector.h b/libtransport/src/io_modules/raw_socket/raw_socket_connector.h deleted file mode 100644 index 06892b3d8..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_connector.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <core/connector.h> -#include <hicn/transport/config.h> -#include <hicn/transport/core/asio_wrapper.h> -#include <hicn/transport/core/name.h> -#include <linux/if_packet.h> -#include <net/ethernet.h> -#include <sys/socket.h> - -#include <deque> - -namespace transport { - -namespace core { - -using asio::generic::raw_protocol; -using raw_endpoint = asio::generic::basic_endpoint<raw_protocol>; - -class RawSocketConnector : public Connector { - public: - RawSocketConnector(PacketReceivedCallback &&receive_callback, - OnReconnect &&reconnect_callback, - asio::io_service &io_service, - std::string app_name = "Libtransport"); - - ~RawSocketConnector() override; - - void send(const Packet::MemBufPtr &packet) override; - - void send(const uint8_t *packet, std::size_t len, - const PacketSentCallback &packet_sent = 0) override; - - void close() override; - - void connect(const std::string &interface_name, - const std::string &mac_address_str); - - private: - void doConnect(); - - void doRecvPacket(); - - void doSendPacket(); - - private: - asio::io_service &io_service_; - raw_protocol::socket socket_; - - struct ether_header ethernet_header_; - - struct sockaddr_ll link_layer_address_; - - asio::steady_timer timer_; - - utils::ObjectPool<utils::MemBuf>::Ptr read_msg_; - - bool data_available_; - std::string app_name_; -}; - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc b/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc deleted file mode 100644 index dcf489f59..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_interface.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <core/raw_socket_interface.h> -#include <hicn/transport/utils/linux.h> - -#include <fstream> - -namespace transport { - -namespace core { - -static std::string config_folder_path = "/etc/transport/interface.conf.d"; - -RawSocketInterface::RawSocketInterface(RawSocketConnector &connector) - : ForwarderInterface<RawSocketInterface, RawSocketConnector>(connector) {} - -RawSocketInterface::~RawSocketInterface() {} - -void RawSocketInterface::connect(bool is_consumer) { - std::string complete_filename = - config_folder_path + std::string("/") + output_interface_; - - std::ifstream is(complete_filename); - std::string interface; - - if (is) { - is >> remote_mac_address_; - } - - // Get interface ip address - struct sockaddr_in6 address = {0}; - utils::retrieveInterfaceAddress(output_interface_, &address); - - std::memcpy(&inet6_address_.v6.as_u8, &address.sin6_addr, - sizeof(address.sin6_addr)); - connector_.connect(output_interface_, remote_mac_address_); -} - -void RawSocketInterface::registerRoute(Prefix &prefix) { return; } - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/raw_socket/raw_socket_interface.h b/libtransport/src/io_modules/raw_socket/raw_socket_interface.h deleted file mode 100644 index 7036cac7e..000000000 --- a/libtransport/src/io_modules/raw_socket/raw_socket_interface.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <core/forwarder_interface.h> -#include <core/raw_socket_connector.h> -#include <hicn/transport/core/prefix.h> - -#include <atomic> -#include <deque> - -namespace transport { - -namespace core { - -class RawSocketInterface - : public ForwarderInterface<RawSocketInterface, RawSocketConnector> { - public: - typedef RawSocketConnector ConnectorType; - - RawSocketInterface(RawSocketConnector &connector); - - ~RawSocketInterface(); - - void connect(bool is_consumer); - - void registerRoute(Prefix &prefix); - - std::uint16_t getMtu() { return interface_mtu; } - - TRANSPORT_ALWAYS_INLINE static bool isControlMessageImpl( - const uint8_t *message) { - return false; - } - - TRANSPORT_ALWAYS_INLINE void processControlMessageReplyImpl( - Packet::MemBufPtr &&packet_buffer) {} - - TRANSPORT_ALWAYS_INLINE void closeConnection(){}; - - private: - static constexpr std::uint16_t interface_mtu = 1500; - std::string remote_mac_address_; -}; - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/udp/CMakeLists.txt b/libtransport/src/io_modules/udp/CMakeLists.txt deleted file mode 100644 index b9c19d063..000000000 --- a/libtransport/src/io_modules/udp/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -list(APPEND MODULE_HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.h - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.h -) - -list(APPEND MODULE_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_module.cc - ${CMAKE_CURRENT_SOURCE_DIR}/udp_socket_connector.cc -) - -build_module(hicnlight_module - SHARED - SOURCES ${MODULE_SOURCE_FILES} - DEPENDS ${DEPENDENCIES} - COMPONENT ${LIBTRANSPORT_COMPONENT} - INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} - DEFINITIONS ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILE_FLAGS} -) diff --git a/libtransport/src/io_modules/udp/hicn_forwarder_module.cc b/libtransport/src/io_modules/udp/hicn_forwarder_module.cc deleted file mode 100644 index ba08dd8c0..000000000 --- a/libtransport/src/io_modules/udp/hicn_forwarder_module.cc +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2017-2020 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <io_modules/udp/hicn_forwarder_module.h> -#include <io_modules/udp/udp_socket_connector.h> - -union AddressLight { - uint32_t ipv4; - struct in6_addr ipv6; -}; - -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; - uint32_t seq_num; -} CommandHeader; - -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; - uint32_t seq_num; - char symbolic_or_connid[16]; - union AddressLight address; - uint16_t cost; - uint8_t address_type; - uint8_t len; -} RouteToSelfCommand; - -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; - uint32_t seq_num; - char symbolic_or_connid[16]; -} DeleteSelfConnectionCommand; - -namespace { -static constexpr uint8_t addr_inet = 1; -static constexpr uint8_t addr_inet6 = 2; -static constexpr uint8_t add_route_command = 3; -static constexpr uint8_t delete_connection_command = 5; -static constexpr uint8_t request_light = 0xc0; -static constexpr char identifier[] = "SELF"; - -void fillCommandHeader(CommandHeader *header) { - // Allocate and fill the header - header->message_type = request_light; - header->length = 1; -} - -RouteToSelfCommand createCommandRoute(std::unique_ptr<sockaddr> &&addr, - uint8_t prefix_length) { - RouteToSelfCommand command = {0}; - - // check and set IP address - if (addr->sa_family == AF_INET) { - command.address_type = addr_inet; - command.address.ipv4 = ((sockaddr_in *)addr.get())->sin_addr.s_addr; - } else if (addr->sa_family == AF_INET6) { - command.address_type = addr_inet6; - command.address.ipv6 = ((sockaddr_in6 *)addr.get())->sin6_addr; - } - - // Fill remaining payload fields -#ifndef _WIN32 - strcpy(command.symbolic_or_connid, identifier); -#else - strcpy_s(command.symbolic_or_connid, 16, identifier); -#endif - command.cost = 1; - command.len = (uint8_t)prefix_length; - - // Allocate and fill the header - command.command_id = add_route_command; - fillCommandHeader((CommandHeader *)&command); - - return command; -} - -DeleteSelfConnectionCommand createCommandDeleteConnection() { - DeleteSelfConnectionCommand command = {0}; - fillCommandHeader((CommandHeader *)&command); - command.command_id = delete_connection_command; - -#ifndef _WIN32 - strcpy(command.symbolic_or_connid, identifier); -#else - strcpy_s(command.symbolic_or_connid, 16, identifier); -#endif - - return command; -} - -} // namespace - -namespace transport { - -namespace core { - -HicnForwarderModule::HicnForwarderModule() : IoModule(), connector_(nullptr) {} - -HicnForwarderModule::~HicnForwarderModule() {} - -void HicnForwarderModule::connect(bool is_consumer) { - connector_->connect(); - connector_->setRole(is_consumer ? Connector::Role::CONSUMER - : Connector::Role::PRODUCER); -} - -bool HicnForwarderModule::isConnected() { return connector_->isConnected(); } - -void HicnForwarderModule::send(Packet &packet) { - IoModule::send(packet); - packet.setChecksum(); - connector_->send(packet); -} - -void HicnForwarderModule::send(const uint8_t *packet, std::size_t len) { - counters_.tx_packets++; - counters_.tx_bytes += len; - - // Perfect forwarding - connector_->send(packet, len); -} - -void HicnForwarderModule::registerRoute(const Prefix &prefix) { - auto command = createCommandRoute(prefix.toSockaddr(), - (uint8_t)prefix.getPrefixLength()); - send((uint8_t *)&command, sizeof(RouteToSelfCommand)); -} - -void HicnForwarderModule::closeConnection() { - auto command = createCommandDeleteConnection(); - send((uint8_t *)&command, sizeof(DeleteSelfConnectionCommand)); - connector_->close(); -} - -void HicnForwarderModule::init( - Connector::PacketReceivedCallback &&receive_callback, - Connector::OnReconnectCallback &&reconnect_callback, - asio::io_service &io_service, const std::string &app_name) { - if (!connector_) { - connector_ = new UdpSocketConnector(std::move(receive_callback), nullptr, - nullptr, std::move(reconnect_callback), - io_service, app_name); - } -} - -void HicnForwarderModule::processControlMessageReply( - utils::MemBuf &packet_buffer) { - if (packet_buffer.data()[0] == nack_code) { - throw errors::RuntimeException( - "Received Nack message from hicn light forwarder."); - } -} - -std::uint32_t HicnForwarderModule::getMtu() { return interface_mtu; } - -bool HicnForwarderModule::isControlMessage(const uint8_t *message) { - return message[0] == ack_code || message[0] == nack_code; -} - -extern "C" IoModule *create_module(void) { return new HicnForwarderModule(); } - -} // namespace core - -} // namespace transport diff --git a/libtransport/src/io_modules/udp/udp_socket_connector.cc b/libtransport/src/io_modules/udp/udp_socket_connector.cc deleted file mode 100644 index 1412d8c07..000000000 --- a/libtransport/src/io_modules/udp/udp_socket_connector.cc +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef _WIN32 -#include <hicn/transport/portability/win_portability.h> -#endif - -#include <glog/logging.h> -#include <hicn/transport/errors/errors.h> -#include <hicn/transport/utils/object_pool.h> -#include <io_modules/udp/udp_socket_connector.h> - -#include <thread> -#include <vector> - -namespace transport { - -namespace core { - -UdpSocketConnector::UdpSocketConnector( - PacketReceivedCallback &&receive_callback, PacketSentCallback &&packet_sent, - OnCloseCallback &&close_callback, OnReconnectCallback &&on_reconnect, - asio::io_service &io_service, std::string app_name) - : Connector(std::move(receive_callback), std::move(packet_sent), - std::move(close_callback), std::move(on_reconnect)), - io_service_(io_service), - socket_(io_service_), - resolver_(io_service_), - connection_timer_(io_service_), - read_msg_(std::make_pair(nullptr, 0)), - is_reconnection_(false), - data_available_(false), - app_name_(app_name) {} - -UdpSocketConnector::~UdpSocketConnector() {} - -void UdpSocketConnector::connect(std::string ip_address, std::string port) { - endpoint_iterator_ = resolver_.resolve( - {ip_address, port, asio::ip::resolver_query_base::numeric_service}); - - state_ = Connector::State::CONNECTING; - doConnect(); -} - -void UdpSocketConnector::send(const uint8_t *packet, std::size_t len) { - socket_.async_send(asio::buffer(packet, len), - [this](std::error_code ec, std::size_t /*length*/) { - if (sent_callback_) { - sent_callback_(this, ec); - } - }); -} - -void UdpSocketConnector::send(Packet &packet) { - io_service_.post([this, _packet{packet.shared_from_this()}]() { - bool write_in_progress = !output_buffer_.empty(); - output_buffer_.push_back(std::move(_packet)); - if (TRANSPORT_EXPECT_TRUE(state_ == Connector::State::CONNECTED)) { - if (!write_in_progress) { - doWrite(); - } - } else { - // Tell the handle connect it has data to write - data_available_ = true; - } - }); -} - -void UdpSocketConnector::close() { - if (io_service_.stopped()) { - doClose(); - } else { - io_service_.dispatch(std::bind(&UdpSocketConnector::doClose, this)); - } -} - -void UdpSocketConnector::doClose() { - if (state_ != Connector::State::CLOSED) { - state_ = Connector::State::CLOSED; - if (socket_.is_open()) { - socket_.shutdown(asio::ip::tcp::socket::shutdown_type::shutdown_both); - socket_.close(); - } - } -} - -void UdpSocketConnector::doWrite() { - auto packet = output_buffer_.front().get(); - auto array = std::vector<asio::const_buffer>(); - - const utils::MemBuf *current = packet; - do { - array.push_back(asio::const_buffer(current->data(), current->length())); - current = current->next(); - } while (current != packet); - - socket_.async_send(std::move(array), [this](std::error_code ec, - std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - output_buffer_.pop_front(); - if (!output_buffer_.empty()) { - doWrite(); - } - } else if (ec.value() == static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - tryReconnect(); - } - }); -} - -void UdpSocketConnector::doRead() { - read_msg_ = getRawBuffer(); - socket_.async_receive( - asio::buffer(read_msg_.first, read_msg_.second), - [this](std::error_code ec, std::size_t length) { - if (TRANSPORT_EXPECT_TRUE(!ec)) { - auto packet = getPacketFromBuffer(read_msg_.first, length); - receive_callback_(this, *packet, std::make_error_code(std::errc(0))); - doRead(); - } else if (ec.value() == - static_cast<int>(std::errc::operation_canceled)) { - // The connection has been closed by the application. - return; - } else { - LOG(ERROR) << ec.value() << " " << ec.message(); - tryReconnect(); - } - }); -} - -void UdpSocketConnector::tryReconnect() { - if (state_ == Connector::State::CONNECTED) { - LOG(ERROR) << "Connection lost. Trying to reconnect..."; - state_ = Connector::State::CONNECTING; - is_reconnection_ = true; - io_service_.post([this]() { - if (socket_.is_open()) { - socket_.shutdown(asio::ip::tcp::socket::shutdown_type::shutdown_both); - socket_.close(); - } - - doConnect(); - startConnectionTimer(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - }); - } -} - -void UdpSocketConnector::doConnect() { - asio::async_connect( - socket_, endpoint_iterator_, - [this](std::error_code ec, udp::resolver::iterator) { - if (!ec) { - connection_timer_.cancel(); - state_ = Connector::State::CONNECTED; - doRead(); - - if (data_available_) { - data_available_ = false; - doWrite(); - } - - if (is_reconnection_) { - is_reconnection_ = false; - } - - on_reconnect_callback_(this); - } else { - doConnect(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - }); -} - -bool UdpSocketConnector::checkConnected() { - return state_ == Connector::State::CONNECTED; -} - -void UdpSocketConnector::startConnectionTimer() { - connection_timer_.expires_from_now(std::chrono::seconds(60)); - connection_timer_.async_wait(std::bind(&UdpSocketConnector::handleDeadline, - this, std::placeholders::_1)); -} - -void UdpSocketConnector::handleDeadline(const std::error_code &ec) { - if (!ec) { - io_service_.post([this]() { - socket_.close(); - LOG(ERROR) << "Error connecting. Is the forwarder running?"; - }); - } -} - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/io_modules/udp/udp_socket_connector.h b/libtransport/src/io_modules/udp/udp_socket_connector.h deleted file mode 100644 index c483e14aa..000000000 --- a/libtransport/src/io_modules/udp/udp_socket_connector.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <hicn/transport/config.h> -#include <hicn/transport/core/asio_wrapper.h> -#include <hicn/transport/core/connector.h> -#include <hicn/transport/core/content_object.h> -#include <hicn/transport/core/global_object_pool.h> -#include <hicn/transport/core/interest.h> -#include <hicn/transport/core/name.h> -#include <hicn/transport/core/packet.h> -#include <hicn/transport/utils/branch_prediction.h> - -#include <deque> - -namespace transport { -namespace core { - -using asio::ip::udp; - -class UdpSocketConnector : public Connector { - public: - UdpSocketConnector(PacketReceivedCallback &&receive_callback, - PacketSentCallback &&packet_sent, - OnCloseCallback &&close_callback, - OnReconnectCallback &&on_reconnect, - asio::io_service &io_service, - std::string app_name = "Libtransport"); - - ~UdpSocketConnector() override; - - void send(Packet &packet) override; - - void send(const uint8_t *packet, std::size_t len) override; - - void close() override; - - void connect(std::string ip_address = "127.0.0.1", std::string port = "9695"); - - private: - void doConnect(); - - void doRead(); - - void doWrite(); - - void doClose(); - - bool checkConnected(); - - private: - void handleDeadline(const std::error_code &ec); - - void startConnectionTimer(); - - void tryReconnect(); - - asio::io_service &io_service_; - asio::ip::udp::socket socket_; - asio::ip::udp::resolver resolver_; - asio::ip::udp::resolver::iterator endpoint_iterator_; - asio::steady_timer connection_timer_; - - std::pair<uint8_t *, std::size_t> read_msg_; - - bool is_reconnection_; - bool data_available_; - - std::string app_name_; -}; - -} // end namespace core - -} // end namespace transport diff --git a/libtransport/src/libhicntransport-config.cmake.in b/libtransport/src/libhicntransport-config.cmake.in new file mode 100644 index 000000000..9b8c51962 --- /dev/null +++ b/libtransport/src/libhicntransport-config.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +set(Libhicntransport_VERSION_MAJOR "@VERSION_MAJOR@") +set(Libhicntransport_VERSION_MINOR "@VERSION_MINOR@") +set(Libhicntransport_VERSION_PATCH "@VERSION_PATCH@") + +set_and_check(Libhicntransport_INCLUDE_DIRS "@PACKAGE_Libhicntransport_INCLUDE_DIRS@") +include("${CMAKE_CURRENT_LIST_DIR}/libhicntransport-targets.cmake") diff --git a/libtransport/src/protocols/CMakeLists.txt b/libtransport/src/protocols/CMakeLists.txt index b763e95e2..51879a9ed 100644 --- a/libtransport/src/protocols/CMakeLists.txt +++ b/libtransport/src/protocols/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -52,9 +52,15 @@ list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/errors.cc ) -set(RAAQM_CONFIG_INSTALL_PREFIX - ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn -) +if (${CMAKE_SYSTEM_NAME} MATCHES Darwin OR ${CMAKE_SYSTEM_NAME} MATCHES Linux) + set(RAAQM_CONFIG_INSTALL_PREFIX + ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + ) +else() + set(RAAQM_CONFIG_INSTALL_PREFIX + ${CMAKE_INSTALL_PREFIX}/etc/hicn + ) +endif() set(raaqm_config_path ${RAAQM_CONFIG_INSTALL_PREFIX}/consumer.conf @@ -67,7 +73,7 @@ set(TRANSPORT_CONFIG install( FILES ${TRANSPORT_CONFIG} - DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn + DESTINATION ${RAAQM_CONFIG_INSTALL_PREFIX} COMPONENT ${LIBTRANSPORT_COMPONENT} ) diff --git a/libtransport/src/protocols/byte_stream_reassembly.cc b/libtransport/src/protocols/byte_stream_reassembly.cc index ac36d4e61..3278595b7 100644 --- a/libtransport/src/protocols/byte_stream_reassembly.cc +++ b/libtransport/src/protocols/byte_stream_reassembly.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/byte_stream_reassembly.h b/libtransport/src/protocols/byte_stream_reassembly.h index 278740bd3..bfcac3181 100644 --- a/libtransport/src/protocols/byte_stream_reassembly.h +++ b/libtransport/src/protocols/byte_stream_reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/cbr.cc b/libtransport/src/protocols/cbr.cc index 1548cc68d..446ea8b99 100644 --- a/libtransport/src/protocols/cbr.cc +++ b/libtransport/src/protocols/cbr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -37,11 +37,11 @@ void CbrTransportProtocol::afterDataUnsatisfied(uint64_t segment) {} void CbrTransportProtocol::afterContentReception( const Interest &interest, const ContentObject &content_object) { auto segment = content_object.getName().getSuffix(); - auto now = utils::SteadyClock::now(); - auto rtt = std::chrono::duration_cast<utils::Microseconds>( - now - interest_timepoints_[segment & mask]); + auto now = utils::SteadyTime::Clock::now(); + auto rtt = utils::SteadyTime::getDurationMs( + interest_timepoints_[segment & mask], now); // Update stats - updateStats(segment, rtt.count(), now); + updateStats(segment, rtt, now); } } // end namespace protocol diff --git a/libtransport/src/protocols/cbr.h b/libtransport/src/protocols/cbr.h index 41cdbc98c..c178dbf60 100644 --- a/libtransport/src/protocols/cbr.h +++ b/libtransport/src/protocols/cbr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/congestion_window_protocol.h b/libtransport/src/protocols/congestion_window_protocol.h index 36ac6eb17..f9ff208cc 100644 --- a/libtransport/src/protocols/congestion_window_protocol.h +++ b/libtransport/src/protocols/congestion_window_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/data_processing_events.h b/libtransport/src/protocols/data_processing_events.h index 28732502b..182de3ed8 100644 --- a/libtransport/src/protocols/data_processing_events.h +++ b/libtransport/src/protocols/data_processing_events.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/datagram_reassembly.cc b/libtransport/src/protocols/datagram_reassembly.cc index 069873a52..3a32c81f5 100644 --- a/libtransport/src/protocols/datagram_reassembly.cc +++ b/libtransport/src/protocols/datagram_reassembly.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/datagram_reassembly.h b/libtransport/src/protocols/datagram_reassembly.h index de294df06..0def32dd2 100644 --- a/libtransport/src/protocols/datagram_reassembly.h +++ b/libtransport/src/protocols/datagram_reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/errors.cc b/libtransport/src/protocols/errors.cc index 183fcc574..a7dd26e16 100644 --- a/libtransport/src/protocols/errors.cc +++ b/libtransport/src/protocols/errors.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/errors.h b/libtransport/src/protocols/errors.h index 58dadae5a..33d5fbee4 100644 --- a/libtransport/src/protocols/errors.h +++ b/libtransport/src/protocols/errors.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/fec/CMakeLists.txt b/libtransport/src/protocols/fec/CMakeLists.txt index 6d61ae043..8ae0a7360 100644 --- a/libtransport/src/protocols/fec/CMakeLists.txt +++ b/libtransport/src/protocols/fec/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/fec/fec.cc b/libtransport/src/protocols/fec/fec.cc index 16a04cb98..912e7a40f 100644 --- a/libtransport/src/protocols/fec/fec.cc +++ b/libtransport/src/protocols/fec/fec.cc @@ -44,6 +44,7 @@ #include "fec.h" +#include <assert.h> #include <hicn/transport/portability/platform.h> #include <stdio.h> #include <stdlib.h> @@ -60,72 +61,6 @@ #endif /* - * compatibility stuff - */ -#ifdef MSDOS /* but also for others, e.g. sun... */ -#define NEED_BCOPY -#define bcmp(a, b, n) memcmp(a, b, n) -#endif - -#ifdef ANDROID -#define bcmp(a, b, n) memcmp(a, b, n) -#endif - -#ifdef NEED_BCOPY -#define bcopy(s, d, siz) memcpy((d), (s), (siz)) -#define bzero(d, siz) memset((d), '\0', (siz)) -#endif - -/* - * stuff used for testing purposes only - */ - -#ifdef TEST -#define DEB(x) -#define DDB(x) x -#define DEBUG 0 /* minimal debugging */ -#ifdef MSDOS -#include <time.h> -struct timeval { - unsigned long ticks; -}; -#define gettimeofday(x, dummy) \ - { (x)->ticks = clock(); } -#define DIFF_T(a, b) (1 + 1000000 * (a.ticks - b.ticks) / CLOCKS_PER_SEC) -typedef unsigned long u_long; -typedef unsigned short u_short; -#else /* typically, unix systems */ -#include <sys/time.h> -#define DIFF_T(a, b) \ - (1 + 1000000 * (a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec)) -#endif - -#define TICK(t) \ - { \ - struct timeval x; \ - gettimeofday(&x, NULL); \ - t = x.tv_usec + 1000000 * (x.tv_sec & 0xff); \ - } -#define TOCK(t) \ - { \ - u_long t1; \ - TICK(t1); \ - if (t1 < t) \ - t = 256000000 + t1 - t; \ - else \ - t = t1 - t; \ - if (t == 0) t = 1; \ - } - -u_long ticks[10]; /* vars for timekeeping */ -#else -#define DEB(x) -#define DDB(x) -#define TICK(x) -#define TOCK(x) -#endif /* TEST */ - -/* * You should not need to change anything beyond this point. * The first part of the file implements linear algebra in GF. * @@ -402,31 +337,17 @@ static void matmul(gf *a, gf *b, gf *c, int n, int k, int m) { } } -#ifdef DEBUGG -/* - * returns 1 if the square matrix is identiy - * (only for test) - */ -static int is_identity(gf *m, int k) { - int row, col; - for (row = 0; row < k; row++) - for (col = 0; col < k; col++) - if ((row == col && *m != 1) || (row != col && *m != 0)) - return 0; - else - m++; - return 1; -} -#endif /* debug */ - /* * invert_mat() takes a matrix and produces its inverse * k is the size of the matrix. * (Gauss-Jordan, adapted from Numerical Recipes in C) * Return non-zero if singular. */ -DEB(int pivloops = 0; int pivswaps = 0; /* diagnostic */) +int pivloops = 0; +int pivswaps = 0; /* diagnostic */ static int invert_mat(gf *src, int k) { + assert(k > 0); + gf c, *p; int irow, icol, row, col, i, ix; @@ -436,9 +357,9 @@ static int invert_mat(gf *src, int k) { int *ipiv = (int *)my_malloc(k * sizeof(int), "ipiv"); gf *id_row = NEW_GF_MATRIX(1, k); gf *temp_row = NEW_GF_MATRIX(1, k); - - bzero(id_row, k * sizeof(gf)); - DEB(pivloops = 0; pivswaps = 0; /* diagnostic */) + memset(id_row, '\0', k * sizeof(gf)); + pivloops = 0; + pivswaps = 0; /* diagnostic */ /* * ipiv marks elements already used as pivots. */ @@ -459,7 +380,7 @@ static int invert_mat(gf *src, int k) { for (row = 0; row < k; row++) { if (ipiv[row] != 1) { for (ix = 0; ix < k; ix++) { - DEB(pivloops++;) + pivloops++; if (ipiv[ix] == 0) { if (src[row * k + ix] != 0) { irow = row; @@ -497,12 +418,9 @@ static int invert_mat(gf *src, int k) { fprintf(stderr, "singular matrix 2\n"); goto fail; } - if (c != 1) { /* otherwhise this is a NOP */ - /* - * this is done often , but optimizing is not so - * fruitful, at least in the obvious ways (unrolling) - */ - DEB(pivswaps++;) + + if (c != 1) { + pivswaps++; c = inverse[c]; pivot_row[icol] = 1; for (ix = 0; ix < k; ix++) pivot_row[ix] = gf_mul(c, pivot_row[ix]); @@ -515,7 +433,7 @@ static int invert_mat(gf *src, int k) { * we can optimize the addmul). */ id_row[icol] = 1; - if (bcmp(pivot_row, id_row, k * sizeof(gf)) != 0) { + if (memcmp(pivot_row, id_row, k * sizeof(gf)) != 0) { for (p = src, ix = 0; ix < k; ix++, p += k) { if (ix != icol) { c = p[icol]; @@ -560,6 +478,8 @@ fail: */ int invert_vdm(gf *src, int k) { + assert(k > 0); + int i, j, row, col; gf *b, *c, *p; gf t, xx; @@ -614,14 +534,8 @@ int invert_vdm(gf *src, int k) { static int fec_initialized = 0; static void init_fec() { - TICK(ticks[0]); generate_gf(); - TOCK(ticks[0]); - DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);) - TICK(ticks[0]); init_mul_table(); - TOCK(ticks[0]); - DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);) fec_initialized = 1; } @@ -680,19 +594,14 @@ struct fec_parms *fec_new(int k, int n) { * k*k vandermonde matrix, multiply right the bottom n-k rows * by the inverse, and construct the identity matrix at the top. */ - TICK(ticks[3]); invert_vdm(tmp_m, k); /* much faster than invert_mat */ matmul(tmp_m + k * k, tmp_m, retval->enc_matrix + k * k, n - k, k, k); /* * the upper matrix is I so do not bother with a slow multiply */ - bzero(retval->enc_matrix, k * k * sizeof(gf)); + memset(retval->enc_matrix, '\0', k * k * sizeof(gf)); for (p = retval->enc_matrix, col = 0; col < k; col++, p += k + 1) *p = 1; free(tmp_m); - TOCK(ticks[3]); - - DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", ticks[3]);) - DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) return retval; } @@ -708,10 +617,10 @@ void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) { if (GF_BITS > 8) sz /= 2; if (index < k) - bcopy(src[index], fec, sz * sizeof(gf)); + memcpy(fec, src[index], sz * sizeof(gf)); else if (index < code->n) { p = &(code->enc_matrix[index * k]); - bzero(fec, sz * sizeof(gf)); + memset(fec, '\0', sz * sizeof(gf)); for (i = 0; i < k; i++) addmul(fec, src[i], p[i], sz); } else fprintf(stderr, "Invalid index %d (max %d)\n", index, code->n - 1); @@ -733,22 +642,13 @@ static int shuffle(gf *pkt[], int index[], int k) { int c = index[i]; if (index[c] == c) { - DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) + fprintf(stderr, "\nshuffle, error at %d\n", i); return 1; } SWAP(index[i], index[c], int); SWAP(pkt[i], pkt[c], gf *); } } - DEB(/* just test that it works... */ - for (i = 0; i < k; i++) { - if (index[i] < k && index[i] != i) { - fprintf(stderr, "shuffle: after\n"); - for (i = 0; i < k; i++) fprintf(stderr, "%3d ", index[i]); - fprintf(stderr, "\n"); - return 1; - } - }) return 0; } @@ -757,20 +657,16 @@ static int shuffle(gf *pkt[], int index[], int k) { * indexes. The matrix must be already allocated as * a vector of k*k elements, in row-major order */ -static gf *build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[]) { +static gf *build_decode_matrix(struct fec_parms *code, int index[]) { int i, k = code->k; gf *p, *matrix = NEW_GF_MATRIX(k, k); - TICK(ticks[9]); for (i = 0, p = matrix; i < k; i++, p += k) { -#if 1 /* this is simply an optimization, not very useful indeed */ if (index[i] < k) { - bzero(p, k * sizeof(gf)); + memset(p, '\0', k * sizeof(gf)); p[i] = 1; - } else -#endif - if (index[i] < code->n) - bcopy(&(code->enc_matrix[index[i] * k]), p, k * sizeof(gf)); + } else if (index[i] < code->n) + memcpy(p, &(code->enc_matrix[index[i] * k]), k * sizeof(gf)); else { fprintf(stderr, "decode: invalid index %d (max %d)\n", index[i], code->n - 1); @@ -778,12 +674,10 @@ static gf *build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[]) { return NULL; } } - TICK(ticks[9]); if (invert_mat(matrix, k)) { free(matrix); matrix = NULL; } - TOCK(ticks[9]); return matrix; } @@ -800,39 +694,29 @@ static gf *build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[]) { */ int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) { gf *m_dec; - gf **new_pkt; + gf **new_pkt = nullptr; int row, col, k = code->k; + int i = 0; if (GF_BITS > 8) sz /= 2; if (shuffle(pkt, index, k)) /* error if true */ return 1; - m_dec = build_decode_matrix(code, pkt, index); + m_dec = build_decode_matrix(code, index); if (m_dec == NULL) return 1; /* error */ /* * do the actual decoding */ - new_pkt = (gf **)my_malloc(k * sizeof(gf *), "new pkt pointers"); + new_pkt = pkt + k; for (row = 0; row < k; row++) { if (index[row] >= k) { - new_pkt[row] = (gf *)my_malloc(sz * sizeof(gf), "new pkt buffer"); - bzero(new_pkt[row], sz * sizeof(gf)); + memset(new_pkt[i], '\0', sz * sizeof(gf)); for (col = 0; col < k; col++) - addmul(new_pkt[row], pkt[col], m_dec[row * k + col], sz); - } - } - /* - * move pkts to their final destination - */ - for (row = 0; row < k; row++) { - if (index[row] >= k) { - bcopy(new_pkt[row], pkt[row], sz * sizeof(gf)); - free(new_pkt[row]); + addmul(new_pkt[i], pkt[col], m_dec[row * k + col], sz); + i++; } } - free(new_pkt); free(m_dec); - return 0; } diff --git a/libtransport/src/protocols/fec/rely.cc b/libtransport/src/protocols/fec/rely.cc index 7a30a62e2..d4d98a90b 100644 --- a/libtransport/src/protocols/fec/rely.cc +++ b/libtransport/src/protocols/fec/rely.cc @@ -23,38 +23,39 @@ namespace transport { namespace protocol { namespace fec { -RelyEncoder::RelyEncoder(uint32_t k, uint32_t n, uint32_t seq_offset) +RelyEncoder::RelyEncoder(uint32_t k, uint32_t n, uint32_t /* seq_offset */) : RelyBase(k, n) { configure(kmtu, ktimeout, kmax_stream_size); set_repair_trigger(k_, n_ - k_, n_ - k_); } void RelyEncoder::onPacketProduced(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { // Get pointer to payload, leaving space to insert FEC header. // TODO Check if this additional header is really needed. - auto data = content_object.writableData() + offset - sizeof(fec_header); - auto length = content_object.length() - offset + sizeof(fec_header); + auto data = content_object.writableData() + offset - sizeof(fec_metadata); + auto length = content_object.length() - offset + sizeof(fec_metadata); // Check packet length does not exceed maximum length supported by the // encoder (otherwise segmentation would take place). - assert(length < max_packet_bytes()); + DCHECK(length < max_packet_bytes()); DLOG_IF(INFO, VLOG_IS_ON(4)) - << "Encoding packet of length " << length - sizeof(fec_header); + << "Encoding packet of length " << length - sizeof(fec_metadata); - // Get the suffix. With rely we need to write it in the fec_header in order to - // be able to recognize the seq number upon recovery. + // Get the suffix. With rely we need to write it in the fec_metadata in order + // to be able to recognize the seq number upon recovery. auto suffix = content_object.getName().getSuffix(); DLOG_IF(INFO, VLOG_IS_ON(4)) << "Producing packet " << suffix << " (index == " << current_index_ << ")"; - // Consume payload. Add fec_header in front before feeding payload to encoder, - // and copy original content of packet - fec_header *h = reinterpret_cast<fec_header *>(data); - fec_header copy = *h; + // Consume payload. Add fec_metadata in front before feeding payload to + // encoder, and copy original content of packet + fec_metadata *h = reinterpret_cast<fec_metadata *>(data); + fec_metadata copy = *h; h->setSeqNumberBase(suffix); + h->setMetadataBase(metadata); auto packets = consume(data, length, getCurrentTime()); - assert(packets == 1); + DCHECK(packets == 1); // Update packet counter current_index_ += packets; @@ -62,7 +63,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, // Restore original packet content and increment data pointer to the correct // position *h = copy; - data += sizeof(fec_header); + data += sizeof(fec_metadata); // Check position of this packet inside N size block auto i = current_index_ % n_; @@ -74,24 +75,24 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, // TODO Optimize it by copying only the RELY header // Be sure encoder can produce - assert(can_produce()); + DCHECK(can_produce()); // Check new payload size and make sure it fits in packet buffer auto new_payload_size = produce_bytes(); int difference = new_payload_size - length; - assert(difference > 0); - assert(content_object.ensureCapacity(difference)); + DCHECK(difference > 0); + DCHECK(content_object.ensureCapacity(difference)); // Update length DLOG_IF(INFO, VLOG_IS_ON(4)) << "The packet length will be incremented by " - << difference + sizeof(fec_header); - content_object.append(difference + sizeof(fec_header)); + << difference + sizeof(fec_metadata); + content_object.append(difference + sizeof(fec_metadata)); content_object.updateLength(); // Make sure we got a source packet, otherwise we would put a repair symbol // in a source packet - assert(rely::packet_is_systematic(produce_data())); + DCHECK(rely::packet_is_systematic(produce_data())); // Copy rely packet replacing old source packet. std::memcpy(data, produce_data(), new_payload_size); @@ -111,7 +112,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, while (can_produce()) { // The current index MUST be k_, because we enforce n - k repair to be // produced after k sources - assert(current_index_ == k_); + DCHECK(current_index_ == k_); buffer packet; if (!buffer_callback_) { @@ -130,7 +131,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, std::memcpy(packet->writableData(), produce_data(), produce_bytes()); // Push symbol in repair_packets - packets_.emplace_back(0, std::move(packet)); + packets_.emplace_back(0, metadata, std::move(packet)); // Advance the encoder produce_next(); @@ -143,7 +144,7 @@ void RelyEncoder::onPacketProduced(core::ContentObject &content_object, // If we have generated repair symbols, let's notify caller via the installed // callback if (packets_.size()) { - assert(packets_.size() == n_ - k_); + DCHECK(packets_.size() == n_ - k_); fec_callback_(packets_); packets_.clear(); current_index_ = 0; @@ -156,7 +157,7 @@ RelyDecoder::RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) } void RelyDecoder::onDataPacket(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { // Adjust pointers to point to packet payload auto data = content_object.writableData() + offset; auto size = content_object.length() - offset; @@ -164,30 +165,38 @@ void RelyDecoder::onDataPacket(core::ContentObject &content_object, // Pass payload to decoder consume(data, size, getCurrentTime()); + producePackets(); +} + +void RelyDecoder::producePackets() { // Drain decoder if possible while (can_produce()) { - // Get size of decoded packet - auto size = produce_bytes(); - - // Get buffer to copy packet in - auto packet = core::PacketManager<>::getInstance().getMemBuf(); + auto fec_header_size = sizeof(fec_metadata); + auto payload_size = produce_bytes() - sizeof(fec_metadata); - // Copy buffer - packet->append(size); - std::memcpy(packet->writableData(), produce_data(), size); + buffer packet; + if (!buffer_callback_) { + packet = core::PacketManager<>::getInstance().getMemBuf(); + packet->append(payload_size); + } else { + packet = buffer_callback_(payload_size); + } // Read seq number - fec_header *h = reinterpret_cast<fec_header *>(packet->writableData()); + const fec_metadata *h = + reinterpret_cast<const fec_metadata *>(produce_data()); uint32_t index = h->getSeqNumberBase(); + uint32_t metadata = h->getMetadataBase(); DLOG_IF(INFO, VLOG_IS_ON(4)) << "The index written in the packet is " << index; - // Remove FEC header - packet->trimStart(sizeof(fec_header)); + // Copy payload + std::memcpy(packet->writableData(), produce_data() + fec_header_size, + payload_size); // Save packet in buffer - packets_.emplace_back(index, std::move(packet)); + packets_.emplace_back(index, metadata, std::move(packet)); // Advance to next packet produce_next(); @@ -198,8 +207,28 @@ void RelyDecoder::onDataPacket(core::ContentObject &content_object, fec_callback_(packets_); packets_.clear(); } + + flushOutOfOrder(); +} + +void RelyDecoder::flushOutOfOrder() { + if (flush_timer_ == nullptr) return; + flush_timer_->cancel(); + + if (has_upcoming_flush()) { + flush_timer_->expires_from_now(std::chrono::milliseconds( + std::max((int64_t)0, upcoming_flush(getCurrentTime())))); + + flush_timer_->async_wait([this](const std::error_code &ec) { + if (ec) return; + if (has_upcoming_flush()) { + flush(getCurrentTime()); + producePackets(); + } + }); + } } } // namespace fec } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/fec/rely.h b/libtransport/src/protocols/fec/rely.h index bfbdb30bc..001a26002 100644 --- a/libtransport/src/protocols/fec/rely.h +++ b/libtransport/src/protocols/fec/rely.h @@ -33,8 +33,12 @@ namespace fec { * @brief Table of used codes. */ #define foreach_rely_fec_type \ + _(Rely, 1, 2) \ _(Rely, 1, 3) \ + _(Rely, 1, 4) \ _(Rely, 2, 3) \ + _(Rely, 2, 6) \ + _(Rely, 3, 9) \ _(Rely, 4, 5) \ _(Rely, 4, 6) \ _(Rely, 4, 7) \ @@ -74,11 +78,17 @@ class RelyBase : public virtual FECBase { * decoding operations. It may be removed once we know the meaning of the * fields in the rely header. */ - struct fec_header { - uint32_t seq_number; - + class fec_metadata { + public: void setSeqNumberBase(uint32_t suffix) { seq_number = htonl(suffix); } - uint32_t getSeqNumberBase() { return ntohl(seq_number); } + uint32_t getSeqNumberBase() const { return ntohl(seq_number); } + + void setMetadataBase(uint32_t value) { metadata = htonl(value); } + uint32_t getMetadataBase() const { return ntohl(metadata); } + + private: + uint32_t seq_number; + uint32_t metadata; }; /** @@ -112,21 +122,20 @@ class RelyBase : public virtual FECBase { #if RELY_DEBUG return time_++; #else - auto _time = utils::SteadyClock::now().time_since_epoch(); - auto time = std::chrono::duration_cast<utils::Milliseconds>(_time).count(); - return time; + return utils::SteadyTime::nowMs().count(); #endif } - protected: uint32_t k_; uint32_t n_; + std::uint32_t seq_offset_; + /** * @brief Vector of packets to be passed to caller callbacks. For encoder it * will contain the repair packets, for decoder the recovered sources. */ - std::vector<std::pair<uint32_t, buffer>> packets_; + BufferArray packets_; /** * @brief Current index to be used for local packet count. @@ -142,50 +151,54 @@ class RelyBase : public virtual FECBase { * @brief The Rely Encoder implementation. * */ -class RelyEncoder : private RelyBase, - private rely::encoder, - public ProducerFEC { +class RelyEncoder : RelyBase, rely::encoder, public ProducerFEC { public: RelyEncoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0); /** * Producers will call this function when they produce a data packet. */ - void onPacketProduced(core::ContentObject &content_object, - uint32_t offset) override; + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets */ std::size_t getFecHeaderSize() override { - return header_bytes() + sizeof(fec_header) + 4; + return header_bytes() + sizeof(fec_metadata) + 4; } - void reset() override {} + void reset() override { + // Nothing to do here + } }; -class RelyDecoder : private RelyBase, - private rely::decoder, - public ConsumerFEC { +class RelyDecoder : RelyBase, rely::decoder, public ConsumerFEC { public: RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0); /** * Consumers will call this function when they receive a data packet */ - void onDataPacket(core::ContentObject &content_object, - uint32_t offset) override; + void onDataPacket(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets */ std::size_t getFecHeaderSize() override { - return header_bytes() + sizeof(fec_header); + return header_bytes() + sizeof(fec_metadata); + } + + void reset() override { + // Nothing to do here } - void reset() override {} + private: + void producePackets(); + void flushOutOfOrder(); }; } // namespace fec } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/fec/rs.cc b/libtransport/src/protocols/fec/rs.cc index 2c23d515d..9c0a3d4fb 100644 --- a/libtransport/src/protocols/fec/rs.cc +++ b/libtransport/src/protocols/fec/rs.cc @@ -46,23 +46,26 @@ bool BlockCode::addRepairSymbol(const fec::buffer &packet, uint32_t i, to_decode_ = true; DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding symbol of size " << packet->length(); return addSymbol(packet, i, offset, - packet->length() - sizeof(fec_header) - offset); + packet->length() - sizeof(fec_header) - offset, + FECBase::INVALID_METADATA); } bool BlockCode::addSourceSymbol(const fec::buffer &packet, uint32_t i, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding source symbol of size " << packet->length() << ", offset " << offset; - return addSymbol(packet, i, offset, packet->length() - offset); + return addSymbol(packet, i, offset, packet->length() - offset, metadata); } bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i, - uint32_t offset, std::size_t size) { + uint32_t offset, std::size_t size, + uint32_t metadata) { if (size > max_buffer_size_) { max_buffer_size_ = size; } - operator[](current_block_size_++) = std::make_tuple(i, packet, offset); + operator[](current_block_size_) = RSBufferInfo(offset, i, metadata, packet); + current_block_size_++; if (current_block_size_ >= k_) { if (to_decode_) { @@ -80,12 +83,13 @@ bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i, void BlockCode::encode() { gf *data[n_]; - uint32_t base = std::get<0>(operator[](0)); + uint32_t base = operator[](0).getIndex(); // Set packet length in first 2 bytes for (uint32_t i = 0; i < k_; i++) { - auto &packet = std::get<1>(operator[](i)); - auto offset = std::get<2>(operator[](i)); + auto &packet = operator[](i).getBuffer(); + auto offset = operator[](i).getOffset(); + auto metadata_base = operator[](i).getMetadata(); auto ret = packet->ensureCapacityAndFillUnused(max_buffer_size_ + offset, 0); @@ -98,10 +102,11 @@ void BlockCode::encode() { // Buffers should hold 2 *after* the padding, in order to be // able to set the length for the encoding operation. // packet->trimStart(offset); - uint16_t *length = reinterpret_cast<uint16_t *>(packet->writableData() + - max_buffer_size_ + offset); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ + offset); auto buffer_length = packet->length() - offset; - *length = htons(buffer_length); + metadata->setPacketLength(buffer_length); + metadata->setMetadataBase(metadata_base); DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current buffer size: " << packet->length(); @@ -109,7 +114,7 @@ void BlockCode::encode() { } // Finish to fill source block with the buffers to hold the repair symbols - auto length = max_buffer_size_ + sizeof(fec_header) + LEN_SIZE_BYTES; + auto length = max_buffer_size_ + sizeof(fec_header) + METADATA_BYTES; for (uint32_t i = k_; i < n_; i++) { buffer packet; if (!params_.buffer_callback_) { @@ -133,19 +138,20 @@ void BlockCode::encode() { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current symbol size: " << packet->length(); data[i] = packet->writableData(); - operator[](i) = std::make_tuple(i, std::move(packet), uint32_t(0)); + operator[](i) = RSBufferInfo(uint32_t(0), i, FECBase::INVALID_METADATA, + std::move(packet)); } // Generate repair symbols and put them in corresponding buffers DLOG_IF(INFO, VLOG_IS_ON(4)) << "Calling encode with max_buffer_size_ = " << max_buffer_size_; for (uint32_t i = k_; i < n_; i++) { - fec_encode(code_, data, data[i], i, max_buffer_size_ + LEN_SIZE_BYTES); + fec_encode(code_, data, data[i], i, max_buffer_size_ + METADATA_BYTES); } // Re-include header in repair packets for (uint32_t i = k_; i < n_; i++) { - auto &packet = std::get<1>(operator[](i)); + auto &packet = operator[](i).getBuffer(); packet->prepend(sizeof(fec_header)); DLOG_IF(INFO, VLOG_IS_ON(4)) << "Produced repair symbol of size = " << packet->length(); @@ -153,16 +159,31 @@ void BlockCode::encode() { } void BlockCode::decode() { - gf *data[k_]; + gf *data[n_]; uint32_t index[k_]; + buffer aux_fec_packets[n_ - k_]; + // FEC packet number k0 + uint32_t k0 = 0; + + // Reorder block by index with in-place sorting + for (uint32_t i = 0; i < k_;) { + uint32_t idx = operator[](i).getIndex(); + if (idx >= k_ || idx == i) { + i++; + } else { + std::swap(operator[](i), operator[](idx)); + } + } for (uint32_t i = 0; i < k_; i++) { - auto &packet = std::get<1>(operator[](i)); - index[i] = std::get<0>(operator[](i)); - auto offset = std::get<2>(operator[](i)); + auto &packet = operator[](i).getBuffer(); + index[i] = operator[](i).getIndex(); + auto offset = operator[](i).getOffset(); + auto metadata_base = operator[](i).getMetadata(); sorted_index_[i] = index[i]; if (index[i] < k_) { + operator[](i).setReceived(); DLOG_IF(INFO, VLOG_IS_ON(4)) << "DECODE SOURCE - index " << index[i] << " - Current buffer size: " << packet->length(); @@ -173,49 +194,51 @@ void BlockCode::decode() { // able to set the length for the encoding operation packet->trimStart(offset); packet->ensureCapacityAndFillUnused(max_buffer_size_, 0); - uint16_t *length = reinterpret_cast<uint16_t *>( - packet->writableData() + max_buffer_size_ - LEN_SIZE_BYTES); - - *length = htons(packet->length()); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ - METADATA_BYTES); + metadata->setPacketLength(packet->length()); + metadata->setMetadataBase(metadata_base); } else { DLOG_IF(INFO, VLOG_IS_ON(4)) << "DECODE SYMBOL - index " << index[i] << " - Current buffer size: " << packet->length(); packet->trimStart(sizeof(fec_header) + offset); + aux_fec_packets[k0] = core::PacketManager<>::getInstance().getMemBuf(); + data[k_ + k0] = aux_fec_packets[k0]->writableData(); + k0++; } - data[i] = packet->writableData(); } - // We decode the source block DLOG_IF(INFO, VLOG_IS_ON(4)) << "Calling decode with max_buffer_size_ = " << max_buffer_size_; + fec_decode(code_, data, reinterpret_cast<int *>(index), max_buffer_size_); // Find the index in the block for recovered packets - for (uint32_t i = 0; i < k_; i++) { - if (index[i] != i) { - for (uint32_t j = 0; j < k_; j++) - if (sorted_index_[j] == uint32_t(index[i])) { - sorted_index_[j] = i; - } - } - } - - // Reorder block by index with in-place sorting - for (uint32_t i = 0; i < k_; i++) { - for (uint32_t j = sorted_index_[i]; j != i; j = sorted_index_[i]) { - std::swap(sorted_index_[j], sorted_index_[i]); - std::swap(operator[](j), operator[](i)); + for (uint32_t i = 0, j = 0; i < k_; i++) { + if (index[i] >= k_) { + operator[](i).setBuffer(aux_fec_packets[j++]); + operator[](i).setIndex(i); } } // Adjust length according to the one written in the source packet for (uint32_t i = 0; i < k_; i++) { - auto &packet = std::get<1>(operator[](i)); - uint16_t *length = reinterpret_cast<uint16_t *>( - packet->writableData() + max_buffer_size_ - LEN_SIZE_BYTES); - packet->setLength(ntohs(*length)); + auto &packet = operator[](i).getBuffer(); + fec_metadata *metadata = reinterpret_cast<fec_metadata *>( + packet->writableData() + max_buffer_size_ - METADATA_BYTES); + // Adjust buffer length + packet->setLength(metadata->getPacketLength()); + // Adjust metadata + operator[](i).setMetadata(metadata->getMetadataBase()); + + // reset the point to the beginning of the packets for all received packets + if (operator[](i).getReceived()) { + auto &packet = operator[](i).getBuffer(); + auto offset = operator[](i).getOffset(); + packet->prepend(offset); + } } } @@ -252,12 +275,11 @@ RSEncoder::RSEncoder(uint32_t k, uint32_t n, uint32_t seq_offset) source_block_(k_, n_, seq_offset_, current_code_, *this) {} void RSEncoder::consume(const fec::buffer &packet, uint32_t index, - uint32_t offset) { - if (!source_block_.addSourceSymbol(packet, index, offset)) { - std::vector<std::pair<uint32_t, buffer>> repair_packets; + uint32_t offset, uint32_t metadata) { + if (!source_block_.addSourceSymbol(packet, index, offset, metadata)) { + fec::BufferArray repair_packets; for (uint32_t i = k_; i < n_; i++) { - repair_packets.emplace_back(std::move(std::get<0>(source_block_[i])), - std::move(std::get<1>(source_block_[i]))); + repair_packets.emplace_back(std::move(source_block_[i])); } fec_callback_(repair_packets); @@ -265,9 +287,9 @@ void RSEncoder::consume(const fec::buffer &packet, uint32_t index, } void RSEncoder::onPacketProduced(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { consume(content_object.shared_from_this(), - content_object.getName().getSuffix(), offset); + content_object.getName().getSuffix(), offset, metadata); } RSDecoder::RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) @@ -276,10 +298,15 @@ RSDecoder::RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset) void RSDecoder::recoverPackets(SourceBlocks::iterator &src_block_it) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "recoverPackets for " << k_; auto &src_block = src_block_it->second; - std::vector<std::pair<uint32_t, buffer>> source_packets(k_); + auto base_index = src_block_it->first; + BufferArray source_packets(k_); + + // Iterate over packets in the block and adjust indexed accordingly. This must + // be done because indexes are from 0 to (n - k - 1), but we need indexes from + // base_index to base_index + (n - k - 1) for (uint32_t i = 0; i < src_block.getK(); i++) { - source_packets[i] = std::make_pair(src_block_it->first + i, - std::move(std::get<1>(src_block[i]))); + src_block[i].setIndex(base_index + src_block[i].getIndex()); + source_packets[i] = FECBufferInfo(std::move(src_block[i])); } setProcessed(src_block_it->first); @@ -296,9 +323,9 @@ void RSDecoder::recoverPackets(SourceBlocks::iterator &src_block_it) { } void RSDecoder::consumeSource(const fec::buffer &packet, uint32_t index, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { // Normalize index - assert(index >= seq_offset_); + DCHECK(index >= seq_offset_); auto i = (index - seq_offset_) % n_; // Get base @@ -324,16 +351,19 @@ void RSDecoder::consumeSource(const fec::buffer &packet, uint32_t index, // protocol that may come in the future. auto it = src_blocks_.find(base); if (it != src_blocks_.end()) { - auto ret = it->second.addSourceSymbol(packet, i, offset); + auto ret = it->second.addSourceSymbol(packet, i, offset, metadata); if (!ret) { recoverPackets(it); } } else { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding to parked source packets"; - auto ret = parked_packets_.emplace( - base, std::vector<std::pair<buffer, uint32_t>>()); - ret.first->second.emplace_back(packet, i); + auto ret = parked_packets_.emplace(base, BufferInfoArray()); + ret.first->second.emplace_back(offset, i, metadata, packet); + /** + * If we reached k source packets, we do not have any missing packet to + * recover via FEC. Delete the block. + */ if (ret.first->second.size() >= k_) { setProcessed(ret.first->first); parked_packets_.erase(ret.first); @@ -356,8 +386,8 @@ void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Decoder consume called for repair symbol. BASE = " << base - << ", index = " << base + i << " and i = " << i << ". K=" << k - << ", N=" << n; + << ", index = " << base + i << " and i = " << (int)i << ". K=" << (int)k + << ", N=" << (int)n; // check if a source block already exist for this symbol auto it = src_blocks_.find(base); @@ -380,8 +410,9 @@ void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { auto it2 = parked_packets_.find(base); if (it2 != parked_packets_.end()) { for (auto &packet_index : it2->second) { - auto ret = it->second.addSourceSymbol(packet_index.first, - packet_index.second, offset); + auto ret = it->second.addSourceSymbol( + packet_index.getBuffer(), packet_index.getIndex(), + packet_index.getOffset(), packet_index.getMetadata()); if (!ret) { recoverPackets(it); // Finish to delete packets in same source block that were @@ -399,7 +430,7 @@ void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) { } void RSDecoder::onDataPacket(core::ContentObject &content_object, - uint32_t offset) { + uint32_t offset, uint32_t metadata) { DLOG_IF(INFO, VLOG_IS_ON(4)) << "Calling fec for data packet " << content_object.getName() << ". Offset: " << offset; @@ -409,7 +440,7 @@ void RSDecoder::onDataPacket(core::ContentObject &content_object, if (isSymbol(suffix)) { consumeRepair(content_object.shared_from_this(), offset); } else { - consumeSource(content_object.shared_from_this(), suffix, offset); + consumeSource(content_object.shared_from_this(), suffix, offset, metadata); } } diff --git a/libtransport/src/protocols/fec/rs.h b/libtransport/src/protocols/fec/rs.h index e159ad9f7..034c32bdc 100644 --- a/libtransport/src/protocols/fec/rs.h +++ b/libtransport/src/protocols/fec/rs.h @@ -34,10 +34,28 @@ namespace protocol { namespace fec { #define foreach_rs_fec_type \ + _(RS, 1, 2) \ _(RS, 1, 3) \ + _(RS, 1, 4) \ + _(RS, 2, 3) \ + _(RS, 2, 4) \ + _(RS, 2, 5) \ + _(RS, 2, 6) \ + _(RS, 3, 6) \ + _(RS, 3, 7) \ + _(RS, 3, 8) \ + _(RS, 3, 9) \ + _(RS, 3, 10) \ + _(RS, 3, 11) \ + _(RS, 3, 12) \ _(RS, 4, 5) \ _(RS, 4, 6) \ _(RS, 4, 7) \ + _(RS, 4, 8) \ + _(RS, 4, 9) \ + _(RS, 4, 10) \ + _(RS, 4, 11) \ + _(RS, 4, 12) \ _(RS, 6, 10) \ _(RS, 8, 10) \ _(RS, 8, 11) \ @@ -45,6 +63,8 @@ namespace fec { _(RS, 8, 14) \ _(RS, 8, 16) \ _(RS, 8, 32) \ + _(RS, 10, 20) \ + _(RS, 10, 25) \ _(RS, 10, 30) \ _(RS, 10, 40) \ _(RS, 10, 60) \ @@ -56,12 +76,24 @@ namespace fec { _(RS, 16, 27) \ _(RS, 17, 21) \ _(RS, 17, 34) \ + _(RS, 20, 45) \ + _(RS, 20, 50) \ + _(RS, 20, 60) \ + _(RS, 20, 70) \ + _(RS, 30, 70) \ + _(RS, 30, 75) \ + _(RS, 30, 85) \ + _(RS, 30, 95) \ _(RS, 32, 36) \ _(RS, 32, 41) \ _(RS, 32, 46) \ _(RS, 32, 54) \ _(RS, 34, 42) \ _(RS, 35, 70) \ + _(RS, 40, 95) \ + _(RS, 40, 100) \ + _(RS, 40, 110) \ + _(RS, 40, 120) \ _(RS, 52, 62) static const constexpr uint16_t MAX_SOURCE_BLOCK_SIZE = 128; @@ -73,9 +105,24 @@ static const constexpr uint16_t MAX_SOURCE_BLOCK_SIZE = 128; * std::array allows to be constructed in place, saving the allocation at the * price os knowing in advance its size. */ -using Packets = std::array<std::tuple</* index */ uint32_t, /* buffer */ buffer, - uint32_t /* offset */>, - MAX_SOURCE_BLOCK_SIZE>; +class RSBufferInfo : public FECBufferInfo { + public: + RSBufferInfo() : FECBufferInfo() {} + + RSBufferInfo(uint32_t offset, uint32_t index, uint32_t metadata, + buffer buffer) + : FECBufferInfo(index, metadata, buffer), offset_(offset) {} + + uint32_t getOffset() { return offset_; } + RSBufferInfo &setOffset(uint32_t offset) { + offset_ = offset; + return *this; + } + + private: + uint32_t offset_ = 0; +}; +using Packets = std::array<RSBufferInfo, MAX_SOURCE_BLOCK_SIZE>; /** * FEC Header, prepended to symbol packets. @@ -123,10 +170,32 @@ class rs; */ class BlockCode : public Packets { /** + * @brief Metadata to include when encoding the buffers. This does not need to + * be sent over the network, but just to be included in the FEC protected + * bytes. + * + */ + class __attribute__((__packed__)) fec_metadata { + public: + void setPacketLength(uint16_t length) { packet_length = htons(length); } + uint32_t getPacketLength() { return ntohs(packet_length); } + + void setMetadataBase(uint32_t value) { metadata = htonl(value); } + uint32_t getMetadataBase() { return ntohl(metadata); } + + private: + uint16_t packet_length; /* Used to get the real size of the packet after we + pad it */ + uint32_t + metadata; /* Caller may specify an integer for storing additional + metadata that can be used when recovering the packet. */ + }; + + /** * For variable length packet we need to prepend to the padded payload the * real length of the packet. This is *not* sent over the network. */ - static constexpr std::size_t LEN_SIZE_BYTES = 2; + static constexpr std::size_t METADATA_BYTES = sizeof(fec_metadata); public: BlockCode(uint32_t k, uint32_t n, uint32_t seq_offset, struct fec_parms *code, @@ -142,7 +211,8 @@ class BlockCode : public Packets { * Add a source symbol to the source block. */ bool addSourceSymbol(const fec::buffer &packet, uint32_t i, - uint32_t offset = 0); + uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); /** * Get current length of source block. @@ -169,7 +239,7 @@ class BlockCode : public Packets { * Add symbol to source block **/ bool addSymbol(const fec::buffer &packet, uint32_t i, uint32_t offset, - std::size_t size); + std::size_t size, uint32_t metadata); /** * Starting from k source symbols, get the n - k repair symbols @@ -310,10 +380,11 @@ class RSEncoder : public rs, public ProducerFEC { /** * Always consume source symbols. */ - void consume(const fec::buffer &packet, uint32_t index, uint32_t offset = 0); + void consume(const fec::buffer &packet, uint32_t index, uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); - void onPacketProduced(core::ContentObject &content_object, - uint32_t offset) override; + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets @@ -348,8 +419,8 @@ class RSDecoder : public rs, public ConsumerFEC { /** * Consume source symbol */ - void consumeSource(const fec::buffer &packet, uint32_t i, - uint32_t offset = 0); + void consumeSource(const fec::buffer &packet, uint32_t i, uint32_t offset = 0, + uint32_t metadata = FECBase::INVALID_METADATA); /** * Consume repair symbol @@ -359,8 +430,8 @@ class RSDecoder : public rs, public ConsumerFEC { /** * Consumers will call this function when they receive a data packet */ - void onDataPacket(core::ContentObject &content_object, - uint32_t offset) override; + void onDataPacket(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) override; /** * @brief Get the fec header size, if added to source packets @@ -398,8 +469,8 @@ class RSDecoder : public rs, public ConsumerFEC { * not make any sense to build the source block, since we received all the * source packet of the block. */ - std::unordered_map<uint32_t, std::vector<std::pair<buffer, uint32_t>>> - parked_packets_; + using BufferInfoArray = std::vector<RSBufferInfo>; + std::unordered_map<uint32_t, BufferInfoArray> parked_packets_; }; } // namespace fec diff --git a/libtransport/src/protocols/fec_base.h b/libtransport/src/protocols/fec_base.h index a1929d85e..bda3ee756 100644 --- a/libtransport/src/protocols/fec_base.h +++ b/libtransport/src/protocols/fec_base.h @@ -15,6 +15,7 @@ #pragma once +#include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/errors/not_implemented_exception.h> @@ -25,12 +26,67 @@ namespace protocol { namespace fec { -using buffer = typename utils::MemBuf::Ptr; -using BufferArray = std::vector<std::pair<uint32_t, buffer>>; +using buffer = utils::MemBuf::Ptr; + +class FECBufferInfo { + public: + FECBufferInfo() + : index_(~0), metadata_(~0), received_(false), buffer_(nullptr) {} + + template <typename T> + FECBufferInfo(uint32_t index, uint32_t metadata, T &&buffer) + : index_(index), + metadata_(metadata), + received_(false), + buffer_(std::forward<T>(buffer)) {} + + // Getters + uint32_t getIndex() const { return index_; } + + uint32_t getMetadata() const { return metadata_; } + + bool getReceived() const { return received_; } + + buffer &getBuffer() { return buffer_; } + + // Setters + void setReceived() { received_ = true; } + + FECBufferInfo &setIndex(uint32_t index) { + index_ = index; + return *this; + } + + FECBufferInfo &setMetadata(uint32_t metadata) { + metadata_ = metadata; + return *this; + } + + FECBufferInfo &setBuffer(buffer &buffer) { + buffer_ = buffer; + return *this; + } + + FECBufferInfo &setBuffer(buffer &&buffer) { + buffer_ = std::move(buffer); + return *this; + } + + private: + uint32_t index_; + uint32_t metadata_; + bool received_; + buffer buffer_; +}; + +using BufferArray = typename std::vector<FECBufferInfo>; class FECBase { public: - virtual ~FECBase() = default; + static inline uint32_t INVALID_METADATA = ~0; + static inline uint32_t INVALID_INDEX = ~0; + + virtual ~FECBase() {} /** * Callback to be called after the encode or the decode operations. In the * former case it will contain the symbols, while in the latter the sources. @@ -64,11 +120,21 @@ class FECBase { buffer_callback_ = buffer_callback; } + /** + * Creates the timer to flush packets. So far needed only if using Rely and + * want to avoid expired packets blocked by missing pkts to wait for a new + * packet to arrive and trigger the flush + */ + void setIOService(asio::io_service &io_service) { + flush_timer_ = std::make_unique<asio::steady_timer>(io_service); + } + virtual void reset() = 0; protected: PacketsReady fec_callback_{0}; BufferRequested buffer_callback_{0}; + std::unique_ptr<asio::steady_timer> flush_timer_; }; /** @@ -80,8 +146,9 @@ class ProducerFEC : public virtual FECBase { /** * Producers will call this function upon production of a new packet. */ - virtual void onPacketProduced(core::ContentObject &content_object, - uint32_t offset) = 0; + virtual void onPacketProduced( + core::ContentObject &content_object, uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) = 0; }; /** @@ -95,9 +162,10 @@ class ConsumerFEC : public virtual FECBase { * Consumers will call this function when they receive a data packet */ virtual void onDataPacket(core::ContentObject &content_object, - uint32_t offset) = 0; + uint32_t offset, + uint32_t metadata = FECBase::INVALID_METADATA) = 0; }; } // namespace fec } // namespace protocol -} // namespace transport
\ No newline at end of file +} // namespace transport diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.cc b/libtransport/src/protocols/incremental_indexer_bytestream.cc index cc302a98a..b94f229e5 100644 --- a/libtransport/src/protocols/incremental_indexer_bytestream.cc +++ b/libtransport/src/protocols/incremental_indexer_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -29,9 +29,9 @@ void IncrementalIndexer::onContentObject(core::Interest &interest, DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content " << content_object.getName(); - assert(reassembly_); + DCHECK(reassembly_); - if (TRANSPORT_EXPECT_FALSE(content_object.testRst())) { + if (TRANSPORT_EXPECT_FALSE(content_object.isLast())) { final_suffix_ = content_object.getName().getSuffix(); } diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.h b/libtransport/src/protocols/incremental_indexer_bytestream.h index c6a669629..422e49ecd 100644 --- a/libtransport/src/protocols/incremental_indexer_bytestream.h +++ b/libtransport/src/protocols/incremental_indexer_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -62,7 +62,7 @@ class IncrementalIndexer : public Indexer { next_reassembly_suffix_ = first_suffix_; } - virtual uint32_t checkNextSuffix() override { + virtual uint32_t checkNextSuffix() const override { return next_download_suffix_ <= final_suffix_ ? next_download_suffix_ : Indexer::invalid_index; } @@ -76,7 +76,7 @@ class IncrementalIndexer : public Indexer { first_suffix_ = suffix; } - uint32_t getFirstSuffix() override { return first_suffix_; } + uint32_t getFirstSuffix() const override { return first_suffix_; } virtual uint32_t jumpToIndex(uint32_t index) override { next_download_suffix_ = index; @@ -95,14 +95,14 @@ class IncrementalIndexer : public Indexer { return final_suffix_ != Indexer::invalid_index; } - virtual uint32_t getFinalSuffix() override { return final_suffix_; } + virtual uint32_t getFinalSuffix() const override { return final_suffix_; } void enableFec(fec::FECType fec_type) override {} void disableFec() override {} void setNFec(uint32_t n_fec) override {} - virtual uint32_t getNFec() override { return 0; } + virtual uint32_t getNFec() const override { return 0; } virtual void onContentObject(core::Interest &interest, core::ContentObject &content_object, diff --git a/libtransport/src/protocols/index_manager_bytestream.cc b/libtransport/src/protocols/index_manager_bytestream.cc index c78dc634d..952f36e0e 100644 --- a/libtransport/src/protocols/index_manager_bytestream.cc +++ b/libtransport/src/protocols/index_manager_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/index_manager_bytestream.h b/libtransport/src/protocols/index_manager_bytestream.h index e14c8845b..7ea31dfa5 100644 --- a/libtransport/src/protocols/index_manager_bytestream.h +++ b/libtransport/src/protocols/index_manager_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -40,7 +40,9 @@ class IndexManager : public IncrementalIndexer { indexer_->setFirstSuffix(suffix); } - uint32_t getFirstSuffix() override { return indexer_->getFirstSuffix(); } + uint32_t getFirstSuffix() const override { + return indexer_->getFirstSuffix(); + } uint32_t getNextReassemblySegment() override { return indexer_->getNextReassemblySegment(); @@ -50,22 +52,26 @@ class IndexManager : public IncrementalIndexer { return indexer_->isFinalSuffixDiscovered(); } - uint32_t getFinalSuffix() override { return indexer_->getFinalSuffix(); } + uint32_t getFinalSuffix() const override { + return indexer_->getFinalSuffix(); + } uint32_t jumpToIndex(uint32_t index) override { return indexer_->jumpToIndex(index); } void setNFec(uint32_t n_fec) override { return indexer_->setNFec(n_fec); } - uint32_t getNFec() override { return indexer_->getNFec(); } + uint32_t getNFec() const override { return indexer_->getNFec(); } void enableFec(fec::FECType fec_type) override { return indexer_->enableFec(fec_type); } - double getFecOverhead() override { return indexer_->getFecOverhead(); } + double getFecOverhead() const override { return indexer_->getFecOverhead(); } - double getMaxFecOverhead() override { return indexer_->getMaxFecOverhead(); } + double getMaxFecOverhead() const override { + return indexer_->getMaxFecOverhead(); + } void disableFec() override { return indexer_->disableFec(); } diff --git a/libtransport/src/protocols/indexer.cc b/libtransport/src/protocols/indexer.cc index 8d4cf04d7..41465755b 100644 --- a/libtransport/src/protocols/indexer.cc +++ b/libtransport/src/protocols/indexer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -14,10 +14,10 @@ */ #include <implementation/socket_consumer.h> +#include <protocols/errors.h> #include <protocols/indexer.h> namespace transport { - namespace protocol { using namespace interface; @@ -36,6 +36,35 @@ void Indexer::setVerifier() { } } -} // end namespace protocol +void Indexer::applyPolicy(core::Interest &interest, + core::ContentObject &content_object, bool reassembly, + auth::VerificationPolicy policy) const { + DCHECK(reassembly_ != nullptr); + + switch (policy) { + case auth::VerificationPolicy::ACCEPT: { + if (reassembly) { + reassembly_->reassemble(content_object); + } + break; + } + case auth::VerificationPolicy::UNKNOWN: + if (reassembly && reassembly_->reassembleUnverified()) { + reassembly_->reassemble(content_object); + } + break; + case auth::VerificationPolicy::DROP: + transport_->onPacketDropped( + interest, content_object, + make_error_code(protocol_error::verification_failed)); + break; + case auth::VerificationPolicy::ABORT: { + transport_->onContentReassembled( + make_error_code(protocol_error::session_aborted)); + break; + } + } +} +} // end namespace protocol } // end namespace transport diff --git a/libtransport/src/protocols/indexer.h b/libtransport/src/protocols/indexer.h index 7e3a52fb0..1bacb13aa 100644 --- a/libtransport/src/protocols/indexer.h +++ b/libtransport/src/protocols/indexer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,6 +15,7 @@ #pragma once +#include <hicn/transport/auth/policies.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <protocols/fec_utils.h> @@ -44,7 +45,7 @@ class Indexer { /** * Suffix getters */ - virtual uint32_t checkNextSuffix() = 0; + virtual uint32_t checkNextSuffix() const = 0; virtual uint32_t getNextSuffix() = 0; virtual uint32_t getNextReassemblySegment() = 0; @@ -52,24 +53,24 @@ class Indexer { * Set first suffix from where to start. */ virtual void setFirstSuffix(uint32_t suffix) = 0; - virtual uint32_t getFirstSuffix() = 0; + virtual uint32_t getFirstSuffix() const = 0; /** * Functions to set/enable/disable fec */ virtual void setNFec(uint32_t n_fec) = 0; - virtual uint32_t getNFec() = 0; + virtual uint32_t getNFec() const = 0; virtual void enableFec(fec::FECType fec_type) = 0; virtual void disableFec() = 0; virtual bool isFec(uint32_t index) { return false; } - virtual double getFecOverhead() { return 0.0; } - virtual double getMaxFecOverhead() { return 0.0; } + virtual double getFecOverhead() const { return 0.0; } + virtual double getMaxFecOverhead() const { return 0.0; } /** * Final suffix helpers. */ virtual bool isFinalSuffixDiscovered() = 0; - virtual uint32_t getFinalSuffix() = 0; + virtual uint32_t getFinalSuffix() const = 0; /** * Set reassembly protocol @@ -84,9 +85,15 @@ class Indexer { virtual void setVerifier(); /** + * Apply a verification policy + */ + virtual void applyPolicy(core::Interest &interest, + core::ContentObject &content_object, bool reassembly, + auth::VerificationPolicy policy) const; + /** * Jump to suffix. This may be useful if, for any protocol dependent - * mechanism, we need to suddenly change current suffix. This does not modify - * the way suffixes re incremented/decremented (that's part of the + * mechanism, we need to suddenly change current suffix. This does not + * modify the way suffixes re incremented/decremented (that's part of the * implementation). */ virtual uint32_t jumpToIndex(uint32_t index) = 0; @@ -108,6 +115,7 @@ class Indexer { TransportProtocol *transport_; Reassembly *reassembly_; std::shared_ptr<auth::Verifier> verifier_; + auth::CryptoHashType manifest_hash_type_; }; } // end namespace protocol diff --git a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc index 168aa57af..b5ab8184f 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc +++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -31,8 +31,7 @@ ManifestIncrementalIndexer::ManifestIncrementalIndexer( implementation::ConsumerSocket *icn_socket, TransportProtocol *transport) : IncrementalIndexer(icn_socket, transport), suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy( - NextSegmentCalculationStrategy::INCREMENTAL, next_download_suffix_, - 0)) {} + utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) {} void ManifestIncrementalIndexer::onContentObject( core::Interest &interest, core::ContentObject &content_object, @@ -59,12 +58,7 @@ void ManifestIncrementalIndexer::onContentObject( void ManifestIncrementalIndexer::onUntrustedManifest( core::Interest &interest, core::ContentObject &content_object, bool reassembly) { - auto manifest = - std::make_unique<ContentObjectManifest>(std::move(content_object)); - - auth::VerificationPolicy policy = verifier_->verifyPackets(manifest.get()); - - manifest->decode(); + auth::VerificationPolicy policy = verifier_->verifyPackets(&content_object); if (policy != auth::VerificationPolicy::ACCEPT) { transport_->onContentReassembled( @@ -72,6 +66,10 @@ void ManifestIncrementalIndexer::onUntrustedManifest( return; } + auto manifest = + std::make_unique<ContentObjectManifest>(std::move(content_object)); + manifest->decode(); + processTrustedManifest(interest, std::move(manifest), reassembly); } @@ -83,9 +81,10 @@ void ManifestIncrementalIndexer::processTrustedManifest( throw errors::RuntimeException("Received manifest with unknown version."); } - switch (manifest->getManifestType()) { + switch (manifest->getType()) { case core::ManifestType::INLINE_MANIFEST: { - suffix_strategy_->setFinalSuffix(manifest->getFinalBlockNumber()); + suffix_strategy_->setFinalSuffix( + manifest->getParamsBytestream().final_segment); // The packets to verify with the received manifest std::vector<auth::PacketPtr> packets; @@ -162,45 +161,15 @@ void ManifestIncrementalIndexer::onUntrustedContentObject( applyPolicy(interest, content_object, reassembly, policy); } -void ManifestIncrementalIndexer::applyPolicy( - core::Interest &interest, core::ContentObject &content_object, - bool reassembly, auth::VerificationPolicy policy) { - assert(reassembly_); - switch (policy) { - case auth::VerificationPolicy::ACCEPT: { - if (reassembly && !reassembly_->reassembleUnverified()) { - reassembly_->reassemble(content_object); - } - break; - } - case auth::VerificationPolicy::DROP: { - transport_->onPacketDropped( - interest, content_object, - make_error_code(protocol_error::verification_failed)); - break; - } - case auth::VerificationPolicy::ABORT: { - transport_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } - case auth::VerificationPolicy::UNKNOWN: { - if (reassembly && reassembly_->reassembleUnverified()) { - reassembly_->reassemble(content_object); - } - } - } -} - -uint32_t ManifestIncrementalIndexer::checkNextSuffix() { - return suffix_strategy_->getNextSuffix(); +uint32_t ManifestIncrementalIndexer::checkNextSuffix() const { + return suffix_strategy_->checkNextSuffix(); } uint32_t ManifestIncrementalIndexer::getNextSuffix() { auto ret = suffix_strategy_->getNextSuffix(); if (ret <= suffix_strategy_->getFinalSuffix() && - ret != utils::SuffixStrategy::INVALID_SUFFIX) { + ret != utils::SuffixStrategy::MAX_SUFFIX) { suffix_queue_.push(ret); return ret; } @@ -208,7 +177,7 @@ uint32_t ManifestIncrementalIndexer::getNextSuffix() { return Indexer::invalid_index; } -uint32_t ManifestIncrementalIndexer::getFinalSuffix() { +uint32_t ManifestIncrementalIndexer::getFinalSuffix() const { return suffix_strategy_->getFinalSuffix(); } diff --git a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h index d8cf5892f..12876f35c 100644 --- a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h +++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -39,8 +39,7 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { ManifestIncrementalIndexer(IncrementalIndexer &&indexer) : IncrementalIndexer(std::move(indexer)), suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy( - core::NextSegmentCalculationStrategy::INCREMENTAL, - next_download_suffix_, 0)) { + utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) { for (uint32_t i = first_suffix_; i < next_download_suffix_; i++) { suffix_queue_.push(i); } @@ -54,7 +53,7 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { core::ContentObject &content_object, bool reassembly) override; - uint32_t checkNextSuffix() override; + uint32_t checkNextSuffix() const override; uint32_t getNextSuffix() override; @@ -62,7 +61,7 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { bool isFinalSuffixDiscovered() override; - uint32_t getFinalSuffix() override; + uint32_t getFinalSuffix() const override; protected: std::unique_ptr<utils::SuffixStrategy> suffix_strategy_; @@ -82,9 +81,6 @@ class ManifestIncrementalIndexer : public IncrementalIndexer { void onUntrustedContentObject(core::Interest &interest, core::ContentObject &content_object, bool reassembly); - void applyPolicy(core::Interest &interest, - core::ContentObject &content_object, bool reassembly, - auth::VerificationPolicy policy); }; } // end namespace protocol diff --git a/libtransport/src/protocols/prod_protocol_bytestream.cc b/libtransport/src/protocols/prod_protocol_bytestream.cc index f659cb37c..2a3ec07e1 100644 --- a/libtransport/src/protocols/prod_protocol_bytestream.cc +++ b/libtransport/src/protocols/prod_protocol_bytestream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -29,12 +29,7 @@ ByteStreamProductionProtocol::ByteStreamProductionProtocol( implementation::ProducerSocket *icn_socket) : ProductionProtocol(icn_socket) {} -ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { - stop(); - if (listening_thread_.joinable()) { - listening_thread_.join(); - } -} +ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { stop(); } uint32_t ByteStreamProductionProtocol::produceDatagram( const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) { @@ -68,16 +63,16 @@ uint32_t ByteStreamProductionProtocol::produceStream( return 0; } - Name name(content_name); - - // Get the atomic variables to ensure they keep the same value - // during the production - // Total size of the data packet uint32_t data_packet_size; socket_->getSocketOption(GeneralTransportOptions::DATA_PACKET_SIZE, data_packet_size); + // Maximum size of a segment + uint32_t max_segment_size; + socket_->getSocketOption(GeneralTransportOptions::MAX_SEGMENT_SIZE, + max_segment_size); + // Expiry time uint32_t content_object_expiry_time; socket_->getSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, @@ -88,106 +83,90 @@ uint32_t ByteStreamProductionProtocol::produceStream( socket_->getSocketOption(GeneralTransportOptions::HASH_ALGORITHM, hash_algo); // Suffix calculation strategy - core::NextSegmentCalculationStrategy _suffix_strategy; + std::shared_ptr<utils::SuffixStrategy> suffix_strategy; socket_->getSocketOption(GeneralTransportOptions::SUFFIX_STRATEGY, - _suffix_strategy); - auto suffix_strategy = utils::SuffixStrategyFactory::getSuffixStrategy( - _suffix_strategy, start_offset); + suffix_strategy); + suffix_strategy->reset(start_offset); - auto buffer_size = buffer->length(); + // Default format + core::Packet::Format default_format; + socket_->getSocketOption(GeneralTransportOptions::PACKET_FORMAT, + default_format); + + Name name(content_name); + size_t buffer_size = buffer->length(); + size_t signature_length = signer_->getSignatureFieldSize(); + uint32_t final_block_number = start_offset; + + // Content-related + core::Packet::Format content_format; + uint32_t content_header_size; + uint64_t content_free_space; + uint32_t nb_segments; int bytes_segmented = 0; - std::size_t header_size; - std::size_t manifest_header_size = 0; - std::size_t signature_length = 0; - std::uint32_t final_block_number = start_offset; - uint64_t free_space_for_content = 0; - core::Packet::Format format; + // Manifest-related + core::Packet::Format manifest_format; + uint32_t manifest_header_size; + uint64_t manifest_free_space; + uint32_t nb_manifests; std::shared_ptr<core::ContentObjectManifest> manifest; + uint32_t manifest_capacity = making_manifest_; bool is_last_manifest = false; - - // TODO Manifest may still be used for indexing - if (making_manifest_ && !signer_) { - LOG(FATAL) << "Making manifests without setting producer identity."; - } - - core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC; - core::Packet::Format hf_format_ah = core::Packet::Format::HF_UNSPEC; - - if (name.getType() == HNT_CONTIGUOUS_V4 || name.getType() == HNT_IOV_V4) { - hf_format = core::Packet::Format::HF_INET_TCP; - hf_format_ah = core::Packet::Format::HF_INET_TCP_AH; - } else if (name.getType() == HNT_CONTIGUOUS_V6 || - name.getType() == HNT_IOV_V6) { - hf_format = core::Packet::Format::HF_INET6_TCP; - hf_format_ah = core::Packet::Format::HF_INET6_TCP_AH; - } else { - throw errors::RuntimeException("Unknown name format."); - } - - format = hf_format; - if (making_manifest_) { - manifest_header_size = core::Packet::getHeaderSizeFromFormat( - signer_ ? hf_format_ah : hf_format, - signer_ ? signer_->getSignatureFieldSize() : 0); - } else if (signer_) { - format = hf_format_ah; - signature_length = signer_->getSignatureFieldSize(); + ParamsBytestream transport_params; + + manifest_format = Packet::toAHFormat(default_format); + content_format = + !making_manifest_ ? Packet::toAHFormat(default_format) : default_format; + + content_header_size = + core::Packet::getHeaderSizeFromFormat(content_format, signature_length); + manifest_header_size = + core::Packet::getHeaderSizeFromFormat(manifest_format, signature_length); + content_free_space = + std::min(max_segment_size, data_packet_size - content_header_size); + manifest_free_space = + std::min(max_segment_size, data_packet_size - manifest_header_size); + + // Compute the number of segments the data will be split into + nb_segments = + uint32_t(std::ceil(double(buffer_size) / double(content_free_space))); + if (content_free_space * nb_segments < buffer_size) { + nb_segments++; } - header_size = core::Packet::getHeaderSizeFromFormat(format, signature_length); - free_space_for_content = data_packet_size - header_size; - uint32_t number_of_segments = - uint32_t(std::ceil(double(buffer_size) / double(free_space_for_content))); - if (free_space_for_content * number_of_segments < buffer_size) { - number_of_segments++; - } - - // TODO allocate space for all the headers if (making_manifest_) { - uint32_t segment_in_manifest = static_cast<uint32_t>( - std::floor(double(data_packet_size - manifest_header_size - - ContentObjectManifest::getManifestHeaderSize()) / - ContentObjectManifest::getManifestEntrySize()) - - 1.0); - uint32_t number_of_manifests = static_cast<uint32_t>( - std::ceil(float(number_of_segments) / segment_in_manifest)); - final_block_number += number_of_segments + number_of_manifests - 1; + nb_manifests = static_cast<uint32_t>( + std::ceil(float(nb_segments) / manifest_capacity)); + final_block_number += nb_segments + nb_manifests - 1; + transport_params.final_segment = + is_last ? final_block_number : utils::SuffixStrategy::MAX_SUFFIX; manifest.reset(ContentObjectManifest::createManifest( + manifest_format, name.setSuffix(suffix_strategy->getNextManifestSuffix()), core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, - hash_algo, is_last_manifest, name, _suffix_strategy, - signer_ ? signer_->getSignatureFieldSize() : 0)); - manifest->setLifetime(content_object_expiry_time); + is_last_manifest, name, hash_algo, signature_length)); - if (is_last) { - manifest->setFinalBlockNumber(final_block_number); - } else { - manifest->setFinalBlockNumber(utils::SuffixStrategy::INVALID_SUFFIX); - } + manifest->setLifetime(content_object_expiry_time); + manifest->setParamsBytestream(transport_params); } - for (unsigned int packaged_segments = 0; - packaged_segments < number_of_segments; packaged_segments++) { + auto self = shared_from_this(); + for (unsigned int packaged_segments = 0; packaged_segments < nb_segments; + packaged_segments++) { if (making_manifest_) { - if (manifest->estimateManifestSize(2) > - data_packet_size - manifest_header_size) { + if (manifest->estimateManifestSize(1) > manifest_free_space) { manifest->encode(); - - // If identity set, sign manifest - if (signer_) { - signer_->signPacket(manifest.get()); - } + signer_->signPacket(manifest.get()); // Send the current manifest - passContentObjectToCallbacks(manifest); - + passContentObjectToCallbacks(manifest, self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest->getName(); // Send content objects stored in the queue while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); + passContentObjectToCallbacks(content_queue_.front(), self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content " << content_queue_.front()->getName(); content_queue_.pop(); @@ -195,86 +174,84 @@ uint32_t ByteStreamProductionProtocol::produceStream( // Create new manifest. The reference to the last manifest has been // acquired in the passContentObjectToCallbacks function, so we can - // safely release this reference + // safely release this reference. manifest.reset(ContentObjectManifest::createManifest( + manifest_format, name.setSuffix(suffix_strategy->getNextManifestSuffix()), core::ManifestVersion::VERSION_1, - core::ManifestType::INLINE_MANIFEST, hash_algo, is_last_manifest, - name, _suffix_strategy, - signer_ ? signer_->getSignatureFieldSize() : 0)); + core::ManifestType::INLINE_MANIFEST, is_last_manifest, name, + hash_algo, signature_length)); manifest->setLifetime(content_object_expiry_time); - manifest->setFinalBlockNumber( - is_last ? final_block_number - : utils::SuffixStrategy::INVALID_SUFFIX); + manifest->setParamsBytestream(transport_params); } } - auto content_suffix = suffix_strategy->getNextContentSuffix(); + // Create content object + uint32_t content_suffix = suffix_strategy->getNextContentSuffix(); auto content_object = std::make_shared<ContentObject>( - name.setSuffix(content_suffix), format, - signer_ && !making_manifest_ ? signer_->getSignatureFieldSize() : 0); + name.setSuffix(content_suffix), content_format, + !making_manifest_ ? signature_length : 0); content_object->setLifetime(content_object_expiry_time); auto b = buffer->cloneOne(); - b->trimStart(free_space_for_content * packaged_segments); + b->trimStart(content_free_space * packaged_segments); b->trimEnd(b->length()); - if (TRANSPORT_EXPECT_FALSE(packaged_segments == number_of_segments - 1)) { + // Segment the input data + if (TRANSPORT_EXPECT_FALSE(packaged_segments == nb_segments - 1)) { b->append(buffer_size - bytes_segmented); bytes_segmented += (int)(buffer_size - bytes_segmented); if (is_last && making_manifest_) { is_last_manifest = true; } else if (is_last) { - content_object->setRst(); + content_object->setLast(); } } else { - b->append(free_space_for_content); - bytes_segmented += (int)(free_space_for_content); + b->append(content_free_space); + bytes_segmented += (int)(content_free_space); } + // Set the segmented data as payload content_object->appendPayload(std::move(b)); + // Either we sign the content object or we save its hash into the current + // manifest if (making_manifest_) { - using namespace std::chrono_literals; auth::CryptoHash hash = content_object->computeDigest(hash_algo); manifest->addSuffixHash(content_suffix, hash); content_queue_.push(content_object); } else { - if (signer_) { - signer_->signPacket(content_object.get()); - } - passContentObjectToCallbacks(content_object); + signer_->signPacket(content_object.get()); + passContentObjectToCallbacks(content_object, self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content " << content_object->getName(); } } + // We send the manifest that hasn't been fully filled yet if (making_manifest_) { if (is_last_manifest) { - manifest->setFinalManifest(is_last_manifest); + manifest->setIsLast(is_last_manifest); } manifest->encode(); + signer_->signPacket(manifest.get()); - if (signer_) { - signer_->signPacket(manifest.get()); - } - - passContentObjectToCallbacks(manifest); + passContentObjectToCallbacks(manifest, self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest->getName(); while (!content_queue_.empty()) { - passContentObjectToCallbacks(content_queue_.front()); + passContentObjectToCallbacks(content_queue_.front(), self); DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content " << content_queue_.front()->getName(); content_queue_.pop(); } } - portal_->getIoService().post([this]() { + portal_->getThread().add([this, self]() { std::shared_ptr<ContentObject> co; while (object_queue_for_callbacks_.pop(co)) { if (*on_new_segment_) { @@ -296,7 +273,7 @@ uint32_t ByteStreamProductionProtocol::produceStream( } }); - portal_->getIoService().dispatch([this, buffer_size]() { + portal_->getThread().add([this, buffer_size, self]() { if (*on_content_produced_) { on_content_produced_->operator()(*socket_->getInterface(), std::make_error_code(std::errc(0)), @@ -307,9 +284,10 @@ uint32_t ByteStreamProductionProtocol::produceStream( return suffix_strategy->getTotalCount(); } -void ByteStreamProductionProtocol::scheduleSendBurst() { - portal_->getIoService().post([this]() { - std::shared_ptr<ContentObject> co; +void ByteStreamProductionProtocol::scheduleSendBurst( + const std::shared_ptr<ByteStreamProductionProtocol> &self) { + portal_->getThread().add([this, self]() { + ContentObject::Ptr co; for (uint32_t i = 0; i < burst_size; i++) { if (object_queue_for_callbacks_.pop(co)) { @@ -321,11 +299,15 @@ void ByteStreamProductionProtocol::scheduleSendBurst() { on_content_object_to_sign_->operator()(*socket_->getInterface(), *co); } + output_buffer_.insert(co); + if (*on_content_object_in_output_buffer_) { on_content_object_in_output_buffer_->operator()( *socket_->getInterface(), *co); } + portal_->sendContentObject(*co); + if (*on_content_object_output_) { on_content_object_output_->operator()(*socket_->getInterface(), *co); } @@ -337,13 +319,12 @@ void ByteStreamProductionProtocol::scheduleSendBurst() { } void ByteStreamProductionProtocol::passContentObjectToCallbacks( - const std::shared_ptr<ContentObject> &content_object) { - output_buffer_.insert(content_object); - portal_->sendContentObject(*content_object); + const std::shared_ptr<ContentObject> &content_object, + const std::shared_ptr<ByteStreamProductionProtocol> &self) { object_queue_for_callbacks_.push(std::move(content_object)); if (object_queue_for_callbacks_.size() >= burst_size) { - scheduleSendBurst(); + scheduleSendBurst(self); } } @@ -376,7 +357,5 @@ void ByteStreamProductionProtocol::onInterest(Interest &interest) { } } -void ByteStreamProductionProtocol::onError(std::error_code ec) {} - } // namespace protocol } // end namespace transport diff --git a/libtransport/src/protocols/prod_protocol_bytestream.h b/libtransport/src/protocols/prod_protocol_bytestream.h index cf36b90a5..809ad8d5c 100644 --- a/libtransport/src/protocols/prod_protocol_bytestream.h +++ b/libtransport/src/protocols/prod_protocol_bytestream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -50,16 +50,19 @@ class ByteStreamProductionProtocol : public ProductionProtocol { uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer, size_t buffer_size) override; + auto shared_from_this() { return utils::shared_from(this); } + protected: // Consumer Callback // void reset() override; void onInterest(core::Interest &i) override; - void onError(std::error_code ec) override; private: void passContentObjectToCallbacks( - const std::shared_ptr<ContentObject> &content_object); - void scheduleSendBurst(); + const std::shared_ptr<ContentObject> &content_object, + const std::shared_ptr<ByteStreamProductionProtocol> &self); + void scheduleSendBurst( + const std::shared_ptr<ByteStreamProductionProtocol> &self); private: // While manifests are being built, contents are stored in a queue diff --git a/libtransport/src/protocols/prod_protocol_rtc.cc b/libtransport/src/protocols/prod_protocol_rtc.cc index cdc882d81..242abd30d 100644 --- a/libtransport/src/protocols/prod_protocol_rtc.cc +++ b/libtransport/src/protocols/prod_protocol_rtc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,9 +13,11 @@ * limitations under the License. */ +#include <glog/logging.h> #include <hicn/transport/core/global_object_pool.h> #include <implementation/socket_producer.h> #include <protocols/prod_protocol_rtc.h> +#include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_consts.h> #include <stdlib.h> #include <time.h> @@ -38,71 +40,89 @@ RTCProductionProtocol::RTCProductionProtocol( bytes_production_rate_(0), packets_production_rate_(0), fec_packets_production_rate_(0), - last_round_(std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count()), + last_produced_data_ts_(0), + last_round_(utils::SteadyTime::nowMs().count()), allow_delayed_nacks_(false), queue_timer_on_(false), consumer_in_sync_(false), - on_consumer_in_sync_(nullptr) { - srand((unsigned int)time(NULL)); - prod_label_ = rand() % 256; + on_consumer_in_sync_(nullptr), + pending_fec_pace_(false), + max_len_(0), + queue_len_(0), + data_aggregation_(true), + data_aggregation_timer_switch_(false) { + std::uniform_int_distribution<> dis(0, 255); + prod_label_ = dis(gen_); cache_label_ = (prod_label_ + 1) % 256; interests_queue_timer_ = - std::make_unique<asio::steady_timer>(portal_->getIoService()); - round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + round_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + fec_pacing_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + app_packets_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); setOutputBufferSize(10000); - scheduleRoundTimer(); +} + +RTCProductionProtocol::~RTCProductionProtocol() {} + +void RTCProductionProtocol::setProducerParam() { + // Flow name: here we assume there is only one prefix registered in the portal + flow_name_ = portal_->getServedNamespaces().begin()->getName(); + + // Manifest + uint32_t making_manifest; + socket_->getSocketOption(interface::GeneralTransportOptions::MAKE_MANIFEST, + making_manifest); + + // Signer + std::shared_ptr<auth::Signer> signer; + socket_->getSocketOption(interface::GeneralTransportOptions::SIGNER, signer); + + // Default format + core::Packet::Format default_format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + default_format); // FEC using namespace std::placeholders; enableFEC(std::bind(&RTCProductionProtocol::onFecPackets, this, _1), std::bind(&RTCProductionProtocol::getBuffer, this, _1)); -} -RTCProductionProtocol::~RTCProductionProtocol() {} + // Aggregated data + socket_->getSocketOption(interface::RtcTransportOptions::AGGREGATED_DATA, + data_aggregation_); -void RTCProductionProtocol::registerNamespaceWithNetwork( - const Prefix &producer_namespace) { - ProductionProtocol::registerNamespaceWithNetwork(producer_namespace); - - flow_name_ = producer_namespace.getName(); - auto family = flow_name_.getAddressFamily(); - - switch (family) { - case AF_INET6: - data_header_size_ = - signer_ && !making_manifest_ - ? (uint32_t)Packet::getHeaderSizeFromFormat( - HF_INET6_TCP_AH, signer_->getSignatureFieldSize()) - : (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET6_TCP); - ; - break; - case AF_INET: - data_header_size_ = - signer_ && !making_manifest_ - ? (uint32_t)Packet::getHeaderSizeFromFormat( - HF_INET_TCP_AH, signer_->getSignatureFieldSize()) - : (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET_TCP); - break; - default: - throw errors::RuntimeException("Unknown name format."); - } + size_t signature_size = signer->getSignatureFieldSize(); + data_header_format_ = { + !making_manifest ? Packet::toAHFormat(default_format) : default_format, + !making_manifest ? signature_size : 0}; + manifest_header_format_ = {Packet::toAHFormat(default_format), + signature_size}; + nack_header_format_ = {Packet::toAHFormat(default_format), signature_size}; + fec_header_format_ = {Packet::toAHFormat(default_format), signature_size}; + + // Schedule round timer + scheduleRoundTimer(); } void RTCProductionProtocol::scheduleRoundTimer() { round_timer_->expires_from_now( std::chrono::milliseconds(rtc::PRODUCER_STATS_INTERVAL)); - round_timer_->async_wait([this](std::error_code ec) { + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + round_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - updateStats(); + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + sp->updateStats(); + } }); } void RTCProductionProtocol::updateStats() { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t duration = now - last_round_; if (duration == 0) duration = 1; double per_second = rtc::MILLI_IN_A_SEC / duration; @@ -125,11 +145,12 @@ void RTCProductionProtocol::updateStats() { if (max_packet_production_ < rtc::WIN_MIN) max_packet_production_ = rtc::WIN_MIN; - if (packets_production_rate_ != 0) { - allow_delayed_nacks_ = false; - } else if (prev_packets_production_rate == 0) { - // at least 2 rounds with production rate = 0 + if (packets_production_rate_ <= rtc::MIN_PRODUCTION_RATE || + prev_packets_production_rate <= rtc::MIN_PRODUCTION_RATE) { allow_delayed_nacks_ = true; + } else { + // at least 2 rounds with enough packets + allow_delayed_nacks_ = false; } // check if the production rate is decreased. if yes send nacks if needed @@ -170,42 +191,237 @@ uint32_t RTCProductionProtocol::produceDatagram( socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE, data_packet_size); - if (TRANSPORT_EXPECT_FALSE((buffer_size + data_header_size_ + - rtc::DATA_HEADER_SIZE) > data_packet_size)) { + if (TRANSPORT_EXPECT_FALSE( + (Packet::getHeaderSizeFromFormat(data_header_format_.first, + data_header_format_.second) + + rtc::DATA_HEADER_SIZE + buffer_size) > data_packet_size)) { return 0; } + if (!data_aggregation_) { + // if data aggregation is off emptyQueue will always return doing nothing + emptyQueue(); + + sendManifest(content_name); + + // create content object + auto content_object = + core::PacketManager<>::getInstance().getPacket<ContentObject>( + data_header_format_.first, data_header_format_.second); + + // add rtc header to the payload + struct rtc::data_packet_t header; + content_object->appendPayload((const uint8_t *)&header, + rtc::DATA_HEADER_SIZE); + content_object->appendPayload(buffer->data(), buffer->length()); + + // schedule actual sending on internal thread + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(content_object)}, + content_name]() mutable { + produceInternal(std::move(content_object), content_name); + }); + } else { + // XXX here we assume that all the packets that we push to the queue have + // the same name + auto app_pkt = utils::MemBuf::copyBuffer(buffer->data(), buffer->length()); + addPacketToQueue(std::move(app_pkt)); + } + + return 1; +} + +void RTCProductionProtocol::addPacketToQueue( + std::unique_ptr<utils::MemBuf> &&buffer) { + std::size_t buffer_size = buffer->length(); + if ((queue_len_ + buffer_size) > rtc::MAX_RTC_PAYLOAD_SIZE) { + emptyQueue(); // this should guaranty that the generated packet will never + // be larger than an MTU + } + + waiting_app_packets_.push(std::move(buffer)); + if (max_len_ < buffer_size) max_len_ = buffer_size; + queue_len_ += buffer_size; + + if (waiting_app_packets_.size() >= rtc::MAX_AGGREGATED_PACKETS) { + emptyQueue(); + } + + if (waiting_app_packets_.size() >= 1 && !data_aggregation_timer_switch_) { + data_aggregation_timer_switch_ = true; + app_packets_timer_->expires_from_now( + std::chrono::milliseconds(rtc::AGGREGATED_PACKETS_TIMER)); + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + app_packets_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->data_aggregation_timer_switch_) return; + ptr->emptyQueue(); + } + }); + } +} + +void RTCProductionProtocol::emptyQueue() { + if (waiting_app_packets_.size() == 0) return; // queue is empty + + Name n(flow_name_); + + // cancel timer is scheduled + if (data_aggregation_timer_switch_) { + data_aggregation_timer_switch_ = false; + app_packets_timer_->cancel(); + } + + // send a manifest beforehand if the hash buffer if full + sendManifest(n); + + // create content object auto content_object = core::PacketManager<>::getInstance().getPacket<ContentObject>( - signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP, - signer_ ? signer_->getSignatureFieldSize() : 0); + data_header_format_.first, data_header_format_.second); + // add rtc header to the payload struct rtc::data_packet_t header; content_object->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); - content_object->appendPayload(buffer->data(), buffer->length()); - // schedule actual sending on internal thread - portal_->getIoService().dispatch([this, - content_object{std::move(content_object)}, - content_name]() mutable { - produceInternal(std::move(content_object), content_name); + // init aggregated header + rtc::AggrPktHeader hdr( + (uint8_t *)(content_object->getPayload()->data() + rtc::DATA_HEADER_SIZE), + max_len_, waiting_app_packets_.size()); + uint32_t header_size = hdr.getHeaderLen(); + content_object->append(header_size); // leave space for the aggregated header + + uint8_t index = 0; + while (waiting_app_packets_.size() != 0) { + std::unique_ptr<utils::MemBuf> pkt = + std::move(waiting_app_packets_.front()); + waiting_app_packets_.pop(); + // XXX for the moment we have a single name, so this works, otherwise we + // need to do something else + hdr.addPacketToHeader(index, pkt->length()); + // append packet + content_object->appendPayload(pkt->data(), pkt->length()); + index++; + } + + // reset queue values + max_len_ = 0; + queue_len_ = 0; + + // the packet is ready we need to send it + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(content_object)}, n]() mutable { + produceInternal(std::move(content_object), n); + }); +} + +void RTCProductionProtocol::sendManifest(const Name &name) { + if (!making_manifest_) { + return; + } + + Name manifest_name(name); + + uint32_t data_packet_size; + socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE, + data_packet_size); + + // The maximum number of entries a manifest can hold + uint32_t manifest_capacity = making_manifest_; + + // If there is not enough hashes to fill a manifest, return early + if (manifest_entries_.size() < manifest_capacity) { + return; + } + + // Create a new manifest + std::shared_ptr<core::ContentObjectManifest> manifest = + createManifest(manifest_name.setSuffix(current_seg_)); + + // Fill the manifest with packet hashes that were previously saved + uint32_t nb_entries; + for (nb_entries = 0; nb_entries < manifest_capacity; ++nb_entries) { + if (manifest_entries_.empty()) { + break; + } + std::pair<uint32_t, auth::CryptoHash> front = manifest_entries_.front(); + manifest->addSuffixHash(front.first, front.second); + manifest_entries_.pop(); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Sending manifest " << manifest->getName().getSuffix() << " of size " + << nb_entries; + + // Encode and send the manifest + manifest->encode(); + portal_->getThread().tryRunHandlerNow( + [this, content_object{std::move(manifest)}, manifest_name]() mutable { + produceInternal(std::move(content_object), manifest_name); + }); +} + +std::shared_ptr<core::ContentObjectManifest> +RTCProductionProtocol::createManifest(const Name &content_name) const { + Name name(content_name); + + auth::CryptoHashType hash_algo; + socket_->getSocketOption(interface::GeneralTransportOptions::HASH_ALGORITHM, + hash_algo); + + uint64_t now = utils::SteadyTime::nowMs().count(); + + // Create a new manifest + std::shared_ptr<core::ContentObjectManifest> manifest( + ContentObjectManifest::createManifest( + manifest_header_format_.first, name, core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, false, name, hash_algo, + manifest_header_format_.second)); + + // Set connection parameters + manifest->setParamsRTC(ParamsRTC{ + .timestamp = now, + .prod_rate = bytes_production_rate_, + .prod_seg = current_seg_, + .support_fec = false, }); - return 1; + return manifest; } void RTCProductionProtocol::produceInternal( std::shared_ptr<ContentObject> &&content_object, const Name &content_name, bool fec) { + uint64_t now = utils::SteadyTime::nowMs().count(); + + if (fec && (now - last_produced_data_ts_) < rtc::FEC_PACING_TIME) { + paced_fec_packets_.push(std::pair<uint64_t, ContentObject::Ptr>( + now, std::move(content_object))); + postponeFecPacket(); + } else { + // need to check if there are FEC packets waiting to be sent + flushFecPkts(current_seg_); + producePktInternal(std::move(content_object), content_name, fec); + } +} + +void RTCProductionProtocol::producePktInternal( + std::shared_ptr<ContentObject> &&content_object, const Name &content_name, + bool fec) { + bool is_manifest = content_object->getPayloadType() == PayloadType::MANIFEST; + uint64_t now = utils::SteadyTime::nowMs().count(); + // set rtc header - struct rtc::data_packet_t *data_pkt = - (struct rtc::data_packet_t *)content_object->getPayload()->data(); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - data_pkt->setTimestamp(now); - data_pkt->setProductionRate(bytes_production_rate_); + if (!is_manifest) { + struct rtc::data_packet_t *data_pkt = + (struct rtc::data_packet_t *)content_object->getPayload()->data(); + data_pkt->setTimestamp(now); + data_pkt->setProductionRate(bytes_production_rate_); + } // set hicn stuff Name n(content_name); @@ -213,11 +429,6 @@ void RTCProductionProtocol::produceInternal( content_object->setLifetime(500); // XXX this should be set by the APP content_object->setPathLabel(prod_label_); - // sign packet - if (signer_) { - signer_->signPacket(content_object.get()); - } - // update stats if (!fec) { produced_bytes_ += @@ -227,7 +438,7 @@ void RTCProductionProtocol::produceInternal( produced_fec_packets_++; } - if (produced_packets_ >= max_packet_production_) { + if (!data_aggregation_ && produced_packets_ >= max_packet_production_) { // in this case all the pending interests may be used to accomodate the // sudden increase in the production rate. calling the updateStats we will // notify all the clients @@ -240,8 +451,12 @@ void RTCProductionProtocol::produceInternal( // pass packet to FEC encoder if (fec_encoder_ && !fec) { - fec_encoder_->onPacketProduced( - *content_object, content_object->headerSize() + rtc::DATA_HEADER_SIZE); + uint32_t offset = + is_manifest ? content_object->headerSize() + : content_object->headerSize() + rtc::DATA_HEADER_SIZE; + uint32_t metadata = static_cast<uint32_t>(content_object->getPayloadType()); + + fec_encoder_->onPacketProduced(*content_object, offset, metadata); } output_buffer_.insert(content_object); @@ -253,7 +468,7 @@ void RTCProductionProtocol::produceInternal( auto seq_it = seqs_map_.find(current_seg_); if (seq_it != seqs_map_.end()) { - portal_->sendContentObject(*content_object); + sendContentObject(content_object, false, fec); } if (*on_content_object_output_) { @@ -264,6 +479,8 @@ void RTCProductionProtocol::produceInternal( // remove interests from the interest cache if it exists removeFromInterestQueue(current_seg_); + if (!fec) last_produced_data_ts_ = now; + // Update current segment current_seg_ = (current_seg_ + 1) % rtc::MIN_PROBE_SEQ; @@ -277,6 +494,55 @@ void RTCProductionProtocol::produceInternal( } } +void RTCProductionProtocol::flushFecPkts(uint32_t current_seq_num) { + // Currently we immediately send all the pending fec packets + // A pacing policy may be helpful, but we do not want to delay too much + // the packets at this moment. + while (paced_fec_packets_.size() > 0) { + producePktInternal(std::move(paced_fec_packets_.front().second), flow_name_, + true); + paced_fec_packets_.pop(); + } + fec_pacing_timer_->cancel(); + pending_fec_pace_ = false; + postponeFecPacket(); +} + +void RTCProductionProtocol::postponeFecPacket() { + if (paced_fec_packets_.size() == 0) return; + if (pending_fec_pace_) { + return; + } + + uint64_t produced_time = paced_fec_packets_.front().first; + uint64_t now = utils::SteadyTime::nowMs().count(); + + uint64_t wait_time = 0; + if ((produced_time + rtc::FEC_PACING_TIME) > now) + wait_time = produced_time + rtc::FEC_PACING_TIME - now; + + fec_pacing_timer_->expires_from_now(std::chrono::milliseconds(wait_time)); + pending_fec_pace_ = true; + + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + fec_pacing_timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + if (!sp->pending_fec_pace_) return; + + if (sp->paced_fec_packets_.size() > 0) { + sp->producePktInternal(std::move(sp->paced_fec_packets_.front().second), + sp->flow_name_, true); + sp->paced_fec_packets_.pop(); + } + sp->pending_fec_pace_ = false; + sp->postponeFecPacket(); + } + }); +} + void RTCProductionProtocol::onInterest(Interest &interest) { if (*on_interest_input_) { on_interest_input_->operator()(*socket_->getInterface(), interest); @@ -284,7 +550,7 @@ void RTCProductionProtocol::onInterest(Interest &interest) { auto suffix = interest.firstSuffix(); // numberOfSuffixes returns only the prefixes in the payalod - // we add + 1 to count anche the seq in the name + // we add + 1 to count also the seq in the name auto n_suffixes = interest.numberOfSuffixes() + 1; Name name = interest.getName(); bool prev_consumer_state = consumer_in_sync_; @@ -293,6 +559,7 @@ void RTCProductionProtocol::onInterest(Interest &interest) { if (i > 0) { name.setSuffix(*(suffix + (i - 1))); } + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received interest " << name; const std::shared_ptr<ContentObject> content_object = @@ -312,7 +579,7 @@ void RTCProductionProtocol::onInterest(Interest &interest) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send content %u (onInterest) " << content_object->getName(); content_object->setPathLabel(cache_label_); - portal_->sendContentObject(*content_object); + sendContentObject(content_object); } else { if (*on_interest_process_) { on_interest_process_->operator()(*socket_->getInterface(), interest); @@ -332,14 +599,19 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, consumer_in_sync_ = false; } - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); - if (interest_seg > rtc::MIN_PROBE_SEQ) { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << interest_seg; - sendNack(interest_seg); - return; + switch (rtc::ProbeHandler::getProbeType(interest_seg)) { + case rtc::ProbeType::INIT: + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received init probe " << interest_seg; + sendManifestProbe(interest_seg); + return; + case rtc::ProbeType::RTT: + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received RTT probe " << interest_seg; + sendNack(interest_seg); + return; + default: + break; } // if the production rate 0 use delayed nacks @@ -349,7 +621,7 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, next_timer = timers_map_.begin()->first; } - uint64_t expiration = now + rtc::SENTINEL_TIMER_INTERVAL; + uint64_t expiration = now + rtc::NACK_DELAY; addToInterestQueue(interest_seg, expiration); // here we have at least one interest in the queue, we need to start or @@ -369,8 +641,8 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, } if (queue_timer_on_) { - // the producer is producing. Send nacks to packets that will expire before - // the data production and remove the timer + // the producer is producing. Send nacks to packets that will expire + // before the data production and remove the timer queue_timer_on_ = false; interests_queue_timer_->cancel(); sendNacksForPendingInterests(); @@ -387,8 +659,8 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, sendNack(interest_seg); } else { if (!consumer_in_sync_ && on_consumer_in_sync_) { - // we consider the remote consumer to be in sync as soon as it covers 70% - // of the production window with interests + // we consider the remote consumer to be in sync as soon as it covers + // 70% of the production window with interests uint32_t perc = ceil((double)max_gap * 0.7); if (interest_seg > (perc + current_seg_)) { consumer_in_sync_ = true; @@ -401,13 +673,18 @@ void RTCProductionProtocol::processInterest(uint32_t interest_seg, } } -void RTCProductionProtocol::onError(std::error_code ec) {} - void RTCProductionProtocol::scheduleQueueTimer(uint64_t wait) { interests_queue_timer_->expires_from_now(std::chrono::milliseconds(wait)); - interests_queue_timer_->async_wait([this](std::error_code ec) { - if (ec) return; - interestQueueTimer(); + std::weak_ptr<RTCProductionProtocol> self = shared_from_this(); + interests_queue_timer_->async_wait([self](const std::error_code &ec) { + if (ec) { + return; + } + + auto sp = self.lock(); + if (sp && sp->isRunning()) { + sp->interestQueueTimer(); + } }); } @@ -450,9 +727,7 @@ void RTCProductionProtocol::sendNacksForPendingInterests() { if (packets_production_rate_ != 0) packet_gap = ceil(rtc::MILLI_IN_A_SEC / (double)packets_production_rate_); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); for (auto it = seqs_map_.begin(); it != seqs_map_.end(); it++) { if (it->first > current_seg_) { @@ -486,9 +761,7 @@ void RTCProductionProtocol::removeFromInterestQueue(uint32_t interest_seg) { } void RTCProductionProtocol::interestQueueTimer() { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); for (auto it_timers = timers_map_.begin(); it_timers != timers_map_.end();) { uint64_t expire = it_timers->first; @@ -511,20 +784,37 @@ void RTCProductionProtocol::interestQueueTimer() { } } +void RTCProductionProtocol::sendManifestProbe(uint32_t sequence) { + Name manifest_name(flow_name_); + manifest_name.setSuffix(sequence); + + std::shared_ptr<core::ContentObjectManifest> manifest_probe = + createManifest(manifest_name); + + manifest_probe->setLifetime(0); + manifest_probe->setPathLabel(prod_label_); + manifest_probe->encode(); + + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), + *manifest_probe); + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send init probe " << sequence; + sendContentObject(manifest_probe, true, false); +} + void RTCProductionProtocol::sendNack(uint32_t sequence) { auto nack = core::PacketManager<>::getInstance().getPacket<ContentObject>( - signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP, - signer_ ? signer_->getSignatureFieldSize() : 0); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + nack_header_format_.first, nack_header_format_.second); + uint64_t now = utils::SteadyTime::nowMs().count(); uint32_t next_packet = current_seg_; uint32_t prod_rate = bytes_production_rate_; struct rtc::nack_packet_t header; header.setTimestamp(now); header.setProductionRate(prod_rate); - header.setProductionSegement(next_packet); + header.setProductionSegment(next_packet); nack->appendPayload((const uint8_t *)&header, rtc::NACK_HEADER_SIZE); Name n(flow_name_); @@ -533,14 +823,16 @@ void RTCProductionProtocol::sendNack(uint32_t sequence) { nack->setLifetime(0); nack->setPathLabel(prod_label_); - if (signer_) { - signer_->signPacket(nack.get()); - } - if (!consumer_in_sync_ && on_consumer_in_sync_ && - sequence < rtc::MIN_PROBE_SEQ && sequence > next_packet) { + rtc::ProbeHandler::getProbeType(sequence) == rtc::ProbeType::NOT_PROBE && + sequence > next_packet) { consumer_in_sync_ = true; - auto interest = core::PacketManager<>::getInstance().getPacket<Interest>(); + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + + auto interest = + core::PacketManager<>::getInstance().getPacket<Interest>(format); interest->setName(n); on_consumer_in_sync_(*socket_->getInterface(), *interest); } @@ -550,16 +842,37 @@ void RTCProductionProtocol::sendNack(uint32_t sequence) { } DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send nack " << sequence; - portal_->sendContentObject(*nack); + sendContentObject(nack, true, false); } -void RTCProductionProtocol::onFecPackets( - std::vector<std::pair<uint32_t, fec::buffer>> &packets) { +void RTCProductionProtocol::sendContentObject( + std::shared_ptr<ContentObject> content_object, bool nack, bool fec) { + bool is_ah = _is_ah(content_object->getFormat()); + + // Compute signature + if (is_ah) { + signer_->signPacket(content_object.get()); + } + + portal_->sendContentObject(*content_object); + + // Compute and save data packet digest + if (making_manifest_ && !is_ah) { + auth::CryptoHashType hash_algo; + socket_->getSocketOption(interface::GeneralTransportOptions::HASH_ALGORITHM, + hash_algo); + manifest_entries_.push({content_object->getName().getSuffix(), + content_object->computeDigest(hash_algo)}); + } +} + +void RTCProductionProtocol::onFecPackets(fec::BufferArray &packets) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Produced " << packets.size() << " FEC packets"; + for (auto &packet : packets) { auto content_object = - std::static_pointer_cast<ContentObject>(packet.second); + std::static_pointer_cast<ContentObject>(packet.getBuffer()); content_object->prepend(content_object->headerSize() + rtc::DATA_HEADER_SIZE); pending_fec_packets_.push(std::move(content_object)); @@ -569,19 +882,21 @@ void RTCProductionProtocol::onFecPackets( fec::buffer RTCProductionProtocol::getBuffer(std::size_t size) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Asked buffer for FEC symbol of size " << size; + auto ret = core::PacketManager<>::getInstance().getPacket<ContentObject>( - signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP, - signer_ ? signer_->getSignatureFieldSize() : 0); + fec_header_format_.first, fec_header_format_.second); + ret->updateLength(rtc::DATA_HEADER_SIZE + size); ret->append(rtc::DATA_HEADER_SIZE + size); ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Responding with buffer of length " << ret->length(); - assert(ret->length() >= size); + DCHECK(ret->length() >= size); return ret; } } // namespace protocol -} // end namespace transport +} // namespace transport diff --git a/libtransport/src/protocols/prod_protocol_rtc.h b/libtransport/src/protocols/prod_protocol_rtc.h index 96ad5673d..7f50a2505 100644 --- a/libtransport/src/protocols/prod_protocol_rtc.h +++ b/libtransport/src/protocols/prod_protocol_rtc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -32,6 +32,7 @@ class RTCProductionProtocol : public ProductionProtocol { using ProductionProtocol::start; using ProductionProtocol::stop; + void setProducerParam() override; void produce(ContentObject &content_object) override; uint32_t produceStream(const Name &content_name, @@ -49,21 +50,31 @@ class RTCProductionProtocol : public ProductionProtocol { buffer, buffer_size, buffer_size)); } - void registerNamespaceWithNetwork(const Prefix &producer_namespace) override; - void setConsumerInSyncCallback( interface::ProducerInterestCallback &&callback) { on_consumer_in_sync_ = std::move(callback); } + auto shared_from_this() { return utils::shared_from(this); } + private: // packet handlers void onInterest(Interest &interest) override; - void onError(std::error_code ec) override; + void onError(const std::error_code &ec) override{}; void processInterest(uint32_t interest_seg, uint32_t lifetime); + void producePktInternal(std::shared_ptr<ContentObject> &&content_object, + const Name &content_name, bool fec = false); void produceInternal(std::shared_ptr<ContentObject> &&content_object, const Name &content_name, bool fec = false); void sendNack(uint32_t sequence); + void sendContentObject(std::shared_ptr<ContentObject> content_object, + bool nac = false, bool fec = false); + + // manifests + void sendManifestProbe(uint32_t sequence); + void sendManifest(const Name &content_name); + std::shared_ptr<core::ContentObjectManifest> createManifest( + const Name &name) const; // stats void updateStats(); @@ -77,15 +88,25 @@ class RTCProductionProtocol : public ProductionProtocol { void interestQueueTimer(); // FEC functions - void onFecPackets(std::vector<std::pair<uint32_t, fec::buffer>> &packets); + void onFecPackets(fec::BufferArray &packets); fec::buffer getBuffer(std::size_t size); + void postponeFecPacket(); + void dispatchFecPacket(); + void flushFecPkts(uint32_t current_seq_num); + // aggregated data functions + void emptyQueue(); + void addPacketToQueue(std::unique_ptr<utils::MemBuf> &&buffer); core::Name flow_name_; - uint32_t current_seg_; // seq id of the next packet produced - uint32_t prod_label_; // path lable of the producer - uint32_t cache_label_; // path lable for content from the producer cache - uint16_t data_header_size_; // hicn data header size + std::pair<core::Packet::Format, size_t> data_header_format_; + std::pair<core::Packet::Format, size_t> manifest_header_format_; + std::pair<core::Packet::Format, size_t> fec_header_format_; + std::pair<core::Packet::Format, size_t> nack_header_format_; + + uint32_t current_seg_; // seq id of the next packet produced + uint32_t prod_label_; // path label of the producer + uint32_t cache_label_; // path label for content from the producer cache uint32_t produced_bytes_; // bytes produced in the last round uint32_t produced_packets_; // packet produed in the last round @@ -98,7 +119,11 @@ class RTCProductionProtocol : public ProductionProtocol { uint32_t packets_production_rate_; // pps uint32_t fec_packets_production_rate_; // pps + uint64_t last_produced_data_ts_; // ms + std::unique_ptr<asio::steady_timer> round_timer_; + std::unique_ptr<asio::steady_timer> fec_pacing_timer_; + uint64_t last_round_; // delayed nacks are used by the producer to avoid to send too @@ -131,6 +156,21 @@ class RTCProductionProtocol : public ProductionProtocol { // Save FEC packets here before sending them std::queue<ContentObject::Ptr> pending_fec_packets_; + std::queue<std::pair<uint64_t, ContentObject::Ptr>> paced_fec_packets_; + bool pending_fec_pace_; + + // Save application packets if they are small + std::queue<std::unique_ptr<utils::MemBuf>> waiting_app_packets_; + uint16_t max_len_; // len of the largest packet + uint16_t queue_len_; // total size of all packet in the queue + bool data_aggregation_; // turns on/off data aggregation + // timer to check the queue len + std::unique_ptr<asio::steady_timer> app_packets_timer_; + bool data_aggregation_timer_switch_; // bool to check if the timer is on + + // Manifest + std::queue<std::pair<uint32_t, auth::CryptoHash>> + manifest_entries_; // map a packet suffix to a packet hash }; } // namespace protocol diff --git a/libtransport/src/protocols/production_protocol.cc b/libtransport/src/protocols/production_protocol.cc index 6b317d47d..8b781e38a 100644 --- a/libtransport/src/protocols/production_protocol.cc +++ b/libtransport/src/protocols/production_protocol.cc @@ -24,8 +24,8 @@ using namespace interface; ProductionProtocol::ProductionProtocol( implementation::ProducerSocket *icn_socket) - : socket_(icn_socket), - is_running_(false), + : Protocol(), + socket_(icn_socket), fec_encoder_(nullptr), on_interest_input_(VOID_HANDLER), on_interest_dropped_input_buffer_(VOID_HANDLER), @@ -38,101 +38,92 @@ ProductionProtocol::ProductionProtocol( on_content_object_output_(VOID_HANDLER), on_content_object_evicted_from_output_buffer_(VOID_HANDLER), on_content_produced_(VOID_HANDLER), + producer_callback_(VOID_HANDLER), fec_type_(fec::FECType::UNKNOWN) { socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); // TODO add statistics for producer // socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); } -ProductionProtocol::~ProductionProtocol() { - if (!is_async_ && is_running_) { - stop(); - } +ProductionProtocol::~ProductionProtocol() {} - if (listening_thread_.joinable()) { - listening_thread_.join(); +int ProductionProtocol::start() { + if (isRunning()) { + return -1; } -} -int ProductionProtocol::start() { - socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, - &on_interest_input_); - socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_DROP, - &on_interest_dropped_input_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_PASS, - &on_interest_inserted_input_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::CACHE_HIT, - &on_interest_satisfied_output_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::CACHE_MISS, - &on_interest_process_); - socket_->getSocketOption(ProducerCallbacksOptions::NEW_CONTENT_OBJECT, - &on_new_segment_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_READY, - &on_content_object_in_output_buffer_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT, - &on_content_object_output_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN, - &on_content_object_to_sign_); - socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, - &on_content_produced_); - - socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); - socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_); - socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST, - making_manifest_); - - bool first = true; - - for (core::Prefix &producer_namespace : served_namespaces_) { - if (first) { - core::BindConfig bind_config(producer_namespace, 1000); - portal_->bind(bind_config); - portal_->setProducerCallback(this); - first = !first; - } else { - portal_->registerRoute(producer_namespace); + portal_->getThread().addAndWaitForExecution([this]() { + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT, + &on_interest_input_); + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_DROP, + &on_interest_dropped_input_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_PASS, + &on_interest_inserted_input_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CACHE_HIT, + &on_interest_satisfied_output_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CACHE_MISS, + &on_interest_process_); + socket_->getSocketOption(ProducerCallbacksOptions::NEW_CONTENT_OBJECT, + &on_new_segment_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_READY, + &on_content_object_in_output_buffer_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT, + &on_content_object_output_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN, + &on_content_object_to_sign_); + socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED, + &on_content_produced_); + socket_->getSocketOption(ProducerCallbacksOptions::PRODUCER_CALLBACK, + &producer_callback_); + + socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); + socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_); + socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST, + making_manifest_); + + std::string fec_type_str = ""; + socket_->getSocketOption(GeneralTransportOptions::FEC_TYPE, fec_type_str); + if (fec_type_str != "") { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_type_str.c_str()); } - } - is_running_ = true; + portal_->registerTransportCallback(this); + setProducerParam(); - if (!is_async_) { - listening_thread_ = std::thread([this]() { portal_->runEventsLoop(); }); - } + setRunning(); + }); return 0; } -void ProductionProtocol::stop() { - is_running_ = false; - - if (!is_async_) { - portal_->stopEventsLoop(); - } else { - portal_->clear(); - } -} - void ProductionProtocol::produce(ContentObject &content_object) { - if (*on_content_object_in_output_buffer_) { - on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), - content_object); - } + auto content_object_ptr = content_object.shared_from_this(); + portal_->getThread().add([this, co = std::move(content_object_ptr)]() { + if (*on_content_object_in_output_buffer_) { + on_content_object_in_output_buffer_->operator()(*socket_->getInterface(), + *co); + } - output_buffer_.insert(std::static_pointer_cast<ContentObject>( - content_object.shared_from_this())); + output_buffer_.insert(co); - if (*on_content_object_output_) { - on_content_object_output_->operator()(*socket_->getInterface(), - content_object); - } + if (*on_content_object_output_) { + on_content_object_output_->operator()(*socket_->getInterface(), *co); + } - portal_->sendContentObject(content_object); + portal_->sendContentObject(*co); + }); } -void ProductionProtocol::registerNamespaceWithNetwork( - const Prefix &producer_namespace) { - served_namespaces_.push_back(producer_namespace); +void ProductionProtocol::sendMapme() { portal_->sendMapme(); } + +void ProductionProtocol::onError(const std::error_code &ec) { + // Stop production protocol + stop(); + + // Call error callback + if (producer_callback_) { + producer_callback_->produceError(ec); + } } } // namespace protocol diff --git a/libtransport/src/protocols/production_protocol.h b/libtransport/src/protocols/production_protocol.h index 7366311eb..8e10d2f40 100644 --- a/libtransport/src/protocols/production_protocol.h +++ b/libtransport/src/protocols/production_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -22,6 +22,7 @@ #include <implementation/socket.h> #include <protocols/fec_base.h> #include <protocols/fec_utils.h> +#include <protocols/protocol.h> #include <utils/content_store.h> #include <atomic> @@ -33,17 +34,20 @@ namespace protocol { using namespace core; -class ProductionProtocol : public Portal::ProducerCallback { +class ProductionProtocol + : public Protocol, + public std::enable_shared_from_this<ProductionProtocol> { public: ProductionProtocol(implementation::ProducerSocket *icn_socket); virtual ~ProductionProtocol(); - bool isRunning() { return is_running_; } - virtual int start(); - virtual void stop(); + using Protocol::stop; + + virtual void setProducerParam(){}; virtual void produce(ContentObject &content_object); + virtual void sendMapme(); virtual uint32_t produceStream(const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer, bool is_last = true, @@ -61,20 +65,18 @@ class ProductionProtocol : public Portal::ProducerCallback { void setOutputBufferSize(std::size_t size) { output_buffer_.setLimit(size); } std::size_t getOutputBufferSize() { return output_buffer_.getLimit(); } - virtual void registerNamespaceWithNetwork(const Prefix &producer_namespace); - const std::list<Prefix> &getNamespaces() const { return served_namespaces_; } - protected: // Producer callback virtual void onInterest(core::Interest &i) override = 0; - virtual void onError(std::error_code ec) override{}; + virtual void onError(const std::error_code &ec) override; template <typename FECHandler, typename AllocatorHandler> void enableFEC(FECHandler &&fec_handler, AllocatorHandler &&allocator_handler) { if (!fec_encoder_) { // Try to get FEC from environment - if (const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE")) { + const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE"); + if (fec_str && (fec_type_ == fec::FECType::UNKNOWN)) { LOG(INFO) << "Using FEC " << fec_str; fec_type_ = fec::FECUtils::fecTypeFromString(fec_str); } @@ -95,11 +97,6 @@ class ProductionProtocol : public Portal::ProducerCallback { // Thread pool responsible for IO operations (send data / receive interests) std::vector<utils::EventThread> io_threads_; - - // TODO remove this thread - std::thread listening_thread_; - std::shared_ptr<Portal> portal_; - std::atomic<bool> is_running_; interface::ProductionStatistics *stats_; std::unique_ptr<fec::ProducerFEC> fec_encoder_; @@ -119,15 +116,14 @@ class ProductionProtocol : public Portal::ProducerCallback { interface::ProducerContentCallback *on_content_produced_; + interface::ProducerSocket::Callback *producer_callback_; + // Output buffer utils::ContentStore output_buffer_; - // List ot routes served by current producer protocol - std::list<Prefix> served_namespaces_; - // Signature and manifest std::shared_ptr<auth::Signer> signer_; - bool making_manifest_; + uint32_t making_manifest_; bool is_async_; fec::FECType fec_type_; diff --git a/libtransport/src/protocols/protocol.h b/libtransport/src/protocols/protocol.h new file mode 100644 index 000000000..a9f929db9 --- /dev/null +++ b/libtransport/src/protocols/protocol.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <core/portal.h> +#include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/utils/noncopyable.h> + +#include <random> + +namespace transport { + +namespace protocol { + +class Protocol : public core::Portal::TransportCallback, utils::NonCopyable { + public: + virtual void stop() { + portal_->getThread().addAndWaitForExecution([this]() { + unSetRunning(); + portal_->unregisterTransportCallback(); + portal_->clear(); + }); + } + + virtual void onInterest(core::Interest &i) { + throw errors::RuntimeException("Not implemented"); + } + + virtual void onContentObject(core::Interest &i, core::ContentObject &c) { + throw errors::RuntimeException("Not implemented"); + } + + virtual void onTimeout(core::Interest::Ptr &i, const core::Name &n) { + throw errors::RuntimeException("Not implemented"); + } + + virtual void onError(const std::error_code &ec) { + throw errors::RuntimeException("Not implemented"); + } + + bool isRunning() { return is_running_; } + void setRunning() { is_running_ = true; } + void unSetRunning() { is_running_ = false; } + + protected: + Protocol() : portal_(nullptr), is_running_(false), gen_(rd_()) {} + virtual ~Protocol() {} + + protected: + std::shared_ptr<core::Portal> portal_; + std::atomic_bool is_running_; + + // Random engine + std::random_device rd_; + std::mt19937 gen_; +}; + +} // namespace protocol + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/protocols/raaqm.cc b/libtransport/src/protocols/raaqm.cc index 1247af400..131367d78 100644 --- a/libtransport/src/protocols/raaqm.cc +++ b/libtransport/src/protocols/raaqm.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -36,8 +36,9 @@ RaaqmTransportProtocol::RaaqmTransportProtocol( current_window_size_(1), interests_in_flight_(0), cur_path_(nullptr), - t0_(utils::SteadyClock::now()), + t0_(utils::SteadyTime::Clock::now()), rate_estimator_(nullptr), + dis_(0, 1.0), schedule_interests_(true) { init(); } @@ -60,7 +61,7 @@ void RaaqmTransportProtocol::reset() { // Reset protocol variables interests_in_flight_ = 0; - t0_ = utils::SteadyClock::now(); + t0_ = utils::SteadyTime::Clock::now(); // Optionally reset congestion window bool reset_window; @@ -389,6 +390,8 @@ void RaaqmTransportProtocol::sendInterest( uint32_t len) { interests_in_flight_++; interest_retransmissions_[interest_name.getSuffix() & mask]++; + interest_timepoints_[interest_name.getSuffix() & mask] = + utils::SteadyTime::Clock::now(); TransportProtocol::sendInterest(interest_name, additional_suffixes, len); } @@ -477,7 +480,7 @@ void RaaqmTransportProtocol::scheduleNextInterests() { } } -void RaaqmTransportProtocol::onContentReassembled(std::error_code ec) { +void RaaqmTransportProtocol::onContentReassembled(const std::error_code &ec) { rate_estimator_->onDownloadFinished(); TransportProtocol::onContentReassembled(ec); schedule_interests_ = false; @@ -487,18 +490,18 @@ void RaaqmTransportProtocol::updateRtt(uint64_t segment) { if (TRANSPORT_EXPECT_FALSE(!cur_path_)) { throw std::runtime_error("RAAQM ERROR: no current path found, exit"); } else { - auto now = utils::SteadyClock::now(); - utils::Microseconds rtt = std::chrono::duration_cast<utils::Microseconds>( - now - interest_timepoints_[segment & mask]); + auto now = utils::SteadyTime::Clock::now(); + utils::SteadyTime::Milliseconds rtt = utils::SteadyTime::getDurationMs( + interest_timepoints_[segment & mask], now); // Update stats - updateStats((uint32_t)segment, rtt.count(), now); + updateStats((uint32_t)segment, rtt, now); if (rate_estimator_) { - rate_estimator_->onRttUpdate((double)rtt.count()); + rate_estimator_->onRttUpdate(rtt); } - cur_path_->insertNewRtt(rtt.count(), now); + cur_path_->insertNewRtt(rtt, now); cur_path_->smoothTimer(); if (cur_path_->newPropagationDelayAvailable()) { @@ -510,27 +513,27 @@ void RaaqmTransportProtocol::updateRtt(uint64_t segment) { void RaaqmTransportProtocol::RAAQM() { if (!cur_path_) { throw errors::RuntimeException("ERROR: no current path found, exit"); - exit(EXIT_FAILURE); } else { // Change drop probability according to RTT statistics cur_path_->updateDropProb(); - double coin = ((double)rand() / (RAND_MAX)); + double coin = dis_(gen_); if (coin <= cur_path_->getDropProb()) { decreaseWindow(); } } } -void RaaqmTransportProtocol::updateStats(uint32_t suffix, uint64_t rtt, - utils::TimePoint &now) { +void RaaqmTransportProtocol::updateStats( + uint32_t suffix, const utils::SteadyTime::Milliseconds &rtt, + utils::SteadyTime::TimePoint &now) { // Update RTT statistics stats_->updateAverageRtt(rtt); stats_->updateAverageWindowSize(current_window_size_); // Call statistics callback if (*stats_summary_) { - auto dt = std::chrono::duration_cast<utils::Milliseconds>(now - t0_); + auto dt = utils::SteadyTime::getDurationMs(t0_, now); uint32_t timer_interval_milliseconds = 0; socket_->getSocketOption(GeneralTransportOptions::STATS_INTERVAL, diff --git a/libtransport/src/protocols/raaqm.h b/libtransport/src/protocols/raaqm.h index ffbb30d3a..ec344c23a 100644 --- a/libtransport/src/protocols/raaqm.h +++ b/libtransport/src/protocols/raaqm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -23,6 +23,7 @@ #include <protocols/transport_protocol.h> #include <queue> +#include <random> #include <vector> namespace transport { @@ -55,8 +56,9 @@ class RaaqmTransportProtocol : public TransportProtocol, const ContentObject &content_object); virtual void afterDataUnsatisfied(uint64_t segment); - virtual void updateStats(uint32_t suffix, uint64_t rtt, - utils::TimePoint &now); + virtual void updateStats(uint32_t suffix, + const utils::SteadyTime::Milliseconds &rtt, + utils::SteadyTime::TimePoint &now); private: void init(); @@ -73,7 +75,7 @@ class RaaqmTransportProtocol : public TransportProtocol, *additional_suffixes = nullptr, uint32_t len = 0) override; - void onContentReassembled(std::error_code ec) override; + void onContentReassembled(const std::error_code &ec) override; void updateRtt(uint64_t segment); void RAAQM(); void updatePathTable(const ContentObject &content_object); @@ -81,13 +83,15 @@ class RaaqmTransportProtocol : public TransportProtocol, void checkForStalePaths(); void printRtt(); + auto shared_from_this() { return utils::shared_from(this); } + protected: // Congestion window management double current_window_size_; // Protocol management uint64_t interests_in_flight_; std::array<std::uint32_t, buffer_size> interest_retransmissions_; - std::array<utils::TimePoint, buffer_size> interest_timepoints_; + std::array<utils::SteadyTime::TimePoint, buffer_size> interest_timepoints_; std::queue<uint32_t> interest_to_retransmit_; private: @@ -102,13 +106,16 @@ class RaaqmTransportProtocol : public TransportProtocol, PathTable path_table_; // TimePoints for statistic - utils::TimePoint t0_; + utils::SteadyTime::TimePoint t0_; bool set_interest_filter_; // for rate-estimation at packet level IcnRateEstimator *rate_estimator_; + // Real distribution + std::uniform_real_distribution<> dis_; + // params for autotuning bool raaqm_autotune_; double default_beta_; diff --git a/libtransport/src/protocols/raaqm_data_path.cc b/libtransport/src/protocols/raaqm_data_path.cc index f2c21b9ef..d06fee918 100644 --- a/libtransport/src/protocols/raaqm_data_path.cc +++ b/libtransport/src/protocols/raaqm_data_path.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -23,16 +23,18 @@ namespace protocol { RaaqmDataPath::RaaqmDataPath(double drop_factor, double minimum_drop_probability, unsigned new_timer, unsigned int samples, - uint64_t new_rtt, uint64_t new_rtt_min, - uint64_t new_rtt_max, unsigned new_pd) + const utils::SteadyTime::Milliseconds &new_rtt, + const utils::SteadyTime::Milliseconds &new_rtt_min, + const utils::SteadyTime::Milliseconds &new_rtt_max, + unsigned new_pd) : drop_factor_(drop_factor), minimum_drop_probability_(minimum_drop_probability), timer_(new_timer), samples_(samples), - rtt_(new_rtt), - rtt_min_(new_rtt_min), - rtt_max_(new_rtt_max), + rtt_(new_rtt.count()), + rtt_min_(new_rtt_min.count()), + rtt_max_(new_rtt_max.count()), prop_delay_(new_pd), new_prop_delay_(false), drop_prob_(0), @@ -43,14 +45,15 @@ RaaqmDataPath::RaaqmDataPath(double drop_factor, raw_data_bytes_received_(0), last_raw_data_bytes_received_(0), rtt_samples_(samples_), - last_received_pkt_(utils::SteadyClock::now()), + last_received_pkt_(utils::SteadyTime::Clock::now()), average_rtt_(0), alpha_(ALPHA) {} -RaaqmDataPath &RaaqmDataPath::insertNewRtt(uint64_t new_rtt, - const utils::TimePoint &now) { - rtt_ = new_rtt; - rtt_samples_.pushBack(new_rtt); +RaaqmDataPath &RaaqmDataPath::insertNewRtt( + const utils::SteadyTime::Milliseconds &new_rtt, + const utils::SteadyTime::TimePoint &now) { + rtt_ = new_rtt.count(); + rtt_samples_.pushBack(rtt_); rtt_max_ = rtt_samples_.rBegin(); rtt_min_ = rtt_samples_.begin(); @@ -143,10 +146,8 @@ unsigned int RaaqmDataPath::getPropagationDelay() { } bool RaaqmDataPath::isStale() { - utils::TimePoint now = utils::SteadyClock::now(); - auto time = - std::chrono::duration_cast<utils::Microseconds>(now - last_received_pkt_) - .count(); + utils::SteadyTime::TimePoint now = utils::SteadyTime::Clock::now(); + auto time = utils::SteadyTime::getDurationUs(last_received_pkt_, now).count(); if (time > 2000000) { return true; } diff --git a/libtransport/src/protocols/raaqm_data_path.h b/libtransport/src/protocols/raaqm_data_path.h index c0b53a690..b6f7c5ac1 100644 --- a/libtransport/src/protocols/raaqm_data_path.h +++ b/libtransport/src/protocols/raaqm_data_path.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -34,8 +34,13 @@ class RaaqmDataPath { public: RaaqmDataPath(double drop_factor, double minimum_drop_probability, unsigned new_timer, unsigned int samples, - uint64_t new_rtt = 1000, uint64_t new_rtt_min = 1000, - uint64_t new_rtt_max = 1000, unsigned new_pd = UINT_MAX); + const utils::SteadyTime::Milliseconds &new_rtt = + utils::SteadyTime::Milliseconds(1000), + const utils::SteadyTime::Milliseconds &new_rtt_min = + utils::SteadyTime::Milliseconds(1000), + const utils::SteadyTime::Milliseconds &new_rtt_max = + utils::SteadyTime::Milliseconds(1000), + unsigned new_pd = UINT_MAX); public: /* @@ -44,7 +49,8 @@ class RaaqmDataPath { * max of RTT. * @param new_rtt is the value of the new RTT */ - RaaqmDataPath &insertNewRtt(uint64_t new_rtt, const utils::TimePoint &now); + RaaqmDataPath &insertNewRtt(const utils::SteadyTime::Milliseconds &new_rtt, + const utils::SteadyTime::TimePoint &now); /** * @brief Update the path statistics @@ -214,7 +220,7 @@ class RaaqmDataPath { /** * Time of the last call to the path reporter method */ - utils::TimePoint last_received_pkt_; + utils::SteadyTime::TimePoint last_received_pkt_; double average_rtt_; double alpha_; diff --git a/libtransport/src/protocols/rate_estimation.cc b/libtransport/src/protocols/rate_estimation.cc index 2337e18be..d834b53e6 100644 --- a/libtransport/src/protocols/rate_estimation.cc +++ b/libtransport/src/protocols/rate_estimation.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -14,6 +14,7 @@ */ #include <glog/logging.h> +#include <hicn/transport/errors/runtime_exception.h> #include <hicn/transport/interfaces/socket_options_default_values.h> #include <protocols/rate_estimation.h> @@ -92,8 +93,8 @@ InterRttEstimator::InterRttEstimator(double alpha_arg) { this->win_current_ = 1.0; pthread_mutex_init(&(this->mutex_), NULL); - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } InterRttEstimator::~InterRttEstimator() { @@ -105,33 +106,35 @@ InterRttEstimator::~InterRttEstimator() { pthread_mutex_destroy(&(this->mutex_)); } -void InterRttEstimator::onRttUpdate(double rtt) { +void InterRttEstimator::onRttUpdate( + const utils::SteadyTime::Milliseconds &rtt) { pthread_mutex_lock(&(this->mutex_)); - this->rtt_ = rtt; + this->rtt_ = rtt.count(); this->number_of_packets_++; - this->avg_rtt_ += rtt; + this->avg_rtt_ += this->rtt_; pthread_mutex_unlock(&(this->mutex_)); if (!thread_is_running_) { my_th_ = (pthread_t *)malloc(sizeof(pthread_t)); if (!my_th_) { - LOG(ERROR) << "Error allocating thread."; - my_th_ = NULL; + throw errors::RuntimeException("Error allocating thread."); } - if (/*int err = */ pthread_create(my_th_, NULL, transport::protocol::Timer, - (void *)this)) { - LOG(ERROR) << "Error creating the thread"; + + if (pthread_create(my_th_, NULL, transport::protocol::Timer, + (void *)this)) { + free(my_th_); my_th_ = NULL; + throw errors::RuntimeException("Error allocating thread."); } + thread_is_running_ = true; } } void InterRttEstimator::onWindowIncrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); pthread_mutex_lock(&(this->mutex_)); this->avg_win_ += this->win_current_ * delay; @@ -139,14 +142,13 @@ void InterRttEstimator::onWindowIncrease(double win_current) { this->win_change_ += delay; pthread_mutex_unlock(&(this->mutex_)); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void InterRttEstimator::onWindowDecrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); pthread_mutex_lock(&(this->mutex_)); this->avg_win_ += this->win_current_ * delay; @@ -154,25 +156,24 @@ void InterRttEstimator::onWindowDecrease(double win_current) { this->win_change_ += delay; pthread_mutex_unlock(&(this->mutex_)); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } ALaTcpEstimator::ALaTcpEstimator() { this->estimation_ = 0.0; this->observer_ = NULL; - this->start_time_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); this->totalSize_ = 0.0; } void ALaTcpEstimator::onStart() { this->totalSize_ = 0.0; - this->start_time_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); } void ALaTcpEstimator::onDownloadFinished() { - TimePoint end = std::chrono::steady_clock::now(); - auto delay = - std::chrono::duration_cast<Microseconds>(end - this->start_time_).count(); + auto end = utils::SteadyTime::now(); + auto delay = utils::SteadyTime::getDurationUs(this->start_time_, end).count(); this->estimation_ = this->totalSize_ * 8 * 1000000 / delay; if (observer_) { observer_->notifyStats(this->estimation_); @@ -192,22 +193,21 @@ SimpleEstimator::SimpleEstimator(double alphaArg, int batching_param) { this->number_of_packets_ = 0; this->base_alpha_ = alphaArg; this->alpha_ = alphaArg; - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void SimpleEstimator::onStart() { this->estimated_ = false; this->number_of_packets_ = 0; this->total_size_ = 0.0; - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void SimpleEstimator::onDownloadFinished() { - TimePoint end = std::chrono::steady_clock::now(); - auto delay = - std::chrono::duration_cast<Microseconds>(end - this->start_time_).count(); + auto end = utils::SteadyTime::now(); + auto delay = utils::SteadyTime::getDurationUs(this->start_time_, end).count(); if (observer_) { observer_->notifyDownloadTime((double)delay); } @@ -229,8 +229,7 @@ void SimpleEstimator::onDownloadFinished() { } else { if (this->number_of_packets_ >= (int)(75.0 * (double)this->batching_param_ / 100.0)) { - delay = std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + delay = utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); // Assuming all packets carry max_packet_size_ bytes of data // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds if (this->estimation_) { @@ -249,22 +248,21 @@ void SimpleEstimator::onDownloadFinished() { } this->number_of_packets_ = 0; this->total_size_ = 0.0; - this->start_time_ = std::chrono::steady_clock::now(); - this->begin_batch_ = std::chrono::steady_clock::now(); + this->start_time_ = utils::SteadyTime::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void SimpleEstimator::onDataReceived(int packet_size) { this->total_size_ += packet_size; } -void SimpleEstimator::onRttUpdate(double rtt) { +void SimpleEstimator::onRttUpdate(const utils::SteadyTime::Milliseconds &rtt) { this->number_of_packets_++; if (this->number_of_packets_ == this->batching_param_) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); // Assuming all packets carry max_packet_size_ bytes of data // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds if (this->estimation_) { @@ -280,7 +278,7 @@ void SimpleEstimator::onRttUpdate(double rtt) { this->alpha_ = this->base_alpha_; this->number_of_packets_ = 0; this->total_size_ = 0.0; - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } } @@ -297,13 +295,14 @@ BatchingPacketsEstimator::BatchingPacketsEstimator(double alpha_arg, this->max_packet_size_ = 0; this->estimation_ = 0.0; this->win_current_ = 1.0; - this->begin_batch_ = std::chrono::steady_clock::now(); - this->start_time_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); + this->start_time_ = utils::SteadyTime::now(); } -void BatchingPacketsEstimator::onRttUpdate(double rtt) { +void BatchingPacketsEstimator::onRttUpdate( + const utils::SteadyTime::Milliseconds &rtt) { this->number_of_packets_++; - this->avg_rtt_ += rtt; + this->avg_rtt_ += rtt.count(); if (number_of_packets_ == this->batching_param_) { if (estimation_ == 0) { @@ -329,25 +328,23 @@ void BatchingPacketsEstimator::onRttUpdate(double rtt) { } void BatchingPacketsEstimator::onWindowIncrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); this->avg_win_ += this->win_current_ * delay; this->win_current_ = win_current; this->win_change_ += delay; - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } void BatchingPacketsEstimator::onWindowDecrease(double win_current) { - TimePoint end = std::chrono::steady_clock::now(); + auto end = utils::SteadyTime::now(); auto delay = - std::chrono::duration_cast<Microseconds>(end - this->begin_batch_) - .count(); + utils::SteadyTime::getDurationUs(this->begin_batch_, end).count(); this->avg_win_ += this->win_current_ * delay; this->win_current_ = win_current; this->win_change_ += delay; - this->begin_batch_ = std::chrono::steady_clock::now(); + this->begin_batch_ = utils::SteadyTime::now(); } } // end namespace protocol diff --git a/libtransport/src/protocols/rate_estimation.h b/libtransport/src/protocols/rate_estimation.h index 42ae74194..b71de12e4 100644 --- a/libtransport/src/protocols/rate_estimation.h +++ b/libtransport/src/protocols/rate_estimation.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/interfaces/statistics.h> +#include <hicn/transport/utils/noncopyable.h> #include <protocols/raaqm_data_path.h> #include <chrono> @@ -24,16 +25,13 @@ namespace transport { namespace protocol { -class IcnRateEstimator { +class IcnRateEstimator : utils::NonCopyable { public: - using TimePoint = std::chrono::steady_clock::time_point; - using Microseconds = std::chrono::microseconds; - IcnRateEstimator(){}; virtual ~IcnRateEstimator(){}; - virtual void onRttUpdate(double rtt){}; + virtual void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt){}; virtual void onDataReceived(int packetSize){}; @@ -48,9 +46,10 @@ class IcnRateEstimator { virtual void setObserver(interface::IcnObserver *observer) { this->observer_ = observer; }; + interface::IcnObserver *observer_; - TimePoint start_time_; - TimePoint begin_batch_; + utils::SteadyTime::TimePoint start_time_; + utils::SteadyTime::TimePoint begin_batch_; double base_alpha_; double alpha_; double estimation_; @@ -67,7 +66,7 @@ class InterRttEstimator : public IcnRateEstimator { ~InterRttEstimator(); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt); void onDataReceived(int packet_size) { if (packet_size > this->max_packet_size_) { @@ -102,7 +101,7 @@ class BatchingPacketsEstimator : public IcnRateEstimator { public: BatchingPacketsEstimator(double alpha_arg, int batchingParam); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt); void onDataReceived(int packet_size) { if (packet_size > this->max_packet_size_) { @@ -149,7 +148,7 @@ class SimpleEstimator : public IcnRateEstimator { public: SimpleEstimator(double alpha, int batching_param); - void onRttUpdate(double rtt); + void onRttUpdate(const utils::SteadyTime::Milliseconds &rtt); void onDataReceived(int packet_size); diff --git a/libtransport/src/protocols/reassembly.cc b/libtransport/src/protocols/reassembly.cc index ce24fce1b..065458f7e 100644 --- a/libtransport/src/protocols/reassembly.cc +++ b/libtransport/src/protocols/reassembly.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/protocols/reassembly.h b/libtransport/src/protocols/reassembly.h index e072ad123..b0879201d 100644 --- a/libtransport/src/protocols/reassembly.h +++ b/libtransport/src/protocols/reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -36,7 +36,7 @@ class Reassembly { public: class ContentReassembledCallback { public: - virtual void onContentReassembled(std::error_code ec) = 0; + virtual void onContentReassembled(const std::error_code &ec) = 0; }; Reassembly(implementation::ConsumerSocket *icn_socket, @@ -47,12 +47,12 @@ class Reassembly { virtual ~Reassembly() = default; /** - * Hanle reassembly of content object. + * Handle reassembly of content object. */ virtual void reassemble(core::ContentObject &content_object) = 0; /** - * Hanle reassembly of content object. + * Handle reassembly of content object. */ virtual void reassemble(utils::MemBuf &buffer, uint32_t suffix) = 0; diff --git a/libtransport/src/protocols/rtc/CMakeLists.txt b/libtransport/src/protocols/rtc/CMakeLists.txt index 873b345d0..be8e0189c 100644 --- a/libtransport/src/protocols/rtc/CMakeLists.txt +++ b/libtransport/src/protocols/rtc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -16,22 +16,43 @@ list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_consts.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_forwarding_strategy.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_indexer.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_packet.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_congestion_detection.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_iat.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_recovery_strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_delay.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_fec_only.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_low_rate.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_recovery_off.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_rtx_only.h ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_verifier.h ) list(APPEND SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_forwarding_strategy.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_congestion_detection.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_iat.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_recovery_strategy.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_delay.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_fec_only.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_low_rate.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_recovery_off.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_rtx_only.cc ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_verifier.cc ) set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) diff --git a/libtransport/src/protocols/rtc/probe_handler.cc b/libtransport/src/protocols/rtc/probe_handler.cc index abaca6ad9..abb234757 100644 --- a/libtransport/src/protocols/rtc/probe_handler.cc +++ b/libtransport/src/protocols/rtc/probe_handler.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,6 +13,7 @@ * limitations under the License. */ +#include <hicn/transport/utils/chrono_typedefs.h> #include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_consts.h> @@ -27,6 +28,7 @@ ProbeHandler::ProbeHandler(SendProbeCallback &&send_callback, : probe_interval_(0), max_probes_(0), sent_probes_(0), + recv_probes_(0), probe_timer_(std::make_unique<asio::steady_timer>(io_service)), rand_eng_((std::random_device())()), distr_(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ), @@ -39,17 +41,25 @@ uint64_t ProbeHandler::getRtt(uint32_t seq) { if (it == pending_probes_.end()) return 0; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t rtt = now - it->second; if (rtt < 1) rtt = 1; pending_probes_.erase(it); + recv_probes_++; return rtt; } +double ProbeHandler::getProbeLossRate() { + return 1.0 - ((double)recv_probes_ / (double)sent_probes_); +} + +void ProbeHandler::setSuffixRange(uint32_t min, uint32_t max) { + assert(min <= max && min >= MIN_PROBE_SEQ); + distr_ = std::uniform_int_distribution<uint32_t>(min, max); +} + void ProbeHandler::setProbes(uint32_t probe_interval, uint32_t max_probes) { stopProbes(); probe_interval_ = probe_interval; @@ -60,6 +70,7 @@ void ProbeHandler::stopProbes() { probe_interval_ = 0; max_probes_ = 0; sent_probes_ = 0; + recv_probes_ = 0; probe_timer_->cancel(); } @@ -67,9 +78,7 @@ void ProbeHandler::sendProbes() { if (probe_interval_ == 0) return; if (max_probes_ != 0 && sent_probes_ >= max_probes_) return; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint32_t seq = distr_(rand_eng_); pending_probes_.insert(std::pair<uint32_t, uint64_t>(seq, now)); @@ -92,14 +101,25 @@ void ProbeHandler::sendProbes() { std::weak_ptr<ProbeHandler> self(shared_from_this()); probe_timer_->expires_from_now(std::chrono::microseconds(probe_interval_)); - probe_timer_->async_wait([self](std::error_code ec) { + probe_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - if (auto s = self.lock()) { + auto s = self.lock(); + if (s) { s->sendProbes(); } }); } +ProbeType ProbeHandler::getProbeType(uint32_t seq) { + if (MIN_INIT_PROBE_SEQ <= seq && seq <= MAX_INIT_PROBE_SEQ) { + return ProbeType::INIT; + } + if (MIN_RTT_PROBE_SEQ <= seq && seq <= MAX_RTT_PROBE_SEQ) { + return ProbeType::RTT; + } + return ProbeType::NOT_PROBE; +} + } // namespace rtc } // namespace protocol diff --git a/libtransport/src/protocols/rtc/probe_handler.h b/libtransport/src/protocols/rtc/probe_handler.h index e34b23df0..2de908176 100644 --- a/libtransport/src/protocols/rtc/probe_handler.h +++ b/libtransport/src/protocols/rtc/probe_handler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -26,6 +26,12 @@ namespace protocol { namespace rtc { +enum class ProbeType { + NOT_PROBE, + INIT, + RTT, +}; + class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> { public: using SendProbeCallback = std::function<void(uint32_t)>; @@ -35,31 +41,39 @@ class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> { ~ProbeHandler(); - // if the function returns 0 the probe is not valaid + // If the function returns 0 the probe is not valid. uint64_t getRtt(uint32_t seq); - // reset the probes parameters. it stop the current probing. - // to restar call sendProbes. - // probe_interval = 0 means that no event will be scheduled - // max_probe = 0 means no limit to the number of probe to send + // this function may return a residual loss rate higher than the real one if + // we don't wait enough time for the probes to come back + double getProbeLossRate(); + + // Set the probe suffix range [min, max] + void setSuffixRange(uint32_t min, uint32_t max); + + // Reset the probes parameters and stops the current probing. + // probe_interval = 0 means that no event will be scheduled. + // max_probe = 0 means no limit to the number of probe to send. void setProbes(uint32_t probe_interval, uint32_t max_probes); - // stop to schedule probes void stopProbes(); void sendProbes(); + static ProbeType getProbeType(uint32_t seq); + private: uint32_t probe_interval_; // us uint32_t max_probes_; // packets uint32_t sent_probes_; // packets + uint32_t recv_probes_; // packets std::unique_ptr<asio::steady_timer> probe_timer_; - // map from seqnumber to timestamp + // Map from packet suffixes to timestamp std::unordered_map<uint32_t, uint64_t> pending_probes_; - // random generator + // Random generator std::default_random_engine rand_eng_; std::uniform_int_distribution<uint32_t> distr_; diff --git a/libtransport/src/protocols/rtc/rtc.cc b/libtransport/src/protocols/rtc/rtc.cc index 0cb4cda1d..df6522471 100644 --- a/libtransport/src/protocols/rtc/rtc.cc +++ b/libtransport/src/protocols/rtc/rtc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -22,7 +22,7 @@ #include <protocols/rtc/rtc.h> #include <protocols/rtc/rtc_consts.h> #include <protocols/rtc/rtc_indexer.h> -#include <protocols/rtc/rtc_rc_queue.h> +#include <protocols/rtc/rtc_rc_congestion_detection.h> #include <algorithm> @@ -37,13 +37,15 @@ using namespace interface; RTCTransportProtocol::RTCTransportProtocol( implementation::ConsumerSocket *icn_socket) : TransportProtocol(icn_socket, new RtcIndexer<>(icn_socket, this), - new DatagramReassembly(icn_socket, this)), + new RtcReassembly(icn_socket, this)), number_(0) { icn_socket->getSocketOption(PORTAL, portal_); - round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); + round_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); scheduler_timer_ = - std::make_unique<asio::steady_timer>(portal_->getIoService()); - pacing_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService()); + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); + pacing_timer_ = + std::make_unique<asio::steady_timer>(portal_->getThread().getIoService()); } RTCTransportProtocol::~RTCTransportProtocol() {} @@ -61,25 +63,52 @@ std::size_t RTCTransportProtocol::transportHeaderLength() { // private void RTCTransportProtocol::initParams() { TransportProtocol::reset(); + fwd_strategy_.setCallback(on_fwd_strategy_); - rc_ = std::make_shared<RTCRateControlQueue>(); + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + + std::shared_ptr<auth::Verifier> verifier; + socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier); + + uint32_t max_unverified_delay; + socket_->getSocketOption(GeneralTransportOptions::MAX_UNVERIFIED_TIME, + max_unverified_delay); + + rc_ = std::make_shared<RTCRateControlCongestionDetection>(); ldr_ = std::make_shared<RTCLossDetectionAndRecovery>( - indexer_verifier_.get(), - std::bind(&RTCTransportProtocol::sendRtxInterest, this, - std::placeholders::_1), - portal_->getIoService()); + indexer_verifier_.get(), portal_->getThread().getIoService(), + interface::RtcTransportRecoveryStrategies::RTX_ONLY, + [self](uint32_t seq) { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + ptr->sendRtxInterest(seq); + } + }, + on_rec_strategy_); + verifier_ = std::make_shared<RTCVerifier>(verifier, max_unverified_delay); state_ = std::make_shared<RTCState>( indexer_verifier_.get(), - std::bind(&RTCTransportProtocol::sendProbeInterest, this, - std::placeholders::_1), - std::bind(&RTCTransportProtocol::discoveredRtt, this), - portal_->getIoService()); + [self](uint32_t seq) { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + ptr->sendProbeInterest(seq); + } + }, + [self]() { + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + ptr->discoveredRtt(); + } + }, + portal_->getThread().getIoService()); + state_->initParams(); rc_->setState(state_); - // TODO: for the moment we keep the congestion control disabled - // rc_->tunrOnRateControl(); - ldr_->setState(state_); + rc_->turnOnRateControl(); + ldr_->setState(state_.get()); + ldr_->setRateControl(rc_.get()); + verifier_->setState(state_); // protocol state start_send_interest_ = false; @@ -102,6 +131,10 @@ void RTCTransportProtocol::initParams() { } #else max_aggregated_interest_ = 1; + if (const char *max_aggr = std::getenv("MAX_AGGREGATED_INTERESTS")) { + LOG(INFO) << "Max Aggregated: " << max_aggr; + max_aggregated_interest_ = std::stoul(std::string(max_aggr)); + } #endif max_sent_int_ = @@ -131,6 +164,7 @@ void RTCTransportProtocol::initParams() { indexer_verifier_->setNFec(0); ldr_->setFecParams(fec::FECUtils::getBlockSymbols(fec_type_), fec::FECUtils::getSourceSymbols(fec_type_)); + fec_decoder_->setIOService(portal_->getThread().getIoService()); } else { indexer_verifier_->disableFec(); } @@ -162,61 +196,97 @@ void RTCTransportProtocol::inactiveProducer() { void RTCTransportProtocol::newRound() { round_timer_->expires_from_now(std::chrono::milliseconds(ROUND_LEN)); - // TODO pass weak_ptr here - round_timer_->async_wait([this, n{number_}](std::error_code ec) { - if (ec) return; - if (n != number_) { + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + round_timer_->async_wait([self](const std::error_code &ec) { + if (ec) { return; } + auto ptr = self.lock(); + + if (!ptr || !ptr->isRunning()) { + return; + } + + auto &state = ptr->state_; + // saving counters that will be reset on new round - uint32_t sent_retx = state_->getSentRtxInRound(); - uint32_t received_bytes = state_->getReceivedBytesInRound(); - uint32_t sent_interest = state_->getSentInterestInRound(); - uint32_t lost_data = state_->getLostData(); - uint32_t definitely_lost = state_->getDefinitelyLostPackets(); - uint32_t recovered_losses = state_->getRecoveredLosses(); - uint32_t received_nacks = state_->getReceivedNacksInRound(); - uint32_t received_fec = state_->getReceivedFecPackets(); - - bool in_sync = (current_state_ == SyncState::in_sync); - ldr_->onNewRound(in_sync); - state_->onNewRound((double)ROUND_LEN, in_sync); - rc_->onNewRound((double)ROUND_LEN); + uint32_t sent_retx = state->getSentRtxInRound(); + uint32_t received_bytes = + (state->getReceivedBytesInRound() + // data packets received + state->getReceivedFecBytesInRound()); // fec packets received + uint32_t sent_interest = state->getSentInterestInRound(); + uint32_t lost_data = state->getLostData(); + uint32_t definitely_lost = state->getDefinitelyLostPackets(); + uint32_t recovered_losses = state->getRecoveredLosses(); + uint32_t received_nacks = state->getReceivedNacksInRound(); + uint32_t received_fec = state->getReceivedFecPackets(); + + bool in_sync = (ptr->current_state_ == SyncState::in_sync); + ptr->ldr_->onNewRound(in_sync); + ptr->state_->onNewRound((double)ROUND_LEN, in_sync); + ptr->rc_->onNewRound((double)ROUND_LEN); // update sync state if needed - if (current_state_ == SyncState::in_sync) { - double cache_rate = state_->getPacketFromCacheRatio(); + if (ptr->current_state_ == SyncState::in_sync) { + double cache_rate = state->getPacketFromCacheRatio(); if (cache_rate > MAX_DATA_FROM_CACHE) { - current_state_ = SyncState::catch_up; + ptr->current_state_ = SyncState::catch_up; } } else { - double target_rate = state_->getProducerRate() * PRODUCTION_RATE_FRACTION; - double received_rate = state_->getReceivedRate(); - uint32_t round_without_nacks = state_->getRoundsWithoutNacks(); - double cache_ratio = state_->getPacketFromCacheRatio(); + double target_rate = state->getProducerRate() * PRODUCTION_RATE_FRACTION; + double received_rate = + state->getReceivedRate() + state->getRecoveredFecRate(); + uint32_t round_without_nacks = state->getRoundsWithoutNacks(); + double cache_ratio = state->getPacketFromCacheRatio(); if (round_without_nacks >= ROUNDS_IN_SYNC_BEFORE_SWITCH && received_rate >= target_rate && cache_ratio < MAX_DATA_FROM_CACHE) { - current_state_ = SyncState::in_sync; + ptr->current_state_ = SyncState::in_sync; } } DLOG_IF(INFO, VLOG_IS_ON(3)) << "Calling updateSyncWindow in newRound function"; - updateSyncWindow(); + ptr->updateSyncWindow(); - sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data, - definitely_lost, recovered_losses, received_nacks, - received_fec); - newRound(); + ptr->sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data, + definitely_lost, recovered_losses, received_nacks, + received_fec); + ptr->fwd_strategy_.checkStrategy(); + ptr->newRound(); }); } void RTCTransportProtocol::discoveredRtt() { start_send_interest_ = true; - ldr_->turnOnRTX(); + uint32_t strategy; + socket_->getSocketOption(RtcTransportOptions::RECOVERY_STRATEGY, strategy); + ldr_->changeRecoveryStrategy( + (interface::RtcTransportRecoveryStrategies)strategy); + ldr_->turnOnRecovery(); ldr_->onNewRound(false); + + // set forwarding strategy switch if selected + Name *name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + Prefix prefix(*name, 128); + if ((interface::RtcTransportRecoveryStrategies)strategy == + interface::RtcTransportRecoveryStrategies::LOW_RATE_AND_BESTPATH) { + fwd_strategy_.initFwdStrategy(portal_, prefix, state_.get(), + RTCForwardingStrategy::BEST_PATH); + } else if ((interface::RtcTransportRecoveryStrategies)strategy == + interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_REPLICATION) { + fwd_strategy_.initFwdStrategy(portal_, prefix, state_.get(), + RTCForwardingStrategy::REPLICATION); + } else if ((interface::RtcTransportRecoveryStrategies)strategy == + interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES) { + fwd_strategy_.initFwdStrategy(portal_, prefix, state_.get(), + RTCForwardingStrategy::BOTH); + } + updateSyncWindow(); } @@ -244,7 +314,7 @@ void RTCTransportProtocol::computeMaxSyncWindow() { (production_rate * lifetime_ms * INTEREST_LIFETIME_REDUCTION_FACTOR) / packet_size); - max_sync_win_ = std::min(max_sync_win_, rc_->getCongesionWindow()); + max_sync_win_ = std::min(max_sync_win_, rc_->getCongestionWindow()); } void RTCTransportProtocol::updateSyncWindow() { @@ -259,25 +329,14 @@ void RTCTransportProtocol::updateSyncWindow() { } double prod_rate = state_->getProducerRate(); - double rtt = (double)state_->getRTT() / MILLI_IN_A_SEC; + double rtt = (double)state_->getMinRTT() / MILLI_IN_A_SEC; double packet_size = state_->getAveragePacketSize(); // if some of the info are not available do not update the current win if (prod_rate != 0.0 && rtt != 0.0 && packet_size != 0.0) { - double fec_interest_overhead = (double)state_->getPendingFecPackets() / - (double)(state_->getPendingInterestNumber() - - state_->getPendingFecPackets()); - - double fec_overhead = - std::max(indexer_verifier_->getFecOverhead(), fec_interest_overhead); - - prod_rate += (prod_rate * fec_overhead); - current_sync_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size); uint32_t buffer = PRODUCER_BUFFER_MS; - if (rtt > 150) - buffer = buffer * 2; // if the RTT is too large we increase the - // the size of the buffer + current_sync_win_ += ceil(prod_rate * (buffer / MILLI_IN_A_SEC) / packet_size); @@ -285,8 +344,17 @@ void RTCTransportProtocol::updateSyncWindow() { current_sync_win_ = current_sync_win_ * CATCH_UP_WIN_INCREMENT; } + uint32_t min_win = WIN_MIN; + bool aggregated_data_on; + socket_->getSocketOption(RtcTransportOptions::AGGREGATED_DATA, + aggregated_data_on); + if (aggregated_data_on) { + min_win = WIN_MIN_WITH_AGGREGARED_DATA; + min_win += (min_win * (1 - (std::max(0.3, rtt) - rtt) / 0.3)); + } + current_sync_win_ = std::min(current_sync_win_, max_sync_win_); - current_sync_win_ = std::max(current_sync_win_, WIN_MIN); + current_sync_win_ = std::max(current_sync_win_, min_win); } scheduleNextInterests(); @@ -322,7 +390,7 @@ void RTCTransportProtocol::sendProbeInterest(uint32_t seq) { socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &interest_name); - DLOG_IF(INFO, VLOG_IS_ON(3)) << "send probe " << seq; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send probe " << seq; interest_name->setSuffix(seq); sendInterest(*interest_name); } @@ -330,13 +398,18 @@ void RTCTransportProtocol::sendProbeInterest(uint32_t seq) { void RTCTransportProtocol::scheduleNextInterests() { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Schedule next interests"; - if (!isRunning() && !is_first_) return; + if (!isRunning() && !is_first_) { + return; + } - if (pacing_timer_on_) return; // wait pacing timer for the next send + if (pacing_timer_on_) { + return; // wait pacing timer for the next send + } - if (!start_send_interest_) + if (!start_send_interest_) { return; // RTT discovering phase is not finished so // do not start to send interests + } if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Inactive producer."; @@ -352,7 +425,7 @@ void RTCTransportProtocol::scheduleNextInterests() { &interest_name); uint32_t next_seg = 0; - DLOG_IF(INFO, VLOG_IS_ON(3)) << "send interest " << next_seg; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send interest " << next_seg; interest_name->setSuffix(next_seg); if (portal_->interestIsPending(*interest_name)) { @@ -370,28 +443,37 @@ void RTCTransportProtocol::scheduleNextInterests() { << " -- current_sync_win_: " << current_sync_win_; uint32_t pending = state_->getPendingInterestNumber(); - if (pending >= current_sync_win_) return; // no space in the window + uint32_t pending_fec = state_->getPendingFecPackets(); + + if ((pending - pending_fec) >= current_sync_win_) + return; // no space in the window - if ((current_sync_win_ - pending) < max_aggregated_interest_) { + // XXX double check if aggregated interests are still working here + if ((current_sync_win_ - (pending - pending_fec)) < + max_aggregated_interest_) { if (scheduler_timer_on_) return; // timer already scheduled - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t time = now - last_interest_sent_time_; if (time < WAIT_FOR_INTEREST_BATCH) { uint64_t next = WAIT_FOR_INTEREST_BATCH - time; scheduler_timer_on_ = true; scheduler_timer_->expires_from_now(std::chrono::milliseconds(next)); - scheduler_timer_->async_wait([this](std::error_code ec) { + + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + scheduler_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - if (!scheduler_timer_on_) return; - scheduler_timer_on_ = false; - scheduleNextInterests(); + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->scheduler_timer_on_) return; + + ptr->scheduler_timer_on_ = false; + ptr->scheduleNextInterests(); + } }); - return; // whait for the timer + return; // wait for the timer } } @@ -403,7 +485,7 @@ void RTCTransportProtocol::scheduleNextInterests() { indexer_verifier_->jumpToIndex(state_->getLastSeqNacked() + 1); } - // skipe received packets + // skip received packets if (indexer_verifier_->checkNextSuffix() <= state_->getHighestSeqReceivedInOrder()) { indexer_verifier_->jumpToIndex(state_->getHighestSeqReceivedInOrder() + 1); @@ -417,7 +499,8 @@ void RTCTransportProtocol::scheduleNextInterests() { socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); std::array<uint32_t, MAX_AGGREGATED_INTEREST> additional_suffixes; - while ((state_->getPendingInterestNumber() < current_sync_win_) && + while (((state_->getPendingInterestNumber() - + state_->getPendingFecPackets()) < current_sync_win_) && (sent_interests < max_sent_int_)) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "In while loop. Window size: " << current_sync_win_; @@ -428,19 +511,20 @@ void RTCTransportProtocol::scheduleNextInterests() { // send the packet only if: // 1) it is not pending yet (not true for rtx) - // 2) the packet is not received or lost + // 2) the packet is not received or def lost // 3) is not in the rtx list // 4) is fec and is not in order (!= last sent + 1) + PacketState packet_state = state_->getPacketState(next_seg); if (portal_->interestIsPending(*name) || - state_->isReceivedOrLost(next_seg) != PacketState::UNKNOWN || - ldr_->isRtx(next_seg) || + packet_state == PacketState::RECEIVED || + packet_state == PacketState::DEFINITELY_LOST || ldr_->isRtx(next_seg) || (indexer_verifier_->isFec(next_seg) && next_seg != last_interest_sent_seq_ + 1)) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "skip interest " << next_seg << " because: pending " - << portal_->interestIsPending(*name) << ", recv " - << (state_->isReceivedOrLost(next_seg) != PacketState::UNKNOWN) - << ", rtx " << (ldr_->isRtx(next_seg)) << ", is old fec " + << portal_->interestIsPending(*name) << ", recv or lost" + << (int)packet_state << ", rtx " << (ldr_->isRtx(next_seg)) + << ", is old fec " << ((indexer_verifier_->isFec(next_seg) && next_seg != last_interest_sent_seq_ + 1)); continue; @@ -462,10 +546,7 @@ void RTCTransportProtocol::scheduleNextInterests() { sent_packets++; sent_interests++; sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1); - last_interest_sent_time_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + last_interest_sent_time_ = utils::SteadyTime::nowMs().count(); aggregated_counter = 0; } } @@ -473,25 +554,29 @@ void RTCTransportProtocol::scheduleNextInterests() { // exiting the while we may have some pending interest to send if (aggregated_counter != 0) { sent_packets++; - last_interest_sent_time_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + last_interest_sent_time_ = utils::SteadyTime::nowMs().count(); sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1); } - if (state_->getPendingInterestNumber() < current_sync_win_) { + if ((state_->getPendingInterestNumber() - state_->getPendingFecPackets()) < + current_sync_win_) { // we still have space in the window but we already sent too many packets // wait PACING_WAIT to avoid drops in the kernel pacing_timer_on_ = true; pacing_timer_->expires_from_now(std::chrono::microseconds(PACING_WAIT)); - scheduler_timer_->async_wait([this](std::error_code ec) { + + std::weak_ptr<RTCTransportProtocol> self = shared_from_this(); + scheduler_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - if (!pacing_timer_on_) return; - pacing_timer_on_ = false; - scheduleNextInterests(); + auto ptr = self.lock(); + if (ptr && ptr->isRunning()) { + if (!ptr->pacing_timer_on_) return; + + ptr->pacing_timer_on_ = false; + ptr->scheduleNextInterests(); + } }); } } @@ -500,13 +585,13 @@ void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, const Name &name) { uint32_t segment_number = name.getSuffix(); - if (segment_number >= MIN_PROBE_SEQ) { + if (ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE) { // this is a timeout on a probe, do nothing return; } - PacketState state = state_->isReceivedOrLost(segment_number); - if (state != PacketState::UNKNOWN) { + PacketState state = state_->getPacketState(segment_number); + if (state == PacketState::RECEIVED || state == PacketState::DEFINITELY_LOST) { // we may recover a packets using fec, ignore this timer return; } @@ -524,13 +609,18 @@ void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, DLOG_IF(INFO, VLOG_IS_ON(3)) << "handle timeout for packet " << segment_number << " using rtx"; if (ldr_->isRtxOn()) { - ldr_->onTimeout(segment_number); - if (indexer_verifier_->isFec(segment_number)) + if (indexer_verifier_->isFec(segment_number)) { + // if this is a fec packet we do not recover it with rtx so we consider + // the packet to be lost + ldr_->onTimeout(segment_number, true); state_->onTimeout(segment_number, true); - else + } else { + ldr_->onTimeout(segment_number, false); state_->onTimeout(segment_number, false); + } } else { // in this case we wil never recover the timeout + ldr_->onTimeout(segment_number, true); state_->onTimeout(segment_number, true); } scheduleNextInterests(); @@ -559,7 +649,7 @@ void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest, void RTCTransportProtocol::onNack(const ContentObject &content_object) { struct nack_packet_t *nack = (struct nack_packet_t *)content_object.getPayload()->data(); - uint32_t production_seg = nack->getProductionSegement(); + uint32_t production_seg = nack->getProductionSegment(); uint32_t nack_segment = content_object.getName().getSuffix(); bool is_rtx = ldr_->isRtx(nack_segment); @@ -592,6 +682,8 @@ void RTCTransportProtocol::onNack(const ContentObject &content_object) { // remove the nack is it exists if (tn_it != timeouts_or_nacks_.end()) timeouts_or_nacks_.erase(tn_it); + state_->onJumpForward(production_seg); + verifier_->onJumpForward(production_seg); // the client is asking for content in the past // switch to catch up state and increase the window // this is true only if the packet is not an RTX @@ -618,11 +710,9 @@ void RTCTransportProtocol::onProbe(const ContentObject &content_object) { bool valid = state_->onProbePacketReceived(content_object); if (!valid) return; - struct nack_packet_t *probe = - (struct nack_packet_t *)content_object.getPayload()->data(); - uint32_t production_seg = probe->getProductionSegement(); + uint32_t production_seg = RTCState::getProbeParams(content_object).prod_seg; - // as for the nacks set next_segment + // As for the nacks set next_segment DLOG_IF(INFO, VLOG_IS_ON(3)) << "on probe next seg = " << indexer_verifier_->checkNextSuffix() << ", jump to " << production_seg; @@ -636,12 +726,39 @@ void RTCTransportProtocol::onContentObjectReceived( Interest &interest, ContentObject &content_object, std::error_code &ec) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content object of size: " << content_object.payloadSize(); - uint32_t payload_size = content_object.payloadSize(); + uint32_t segment_number = content_object.getName().getSuffix(); + PayloadType payload_type = content_object.getPayloadType(); + PacketState state; + + ContentObject *content_ptr = &content_object; + ContentObject::Ptr manifest_ptr = nullptr; + + bool is_probe = + ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE; + bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE; + bool is_fec = indexer_verifier_->isFec(segment_number); + bool is_manifest = + !is_probe && !is_nack && !is_fec && payload_type == PayloadType::MANIFEST; + bool is_data = + !is_probe && !is_nack && !is_fec && payload_type == PayloadType::DATA; + bool compute_stats = is_data || is_manifest; ec = make_error_code(protocol_error::not_reassemblable); - if (segment_number >= MIN_PROBE_SEQ) { + // A helper function to process manifests or data packets received + auto onDataPacketReceived = [this](ContentObject &content_object, + bool compute_stats) { + ldr_->onDataPacketReceived(content_object); + rc_->onDataPacketReceived(content_object, compute_stats); + updateSyncWindow(); + }; + + // First verify the packet signature and apply the corresponding policy + auth::VerificationPolicy policy = verifier_->verify(content_object, is_fec); + indexer_verifier_->applyPolicy(interest, content_object, false, policy); + + if (is_probe) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << segment_number; if (*on_content_object_input_) { (*on_content_object_input_)(*socket_->getInterface(), content_object); @@ -650,7 +767,7 @@ void RTCTransportProtocol::onContentObjectReceived( return; } - if (payload_size == NACK_HEADER_SIZE) { + if (is_nack) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received nack " << segment_number; if (*on_content_object_input_) { (*on_content_object_input_)(*socket_->getInterface(), content_object); @@ -659,57 +776,122 @@ void RTCTransportProtocol::onContentObjectReceived( return; } + // content_ptr will point either to the input data packet or to a manifest + // whose FEC header has been removed + if (is_manifest) { + manifest_ptr = removeFecHeader(content_object); + if (manifest_ptr) { + content_ptr = manifest_ptr.get(); + } + } + + // From there, the packet is either a FEC, a manifest or a data packet. DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content " << segment_number; - bool compute_stats = true; + // Do not count timed out packets in stats auto tn_it = timeouts_or_nacks_.find(segment_number); if (tn_it != timeouts_or_nacks_.end()) { compute_stats = false; timeouts_or_nacks_.erase(tn_it); } - if (ldr_->isRtx(segment_number)) { + + // Do not count retransmissions or losses in stats + if (ldr_->isRtx(segment_number) || + ldr_->isPossibleLossWithNoRtx(segment_number)) { compute_stats = false; } - // check if the packet was already received - PacketState state = state_->isReceivedOrLost(segment_number); + // Fetch packet state + state = state_->getPacketState(segment_number); - if (state != PacketState::RECEIVED) { - // send packet to decoder - if (fec_decoder_) { - DLOG_IF(INFO, VLOG_IS_ON(4)) - << "send packet " << segment_number << " to FEC decoder"; - fec_decoder_->onDataPacket( - content_object, content_object.headerSize() + rtc::DATA_HEADER_SIZE); - } - if (!indexer_verifier_->isFec(segment_number)) { - // the packet may be alredy sent to the ap by the decoder, check again if - // it is already received - state = state_->isReceivedOrLost(segment_number); - if (state != PacketState::RECEIVED) { - DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received content " << segment_number; + // Check if the packet is a retransmission + if (ldr_->isRtx(segment_number) && state != PacketState::RECEIVED) { + if (is_data || is_manifest) { + state_->onPacketRecoveredRtx(segment_number); - state_->onDataPacketReceived(content_object, compute_stats); + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } - if (*on_content_object_input_) { - (*on_content_object_input_)(*socket_->getInterface(), content_object); - } - ec = make_error_code(protocol_error::success); + if (is_manifest) { + processManifest(interest, *content_ptr); } - } else { - DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received fec " << segment_number; - state_->onFecPacketReceived(content_object); + + ec = is_manifest ? make_error_code(protocol_error::not_reassemblable) + : make_error_code(protocol_error::success); + + // The packet is considered received, return early + onDataPacketReceived(*content_ptr, compute_stats); + return; } - } else { + + if (is_fec) { + state_->onFecPacketRecoveredRtx(segment_number); + } + } + + // Fetch packet state again; it may have changed + state = state_->getPacketState(segment_number); + + // Check if the packet was already received + if (state == PacketState::RECEIVED || state == PacketState::TO_BE_RECEIVED) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received duplicated content " << segment_number << ", drop it"; ec = make_error_code(protocol_error::duplicated_content); + onDataPacketReceived(*content_ptr, compute_stats); + return; } - ldr_->onDataPacketReceived(content_object); - rc_->onDataPacketReceived(content_object); + if (!is_fec) { + state_->dataToBeReceived(segment_number); + } - updateSyncWindow(); + // Send packet to FEC decoder + if (fec_decoder_) { + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Send packet " << segment_number << " to FEC decoder"; + + uint32_t offset = is_manifest + ? content_object.headerSize() + : content_object.headerSize() + rtc::DATA_HEADER_SIZE; + uint32_t metadata = static_cast<uint32_t>(content_object.getPayloadType()); + + fec_decoder_->onDataPacket(content_object, offset, metadata); + } + + // We can return early if FEC + if (is_fec) { + DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received FEC " << segment_number; + state_->onFecPacketReceived(content_object); + onDataPacketReceived(*content_ptr, compute_stats); + return; + } + + // The packet may have been already sent to the app by the decoder, check + // again if it is already received + state = state_->getPacketState( + segment_number); // state == RECEIVED or TO_BE_RECEIVED + + if (state != PacketState::RECEIVED) { + DLOG_IF(INFO, VLOG_IS_ON(4)) + << (is_manifest ? "Received manifest " : "Received data ") + << segment_number; + + if (is_manifest) { + processManifest(interest, *content_ptr); + } + + state_->onDataPacketReceived(*content_ptr, compute_stats); + + if (*on_content_object_input_) { + (*on_content_object_input_)(*socket_->getInterface(), content_object); + } + + ec = is_manifest ? make_error_code(protocol_error::not_reassemblable) + : make_error_code(protocol_error::success); + } + + onDataPacketReceived(*content_ptr, compute_stats); } void RTCTransportProtocol::sendStatsToApp( @@ -729,33 +911,149 @@ void RTCTransportProtocol::sendStatsToApp( stats_->updateReceivedNacks(received_nacks); stats_->updateReceivedFEC(received_fec); - stats_->updateAverageWindowSize(current_sync_win_); - stats_->updateLossRatio(state_->getLossRate()); - stats_->updateAverageRtt(state_->getRTT()); + stats_->updateAverageWindowSize(state_->getPendingInterestNumber()); + stats_->updateLossRatio(state_->getPerSecondLossRate()); + uint64_t rtt = state_->getAvgRTT(); + stats_->updateAverageRtt(utils::SteadyTime::Milliseconds(rtt)); + stats_->updateQueuingDelay(state_->getQueuing()); stats_->updateLostData(lost_data); stats_->updateDefinitelyLostData(definitely_lost); stats_->updateRecoveredData(recovered_losses); stats_->updateCCState((unsigned int)current_state_ ? 1 : 0); (*stats_summary_)(*socket_->getInterface(), *stats_); + bool in_congestion = rc_->inCongestionState(); + stats_->updateCongestionState(in_congestion); + double residual_losses = state_->getResidualLossRate(); + stats_->updateResidualLossRate(residual_losses); + stats_->updateQualityScore(state_->getQualityScore()); + + // set alerts + if (rtt > MAX_RTT) + stats_->setAlert(interface::TransportStatistics::statsAlerts::LATENCY); + else + stats_->clearAlert(interface::TransportStatistics::statsAlerts::LATENCY); + + if (in_congestion) + stats_->setAlert(interface::TransportStatistics::statsAlerts::CONGESTION); + else + stats_->clearAlert( + interface::TransportStatistics::statsAlerts::CONGESTION); + + if (residual_losses > MAX_RESIDUAL_LOSSES) + stats_->setAlert(interface::TransportStatistics::statsAlerts::LOSSES); + else + stats_->clearAlert(interface::TransportStatistics::statsAlerts::LOSSES); } } -void RTCTransportProtocol::onFecPackets( - std::vector<std::pair<uint32_t, fec::buffer>> &packets) { +void RTCTransportProtocol::onFecPackets(fec::BufferArray &packets) { + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + + Name *name = nullptr; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name); + for (auto &packet : packets) { - PacketState state = state_->isReceivedOrLost(packet.first); - if (state != PacketState::RECEIVED) { - state_->onPacketRecoveredFec(packet.first); - ldr_->onPacketRecoveredFec(packet.first); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "Recovered packet " << packet.first << " through FEC."; - reassembly_->reassemble(*packet.second, packet.first); - } else { - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "Packet" << packet.first << "already received."; + uint32_t seq_number = packet.getIndex(); + uint32_t metadata = packet.getMetadata(); + fec::buffer buffer = packet.getBuffer(); + + PayloadType payload_type = static_cast<PayloadType>(metadata); + switch (payload_type) { + case PayloadType::DATA: + case PayloadType::MANIFEST: + break; + case PayloadType::UNSPECIFIED: + default: + payload_type = PayloadType::DATA; + break; } + + switch (state_->getPacketState(seq_number)) { + case PacketState::RECEIVED: + case PacketState::TO_BE_RECEIVED: { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet " << seq_number << " already received"; + break; + } + default: { + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Recovered packet " << seq_number << " through FEC"; + + if (payload_type == PayloadType::MANIFEST) { + name->setSuffix(seq_number); + + auto interest = + core::PacketManager<>::getInstance().getPacket<Interest>(format); + interest->setName(*name); + + auto content_object = toContentObject( + *name, format, payload_type, buffer->data(), buffer->length()); + + processManifest(*interest, *content_object); + } + + state_->onPacketRecoveredFec(seq_number, buffer->length()); + ldr_->onPacketRecoveredFec(seq_number); + + if (payload_type == PayloadType::DATA) { + verifier_->onDataRecoveredFec(seq_number); + reassembly_->reassemble(*buffer, seq_number); + } + + break; + } + } + } +} + +void RTCTransportProtocol::processManifest(Interest &interest, + ContentObject &manifest) { + auth::VerificationPolicy policy = verifier_->processManifest(manifest); + indexer_verifier_->applyPolicy(interest, manifest, false, policy); +} + +ContentObject::Ptr RTCTransportProtocol::removeFecHeader( + const ContentObject &content_object) { + if (!fec_decoder_ || !fec_decoder_->getFecHeaderSize()) { + return nullptr; } + + size_t fec_header_size = fec_decoder_->getFecHeaderSize(); + const uint8_t *payload = + content_object.data() + content_object.headerSize() + fec_header_size; + size_t payload_size = content_object.payloadSize() - fec_header_size; + + ContentObject::Ptr co = + toContentObject(content_object.getName(), content_object.getFormat(), + content_object.getPayloadType(), payload, payload_size); + + return co; +} + +ContentObject::Ptr RTCTransportProtocol::toContentObject( + const Name &name, Packet::Format format, PayloadType payload_type, + const uint8_t *payload, std::size_t payload_size, + std::size_t additional_header_size) { + // Recreate ContentObject + ContentObject::Ptr co = + core::PacketManager<>::getInstance().getPacket<ContentObject>( + format, additional_header_size); + co->updateLength(payload_size); + co->append(payload_size); + co->trimStart(co->headerSize()); + + // Copy payload + std::memcpy(co->writableData(), payload, payload_size); + + // Restore network headers and some fields + co->prepend(co->headerSize()); + co->setName(name); + co->setPayloadType(payload_type); + + return co; } } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc.h b/libtransport/src/protocols/rtc/rtc.h index e6431264d..37706eb1c 100644 --- a/libtransport/src/protocols/rtc/rtc.h +++ b/libtransport/src/protocols/rtc/rtc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,10 +15,12 @@ #pragma once -#include <protocols/datagram_reassembly.h> +#include <protocols/rtc/rtc_forwarding_strategy.h> #include <protocols/rtc/rtc_ldr.h> #include <protocols/rtc/rtc_rc.h> +#include <protocols/rtc/rtc_reassembly.h> #include <protocols/rtc/rtc_state.h> +#include <protocols/rtc/rtc_verifier.h> #include <protocols/transport_protocol.h> #include <unordered_set> @@ -44,6 +46,8 @@ class RTCTransportProtocol : public TransportProtocol { std::size_t transportHeaderLength() override; + auto shared_from_this() { return utils::shared_from(this); } + private: enum class SyncState { catch_up = 0, in_sync = 1, last }; @@ -76,6 +80,7 @@ class RTCTransportProtocol : public TransportProtocol { void onPacketDropped(Interest &interest, ContentObject &content_object, const std::error_code &reason) override {} void onReassemblyFailed(std::uint32_t missing_segment) override {} + void processManifest(Interest &interest, ContentObject &manifest); // interaction with app functions void sendStatsToApp(uint32_t retx_count, uint32_t received_bytes, @@ -84,11 +89,20 @@ class RTCTransportProtocol : public TransportProtocol { uint32_t received_nacks, uint32_t received_fec); // FEC functions - void onFecPackets(std::vector<std::pair<uint32_t, fec::buffer>> &packets); + void onFecPackets(fec::BufferArray &packets); + + // Utils + ContentObject::Ptr removeFecHeader(const ContentObject &content_object); + ContentObject::Ptr toContentObject(const Name &name, Packet::Format format, + PayloadType payload_type, + const uint8_t *payload, + std::size_t payload_size, + std::size_t additional_header_size = 0); // protocol state bool start_send_interest_; SyncState current_state_; + // cwin vars uint32_t current_sync_win_; uint32_t max_sync_win_; @@ -120,6 +134,10 @@ class RTCTransportProtocol : public TransportProtocol { std::shared_ptr<RTCState> state_; std::shared_ptr<RTCRateControl> rc_; std::shared_ptr<RTCLossDetectionAndRecovery> ldr_; + std::shared_ptr<RTCVerifier> verifier_; + + // forwarding strategy selection + RTCForwardingStrategy fwd_strategy_; uint32_t number_; }; diff --git a/libtransport/src/protocols/rtc/rtc_consts.h b/libtransport/src/protocols/rtc/rtc_consts.h index d04bc1b1f..03efd8e84 100644 --- a/libtransport/src/protocols/rtc/rtc_consts.h +++ b/libtransport/src/protocols/rtc/rtc_consts.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -45,22 +45,22 @@ const uint32_t MAX_INTERESTS_IN_BATCH = 5; // number of seq numbers per const uint32_t WAIT_FOR_INTEREST_BATCH = 20; // msec. timer that we wait to try // to aggregate interest in the // same packet -const uint32_t MAX_PACING_BATCH = 5; -// number of interest that we can send inside -// the loop before they get dropped by the -// kernel. +const uint32_t MAX_PACING_BATCH = 5; // number of interest that we can send + // inside the loop before they get dropped + // by the kernel. const uint32_t PACING_WAIT = 1000; // usec to wait betwing two pacing batch. As // for MAX_PACING_BATCH this value was // computed during tests const uint32_t MAX_RTX_IN_BATCH = 10; // max rtx to send in loop // packet const -const uint32_t HICN_HEADER_SIZE = 40 + 20; // IPv6 + TCP bytes const uint32_t RTC_INTEREST_LIFETIME = 2000; // probes sequence range const uint32_t MIN_PROBE_SEQ = 0xefffffff; -const uint32_t MIN_RTT_PROBE_SEQ = MIN_PROBE_SEQ; +const uint32_t MIN_INIT_PROBE_SEQ = MIN_PROBE_SEQ; +const uint32_t MAX_INIT_PROBE_SEQ = 0xf7ffffff - 1; +const uint32_t MIN_RTT_PROBE_SEQ = MAX_INIT_PROBE_SEQ + 1; const uint32_t MAX_RTT_PROBE_SEQ = 0xffffffff - 1; // RTT_PROBE_INTERVAL will be used during the section while // INIT_RTT_PROBE_INTERVAL is used at the beginning to @@ -78,15 +78,16 @@ const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; // ms const uint32_t MAX_PENDING_PROBES = 10; // congestion -const double MAX_QUEUING_DELAY = 100.0; // ms +const double MAX_QUEUING_DELAY = 50.0; // ms // data from cache const double MAX_DATA_FROM_CACHE = 0.25; // 25% // window const -const uint32_t INITIAL_WIN = 5; // pkts -const uint32_t INITIAL_WIN_MAX = 1000000; // pkts -const uint32_t WIN_MIN = 5; // pkts +const uint32_t INITIAL_WIN = 5; // pkts +const uint32_t INITIAL_WIN_MAX = 1000000; // pkts +const uint32_t WIN_MIN = 5; // pkts +const uint32_t WIN_MIN_WITH_AGGREGARED_DATA = 10; // pkts const double CATCH_UP_WIN_INCREMENT = 1.2; // used in rate control const double WIN_DECREASE_FACTOR = 0.5; @@ -105,10 +106,8 @@ const double MOVING_AVG_ALPHA = 0.8; const double MILLI_IN_A_SEC = 1000.0; const double MICRO_IN_A_SEC = 1000000.0; - -const double MAX_CACHED_PACKETS = 262144; // 2^18 - // about 50 sec of traffic at 50Mbps - // with 1200 bytes packets +const uint32_t ROUNDS_PER_SEC = (uint32_t)(MILLI_IN_A_SEC / ROUND_LEN); +const uint32_t ROUNDS_PER_MIN = (uint32_t)ROUNDS_PER_SEC * 60; const uint32_t MAX_ROUND_WHIOUT_PACKETS = (20 * MILLI_IN_A_SEC) / ROUND_LEN; // 20 sec in rounds; @@ -120,12 +119,24 @@ const uint64_t MAX_TIMER_RTX = ~0; const uint32_t SENTINEL_TIMER_INTERVAL = 100; // ms const uint32_t MAX_RTX_WITH_SENTINEL = 10; // packets const double CATCH_UP_RTT_INCREMENT = 1.2; +const double MAX_RESIDUAL_LOSS_RATE = 2.0; // % +const uint32_t WAIT_BEFORE_FEC_UPDATE = ROUNDS_PER_SEC * 5; // used by producer const uint32_t PRODUCER_STATS_INTERVAL = 200; // ms -const uint32_t MIN_PRODUCTION_RATE = 10; // pps - // min prod rate - // set running several test +const uint32_t MIN_PRODUCTION_RATE = 25; // pps, equal to min window * + // rounds in a second +const uint32_t NACK_DELAY = 1500; // ms +const uint32_t FEC_PACING_TIME = 5; // ms + +// aggregated data consts +const uint16_t MAX_RTC_PAYLOAD_SIZE = 1200; // bytes +const uint16_t MAX_AGGREGATED_PACKETS = 5; // pkt +const uint32_t AGGREGATED_PACKETS_TIMER = 2; // ms + +// alert thresholds +const uint32_t MAX_RTT = 200; // ms +const double MAX_RESIDUAL_LOSSES = 0.05; // % } // namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_data_path.cc b/libtransport/src/protocols/rtc/rtc_data_path.cc index c098088a3..b3abf5ea8 100644 --- a/libtransport/src/protocols/rtc/rtc_data_path.cc +++ b/libtransport/src/protocols/rtc/rtc_data_path.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,14 +13,17 @@ * limitations under the License. */ +#include <hicn/transport/utils/chrono_typedefs.h> #include <protocols/rtc/rtc_data_path.h> #include <stdlib.h> #include <algorithm> #include <cfloat> #include <chrono> +#include <cmath> #define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec +#define AVG_RTT_TIME 1000 // (ms) 1sec namespace transport { @@ -32,6 +35,8 @@ RTCDataPath::RTCDataPath(uint32_t path_id) : path_id_(path_id), min_rtt(UINT_MAX), prev_min_rtt(UINT_MAX), + max_rtt(0), + prev_max_rtt(0), min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the // real OWD, but the measured one, that depends on the // clock of sender and receiver. the only meaningful @@ -46,20 +51,46 @@ RTCDataPath::RTCDataPath(uint32_t path_id) largest_recv_seq_(0), largest_recv_seq_time_(0), avg_inter_arrival_(DBL_MAX), + rtt_sum_(0), + last_avg_rtt_compute_(0), + rtt_samples_(0), + avg_rtt_(0.0), received_nacks_(false), - received_packets_(false), + received_packets_(0), rounds_without_packets_(0), last_received_data_packet_(0), - RTT_history_(HISTORY_LEN), + min_RTT_history_(HISTORY_LEN), + max_RTT_history_(HISTORY_LEN), OWD_history_(HISTORY_LEN){}; -void RTCDataPath::insertRttSample(uint64_t rtt) { - // for the rtt we only keep track of the min one +void RTCDataPath::insertRttSample( + const utils::SteadyTime::Milliseconds& rtt_milliseconds, bool is_probe) { + // compute min rtt + uint64_t rtt = rtt_milliseconds.count(); if (rtt < min_rtt) min_rtt = rtt; - last_received_data_packet_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + + uint64_t now = utils::SteadyTime::nowMs().count(); + last_received_data_packet_ = now; + + // compute avg rtt + if (is_probe) { + // max rtt is computed only on probes to avoid to take into account the + // production time at the server + if (rtt > max_rtt) max_rtt = rtt; + + rtt_sum_ += rtt; + rtt_samples_++; + } + + if ((now - last_avg_rtt_compute_) >= AVG_RTT_TIME) { + // compute a new avg rtt + // if rtt_samples_ = 0 keep the same rtt + if (rtt_samples_ != 0) avg_rtt_ = (double)rtt_sum_ / (double)rtt_samples_; + + rtt_sum_ = 0; + rtt_samples_ = 0; + last_avg_rtt_compute_ = now; + } } void RTCDataPath::insertOwdSample(int64_t owd) { @@ -87,15 +118,13 @@ void RTCDataPath::insertOwdSample(int64_t owd) { // owd is computed only for valid data packets so we count only // this for decide if we recevie traffic or not - received_packets_ = true; + received_packets_++; } void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) { // got packet in sequence, compute gap if (largest_recv_seq_ == (segment_number - 1)) { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t delta = now - largest_recv_seq_time_; largest_recv_seq_ = segment_number; largest_recv_seq_time_ = now; @@ -110,10 +139,7 @@ void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) { // ooo packet, update the stasts if needed if (largest_recv_seq_ <= segment_number) { largest_recv_seq_ = segment_number; - largest_recv_seq_time_ = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + largest_recv_seq_time_ = utils::SteadyTime::nowMs().count(); } } @@ -146,10 +172,20 @@ void RTCDataPath::roundEnd() { min_rtt = prev_min_rtt; } + // same for max_rtt + if (max_rtt != 0) { + prev_max_rtt = max_rtt; + } else { + max_rtt = prev_max_rtt; + } + if (min_rtt == 0) min_rtt = 1; + if (max_rtt == 0) max_rtt = 1; - RTT_history_.pushBack(min_rtt); + min_RTT_history_.pushBack(min_rtt); + max_RTT_history_.pushBack(max_rtt); min_rtt = UINT_MAX; + max_rtt = 0; // do the same for min owd if (min_owd != INT_MAX) { @@ -163,32 +199,47 @@ void RTCDataPath::roundEnd() { min_owd = INT_MAX; } - if (!received_packets_) + if (received_packets_ == 0) rounds_without_packets_++; else rounds_without_packets_ = 0; - received_packets_ = false; + received_packets_ = 0; } uint32_t RTCDataPath::getPathId() { return path_id_; } -double RTCDataPath::getQueuingDealy() { return queuing_delay; } +double RTCDataPath::getQueuingDealy() { + if (queuing_delay == DBL_MAX) return 0; + return queuing_delay; +} uint64_t RTCDataPath::getMinRtt() { - if (RTT_history_.size() != 0) return RTT_history_.begin(); + if (min_RTT_history_.size() != 0) return min_RTT_history_.begin(); + return 0; +} + +uint64_t RTCDataPath::getAvgRtt() { return std::round(avg_rtt_); } + +uint64_t RTCDataPath::getMaxRtt() { + if (max_RTT_history_.size() != 0) return max_RTT_history_.begin(); return 0; } int64_t RTCDataPath::getMinOwd() { if (OWD_history_.size() != 0) return OWD_history_.begin(); - return 0; + return INT_MAX; } double RTCDataPath::getJitter() { return jitter_; } uint64_t RTCDataPath::getLastPacketTS() { return last_received_data_packet_; } -void RTCDataPath::clearRtt() { RTT_history_.clear(); } +uint32_t RTCDataPath::getPacketsLastRound() { return received_packets_; } + +void RTCDataPath::clearRtt() { + min_RTT_history_.clear(); + max_RTT_history_.clear(); +} } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_data_path.h b/libtransport/src/protocols/rtc/rtc_data_path.h index c5c37fc0d..5afbbb87f 100644 --- a/libtransport/src/protocols/rtc/rtc_data_path.h +++ b/libtransport/src/protocols/rtc/rtc_data_path.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,7 +15,9 @@ #pragma once +#include <hicn/transport/utils/chrono_typedefs.h> #include <stdint.h> +#include <utils/max_filter.h> #include <utils/min_filter.h> #include <climits> @@ -34,19 +36,23 @@ class RTCDataPath { RTCDataPath(uint32_t path_id); public: - void insertRttSample(uint64_t rtt); + void insertRttSample(const utils::SteadyTime::Milliseconds &rtt, + bool is_probe); void insertOwdSample(int64_t owd); void computeInterArrivalGap(uint32_t segment_number); void receivedNack(); uint32_t getPathId(); uint64_t getMinRtt(); + uint64_t getAvgRtt(); + uint64_t getMaxRtt(); double getQueuingDealy(); double getInterArrivalGap(); double getJitter(); bool isActive(); bool pathToProducer(); uint64_t getLastPacketTS(); + uint32_t getPacketsLastRound(); void clearRtt(); @@ -60,6 +66,9 @@ class RTCDataPath { uint64_t min_rtt; uint64_t prev_min_rtt; + uint64_t max_rtt; + uint64_t prev_max_rtt; + int64_t min_owd; int64_t prev_min_owd; @@ -74,19 +83,26 @@ class RTCDataPath { uint64_t largest_recv_seq_time_; double avg_inter_arrival_; + // compute the avg rtt over one sec + uint64_t rtt_sum_; + uint64_t last_avg_rtt_compute_; + uint32_t rtt_samples_; + double avg_rtt_; + // flags to check if a path is active // we considere a path active if it reaches a producer //(not a cache) --aka we got at least one nack on this path-- // and if we receives packets bool received_nacks_; - bool received_packets_; - uint8_t rounds_without_packets_; // if we don't get any packet + uint32_t received_packets_; + uint32_t rounds_without_packets_; // if we don't get any packet // for MAX_ROUNDS_WITHOUT_PKTS // we consider the path inactive uint64_t last_received_data_packet_; // timestamp for the last data received // on this path - utils::MinFilter<uint64_t> RTT_history_; + utils::MinFilter<uint64_t> min_RTT_history_; + utils::MaxFilter<uint64_t> max_RTT_history_; utils::MinFilter<int64_t> OWD_history_; }; diff --git a/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc new file mode 100644 index 000000000..9503eed3e --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/interfaces/notification.h> +#include <protocols/rtc/rtc_forwarding_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace transport::interface; + +RTCForwardingStrategy::RTCForwardingStrategy() + : init_(false), + forwarder_set_(false), + selected_strategy_(NONE), + current_strategy_(NONE), + rounds_since_last_set_(0), + portal_(nullptr), + state_(nullptr) {} + +RTCForwardingStrategy::~RTCForwardingStrategy() {} + +void RTCForwardingStrategy::setCallback(interface::StrategyCallback* callback) { + callback_ = callback; +} + +void RTCForwardingStrategy::initFwdStrategy( + std::shared_ptr<core::Portal> portal, core::Prefix& prefix, RTCState* state, + strategy_t strategy) { + init_ = true; + selected_strategy_ = strategy; + if (strategy == BOTH) + current_strategy_ = BEST_PATH; + else + current_strategy_ = strategy; + rounds_since_last_set_ = 0; + prefix_ = prefix; + portal_ = portal; + state_ = state; +} + +void RTCForwardingStrategy::checkStrategy() { + if (*callback_) { + strategy_t used_strategy = selected_strategy_; + if (used_strategy == BOTH) used_strategy = current_strategy_; + assert(used_strategy == BEST_PATH || used_strategy == REPLICATION || + used_strategy == NONE); + + notification::ForwardingStrategy strategy = + notification::ForwardingStrategy::NONE; + switch (used_strategy) { + case BEST_PATH: + strategy = notification::ForwardingStrategy::BEST_PATH; + break; + case REPLICATION: + strategy = notification::ForwardingStrategy::REPLICATION; + break; + default: + break; + } + + (*callback_)(strategy); + } + + if (!init_) return; + + if (selected_strategy_ == NONE) return; + + if (selected_strategy_ == BEST_PATH) { + checkStrategyBestPath(); + return; + } + + if (selected_strategy_ == REPLICATION) { + checkStrategyReplication(); + return; + } + + checkStrategyBoth(); +} + +void RTCForwardingStrategy::checkStrategyBestPath() { + if (!forwarder_set_) { + setStrategy(BEST_PATH); + forwarder_set_ = true; + return; + } + + uint8_t qs = state_->getQualityScore(); + + if (qs >= 4 || rounds_since_last_set_ < 25) { // wait a least 5 sec + // between each switch + rounds_since_last_set_++; + return; + } + + // try to switch path + setStrategy(BEST_PATH); +} + +void RTCForwardingStrategy::checkStrategyReplication() { + if (!forwarder_set_) { + setStrategy(REPLICATION); + forwarder_set_ = true; + return; + } + + // here we have nothing to do for the moment + return; +} + +void RTCForwardingStrategy::checkStrategyBoth() { + if (!forwarder_set_) { + setStrategy(current_strategy_); + forwarder_set_ = true; + return; + } + + checkStrategyBestPath(); + + // TODO + // for the moment we use only best path. + // but later: + // 1. if both paths are bad use replication + // 2. while using replication compute the effectiveness. if the majority of + // the packets are coming from a single path, try to use bestpath +} + +void RTCForwardingStrategy::setStrategy(strategy_t strategy) { + rounds_since_last_set_ = 0; + current_strategy_ = strategy; + portal_->setForwardingStrategy(prefix_, + string_strategies_[current_strategy_]); +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h new file mode 100644 index 000000000..821b28051 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <core/portal.h> +#include <hicn/transport/interfaces/callbacks.h> +#include <protocols/rtc/rtc_state.h> + +#include <array> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCForwardingStrategy { + public: + enum strategy_t { + BEST_PATH, + REPLICATION, + BOTH, + NONE, + }; + + RTCForwardingStrategy(); + ~RTCForwardingStrategy(); + + void initFwdStrategy(std::shared_ptr<core::Portal> portal, + core::Prefix& prefix, RTCState* state, + strategy_t strategy); + + void checkStrategy(); + void setCallback(interface::StrategyCallback* callback); + + private: + void checkStrategyBestPath(); + void checkStrategyReplication(); + void checkStrategyBoth(); + + void setStrategy(strategy_t strategy); + + std::array<std::string, 4> string_strategies_ = {"bestpath", "replication", + "both", "none"}; + + bool init_; // true if all val are initializes + bool forwarder_set_; // true if the strategy is been set at least + // once + strategy_t selected_strategy_; // this is the strategy selected using socket + // options. this can also be equal to BOTH + strategy_t current_strategy_; // if both strategies can be used this + // indicates the one that is currently in use + // that can be only replication or best path + uint32_t rounds_since_last_set_; + core::Prefix prefix_; + std::shared_ptr<core::Portal> portal_; + RTCState* state_; + interface::StrategyCallback* callback_; +}; + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_indexer.h b/libtransport/src/protocols/rtc/rtc_indexer.h index 4aee242bb..cda156b22 100644 --- a/libtransport/src/protocols/rtc/rtc_indexer.h +++ b/libtransport/src/protocols/rtc/rtc_indexer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -18,8 +18,10 @@ #include <protocols/errors.h> #include <protocols/fec_utils.h> #include <protocols/indexer.h> +#include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_consts.h> #include <protocols/transport_protocol.h> +#include <utils/suffix_strategy.h> #include <deque> @@ -54,7 +56,7 @@ class RtcIndexer : public Indexer { n_fec_ = 0; } - uint32_t checkNextSuffix() override { return next_suffix_; } + uint32_t checkNextSuffix() const override { return next_suffix_; } uint32_t getNextSuffix() override { if (isFec(next_suffix_)) { @@ -77,7 +79,7 @@ class RtcIndexer : public Indexer { first_suffix_ = suffix % LIMIT; } - uint32_t getFirstSuffix() override { return first_suffix_; } + uint32_t getFirstSuffix() const override { return first_suffix_; } uint32_t jumpToIndex(uint32_t index) override { next_suffix_ = index % LIMIT; @@ -87,30 +89,8 @@ class RtcIndexer : public Indexer { void onContentObject(core::Interest &interest, core::ContentObject &content_object, bool reassembly) override { - setVerifier(); - auto ret = verifier_->verifyPackets(&content_object); - - switch (ret) { - case auth::VerificationPolicy::ACCEPT: { - if (reassembly) { - reassembly_->reassemble(content_object); - } - break; - } - - case auth::VerificationPolicy::UNKNOWN: - case auth::VerificationPolicy::DROP: { - transport_->onPacketDropped( - interest, content_object, - make_error_code(protocol_error::verification_failed)); - break; - } - - case auth::VerificationPolicy::ABORT: { - transport_->onContentReassembled( - make_error_code(protocol_error::session_aborted)); - break; - } + if (reassembly) { + reassembly_->reassemble(content_object); } } @@ -120,13 +100,12 @@ class RtcIndexer : public Indexer { uint32_t getNextReassemblySegment() override { throw errors::RuntimeException( "Get reassembly segment called on rtc indexer. RTC indexer does not " - "provide " - "reassembly."); + "provide reassembly."); } bool isFinalSuffixDiscovered() override { return true; } - uint32_t getFinalSuffix() override { return LIMIT; } + uint32_t getFinalSuffix() const override { return LIMIT; } void enableFec(fec::FECType fec_type) override { fec_type_ = fec_type; } @@ -137,13 +116,13 @@ class RtcIndexer : public Indexer { n_current_fec_ = n_fec_; } - uint32_t getNFec() override { return n_fec_; } + uint32_t getNFec() const override { return n_fec_; } bool isFec(uint32_t index) override { return isFec(fec_type_, index, first_suffix_); } - double getFecOverhead() override { + double getFecOverhead() const override { if (fec_type_ == fec::FECType::UNKNOWN) { return 0; } @@ -152,7 +131,7 @@ class RtcIndexer : public Indexer { return (double)n_fec_ / k; } - double getMaxFecOverhead() override { + double getMaxFecOverhead() const override { if (fec_type_ == fec::FECType::UNKNOWN) { return 0; } diff --git a/libtransport/src/protocols/rtc/rtc_ldr.cc b/libtransport/src/protocols/rtc/rtc_ldr.cc index f0de48871..1ca1cf48d 100644 --- a/libtransport/src/protocols/rtc/rtc_ldr.cc +++ b/libtransport/src/protocols/rtc/rtc_ldr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,6 +16,12 @@ #include <glog/logging.h> #include <protocols/rtc/rtc_consts.h> #include <protocols/rtc/rtc_ldr.h> +#include <protocols/rtc/rtc_rs_delay.h> +#include <protocols/rtc/rtc_rs_fec_only.h> +#include <protocols/rtc/rtc_rs_low_rate.h> +#include <protocols/rtc/rtc_rs_recovery_off.h> +#include <protocols/rtc/rtc_rs_rtx_only.h> +#include <protocols/rtc/rtc_state.h> #include <algorithm> #include <unordered_set> @@ -27,146 +33,115 @@ namespace protocol { namespace rtc { RTCLossDetectionAndRecovery::RTCLossDetectionAndRecovery( - Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service) - : rtx_on_(false), - fec_on_(false), - next_rtx_timer_(MAX_TIMER_RTX), - last_event_(0), - sentinel_timer_interval_(MAX_TIMER_RTX), - indexer_(indexer), - send_rtx_callback_(std::move(callback)) { - timer_ = std::make_unique<asio::steady_timer>(io_service); - sentinel_timer_ = std::make_unique<asio::steady_timer>(io_service); + Indexer *indexer, asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies type, + RecoveryStrategy::SendRtxCallback &&callback, + interface::StrategyCallback *external_callback) { + rs_type_ = type; + if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) { + rs_ = std::make_shared<RecoveryStrategyRecoveryOff>( + indexer, std::move(callback), io_service, external_callback); + } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED) { + rs_ = std::make_shared<RecoveryStrategyDelayBased>( + indexer, std::move(callback), io_service, external_callback); + } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY) { + rs_ = std::make_shared<RecoveryStrategyFecOnly>( + indexer, std::move(callback), io_service, external_callback); + } else if (type == interface::RtcTransportRecoveryStrategies::LOW_RATE || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_BESTPATH || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_REPLICATION || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES) { + rs_ = std::make_shared<RecoveryStrategyLowRate>( + indexer, std::move(callback), io_service, external_callback); + } else { + // default + rs_type_ = interface::RtcTransportRecoveryStrategies::RTX_ONLY; + rs_ = std::make_shared<RecoveryStrategyRtxOnly>( + indexer, std::move(callback), io_service, external_callback); + } } RTCLossDetectionAndRecovery::~RTCLossDetectionAndRecovery() {} -void RTCLossDetectionAndRecovery::turnOnRTX() { - rtx_on_ = true; - scheduleSentinelTimer(state_->getRTT() * CATCH_UP_RTT_INCREMENT); -} - -void RTCLossDetectionAndRecovery::turnOffRTX() { - rtx_on_ = false; - clear(); -} - -uint32_t RTCLossDetectionAndRecovery::computeFecPacketsToAsk(bool in_sync) { - uint32_t current_fec = indexer_->getNFec(); - double current_loss_rate = state_->getLossRate(); - double last_loss_rate = state_->getLastRoundLossRate(); - - // when in sync ask for fec only if there are losses for 2 rounds - if (in_sync && current_fec == 0 && - (current_loss_rate == 0 || last_loss_rate == 0)) - return 0; - - double loss_rate = state_->getMaxLossRate() * 1.5; - - if (!in_sync && loss_rate == 0) loss_rate = 0.05; - if (loss_rate > 0.5) loss_rate = 0.5; - - double exp_losses = (double)k_ * loss_rate; - uint32_t fec_to_ask = ceil(exp_losses / (1 - loss_rate)); - - if (fec_to_ask > (n_ - k_)) fec_to_ask = n_ - k_; - - return fec_to_ask; +void RTCLossDetectionAndRecovery::changeRecoveryStrategy( + interface::RtcTransportRecoveryStrategies type) { + if (type == rs_type_) return; + + rs_type_ = type; + if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) { + rs_ = + std::make_shared<RecoveryStrategyRecoveryOff>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED) { + rs_ = std::make_shared<RecoveryStrategyDelayBased>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY) { + rs_ = std::make_shared<RecoveryStrategyFecOnly>(std::move(*(rs_.get()))); + } else if (type == interface::RtcTransportRecoveryStrategies::LOW_RATE || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_BESTPATH || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_REPLICATION || + type == interface::RtcTransportRecoveryStrategies:: + LOW_RATE_AND_ALL_FWD_STRATEGIES) { + rs_ = std::make_shared<RecoveryStrategyLowRate>(std::move(*(rs_.get()))); + } else { + // default + rs_ = std::make_shared<RecoveryStrategyRtxOnly>(std::move(*(rs_.get()))); + } } void RTCLossDetectionAndRecovery::onNewRound(bool in_sync) { - uint64_t rtt = state_->getRTT(); - if (!fec_on_ && rtt >= 100) { - // turn on fec, here we may have no info so ask for all packets - fec_on_ = true; - turnOffRTX(); - indexer_->setNFec(computeFecPacketsToAsk(in_sync)); - return; - } - - if (fec_on_ && rtt > 80) { - // keep using fec, maybe update it - indexer_->setNFec(computeFecPacketsToAsk(in_sync)); - return; - } - - if ((fec_on_ && rtt <= 80) || (!rtx_on_ && rtt <= 100)) { - // turn on rtx - fec_on_ = false; - indexer_->setNFec(0); - turnOnRTX(); - return; - } + rs_->incRoundId(); + rs_->onNewRound(in_sync); } -void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq) { - // always add timeouts to the RTX list to avoid to send the same packet as if - // it was not a rtx - addToRetransmissions(seq, seq + 1); - last_event_ = getNow(); +void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq, bool lost) { + if (!lost) { + detectLoss(seq, seq + 1); + } else { + rs_->onLostTimeout(seq); + } } void RTCLossDetectionAndRecovery::onPacketRecoveredFec(uint32_t seq) { - // if an RTX is scheduled for a packet recovered using FEC delete it - deleteRtx(seq); - recover_with_fec_.erase(seq); + rs_->receivedPacket(seq); } void RTCLossDetectionAndRecovery::onDataPacketReceived( const core::ContentObject &content_object) { - last_event_ = getNow(); - uint32_t seq = content_object.getName().getSuffix(); - if (deleteRtx(seq)) { - state_->onPacketRecoveredRtx(seq); - } else { - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "received data. add from " - << state_->getHighestSeqReceivedInOrder() + 1 << " to " << seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, seq); - } + bool is_rtx = rs_->isRtx(seq); + rs_->receivedPacket(seq); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "received data. add from " + << rs_->getState()->getHighestSeqReceivedInOrder() + 1 << " to " << seq; + if (!is_rtx) + detectLoss(rs_->getState()->getHighestSeqReceivedInOrder() + 1, seq); } void RTCLossDetectionAndRecovery::onNackPacketReceived( const core::ContentObject &nack) { - last_event_ = getNow(); - - uint32_t seq = nack.getName().getSuffix(); - struct nack_packet_t *nack_pkt = (struct nack_packet_t *)nack.getPayload()->data(); - uint32_t production_seq = nack_pkt->getProductionSegement(); + uint32_t production_seq = nack_pkt->getProductionSegment(); + uint32_t seq = nack.getName().getSuffix(); - if (production_seq > seq) { - // this is a past nack, all data before productionSeq are lost. if - // productionSeq > state_->getHighestSeqReceivedInOrder() is impossible to - // recover any packet. If this is not the case we can try to recover the - // packets between state_->getHighestSeqReceivedInOrder() and productionSeq. - // e.g.: the client receives packets 8 10 11 9 where 9 is a nack with - // productionSeq = 14. 9 is lost but we can try to recover packets 12 13 and - // 14 that are not arrived yet - deleteRtx(seq); - DLOG_IF(INFO, VLOG_IS_ON(3)) << "received past nack. add from " - << state_->getHighestSeqReceivedInOrder() + 1 - << " to " << production_seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, - production_seq); - } else { - // future nack. here there should be a gap between the last data received - // and this packet and is it possible to recover the packets between the - // last received data and the production seq. we should not use the seq - // number of the nack since we know that is too early to ask for this seq - // number - // e.g.: // e.g.: the client receives packets 10 11 12 20 where 20 is a nack - // with productionSeq = 18. this says that all the packets between 12 and 18 - // may got lost and we should ask them - deleteRtx(seq); - DLOG_IF(INFO, VLOG_IS_ON(3)) << "received futrue nack. add from " - << state_->getHighestSeqReceivedInOrder() + 1 - << " to " << production_seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, - production_seq); - } + // received a nack. we can try to recover all data packets between the last + // received data and the production seq in the nack. this is similar to the + // recption of a probe + // e.g.: the client receives packets 10 11 12 20 where 20 is a nack + // with productionSeq = 18. this says that all the packets between 12 and 18 + // may got lost and we should ask them + + rs_->receivedPacket(seq); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "received nack. add from " + << rs_->getState()->getHighestSeqReceivedInOrder() + 1 << " to " + << production_seq; + detectLoss(rs_->getState()->getHighestSeqReceivedInOrder() + 1, + production_seq); } void RTCLossDetectionAndRecovery::onProbePacketReceived( @@ -174,336 +149,38 @@ void RTCLossDetectionAndRecovery::onProbePacketReceived( // we don't log the reception of a probe packet for the sentinel timer because // probes are not taken into account into the sync window. we use them as // future nacks to detect possible packets lost - struct nack_packet_t *probe_pkt = - (struct nack_packet_t *)probe.getPayload()->data(); - uint32_t production_seq = probe_pkt->getProductionSegement(); + + uint32_t production_seq = RTCState::getProbeParams(probe).prod_seg; + DLOG_IF(INFO, VLOG_IS_ON(3)) << "received probe. add from " - << state_->getHighestSeqReceivedInOrder() + 1 << " to " << production_seq; + << rs_->getState()->getHighestSeqReceivedInOrder() + 1 << " to " + << production_seq; - addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, - production_seq); + detectLoss(rs_->getState()->getHighestSeqReceivedInOrder() + 1, + production_seq); } -void RTCLossDetectionAndRecovery::clear() { - rtx_state_.clear(); - rtx_timers_.clear(); - sentinel_timer_->cancel(); - if (next_rtx_timer_ != MAX_TIMER_RTX) { - next_rtx_timer_ = MAX_TIMER_RTX; - timer_->cancel(); - } -} +void RTCLossDetectionAndRecovery::detectLoss(uint32_t start, uint32_t stop) { + if (start >= stop) return; -void RTCLossDetectionAndRecovery::addToRetransmissions(uint32_t start, - uint32_t stop) { // skip nacked packets - if (start <= state_->getLastSeqNacked()) { - start = state_->getLastSeqNacked() + 1; + if (start <= rs_->getState()->getLastSeqNacked()) { + start = rs_->getState()->getLastSeqNacked() + 1; } // skip received or lost packets - if (start <= state_->getHighestSeqReceivedInOrder()) { - start = state_->getHighestSeqReceivedInOrder() + 1; + if (start <= rs_->getState()->getHighestSeqReceivedInOrder()) { + start = rs_->getState()->getHighestSeqReceivedInOrder() + 1; } for (uint32_t seq = start; seq < stop; seq++) { - if (state_->isReceivedOrLost(seq) == PacketState::UNKNOWN) { - if (rtx_on_) { - if (!indexer_->isFec(seq)) { - // handle it with rtx - if (!isRtx(seq)) { - state_->onLossDetected(seq); - rtxState state; - state.first_send_ = state_->getInterestSentTime(seq); - if (state.first_send_ == 0) // this interest was never sent before - state.first_send_ = getNow(); - state.next_send_ = computeNextSend(seq, true); - state.rtx_count_ = 0; - DLOG_IF(INFO, VLOG_IS_ON(4)) - << "Add " << seq << " to retransmissions. next rtx is %lu " - << state.next_send_ - getNow(); - rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state)); - rtx_timers_.insert( - std::pair<uint64_t, uint32_t>(state.next_send_, seq)); - } - } else { - // is fec, do not send it - auto it = recover_with_fec_.find(seq); - if (it == recover_with_fec_.end()) { - state_->onLossDetected(seq); - recover_with_fec_.insert(seq); - } - } - } else { - // keep track of losses but recover with FEC - auto it = recover_with_fec_.find(seq); - if (it == recover_with_fec_.end()) { - state_->onLossDetected(seq); - recover_with_fec_.insert(seq); - } - } - } - } - scheduleNextRtx(); -} - -uint64_t RTCLossDetectionAndRecovery::computeNextSend(uint32_t seq, - bool new_rtx) { - uint64_t now = getNow(); - if (new_rtx) { - // for the new rtx we wait one estimated IAT after the loss detection. this - // is bacause, assuming that packets arrive with a constant IAT, we should - // get a new packet every IAT - double prod_rate = state_->getProducerRate(); - uint32_t estimated_iat = SENTINEL_TIMER_INTERVAL; - uint32_t jitter = 0; - - if (prod_rate != 0) { - double packet_size = state_->getAveragePacketSize(); - estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); - jitter = ceil(state_->getJitter()); - } - - uint32_t wait = estimated_iat + jitter; - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "first rtx for " << seq << " in " << wait - << " ms, rtt = " << state_->getRTT() << " ait = " << estimated_iat - << " jttr = " << jitter; - - return now + wait; - } else { - // wait one RTT - // however if the IAT is larger than the RTT, wait one IAT - uint32_t wait = SENTINEL_TIMER_INTERVAL; - - double prod_rate = state_->getProducerRate(); - if (prod_rate == 0) { - return now + SENTINEL_TIMER_INTERVAL; - } - - double packet_size = state_->getAveragePacketSize(); - uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); - - uint64_t rtt = state_->getRTT(); - if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL; - wait = rtt; - - if (estimated_iat > rtt) wait = estimated_iat; - - uint32_t jitter = ceil(state_->getJitter()); - wait += jitter; - - // it may happen that the channel is congested and we have some additional - // queuing delay to take into account - uint32_t queue = ceil(state_->getQueuing()); - wait += queue; - - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "next rtx for " << seq << " in " << wait - << " ms, rtt = " << state_->getRTT() << " ait = " << estimated_iat - << " jttr = " << jitter << " queue = " << queue; - - return now + wait; - } -} - -void RTCLossDetectionAndRecovery::retransmit() { - if (rtx_timers_.size() == 0) return; - - uint64_t now = getNow(); - - auto it = rtx_timers_.begin(); - std::unordered_set<uint32_t> lost_pkt; - uint32_t sent_counter = 0; - while (it != rtx_timers_.end() && it->first <= now && - sent_counter < MAX_RTX_IN_BATCH) { - uint32_t seq = it->second; - auto rtx_it = - rtx_state_.find(seq); // this should always return a valid iter - if (rtx_it->second.rtx_count_ >= RTC_MAX_RTX || - (now - rtx_it->second.first_send_) >= RTC_MAX_AGE || - seq < state_->getLastSeqNacked()) { - // max rtx reached or packet too old or packet nacked, this packet is lost - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "packet " << seq << " lost because 1) max rtx: " - << (rtx_it->second.rtx_count_ >= RTC_MAX_RTX) << " 2) max age: " - << ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE) - << " 3) nacked: " << (seq < state_->getLastSeqNacked()); - lost_pkt.insert(seq); - it++; - } else { - // resend the packet - state_->onRetransmission(seq); - double prod_rate = state_->getProducerRate(); - if (prod_rate != 0) rtx_it->second.rtx_count_++; - rtx_it->second.next_send_ = computeNextSend(seq, false); - it = rtx_timers_.erase(it); - rtx_timers_.insert( - std::pair<uint64_t, uint32_t>(rtx_it->second.next_send_, seq)); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "send rtx for sequence " << seq << ", next send in " - << (rtx_it->second.next_send_ - now); - send_rtx_callback_(seq); - sent_counter++; - } - } - - // remove packets if needed - for (auto lost_it = lost_pkt.begin(); lost_it != lost_pkt.end(); lost_it++) { - uint32_t seq = *lost_it; - state_->onPacketLost(seq); - deleteRtx(seq); - } -} - -void RTCLossDetectionAndRecovery::scheduleNextRtx() { - if (rtx_timers_.size() == 0) { - // all the rtx were removed, reset timer - next_rtx_timer_ = MAX_TIMER_RTX; - return; - } - - // check if timer is alreay set - if (next_rtx_timer_ != MAX_TIMER_RTX) { - // a new check for rtx is already scheduled - if (next_rtx_timer_ > rtx_timers_.begin()->first) { - // we need to re-schedule it - timer_->cancel(); - } else { - // wait for the next timer - return; - } - } - - // set a new timer - next_rtx_timer_ = rtx_timers_.begin()->first; - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - uint64_t wait = 1; - if (next_rtx_timer_ != MAX_TIMER_RTX && next_rtx_timer_ > now) - wait = next_rtx_timer_ - now; - - std::weak_ptr<RTCLossDetectionAndRecovery> self(shared_from_this()); - timer_->expires_from_now(std::chrono::milliseconds(wait)); - timer_->async_wait([self](std::error_code ec) { - if (ec) return; - if (auto s = self.lock()) { - s->retransmit(); - s->next_rtx_timer_ = MAX_TIMER_RTX; - s->scheduleNextRtx(); - } - }); -} - -bool RTCLossDetectionAndRecovery::deleteRtx(uint32_t seq) { - auto it_rtx = rtx_state_.find(seq); - if (it_rtx == rtx_state_.end()) return false; // rtx not found - - uint64_t ts = it_rtx->second.next_send_; - auto it_timers = rtx_timers_.find(ts); - while (it_timers != rtx_timers_.end() && it_timers->first == ts) { - if (it_timers->second == seq) { - rtx_timers_.erase(it_timers); - break; - } - it_timers++; - } - - bool lost = it_rtx->second.rtx_count_ > 0; - rtx_state_.erase(it_rtx); - - return lost; -} - -void RTCLossDetectionAndRecovery::scheduleSentinelTimer( - uint64_t expires_from_now) { - std::weak_ptr<RTCLossDetectionAndRecovery> self(shared_from_this()); - sentinel_timer_->expires_from_now( - std::chrono::milliseconds(expires_from_now)); - sentinel_timer_->async_wait([self](std::error_code ec) { - if (ec) return; - if (auto s = self.lock()) { - s->sentinelTimer(); - } - }); -} - -void RTCLossDetectionAndRecovery::sentinelTimer() { - uint64_t now = getNow(); - - bool expired = false; - bool sent = false; - if ((now - last_event_) >= sentinel_timer_interval_) { - // at least a sentinel_timer_interval_ elapsed since last event - expired = true; - if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) { - // this happens at the beginning (or if the producer stops for some - // reason) we need to keep sending interest 0 until we get an answer - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "sentinel timer: the producer is not active, send packet 0"; - state_->onRetransmission(0); - send_rtx_callback_(0); - } else { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "sentinel timer: the producer is active, " - "send the 10 oldest packets"; - sent = true; - uint32_t rtx = 0; - auto it = state_->getPendingInterestsMapBegin(); - auto end = state_->getPendingInterestsMapEnd(); - while (it != end && rtx < MAX_RTX_WITH_SENTINEL) { - uint32_t seq = it->first; - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "sentinel timer, add " << seq << " to the rtx list"; - addToRetransmissions(seq, seq + 1); - rtx++; - it++; - } - } - } else { - // sentinel timer did not expire because we registered at least one event - } - - uint32_t next_timer; - double prod_rate = state_->getProducerRate(); - if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive()) || prod_rate == 0) { - DLOG_IF(INFO, VLOG_IS_ON(3)) << "next timer in " << SENTINEL_TIMER_INTERVAL; - next_timer = SENTINEL_TIMER_INTERVAL; - } else { - double prod_rate = state_->getProducerRate(); - double packet_size = state_->getAveragePacketSize(); - uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); - uint32_t jitter = ceil(state_->getJitter()); - - // try to reduce the number of timers if the estimated IAT is too small - next_timer = std::max((estimated_iat + jitter) * 20, (uint32_t)1); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "next sentinel in " << next_timer - << " ms, rate: " << ((prod_rate * 8.0) / 1000000.0) - << ", iat: " << estimated_iat << ", jitter: " << jitter; - - if (!expired) { - // discount the amout of time that is already passed - uint32_t discount = now - last_event_; - if (next_timer > discount) { - next_timer = next_timer - discount; - } else { - // in this case we trigger the timer in 1 ms - next_timer = 1; + if (rs_->getState()->getPacketState(seq) == PacketState::UNKNOWN) { + if (rs_->lossDetected(seq)) { + rs_->getState()->onLossDetected(seq); } - DLOG_IF(INFO, VLOG_IS_ON(3)) << "timer after discout: " << next_timer; - } else if (sent) { - // wait at least one producer stats interval + owd to check if the - // production rate is reducing. - uint32_t min_wait = PRODUCER_STATS_INTERVAL + ceil(state_->getQueuing()); - next_timer = std::max(next_timer, min_wait); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "wait for updates from prod, next timer: " << next_timer; } } - - scheduleSentinelTimer(next_timer); } } // namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_ldr.h b/libtransport/src/protocols/rtc/rtc_ldr.h index 1b9f9afd6..e7f8ce5db 100644 --- a/libtransport/src/protocols/rtc/rtc_ldr.h +++ b/libtransport/src/protocols/rtc/rtc_ldr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,16 +15,14 @@ #pragma once #include <hicn/transport/config.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +// RtcTransportRecoveryStrategies #include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/name.h> -#include <protocols/indexer.h> -#include <protocols/rtc/rtc_consts.h> -#include <protocols/rtc/rtc_state.h> +#include <protocols/rtc/rtc_recovery_strategy.h> #include <functional> -#include <map> -#include <unordered_map> namespace transport { @@ -34,91 +32,45 @@ namespace rtc { class RTCLossDetectionAndRecovery : public std::enable_shared_from_this<RTCLossDetectionAndRecovery> { - struct rtx_state_ { - uint64_t first_send_; - uint64_t next_send_; - uint32_t rtx_count_; - }; - - using rtxState = struct rtx_state_; - using SendRtxCallback = std::function<void(uint32_t)>; - public: - RTCLossDetectionAndRecovery(Indexer *indexer, SendRtxCallback &&callback, - asio::io_service &io_service); + RTCLossDetectionAndRecovery(Indexer *indexer, asio::io_service &io_service, + interface::RtcTransportRecoveryStrategies type, + RecoveryStrategy::SendRtxCallback &&callback, + interface::StrategyCallback *external_callback); ~RTCLossDetectionAndRecovery(); - void setState(std::shared_ptr<RTCState> state) { state_ = state; } - void setFecParams(uint32_t n, uint32_t k) { - n_ = n; - k_ = k; + void setState(RTCState *state) { rs_->setState(state); } + void setRateControl(RTCRateControl *rateControl) { + rs_->setRateControl(rateControl); } - void turnOnRTX(); - void turnOffRTX(); - bool isRtxOn() { return rtx_on_; } + + void setFecParams(uint32_t n, uint32_t k) { rs_->setFecParams(n, k); } + + void turnOnRecovery() { rs_->tunrOnRecovery(); } + bool isRtxOn() { return rs_->isRtxOn(); } + + void changeRecoveryStrategy(interface::RtcTransportRecoveryStrategies type); void onNewRound(bool in_sync); - void onTimeout(uint32_t seq); + void onTimeout(uint32_t seq, bool lost); void onPacketRecoveredFec(uint32_t seq); void onDataPacketReceived(const core::ContentObject &content_object); void onNackPacketReceived(const core::ContentObject &nack); void onProbePacketReceived(const core::ContentObject &probe); - void clear(); + void clear() { rs_->clear(); } - bool isRtx(uint32_t seq) { - if (rtx_state_.find(seq) != rtx_state_.end()) return true; - return false; + bool isRtx(uint32_t seq) { return rs_->isRtx(seq); } + bool isPossibleLossWithNoRtx(uint32_t seq) { + return rs_->isPossibleLossWithNoRtx(seq); } private: - void addToRetransmissions(uint32_t start, uint32_t stop); - uint64_t computeNextSend(uint32_t seq, bool new_rtx); - void retransmit(); - void scheduleNextRtx(); - bool deleteRtx(uint32_t seq); - void scheduleSentinelTimer(uint64_t expires_from_now); - void sentinelTimer(); - uint32_t computeFecPacketsToAsk(bool in_sync); - - uint64_t getNow() { - using namespace std::chrono; - uint64_t now = - duration_cast<milliseconds>(steady_clock::now().time_since_epoch()) - .count(); - return now; - } - - // this map keeps track of the retransmitted interest, ordered from the oldest - // to the newest one. the state contains the timer of the first send of the - // interest (from pendingIntetests_), the timer of the next send (key of the - // multimap) and the number of rtx - std::map<uint32_t, rtxState> rtx_state_; - // this map stored the rtx by timer. The key is the time at which the rtx - // should be sent, and the val is the interest seq number - std::multimap<uint64_t, uint32_t> rtx_timers_; - - // lost packets that will be recovered with fec - std::unordered_set<uint32_t> recover_with_fec_; - - bool rtx_on_; - bool fec_on_; - uint64_t next_rtx_timer_; - uint64_t last_event_; - uint64_t sentinel_timer_interval_; - - // fec params - uint32_t n_; - uint32_t k_; - - std::unique_ptr<asio::steady_timer> timer_; - std::unique_ptr<asio::steady_timer> sentinel_timer_; - std::shared_ptr<RTCState> state_; - - Indexer *indexer_; + void detectLoss(uint32_t start, uint32_t stop); - SendRtxCallback send_rtx_callback_; + interface::RtcTransportRecoveryStrategies rs_type_; + std::shared_ptr<RecoveryStrategy> rs_; }; } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_packet.h b/libtransport/src/protocols/rtc/rtc_packet.h index 7dc2f82c3..391aedfc6 100644 --- a/libtransport/src/protocols/rtc/rtc_packet.h +++ b/libtransport/src/protocols/rtc/rtc_packet.h @@ -24,6 +24,27 @@ * +-----------------------------------------+ */ +/* aggregated packets + * +---------------------------------+ + * |c| #pkts | len1 | len2 | .... | + * +---------------------------------- + * + * +---------------------------------+ + * |c| #pkts | resv | len 1 | + * +---------------------------------- + * + * aggregated packets header. + * header position. just after the data packet header + * + * c: 1 bit: 0 8bit encoding, 1 16bit encoding + * #pkts: 7 bits: number of application packets contained + * 8bits encoding: + * lenX: 8 bits: len in bites of packet X + * 16bits econding: + * resv: 8 bits: reserved field (unused) + * lenX: 16bits: len in bytes of packet X + */ + #pragma once #ifndef _WIN32 #include <arpa/inet.h> @@ -31,6 +52,8 @@ #include <hicn/transport/portability/win_portability.h> #endif +#include <cstring> + namespace transport { namespace protocol { @@ -82,8 +105,144 @@ struct nack_packet_t { inline uint32_t getProductionRate() const { return ntohl(prod_rate); } inline void setProductionRate(uint32_t rate) { prod_rate = htonl(rate); } - inline uint32_t getProductionSegement() const { return ntohl(prod_seg); } - inline void setProductionSegement(uint32_t seg) { prod_seg = htonl(seg); } + inline uint32_t getProductionSegment() const { return ntohl(prod_seg); } + inline void setProductionSegment(uint32_t seg) { prod_seg = htonl(seg); } +}; + +class AggrPktHeader { + public: + // XXX buf always point to the payload after the data header + AggrPktHeader(uint8_t *buf, uint16_t max_packet_len, uint16_t pkt_number) + : buf_(buf), pkt_num_(pkt_number) { + *buf_ = 0; // reset the first byte to correctly add the header + // encoding and the packet number + if (max_packet_len > 0xff) { + setAggrPktEncoding16bit(); + } else { + setAggrPktEncoding8bit(); + } + setAggrPktNUmber(pkt_number); + header_len_ = computeHeaderLen(); + memset(buf_ + 1, 0, header_len_ - 1); + } + + // XXX buf always point to the payload after the data header + AggrPktHeader(uint8_t *buf) : buf_(buf) { + encoding_ = getAggrPktEncoding(); + pkt_num_ = getAggrPktNumber(); + header_len_ = computeHeaderLen(); + } + + ~AggrPktHeader(){}; + + int addPacketToHeader(uint8_t index, uint16_t len) { + if (index > pkt_num_) return -1; + + setAggrPktLen(index, len); + return 0; + } + + int getPointerToPacket(uint8_t index, uint8_t **pkt_ptr, uint16_t *pkt_len) { + if (index > pkt_num_) return -1; + + uint16_t len = 0; + for (int i = 0; i < index; i++) + len += getAggrPktLen(i); // sum the pkts len from 0 to index - 1 + + uint16_t offset = len + header_len_; + *pkt_ptr = buf_ + offset; + *pkt_len = getAggrPktLen(index); + return 0; + } + + int getPacketOffsets(uint8_t index, uint16_t *pkt_offset, uint16_t *pkt_len) { + if (index > pkt_num_) return -1; + + uint16_t len = 0; + for (int i = 0; i < index; i++) + len += getAggrPktLen(i); // sum the pkts len from 0 to index - 1 + + uint16_t offset = len + header_len_; + *pkt_offset = offset; + *pkt_len = getAggrPktLen(index); + + return 0; + } + + uint8_t *getPayloadAppendPtr() { return buf_ + header_len_; } + + uint16_t getHeaderLen() { return header_len_; } + + uint8_t getNumberOfPackets() { return pkt_num_; } + + private: + inline uint16_t computeHeaderLen() const { + uint16_t len = 4; // min len in bytes + if (!encoding_) { + while (pkt_num_ >= len) { + len += 4; + } + } else { + while (pkt_num_ * 2 >= len) { + len += 4; + } + } + return len; + } + + inline uint8_t getAggrPktEncoding() const { + // get the first bit of the first byte + return (*buf_ >> 7); + } + + inline void setAggrPktEncoding8bit() { + // reset the first bit of the first byte + encoding_ = 0; + *buf_ &= 0x7f; + } + + inline void setAggrPktEncoding16bit() { + // set the first bit of the first byte + encoding_ = 1; + *buf_ ^= 0x80; + } + + inline uint8_t getAggrPktNumber() const { + // return the first byte with the first bit = 0 + return (*buf_ & 0x7f); + } + + inline void setAggrPktNUmber(uint8_t val) { + // set the val without modifying the first bit + *buf_ &= 0x80; // reset everithing but the first bit + val &= 0x7f; // reset the first bit + *buf_ |= val; // or the vals, done! + } + + inline uint16_t getAggrPktLen(uint8_t pkt_index) const { + pkt_index++; + if (!encoding_) { // 8 bits + return (uint16_t) * (buf_ + pkt_index); + } else { // 16 bits + uint16_t *buf_16 = (uint16_t *)buf_; + return ntohs(*(buf_16 + pkt_index)); + } + } + + inline void setAggrPktLen(uint8_t pkt_index, uint16_t len) { + pkt_index++; + if (!encoding_) { // 8 bits + *(buf_ + pkt_index) = (uint8_t)len; + } else { // 16 bits + uint16_t *buf_16 = (uint16_t *)buf_; + *(buf_16 + pkt_index) = htons(len); + } + } + + uint8_t *buf_; + uint8_t encoding_; + uint8_t pkt_num_; + uint16_t header_len_; }; } // end namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_rc.h b/libtransport/src/protocols/rtc/rtc_rc.h index 34d090092..62636ce40 100644 --- a/libtransport/src/protocols/rtc/rtc_rc.h +++ b/libtransport/src/protocols/rtc/rtc_rc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -34,11 +34,15 @@ class RTCRateControl : public std::enable_shared_from_this<RTCRateControl> { void turnOnRateControl() { rc_on_ = true; } void setState(std::shared_ptr<RTCState> state) { protocol_state_ = state; }; - uint32_t getCongesionWindow() { return congestion_win_; }; + uint32_t getCongestionWindow() { return congestion_win_; }; + bool inCongestionState() { + if (congestion_state_ == CongestionState::Congested) return true; + return false; + } virtual void onNewRound(double round_len) = 0; - virtual void onDataPacketReceived( - const core::ContentObject &content_object) = 0; + virtual void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats) = 0; protected: enum class CongestionState { Normal = 0, Underuse = 1, Congested = 2, Last }; diff --git a/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc new file mode 100644 index 000000000..6cd3094b5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rc_congestion_detection.h> + +#include <algorithm> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlCongestionDetection::RTCRateControlCongestionDetection() + : rounds_without_congestion_(4), last_queue_(0) {} // must be > 3 + +RTCRateControlCongestionDetection::~RTCRateControlCongestionDetection() {} + +void RTCRateControlCongestionDetection::onNewRound(double round_len) { + if (!rc_on_) return; + + double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC; + double queue = protocol_state_->getQueuing(); + + if (rtt == 0.0) return; // no info from the producer + + if (last_queue_ == queue) { + // if last_queue == queue the consumer didn't receive any + // packet from the producer. we do not change the current congestion state. + // we just increase the counter of rounds whithout congestion if needed + // (in case of congestion the counter is already set to 0) + if (congestion_state_ == CongestionState::Normal) + rounds_without_congestion_++; + } else { + if (queue > MAX_QUEUING_DELAY) { + // here we detect congestion. + congestion_state_ = CongestionState::Congested; + rounds_without_congestion_ = 0; + } else { + // wait 3 rounds before switch back to no congestion + if (rounds_without_congestion_ > 3) { + // nothing bad is happening + congestion_state_ = CongestionState::Normal; + } + rounds_without_congestion_++; + } + last_queue_ = queue; + } +} + +void RTCRateControlCongestionDetection::onDataPacketReceived( + const core::ContentObject &content_object, bool compute_stats) { + // nothing to do + return; +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h new file mode 100644 index 000000000..9afa6c39a --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <hicn/transport/utils/shared_ptr_utils.h> +#include <protocols/rtc/rtc_rc.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RTCRateControlCongestionDetection : public RTCRateControl { + public: + RTCRateControlCongestionDetection(); + + ~RTCRateControlCongestionDetection(); + + void onNewRound(double round_len); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + uint32_t rounds_without_congestion_; + double last_queue_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_iat.cc b/libtransport/src/protocols/rtc/rtc_rc_iat.cc new file mode 100644 index 000000000..f06f377f3 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_iat.cc @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rc_iat.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RTCRateControlIAT::RTCRateControlIAT() + : rounds_since_last_drop_(0), + rounds_without_congestion_(0), + rounds_with_congestion_(0), + last_queue_(0), + last_rcv_time_(0), + last_prod_time_(0), + last_seq_number_(0), + target_rate_avg_(0), + round_index_(0), + congestion_cause_(CongestionCause::UNKNOWN) {} + +RTCRateControlIAT::~RTCRateControlIAT() {} + +void RTCRateControlIAT::onNewRound(double round_len) { + if (!rc_on_) return; + + double received_rate = protocol_state_->getReceivedRate() + + protocol_state_->getRecoveredFecRate(); + + double target_rate = + protocol_state_->getProducerRate(); // * PRODUCTION_RATE_FRACTION; + double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC; + // double packet_size = protocol_state_->getAveragePacketSize(); + double queue = protocol_state_->getQueuing(); + + if (rtt == 0.0) return; // no info from the producer + + CongestionState prev_congestion_state = congestion_state_; + + target_rate_avg_ = target_rate_avg_ * (1 - MOVING_AVG_ALPHA) + + target_rate * MOVING_AVG_ALPHA; + + if (prev_congestion_state == CongestionState::Congested) { + if (queue > MAX_QUEUING_DELAY || last_queue_ == queue) { + congestion_state_ = CongestionState::Congested; + + received_rate_.push_back(received_rate); + target_rate_.push_back(target_rate); + + // We assume the cause does not change + // Note that the first assumption about the cause could be wrong + // the cause of congestion could change + if (congestion_cause_ == CongestionCause::UNKNOWN) + if (rounds_with_congestion_ >= 1) + congestion_cause_ = apply_classification_tree( + rounds_with_congestion_ > ROUND_TO_WAIT_FORCE_DECISION); + + rounds_with_congestion_++; + } else { + congestion_state_ = CongestionState::Normal; + + // clear past history + reset_congestion_statistics(); + + // TODO maybe we can use some of these values for the stdev of the + // congestion mode + for (int i = 0; i < ROUND_HISTORY_SIZE; i++) { + iat_on_hold_[i].clear(); + } + } + } else if (queue > MAX_QUEUING_DELAY) { + if (prev_congestion_state == CongestionState::Normal) { + rounds_with_congestion_ = 0; + + if (rounds_without_congestion_ > ROUND_TO_RESET_CAUSE) + congestion_cause_ = CongestionCause::UNKNOWN; + } + congestion_state_ = CongestionState::Congested; + received_rate_.push_back(received_rate); + target_rate_.push_back(target_rate); + } else { + // nothing bad is happening + congestion_state_ = CongestionState::Normal; + reset_congestion_statistics(); + + int past_index = (round_index_ + 1) % ROUND_HISTORY_SIZE; + for (std::vector<double>::iterator it = iat_on_hold_[past_index].begin(); + it != iat_on_hold_[past_index].end(); ++it) { + congestion_free_iat_.push_back(*it); + if (congestion_free_iat_.size() > 50) { + congestion_free_iat_.erase(congestion_free_iat_.begin()); + } + } + iat_on_hold_[past_index].clear(); + round_index_ = (round_index_ + 1) % ROUND_HISTORY_SIZE; + } + + last_queue_ = queue; + + if (congestion_state_ == CongestionState::Congested) { + if (prev_congestion_state == CongestionState::Normal) { + // init the congetion window using the received rate + // disabling for the moment the congestion window setup + // congestion_win_ = (uint32_t)ceil(received_rate * rtt / packet_size); + rounds_since_last_drop_ = ROUNDS_BEFORE_TAKE_ACTION + 1; + } + + if (rounds_since_last_drop_ >= ROUNDS_BEFORE_TAKE_ACTION) { + // disabling for the moment the congestion window setup + // uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR; + // congestion_win_ = std::max(win, WIN_MIN); + rounds_since_last_drop_ = 0; + return; + } + + rounds_since_last_drop_++; + } + + if (congestion_state_ == CongestionState::Normal) { + if (prev_congestion_state == CongestionState::Congested) { + rounds_without_congestion_ = 0; + } + + rounds_without_congestion_++; + if (rounds_without_congestion_ < ROUNDS_BEFORE_TAKE_ACTION) return; + + // disabling for the moment the congestion window setup + // congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR; + // congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX); + } + + if (received_rate_.size() > 1000) + received_rate_.erase(received_rate_.begin()); + if (target_rate_.size() > 1000) target_rate_.erase(target_rate_.begin()); +} + +void RTCRateControlIAT::onDataPacketReceived( + const core::ContentObject &content_object, bool compute_stats) { + core::ParamsRTC params = RTCState::getDataParams(content_object); + + uint64_t now = utils::SteadyTime::nowMs().count(); + + uint32_t segment_number = content_object.getName().getSuffix(); + + if (segment_number == (last_seq_number_ + 1) && compute_stats) { + uint64_t iat = now - last_rcv_time_; + uint64_t ist = params.timestamp - last_prod_time_; + if (now >= last_rcv_time_ && params.timestamp > last_prod_time_) { + if (iat >= ist && ist < MIN_IST_VALUE) { + if (congestion_state_ == CongestionState::Congested) { + iat_.push_back((iat - ist)); + } else { + // no congestion, but we do not always add new values, but only when + // there is no sign of congestion + double queue = protocol_state_->getQueuing(); + if (queue <= CONGESTION_FREE_QUEUEING_DELAY) { + iat_on_hold_[round_index_].push_back((iat - ist)); + } + } + } + } + } + + last_seq_number_ = segment_number; + last_rcv_time_ = now; + last_prod_time_ = params.timestamp; + + if (iat_.size() > 1000) iat_.erase(iat_.begin()); + return; +} + +CongestionCause RTCRateControlIAT::apply_classification_tree(bool force_reply) { + if (iat_.size() <= 2 || received_rate_.size() < 2) + return CongestionCause::UNKNOWN; + + double received_ratio = 0; + double iat_ratio = 0; + double iat_stdev = compute_iat_stdev(iat_); + double iat_congestion_free_stdev = compute_iat_stdev(congestion_free_iat_); + + double iat_avg = 0.0; + + double recv_avg = 0.0; + double recv_max = 0.0; + + double target_rate_avg = 0.0; + + int counter = 0; + std::vector<double>::reverse_iterator target_it = target_rate_.rbegin(); + for (std::vector<double>::reverse_iterator it = received_rate_.rbegin(); + it != received_rate_.rend(); ++it) { + recv_avg += *it; + target_rate_avg += *target_it; + if (counter < ROUND_HISTORY_SIZE) + if (recv_max < *it) { + recv_max = *it; // we consider only the last 2 seconds + } + counter++; + target_it++; + } + recv_avg = recv_avg / received_rate_.size(); + target_rate_avg = target_rate_avg / target_rate_.size(); + + for (std::vector<double>::iterator it = iat_.begin(); it != iat_.end(); + ++it) { + iat_avg += *it; + } + iat_avg = iat_avg / iat_.size(); + + double congestion_free_iat_avg = 0.0; + for (std::vector<double>::iterator it = congestion_free_iat_.begin(); + it != congestion_free_iat_.end(); ++it) { + congestion_free_iat_avg += *it; + } + congestion_free_iat_avg = + congestion_free_iat_avg / congestion_free_iat_.size(); + + received_ratio = recv_avg / target_rate_avg; + + iat_ratio = iat_stdev / iat_congestion_free_stdev; + + CongestionCause congestion_cause = CongestionCause::UNKNOWN; + // applying classification tree model + if (received_ratio <= 0.87) + if (iat_stdev <= 6.48) + if (received_ratio <= 0.83) + congestion_cause = CongestionCause::LINK_CAPACITY; + else if (force_reply) + congestion_cause = CongestionCause::LINK_CAPACITY; + else + congestion_cause = CongestionCause::UNKNOWN; // accuracy is too low + else if (iat_ratio <= 2.46) + if (force_reply) + congestion_cause = CongestionCause::LINK_CAPACITY; + else + congestion_cause = CongestionCause::UNKNOWN; // accuracy is too low + else + congestion_cause = CongestionCause::COMPETING_CROSS_TRAFFIC; + else if (received_ratio <= 0.913 && iat_stdev <= 0.784) + congestion_cause = CongestionCause::LINK_CAPACITY; + else + congestion_cause = CongestionCause::COMPETING_CROSS_TRAFFIC; + + return congestion_cause; +} + +void RTCRateControlIAT::reset_congestion_statistics() { + iat_.clear(); + received_rate_.clear(); + target_rate_.clear(); +} + +double RTCRateControlIAT::compute_iat_stdev(std::vector<double> v) { + if (v.size() == 0) return 0; + + float sum = 0.0, mean, standard_deviation = 0.0; + for (std::vector<double>::iterator it = v.begin(); it != v.end(); it++) { + sum += *it; + } + + mean = sum / v.size(); + for (std::vector<double>::iterator it = v.begin(); it != v.end(); it++) { + standard_deviation += pow(*it - mean, 2); + } + return sqrt(standard_deviation / v.size()); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_iat.h b/libtransport/src/protocols/rtc/rtc_rc_iat.h new file mode 100644 index 000000000..715637807 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rc_iat.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <hicn/transport/utils/shared_ptr_utils.h> +#include <protocols/rtc/rtc_rc.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +const int ROUND_HISTORY_SIZE = 10; // equivalent to two seconds +const int ROUND_TO_WAIT_FORCE_DECISION = 5; + +// once congestion is gone, we need to wait for k rounds before changing the +// congestion cause in the case it appears again +const int ROUND_TO_RESET_CAUSE = 5; + +const int MIN_IST_VALUE = 150; // samples of ist larger than 150ms are + // discarded +const double CONGESTION_FREE_QUEUEING_DELAY = 10; + +enum class CongestionCause : uint8_t { + COMPETING_CROSS_TRAFFIC, + FRIENDLY_CROSS_TRAFFIC, + UNKNOWN_CROSS_TRAFFIC, + LINK_CAPACITY, + UNKNOWN +}; + +class RTCRateControlIAT : public RTCRateControl { + public: + RTCRateControlIAT(); + + ~RTCRateControlIAT(); + + void onNewRound(double round_len); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); + + auto shared_from_this() { return utils::shared_from(this); } + + private: + void reset_congestion_statistics(); + + double compute_iat_stdev(std::vector<double> v); + + CongestionCause apply_classification_tree(bool force_reply); + + private: + uint32_t rounds_since_last_drop_; + uint32_t rounds_without_congestion_; + uint32_t rounds_with_congestion_; + double last_queue_; + uint64_t last_rcv_time_; + uint64_t last_prod_time_; + uint32_t last_seq_number_; + double target_rate_avg_; + + // Iat values are not immediately added to the congestion free set of values + std::array<std::vector<double>, ROUND_HISTORY_SIZE> iat_on_hold_; + uint32_t round_index_; + + // with congestion statistics + std::vector<double> iat_; + std::vector<double> received_rate_; + std::vector<double> target_rate_; + + // congestion free statistics + std::vector<double> congestion_free_iat_; + + CongestionCause congestion_cause_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.cc b/libtransport/src/protocols/rtc/rtc_rc_queue.cc index a1c89e329..ecabc5205 100644 --- a/libtransport/src/protocols/rtc/rtc_rc_queue.cc +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -37,7 +37,7 @@ void RTCRateControlQueue::onNewRound(double round_len) { double received_rate = protocol_state_->getReceivedRate(); double target_rate = protocol_state_->getProducerRate() * PRODUCTION_RATE_FRACTION; - double rtt = (double)protocol_state_->getRTT() / MILLI_IN_A_SEC; + double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC; double packet_size = protocol_state_->getAveragePacketSize(); double queue = protocol_state_->getQueuing(); @@ -94,7 +94,7 @@ void RTCRateControlQueue::onNewRound(double round_len) { } void RTCRateControlQueue::onDataPacketReceived( - const core::ContentObject &content_object) { + const core::ContentObject &content_object, bool compute_stats) { // nothing to do return; } diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.h b/libtransport/src/protocols/rtc/rtc_rc_queue.h index 407354d43..cdf78fd47 100644 --- a/libtransport/src/protocols/rtc/rtc_rc_queue.h +++ b/libtransport/src/protocols/rtc/rtc_rc_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -30,7 +30,8 @@ class RTCRateControlQueue : public RTCRateControl { ~RTCRateControlQueue(); void onNewRound(double round_len); - void onDataPacketReceived(const core::ContentObject &content_object); + void onDataPacketReceived(const core::ContentObject &content_object, + bool compute_stats); auto shared_from_this() { return utils::shared_from(this); } diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.cc b/libtransport/src/protocols/rtc/rtc_reassembly.cc new file mode 100644 index 000000000..992bab50e --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_reassembly.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/interfaces/socket_consumer.h> +#include <implementation/socket_consumer.h> +#include <protocols/rtc/rtc_reassembly.h> +#include <protocols/transport_protocol.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RtcReassembly::RtcReassembly(implementation::ConsumerSocket* icn_socket, + TransportProtocol* transport_protocol) + : DatagramReassembly(icn_socket, transport_protocol) { + is_setup_ = false; +} + +void RtcReassembly::reassemble(core::ContentObject& content_object) { + if (!is_setup_) { + is_setup_ = true; + reassembly_consumer_socket_->getSocketOption( + interface::RtcTransportOptions::AGGREGATED_DATA, data_aggregation_); + } + + auto read_buffer = content_object.getPayload(); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Size of payload: " << read_buffer->length(); + + read_buffer->trimStart(transport_protocol_->transportHeaderLength()); + + if (data_aggregation_) { + rtc::AggrPktHeader hdr((uint8_t*)read_buffer->data()); + + for (uint8_t i = 0; i < hdr.getNumberOfPackets(); i++) { + std::unique_ptr<utils::MemBuf> segment = read_buffer->clone(); + + uint16_t pkt_start = 0; + uint16_t pkt_len = 0; + int res = hdr.getPacketOffsets(i, &pkt_start, &pkt_len); + if (res == -1) { + // this should not happen + break; + } + + segment->trimStart(pkt_start); + segment->trimEnd(segment->length() - pkt_len); + + Reassembly::read_buffer_ = std::move(segment); + Reassembly::notifyApplication(); + } + } else { + Reassembly::read_buffer_ = std::move(read_buffer); + Reassembly::notifyApplication(); + } +} + +void RtcReassembly::reassemble(utils::MemBuf& buffer, uint32_t suffix) { + if (!is_setup_) { + is_setup_ = true; + reassembly_consumer_socket_->getSocketOption( + interface::RtcTransportOptions::AGGREGATED_DATA, data_aggregation_); + } + + if (data_aggregation_) { + rtc::AggrPktHeader hdr((uint8_t*)buffer.data()); + + for (uint8_t i = 0; i < hdr.getNumberOfPackets(); i++) { + std::unique_ptr<utils::MemBuf> segment = buffer.clone(); + + uint16_t pkt_start = 0; + uint16_t pkt_len = 0; + int res = hdr.getPacketOffsets(i, &pkt_start, &pkt_len); + if (res == -1) { + // this should not happen + break; + } + + segment->trimStart(pkt_start); + segment->trimEnd(segment->length() - pkt_len); + + Reassembly::read_buffer_ = std::move(segment); + Reassembly::notifyApplication(); + } + + } else { + Reassembly::read_buffer_ = buffer.cloneOne(); + Reassembly::notifyApplication(); + } +} + +} // namespace rtc + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.h b/libtransport/src/protocols/rtc/rtc_reassembly.h index 15722a6d5..132004605 100644 --- a/libtransport/src/protocols/rtc/rtc_reassembly.h +++ b/libtransport/src/protocols/rtc/rtc_reassembly.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -28,17 +28,14 @@ namespace rtc { class RtcReassembly : public DatagramReassembly { public: RtcReassembly(implementation::ConsumerSocket *icn_socket, - TransportProtocol *transport_protocol) - : DatagramReassembly(icn_socket, transport_protocol) {} - - void reassemble(core::ContentObject &content_object) override { - auto read_buffer = content_object.getPayload(); - DLOG_IF(INFO, VLOG_IS_ON(3)) - << "Size of payload: " << read_buffer->length(); - read_buffer->trimStart(transport_protocol_->transportHeaderLength()); - Reassembly::read_buffer_ = std::move(read_buffer); - Reassembly::notifyApplication(); - } + TransportProtocol *transport_protocol); + + void reassemble(core::ContentObject &content_object) override; + void reassemble(utils::MemBuf &buffer, uint32_t suffix) override; + + private: + bool is_setup_; + bool data_aggregation_; }; } // namespace rtc diff --git a/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc new file mode 100644 index 000000000..888105eab --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <hicn/transport/interfaces/notification.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +using namespace transport::interface; + +RecoveryStrategy::RecoveryStrategy( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + bool use_rtx, bool use_fec, interface::StrategyCallback *external_callback) + : recovery_on_(false), + next_rtx_timer_(MAX_TIMER_RTX), + send_rtx_callback_(std::move(callback)), + indexer_(indexer), + round_id_(0), + last_fec_used_(0), + callback_(external_callback) { + setRtxFec(use_rtx, use_fec); + timer_ = std::make_unique<asio::steady_timer>(io_service); +} + +RecoveryStrategy::RecoveryStrategy(RecoveryStrategy &&rs) + : rtx_state_(std::move(rs.rtx_state_)), + rtx_timers_(std::move(rs.rtx_timers_)), + recover_with_fec_(std::move(rs.recover_with_fec_)), + timer_(std::move(rs.timer_)), + next_rtx_timer_(std::move(rs.next_rtx_timer_)), + send_rtx_callback_(std::move(rs.send_rtx_callback_)), + n_(std::move(rs.n_)), + k_(std::move(rs.k_)), + indexer_(std::move(rs.indexer_)), + state_(std::move(rs.state_)), + rc_(std::move(rs.rc_)), + round_id_(std::move(rs.round_id_)), + last_fec_used_(std::move(rs.last_fec_used_)), + callback_(rs.callback_) { + setFecParams(n_, k_); +} + +RecoveryStrategy::~RecoveryStrategy() {} + +void RecoveryStrategy::setFecParams(uint32_t n, uint32_t k) { + n_ = n; + k_ = k; + + // XXX for the moment we go in steps of 5% loss rate. + // max loss rate = 95% + for (uint32_t loss_rate = 5; loss_rate < 100; loss_rate += 5) { + double dec_loss_rate = (double)loss_rate / 100.0; + double exp_losses = (double)k_ * dec_loss_rate; + uint32_t fec_to_ask = ceil(exp_losses / (1 - dec_loss_rate)); + + fec_state_ f; + f.fec_to_ask = std::min(fec_to_ask, (n_ - k_)); + f.last_update = round_id_; + f.avg_residual_losses = 0.0; + f.consecutive_use = 0; + fec_per_loss_rate_.push_back(f); + } +} + +bool RecoveryStrategy::lossDetected(uint32_t seq) { + if (isRtx(seq)) { + // this packet is already in the list of rtx + return false; + } + + auto it = recover_with_fec_.find(seq); + if (it != recover_with_fec_.end()) { + // this packet is already in list of packets to recover with fec + // this list contians also fec packets that will not be recovered with rtx + return false; + } + + // new loss detected, recover it according to the strategy + newPacketLoss(seq); + return true; +} + +void RecoveryStrategy::clear() { + rtx_state_.clear(); + rtx_timers_.clear(); + recover_with_fec_.clear(); + + if (next_rtx_timer_ != MAX_TIMER_RTX) { + next_rtx_timer_ = MAX_TIMER_RTX; + timer_->cancel(); + } +} + +// rtx functions +void RecoveryStrategy::addNewRtx(uint32_t seq, bool force) { + if (!indexer_->isFec(seq) || force) { + // this packet needs to be re-transmitted + rtxState state; + state.first_send_ = state_->getInterestSentTime(seq); + if (state.first_send_ == 0) // this interest was never sent before + state.first_send_ = getNow(); + state.next_send_ = computeNextSend(seq, true); + state.rtx_count_ = 0; + DLOG_IF(INFO, VLOG_IS_ON(4)) + << "Add " << seq << " to retransmissions. next rtx is in " + << state.next_send_ - getNow() << " ms"; + rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state)); + rtx_timers_.insert(std::pair<uint64_t, uint32_t>(state.next_send_, seq)); + + // if a new rtx is introduced, check the rtx timer + scheduleNextRtx(); + } else { + // do not re-send fec packets but keep track of them + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } +} + +uint64_t RecoveryStrategy::computeNextSend(uint32_t seq, bool new_rtx) { + uint64_t now = getNow(); + if (new_rtx) { + // for the new rtx we wait one estimated IAT after the loss detection. this + // is bacause, assuming that packets arrive with a constant IAT, we should + // get a new packet every IAT + double prod_rate = state_->getProducerRate(); + uint32_t estimated_iat = SENTINEL_TIMER_INTERVAL; + uint32_t jitter = 0; + + if (prod_rate != 0) { + double packet_size = state_->getAveragePacketSize(); + estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + jitter = ceil(state_->getJitter()); + } + + uint32_t wait = 1; + if (estimated_iat < 18) { + // for low rate app we do not wait to send a RTX + // we consider low rate stream with less than 50pps (iat >= 20ms) + // (e.g. audio in videoconf, mobile games). + // in the check we use 18ms to accomodate for measurements errors + // for flows with higher rate wait 1 ait + jitter + wait = estimated_iat + jitter; + } + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "first rtx for " << seq << " in " << wait + << " ms, rtt = " << state_->getMinRTT() << " ait = " << estimated_iat + << " jttr = " << jitter; + + return now + wait; + } else { + // wait one RTT + uint32_t wait = SENTINEL_TIMER_INTERVAL; + + double prod_rate = state_->getProducerRate(); + if (prod_rate == 0) { + return now + SENTINEL_TIMER_INTERVAL; + } + + double packet_size = state_->getAveragePacketSize(); + uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size)); + + uint64_t rtt = state_->getMinRTT(); + if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL; + wait = rtt; + + uint32_t jitter = ceil(state_->getJitter()); + wait += jitter; + + // it may happen that the channel is congested and we have some additional + // queuing delay to take into account + uint32_t queue = ceil(state_->getQueuing()); + wait += queue; + + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "next rtx for " << seq << " in " << wait + << " ms, rtt = " << state_->getMinRTT() << " ait = " << estimated_iat + << " jttr = " << jitter << " queue = " << queue; + + return now + wait; + } +} + +void RecoveryStrategy::retransmit() { + if (rtx_timers_.size() == 0) return; + + uint64_t now = getNow(); + + auto it = rtx_timers_.begin(); + std::unordered_set<uint32_t> lost_pkt; + uint32_t sent_counter = 0; + while (it != rtx_timers_.end() && it->first <= now && + sent_counter < MAX_RTX_IN_BATCH) { + uint32_t seq = it->second; + auto rtx_it = + rtx_state_.find(seq); // this should always return a valid iter + if (rtx_it->second.rtx_count_ >= RTC_MAX_RTX || + (now - rtx_it->second.first_send_) >= RTC_MAX_AGE || + seq < state_->getLastSeqNacked()) { + // max rtx reached or packet too old or packet nacked, this packet is lost + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "packet " << seq << " lost because 1) max rtx: " + << (rtx_it->second.rtx_count_ >= RTC_MAX_RTX) << " 2) max age: " + << ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE) + << " 3) nacked: " << (seq < state_->getLastSeqNacked()); + lost_pkt.insert(seq); + it++; + } else { + // resend the packet + state_->onRetransmission(seq); + double prod_rate = state_->getProducerRate(); + if (prod_rate != 0) rtx_it->second.rtx_count_++; + rtx_it->second.next_send_ = computeNextSend(seq, false); + it = rtx_timers_.erase(it); + rtx_timers_.insert( + std::pair<uint64_t, uint32_t>(rtx_it->second.next_send_, seq)); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "send rtx for sequence " << seq << ", next send in " + << (rtx_it->second.next_send_ - now); + send_rtx_callback_(seq); + sent_counter++; + } + } + + // remove packets if needed + for (auto lost_it = lost_pkt.begin(); lost_it != lost_pkt.end(); lost_it++) { + uint32_t seq = *lost_it; + state_->onPacketLost(seq); + deleteRtx(seq); + } +} + +void RecoveryStrategy::scheduleNextRtx() { + if (rtx_timers_.size() == 0) { + // all the rtx were removed, reset timer + next_rtx_timer_ = MAX_TIMER_RTX; + return; + } + + // check if timer is alreay set + if (next_rtx_timer_ != MAX_TIMER_RTX) { + // a new check for rtx is already scheduled + if (next_rtx_timer_ > rtx_timers_.begin()->first) { + // we need to re-schedule it + timer_->cancel(); + } else { + // wait for the next timer + return; + } + } + + // set a new timer + next_rtx_timer_ = rtx_timers_.begin()->first; + uint64_t now = utils::SteadyTime::nowMs().count(); + uint64_t wait = 1; + if (next_rtx_timer_ != MAX_TIMER_RTX && next_rtx_timer_ > now) + wait = next_rtx_timer_ - now; + + std::weak_ptr<RecoveryStrategy> self(shared_from_this()); + timer_->expires_from_now(std::chrono::milliseconds(wait)); + timer_->async_wait([self](const std::error_code &ec) { + if (ec) return; + if (auto s = self.lock()) { + s->retransmit(); + s->next_rtx_timer_ = MAX_TIMER_RTX; + s->scheduleNextRtx(); + } + }); +} + +void RecoveryStrategy::deleteRtx(uint32_t seq) { + auto it_rtx = rtx_state_.find(seq); + if (it_rtx == rtx_state_.end()) return; // rtx not found + + // remove the rtx from the timers list + uint64_t ts = it_rtx->second.next_send_; + auto it_timers = rtx_timers_.find(ts); + while (it_timers != rtx_timers_.end() && it_timers->first == ts) { + if (it_timers->second == seq) { + rtx_timers_.erase(it_timers); + break; + } + it_timers++; + } + // remove rtx + rtx_state_.erase(it_rtx); +} + +// fec functions +uint32_t RecoveryStrategy::computeFecPacketsToAsk(bool in_sync) { + double loss_rate = state_->getMaxLossRate() * 100; // use loss rate in % + + if (loss_rate > 95) loss_rate = 95; // max loss rate + + if (loss_rate == 0) return 0; + + // once per minute try to reduce the fec rate. it may happen that for some bin + // we ask too many fec packet. here we try to reduce this values gently + if (round_id_ % ROUNDS_PER_MIN == 0) { + reduceFec(); + } + + // keep track of the last used fec. if we use a new bin on this round reset + // consecutive use and avg loss in the prev bin + uint32_t bin = ceil(loss_rate / 5.0) - 1; + if (bin > fec_per_loss_rate_.size() - 1) bin = fec_per_loss_rate_.size() - 1; + + if (bin != last_fec_used_) { + fec_per_loss_rate_[last_fec_used_].consecutive_use = 0; + fec_per_loss_rate_[last_fec_used_].avg_residual_losses = 0.0; + } + last_fec_used_ = bin; + fec_per_loss_rate_[last_fec_used_].consecutive_use++; + + // we update the stats only once very 5 rounds (1sec) that is the rate at + // which we compute residual losses + if (round_id_ % ROUNDS_PER_SEC == 0) { + double residual_losses = state_->getResidualLossRate() * 100; + // update residual loss rate + fec_per_loss_rate_[bin].avg_residual_losses = + (fec_per_loss_rate_[bin].avg_residual_losses * MOVING_AVG_ALPHA) + + (1 - MOVING_AVG_ALPHA) * residual_losses; + + if ((fec_per_loss_rate_[bin].last_update - round_id_) < + WAIT_BEFORE_FEC_UPDATE) { + // this bin is been updated recently so don't modify it and + // return the current state + return fec_per_loss_rate_[bin].fec_to_ask; + } + + // if the residual loss rate is too high and we can ask more fec packets and + // we are using this configuration since at least 5 sec update fec + if (fec_per_loss_rate_[bin].avg_residual_losses > MAX_RESIDUAL_LOSS_RATE && + fec_per_loss_rate_[bin].fec_to_ask < (n_ - k_) && + fec_per_loss_rate_[bin].consecutive_use > WAIT_BEFORE_FEC_UPDATE) { + // so increase the number of fec packets to ask + fec_per_loss_rate_[bin].fec_to_ask++; + fec_per_loss_rate_[bin].last_update = round_id_; + fec_per_loss_rate_[bin].avg_residual_losses = 0.0; + } + } + + return fec_per_loss_rate_[bin].fec_to_ask; +} + +void RecoveryStrategy::setRtxFec(std::optional<bool> rtx_on, + std::optional<bool> fec_on) { + if (rtx_on) rtx_on_ = *rtx_on; + if (fec_on) fec_on_ = *fec_on; + + if (*callback_) { + notification::RecoveryStrategy strategy = + notification::RecoveryStrategy::RECOVERY_OFF; + + if (rtx_on_ && fec_on_) + strategy = notification::RecoveryStrategy::RTX_AND_FEC; + else if (rtx_on_) + strategy = notification::RecoveryStrategy::RTX_ONLY; + else if (fec_on_) + strategy = notification::RecoveryStrategy::FEC_ONLY; + + (*callback_)(strategy); + } +} + +// common functions +void RecoveryStrategy::onLostTimeout(uint32_t seq) { removePacketState(seq); } + +void RecoveryStrategy::removePacketState(uint32_t seq) { + auto it_fec = recover_with_fec_.find(seq); + if (it_fec != recover_with_fec_.end()) { + recover_with_fec_.erase(it_fec); + return; + } + + deleteRtx(seq); +} + +// private methods + +void RecoveryStrategy::reduceFec() { + for (uint32_t loss_rate = 5; loss_rate < 100; loss_rate += 5) { + double dec_loss_rate = (double)loss_rate / 100.0; + double exp_losses = (double)k_ * dec_loss_rate; + uint32_t fec_to_ask = ceil(exp_losses / (1 - dec_loss_rate)); + + uint32_t bin = ceil(loss_rate / 5.0) - 1; + if (fec_per_loss_rate_[bin].fec_to_ask > fec_to_ask) { + fec_per_loss_rate_[bin].fec_to_ask--; + // std::cout << "reduce fec to ask for bin " << bin << std::endl; + } + } +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_recovery_strategy.h b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h new file mode 100644 index 000000000..9ffc69a1b --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <hicn/transport/interfaces/callbacks.h> +#include <hicn/transport/utils/chrono_typedefs.h> +#include <protocols/indexer.h> +#include <protocols/rtc/rtc_rc.h> +#include <protocols/rtc/rtc_state.h> + +#include <map> +#include <unordered_map> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategy : public std::enable_shared_from_this<RecoveryStrategy> { + protected: + struct rtx_state_ { + uint64_t first_send_; + uint64_t next_send_; + uint32_t rtx_count_; + }; + + using rtxState = struct rtx_state_; + + public: + using SendRtxCallback = std::function<void(uint32_t)>; + + RecoveryStrategy(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, bool use_rtx, bool use_fec, + interface::StrategyCallback *external_callback); + + RecoveryStrategy(RecoveryStrategy &&rs); + + virtual ~RecoveryStrategy(); + + void setRtxFec(std::optional<bool> rtx_on = {}, + std::optional<bool> fec_on = {}); + void setState(RTCState *state) { state_ = state; } + void setRateControl(RTCRateControl *rateControl) { rc_ = rateControl; } + void setFecParams(uint32_t n, uint32_t k); + + void tunrOnRecovery() { recovery_on_ = true; } + + bool isRtx(uint32_t seq) { + if (rtx_state_.find(seq) != rtx_state_.end()) return true; + return false; + } + + bool isPossibleLossWithNoRtx(uint32_t seq) { + if (recover_with_fec_.find(seq) != recover_with_fec_.end()) return true; + return false; + } + + bool isRtxOn() { return rtx_on_; } + + RTCState *getState() { return state_; } + bool lossDetected(uint32_t seq); + void clear(); + + virtual void onNewRound(bool in_sync) = 0; + virtual void newPacketLoss(uint32_t seq) = 0; + virtual void receivedPacket(uint32_t seq) = 0; + void onLostTimeout(uint32_t seq); + + void incRoundId() { round_id_++; } + + // utils + uint64_t getNow() { + uint64_t now = utils::SteadyTime::nowMs().count(); + return now; + } + + protected: + // rtx functions + void addNewRtx(uint32_t seq, bool force); + uint64_t computeNextSend(uint32_t seq, bool new_rtx); + void retransmit(); + void scheduleNextRtx(); + void deleteRtx(uint32_t seq); + + // fec functions + uint32_t computeFecPacketsToAsk(bool in_sync); + + // common functons + void removePacketState(uint32_t seq); + + bool recovery_on_; + bool rtx_on_; + bool fec_on_; + + // this map keeps track of the retransmitted interest, ordered from the oldest + // to the newest one. the state contains the timer of the first send of the + // interest (from pendingIntetests_), the timer of the next send (key of the + // multimap) and the number of rtx + std::map<uint32_t, rtxState> rtx_state_; + // this map stored the rtx by timer. The key is the time at which the rtx + // should be sent, and the val is the interest seq number + std::multimap<uint64_t, uint32_t> rtx_timers_; + + // lost packets that will be recovered with fec + std::unordered_set<uint32_t> recover_with_fec_; + + // rtx vars + std::unique_ptr<asio::steady_timer> timer_; + uint64_t next_rtx_timer_; + SendRtxCallback send_rtx_callback_; + + // fec vars + uint32_t n_; + uint32_t k_; + Indexer *indexer_; + + RTCState *state_; + RTCRateControl *rc_; + + private: + struct fec_state_ { + uint32_t fec_to_ask; + uint32_t last_update; // round id of the last update + // (wait 10 ruonds (2sec) between updates) + uint32_t consecutive_use; // consecutive ruonds where this fec was used + double avg_residual_losses; + }; + + void reduceFec(); + + uint32_t round_id_; // number of rounds + uint32_t last_fec_used_; + std::vector<fec_state_> fec_per_loss_rate_; + interface::StrategyCallback *callback_; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_delay.cc b/libtransport/src/protocols/rtc/rtc_rs_delay.cc new file mode 100644 index 000000000..e2c60ca77 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_delay.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_delay.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyDelayBased::RecoveryStrategyDelayBased( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + external_callback), // start with rtx + congestion_state_(false), + probing_state_(false), + switch_rounds_(0) {} + +RecoveryStrategyDelayBased::RecoveryStrategyDelayBased(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(true, false); + // we have to re-init congestion and + // probing + congestion_state_ = false; + probing_state_ = false; +} + +RecoveryStrategyDelayBased::~RecoveryStrategyDelayBased() {} + +void RecoveryStrategyDelayBased::softSwitchToFec(uint32_t fec_to_ask) { + if (fec_to_ask == 0) { + setRtxFec(true, false); + switch_rounds_ = 0; + } else { + switch_rounds_++; + if (switch_rounds_ >= 5) { + setRtxFec(false, true); + } else { + setRtxFec({}, true); + } + } +} + +void RecoveryStrategyDelayBased::onNewRound(bool in_sync) { + if (!recovery_on_) { + // disable fec so that no extra packet will be sent + // for rtx we check if recovery is on in newPacketLoss + setRtxFec(true, false); + indexer_->setNFec(0); + return; + } + + uint64_t rtt = state_->getMinRTT(); + + bool congestion = false; + // XXX at the moment we are not looking at congestion events + // congestion = rc_->inCongestionState(); + + if ((!fec_on_ && rtt >= 100) || (fec_on_ && rtt > 80) || congestion) { + // switch from rtx to fec or keep use fec. Notice that if some rtx are + // waiting to be scheduled, they will be sent normally, but no new rtx will + // be created If the loss rate is 0 keep to use RTX. + uint32_t fec_to_ask = computeFecPacketsToAsk(in_sync); + softSwitchToFec(fec_to_ask); + indexer_->setNFec(fec_to_ask); + return; + } + + if ((fec_on_ && rtt <= 80) || (!rtx_on_ && rtt <= 100)) { + // turn on rtx + softSwitchToFec(0); + indexer_->setNFec(0); + return; + } +} + +void RecoveryStrategyDelayBased::newPacketLoss(uint32_t seq) { + if (rtx_on_ && recovery_on_) { + addNewRtx(seq, false); + } else { + if (!state_->isPending(seq) && !indexer_->isFec(seq)) { + addNewRtx(seq, true); + } else { + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } + } +} + +void RecoveryStrategyDelayBased::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +void RecoveryStrategyDelayBased::probing() { + // TODO + // for the moment ask for all fec and exit the probing phase + probing_state_ = false; + setRtxFec(false, true); + indexer_->setNFec(computeFecPacketsToAsk(true)); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_delay.h b/libtransport/src/protocols/rtc/rtc_rs_delay.h new file mode 100644 index 000000000..0dd199965 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_delay.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyDelayBased : public RecoveryStrategy { + public: + RecoveryStrategyDelayBased(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyDelayBased(RecoveryStrategy &&rs); + + ~RecoveryStrategyDelayBased(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); + + private: + void softSwitchToFec(uint32_t fec_to_ask); + + bool congestion_state_; + bool probing_state_; + uint32_t switch_rounds_; + + void probing(); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc new file mode 100644 index 000000000..36d8e39f0 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_fec_only.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyFecOnly::RecoveryStrategyFecOnly( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, true, + external_callback), + congestion_state_(false), + probing_state_(false), + switch_rounds_(0) {} + +RecoveryStrategyFecOnly::RecoveryStrategyFecOnly(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(false, true); + congestion_state_ = false; + probing_state_ = false; +} + +RecoveryStrategyFecOnly::~RecoveryStrategyFecOnly() {} + +void RecoveryStrategyFecOnly::onNewRound(bool in_sync) { + if (!recovery_on_) { + indexer_->setNFec(0); + return; + } + + // XXX for the moment we are considering congestion events + // if(rc_->inCongestionState()){ + // congestion_state_ = true; + // probing_state_ = false; + // indexer_->setNFec(0); + // return; + // } + + // no congestion + if (congestion_state_) { + // this is the first round after congestion + // enter probing phase + probing_state_ = true; + congestion_state_ = false; + } + + if (probing_state_) { + probing(); + } else { + uint32_t fec_to_ask = computeFecPacketsToAsk(in_sync); + // If fec_to_ask == 0 we use rtx even if in these strategy we use only fec. + // In this way the first packet loss that triggers the usage of fec can be + // recovered using rtx, otherwise it will always be lost + if (fec_to_ask == 0) { + setRtxFec(true, false); + switch_rounds_ = 0; + } else { + switch_rounds_++; + if (switch_rounds_ >= 5) { + setRtxFec(false, true); + } else { + setRtxFec({}, true); + } + } + indexer_->setNFec(fec_to_ask); + } +} + +void RecoveryStrategyFecOnly::newPacketLoss(uint32_t seq) { + if (rtx_on_ && recovery_on_) { + addNewRtx(seq, false); + } else { + if (!state_->isPending(seq) && !indexer_->isFec(seq)) { + addNewRtx(seq, true); + } else { + // if not pending add rtc + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } + } +} + +void RecoveryStrategyFecOnly::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +void RecoveryStrategyFecOnly::probing() { + // TODO + // for the moment ask for all fec and exit the probing phase + probing_state_ = false; + uint32_t fec_to_ask = computeFecPacketsToAsk(true); + indexer_->setNFec(fec_to_ask); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_fec_only.h b/libtransport/src/protocols/rtc/rtc_rs_fec_only.h new file mode 100644 index 000000000..37b505d35 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyFecOnly : public RecoveryStrategy { + public: + RecoveryStrategyFecOnly(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyFecOnly(RecoveryStrategy &&rs); + + ~RecoveryStrategyFecOnly(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); + + private: + bool congestion_state_; + bool probing_state_; + uint32_t switch_rounds_; + + void probing(); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc new file mode 100644 index 000000000..bd153d209 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_low_rate.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyLowRate::RecoveryStrategyLowRate( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, true, + external_callback), // start with fec + fec_consecutive_rounds_((MILLI_IN_A_SEC / ROUND_LEN) * 5), // 5 sec + rtx_allowed_consecutive_rounds_(0) { + initSwitchVector(); +} + +RecoveryStrategyLowRate::RecoveryStrategyLowRate(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)), + fec_consecutive_rounds_((MILLI_IN_A_SEC / ROUND_LEN) * 5), // 5 sec + rtx_allowed_consecutive_rounds_(0) { + setRtxFec(false, true); + initSwitchVector(); +} + +RecoveryStrategyLowRate::~RecoveryStrategyLowRate() {} + +void RecoveryStrategyLowRate::initSwitchVector() { + // TODO adjust thresholds here when new data are collected + // see resutls in + // https://confluence-eng-gpk1.cisco.com/conf/display/SPT/dailyreports + thresholds_t t1; + t1.rtt = 15; // 15ms + t1.loss_rtx_to_fec = 15; // 15% + t1.loss_fec_to_rtx = 10; // 10% + thresholds_t t2; + t2.rtt = 35; // 35ms + t2.loss_rtx_to_fec = 5; // 5% + t2.loss_fec_to_rtx = 1; // 1% + switch_vector.push_back(t1); + switch_vector.push_back(t2); +} + +void RecoveryStrategyLowRate::setRecoveryParameters(bool use_rtx, bool use_fec, + uint32_t fec_to_ask) { + setRtxFec(use_rtx, use_fec); + indexer_->setNFec(fec_to_ask); +} + +void RecoveryStrategyLowRate::selectRecoveryStrategy(bool in_sync) { + uint32_t fec_to_ask = computeFecPacketsToAsk(in_sync); + if (fec_to_ask == 0) { + // fec is off, turn on RTX immediatly to avoid packet losses + setRecoveryParameters(true, false, 0); + fec_consecutive_rounds_ = 0; + return; + } + + uint32_t loss_rate = std::round(state_->getPerSecondLossRate() * 100); + uint32_t rtt = state_->getAvgRTT(); + + bool use_rtx = false; + for (size_t i = 0; i < switch_vector.size(); i++) { + uint32_t max_loss_rate = 0; + if (fec_on_) + max_loss_rate = switch_vector[i].loss_fec_to_rtx; + else + max_loss_rate = switch_vector[i].loss_rtx_to_fec; + + if (rtt < switch_vector[i].rtt && loss_rate < max_loss_rate) { + use_rtx = true; + rtx_allowed_consecutive_rounds_++; + break; + } + } + + if (!use_rtx) rtx_allowed_consecutive_rounds_ = 0; + + if (use_rtx) { + if (fec_on_) { + // here we should swtich from RTX to FEC + // wait 10sec where the switch is allowed before actually switch + if (rtx_allowed_consecutive_rounds_ >= + ((MILLI_IN_A_SEC / ROUND_LEN) * 10)) { // 10 sec + // use RTX + setRecoveryParameters(true, false, 0); + fec_consecutive_rounds_ = 0; + } else { + // keep using FEC (and maybe RTX) + setRecoveryParameters(true, true, fec_to_ask); + fec_consecutive_rounds_++; + } + } else { + // keep using RTX + setRecoveryParameters(true, false, 0); + fec_consecutive_rounds_ = 0; + } + } else { + // use FEC and RTX + setRecoveryParameters(true, true, fec_to_ask); + fec_consecutive_rounds_++; + } + + // everytime that we anable FEC we keep also RTX on. in this way the first + // losses that are not covered by FEC are recovered using RTX. after 5 sec we + // disable fec + if (fec_consecutive_rounds_ >= ((MILLI_IN_A_SEC / ROUND_LEN) * 5)) { + // turn off RTX + setRtxFec(false); + } +} + +void RecoveryStrategyLowRate::onNewRound(bool in_sync) { + if (!recovery_on_) { + // disable fec so that no extra packet will be sent + // for rtx we check if recovery is on in newPacketLoss + setRtxFec(true, false); + indexer_->setNFec(0); + return; + } + + // XXX since this strategy will be used only for flow at low rate we do not + // consider congestion events like in other strategies + + selectRecoveryStrategy(in_sync); +} + +void RecoveryStrategyLowRate::newPacketLoss(uint32_t seq) { + if (rtx_on_ && recovery_on_) { + addNewRtx(seq, false); + } else { + if (!state_->isPending(seq) && !indexer_->isFec(seq)) { + addNewRtx(seq, true); + } else { + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + } + } +} + +void RecoveryStrategyLowRate::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_low_rate.h b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h new file mode 100644 index 000000000..f0c7bd0d5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +#include <vector> + +namespace transport { + +namespace protocol { + +namespace rtc { + +struct thresholds_t { + uint32_t rtt; + uint32_t loss_rtx_to_fec; // loss rate used to move from rtx to fec + uint32_t loss_fec_to_rtx; // loss rate used to move from fec to rtx +}; + +class RecoveryStrategyLowRate : public RecoveryStrategy { + public: + RecoveryStrategyLowRate(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyLowRate(RecoveryStrategy &&rs); + + ~RecoveryStrategyLowRate(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); + + private: + void initSwitchVector(); + void setRecoveryParameters(bool use_rtx, bool use_fec, uint32_t fec_to_ask); + void selectRecoveryStrategy(bool in_sync); + + uint32_t fec_consecutive_rounds_; + uint32_t rtx_allowed_consecutive_rounds_; + + // this table contains the thresholds that indicates when to switch from RTX + // to FEC and viceversa. values in the vector are detected with a set of + // experiments. the vector is used in the following way: if rtt and loss rate + // are less than one of the values in the in the vector, losses are + // recovered using RTX. otherwive losses are recovered using FEC. as for FEC + // only and delay based strategy, the swith from RTX to FEC is smooth, + // meaning that FEC and RTX are used together for some rounds + std::vector<thresholds_t> switch_vector; +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc new file mode 100644 index 000000000..499e978f1 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_recovery_off.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, false, false, + external_callback) {} + +RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(false, false); +} + +RecoveryStrategyRecoveryOff::~RecoveryStrategyRecoveryOff() {} + +void RecoveryStrategyRecoveryOff::onNewRound(bool in_sync) { + // nothing to do + return; +} + +void RecoveryStrategyRecoveryOff::newPacketLoss(uint32_t seq) { + // here we only keep track of the lost packets to avoid to + // count them multple times in the counters. for this we + // use the recover_with_fec_ set + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); +} + +void RecoveryStrategyRecoveryOff::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h new file mode 100644 index 000000000..98cd1e6a5 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyRecoveryOff : public RecoveryStrategy { + public: + RecoveryStrategyRecoveryOff(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs); + + ~RecoveryStrategyRecoveryOff(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc new file mode 100644 index 000000000..c1ae9b53d --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <protocols/rtc/rtc_consts.h> +#include <protocols/rtc/rtc_rs_rtx_only.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly( + Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service, + interface::StrategyCallback *external_callback) + : RecoveryStrategy(indexer, std::move(callback), io_service, true, false, + external_callback) {} + +RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly(RecoveryStrategy &&rs) + : RecoveryStrategy(std::move(rs)) { + setRtxFec(true, false); +} + +RecoveryStrategyRtxOnly::~RecoveryStrategyRtxOnly() {} + +void RecoveryStrategyRtxOnly::onNewRound(bool in_sync) { + // nothing to do + return; +} + +void RecoveryStrategyRtxOnly::newPacketLoss(uint32_t seq) { + if (!recovery_on_) { + recover_with_fec_.insert(seq); + state_->onPossibleLossWithNoRtx(seq); + return; + } + addNewRtx(seq, false); +} + +void RecoveryStrategyRtxOnly::receivedPacket(uint32_t seq) { + removePacketState(seq); +} + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h new file mode 100644 index 000000000..7ae909454 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include <protocols/rtc/rtc_recovery_strategy.h> + +namespace transport { + +namespace protocol { + +namespace rtc { + +class RecoveryStrategyRtxOnly : public RecoveryStrategy { + public: + RecoveryStrategyRtxOnly(Indexer *indexer, SendRtxCallback &&callback, + asio::io_service &io_service, + interface::StrategyCallback *external_callback); + + RecoveryStrategyRtxOnly(RecoveryStrategy &&rs); + + ~RecoveryStrategyRtxOnly(); + + void onNewRound(bool in_sync); + void newPacketLoss(uint32_t seq); + void receivedPacket(uint32_t seq); +}; + +} // end namespace rtc + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_state.cc b/libtransport/src/protocols/rtc/rtc_state.cc index c99205a26..6a21531f8 100644 --- a/libtransport/src/protocols/rtc/rtc_state.cc +++ b/libtransport/src/protocols/rtc/rtc_state.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -24,15 +24,15 @@ namespace protocol { namespace rtc { RTCState::RTCState(Indexer *indexer, - ProbeHandler::SendProbeCallback &&rtt_probes_callback, + ProbeHandler::SendProbeCallback &&probe_callback, DiscoveredRttCallback &&discovered_rtt_callback, asio::io_service &io_service) - : indexer_(indexer), - rtt_probes_(std::make_shared<ProbeHandler>(std::move(rtt_probes_callback), - io_service)), + : loss_history_(10), // log 10sec history + indexer_(indexer), + probe_handler_(std::make_shared<ProbeHandler>(std::move(probe_callback), + io_service)), discovered_rtt_callback_(std::move(discovered_rtt_callback)) { init_rtt_timer_ = std::make_unique<asio::steady_timer>(io_service); - initParams(); } RTCState::~RTCState() {} @@ -55,9 +55,19 @@ void RTCState::initParams() { highest_seq_received_in_order_ = 0; last_seq_nacked_ = 0; loss_rate_ = 0.0; - avg_loss_rate_ = 0.0; + avg_loss_rate_ = -1.0; max_loss_rate_ = 0.0; last_round_loss_rate_ = 0.0; + + // loss rate per sec + lost_per_sec_ = 0; + total_expected_packets_ = 0; + per_sec_loss_rate_ = 0.0; + + // residual losses counters + expected_packets_ = 0; + packets_sent_to_app_ = 0; + rounds_from_last_compute_ = 0; residual_loss_rate_ = 0.0; // fec counters @@ -66,9 +76,13 @@ void RTCState::initParams() { // bw counters received_bytes_ = 0; + received_fec_bytes_ = 0; + recovered_bytes_with_fec_ = 0; + avg_packet_size_ = INIT_PACKET_SIZE; production_rate_ = 0.0; received_rate_ = 0.0; + fec_recovered_rate_ = 0.0; // nack counter nack_on_last_round_ = false; @@ -95,31 +109,30 @@ void RTCState::initParams() { path_table_.clear(); main_path_ = nullptr; - // packet received - received_or_lost_packets_.clear(); + // packet cache (not pending anymore) + packet_cache_.clear(); // pending interests pending_interests_.clear(); - // skipped interest + // used to keep track of the skipped interest last_interest_sent_ = 0; - skipped_interests_.clear(); // init rtt first_interest_sent_time_ = ~0; first_interest_sent_seq_ = 0; + // start probing the producer init_rtt_ = false; - rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); - rtt_probes_->sendProbes(); + probe_handler_->setSuffixRange(MIN_INIT_PROBE_SEQ, MAX_INIT_PROBE_SEQ); + probe_handler_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); + probe_handler_->sendProbes(); setInitRttTimer(INIT_RTT_PROBE_RESTART); } // packet events void RTCState::onSendNewInterest(const core::Name *interest_name) { - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint32_t seq = interest_name->getSuffix(); pending_interests_.insert(std::pair<uint32_t, uint64_t>(seq, now)); @@ -137,11 +150,12 @@ void RTCState::onSendNewInterest(const core::Name *interest_name) { } // TODO what happen in case of jumps? - // look for skipped interests - skipped_interests_.erase(seq); // remove seq if it is there + eraseFromPacketCache( + seq); // if we send this interest we don't know its state for (uint32_t i = last_interest_sent_ + 1; i < seq; i++) { if (indexer_->isFec(i)) { - skipped_interests_.insert(i); + // only fec packets can be skipped + addToPacketCache(i, PacketState::SKIPPED); } } @@ -155,6 +169,7 @@ void RTCState::onTimeout(uint32_t seq, bool lost) { auto it = pending_interests_.find(seq); if (it != pending_interests_.end()) { pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; } received_timeouts_++; @@ -162,11 +177,12 @@ void RTCState::onTimeout(uint32_t seq, bool lost) { } void RTCState::onLossDetected(uint32_t seq) { - if (!indexer_->isFec(seq)) { - packets_lost_++; - } else if (skipped_interests_.find(seq) == skipped_interests_.end() && - seq >= first_interest_sent_seq_) { + PacketState state = getPacketState(seq); + + // if the packet is already marked with a state, do nothing + if (state == PacketState::UNKNOWN) { packets_lost_++; + addToPacketCache(seq, PacketState::LOST); } } @@ -178,30 +194,40 @@ void RTCState::onRetransmission(uint32_t seq) { auto it = pending_interests_.find(seq); if (it != pending_interests_.end()) { pending_interests_.erase(it); -#if 0 - packets_lost_++; -#endif + if (indexer_->isFec(seq)) pending_fec_pkt_--; } sent_rtx_++; sent_rtx_last_round_++; } +void RTCState::onPossibleLossWithNoRtx(uint32_t seq) { + // if fec is on or rtx is disable we don't need to do anything to recover a + // packet. however in both cases we need to remove possible missing packets + // from the window of pendinig interest in order to free space without wating + // for the timeout. + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; + } +} + void RTCState::onDataPacketReceived(const core::ContentObject &content_object, bool compute_stats) { uint32_t seq = content_object.getName().getSuffix(); + if (compute_stats) { updatePathStats(content_object, false); received_data_last_round_++; } received_data_++; + packets_sent_to_app_++; - struct data_packet_t *data_pkt = - (struct data_packet_t *)content_object.getPayload()->data(); - uint64_t production_time = data_pkt->getTimestamp(); - if (last_prod_update_ < production_time) { - last_prod_update_ = production_time; - uint32_t production_rate = data_pkt->getProductionRate(); - production_rate_ = (double)production_rate; + core::ParamsRTC params = RTCState::getDataParams(content_object); + + if (last_prod_update_ < params.timestamp) { + last_prod_update_ = params.timestamp; + production_rate_ = (double)params.prod_rate; } updatePacketSize(content_object); @@ -219,9 +245,18 @@ void RTCState::onDataPacketReceived(const core::ContentObject &content_object, void RTCState::onFecPacketReceived(const core::ContentObject &content_object) { uint32_t seq = content_object.getName().getSuffix(); - updateReceivedBytes(content_object); + // updateReceivedBytes(content_object); + received_fec_bytes_ += + (uint32_t)(content_object.headerSize() + content_object.payloadSize()); + + if (seq > highest_seq_received_) highest_seq_received_ = seq; + + PacketState state = getPacketState(seq); + if (state != PacketState::LOST) { + // increase only for not lost packets + received_fec_pkt_++; + } addRecvOrLost(seq, PacketState::RECEIVED); - received_fec_pkt_++; // the producer is responding // it is generating valid data packets so we consider it active producer_is_active_ = true; @@ -233,7 +268,7 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack, struct nack_packet_t *nack_pkt = (struct nack_packet_t *)nack.getPayload()->data(); uint64_t production_time = nack_pkt->getTimestamp(); - uint32_t production_seq = nack_pkt->getProductionSegement(); + uint32_t production_seq = nack_pkt->getProductionSegment(); uint32_t production_rate = nack_pkt->getProductionRate(); if (TRANSPORT_EXPECT_FALSE(main_path_ == nullptr) || @@ -255,6 +290,7 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack, received_nacks_++; received_nacks_last_round_++; + bool to_delete = false; if (production_seq > seq) { // old nack, seq is lost // update last nacked @@ -266,12 +302,19 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack, // future nack // remove the nack from the pending interest map // (the packet is not received/lost yet) - if (indexer_->isFec(seq)) pending_fec_pkt_--; - pending_interests_.erase(seq); + to_delete = true; } else { // this should be a quite rear event. simply remove the // packet from the pending interest list - pending_interests_.erase(seq); + to_delete = true; + } + + if (to_delete) { + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; + } } // the producer is responding @@ -295,44 +338,58 @@ void RTCState::onPacketLost(uint32_t seq) { } #endif if (!indexer_->isFec(seq)) { - definitely_lost_pkt_++; - DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost"; + PacketState state = getPacketState(seq); + if (state == PacketState::LOST || state == PacketState::UNKNOWN) { + definitely_lost_pkt_++; + DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost"; + } } - addRecvOrLost(seq, PacketState::LOST); + addRecvOrLost(seq, PacketState::DEFINITELY_LOST); } void RTCState::onPacketRecoveredRtx(uint32_t seq) { + packets_sent_to_app_++; + if (seq > highest_seq_received_) highest_seq_received_ = seq; losses_recovered_++; addRecvOrLost(seq, PacketState::RECEIVED); } -void RTCState::onPacketRecoveredFec(uint32_t seq) { +void RTCState::onFecPacketRecoveredRtx(uint32_t seq) { + // This is the same as onPacketRecoveredRtx, but in this is case the + // pkt is also a FEC pkt, the addRecvOrLost will be called afterwards + if (seq > highest_seq_received_) highest_seq_received_ = seq; + losses_recovered_++; +} + +void RTCState::onPacketRecoveredFec(uint32_t seq, uint32_t size) { losses_recovered_++; + packets_sent_to_app_++; + recovered_bytes_with_fec_ += size; + + if (seq > highest_seq_received_) highest_seq_received_ = seq; + + // adding header to the count + recovered_bytes_with_fec_ += 60; // XXX get header size some where + + if (getPacketState(seq) == PacketState::UNKNOWN) + onLossDetected(seq); // the pkt was lost but didn't account for it yet + addRecvOrLost(seq, PacketState::RECEIVED); } bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { uint32_t seq = probe.getName().getSuffix(); - uint64_t rtt; - - rtt = rtt_probes_->getRtt(seq); + uint64_t rtt; + rtt = probe_handler_->getRtt(seq); if (rtt == 0) return false; // this is not a valid probe - // like for data and nacks update the path stats. Here the RTT is computed - // by the probe handler. Both probes for rtt and bw are good to esimate - // info on the path + // Like for data and nacks update the path stats. Here the RTT is computed + // by the probe handler. Both probes for rtt and bw are good to estimate + // info on the path. uint32_t path_label = probe.getPathLabel(); - auto path_it = path_table_.find(path_label); - // update production rate and last_seq_nacked like in case of a nack - struct nack_packet_t *probe_pkt = - (struct nack_packet_t *)probe.getPayload()->data(); - uint64_t sender_timestamp = probe_pkt->getTimestamp(); - uint32_t production_seq = probe_pkt->getProductionSegement(); - uint32_t production_rate = probe_pkt->getProductionRate(); - if (path_it == path_table_.end()) { // found a new path std::shared_ptr<RTCDataPath> newPath = @@ -344,26 +401,26 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { auto path = path_it->second; - path->insertRttSample(rtt); + path->insertRttSample(utils::SteadyTime::Milliseconds(rtt), true); path->receivedNack(); - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); + + core::ParamsRTC params = RTCState::getProbeParams(probe); - int64_t OWD = now - sender_timestamp; + int64_t OWD = now - params.timestamp; path->insertOwdSample(OWD); - if (last_prod_update_ < sender_timestamp) { - last_production_seq_ = production_seq; - last_prod_update_ = sender_timestamp; - production_rate_ = (double)production_rate; + if (last_prod_update_ < params.timestamp) { + last_production_seq_ = params.prod_seg; + last_prod_update_ = params.timestamp; + production_rate_ = (double)params.prod_rate; } // the producer is responding // we consider it active only if the production rate is not 0 // or the production sequence numner is not 1 - if (production_rate_ != 0 || production_seq != 1) { + if (production_rate_ != 0 || params.prod_seg != 1) { producer_is_active_ = true; } @@ -375,7 +432,7 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { if (!init_rtt_ && received_probes_ <= INIT_RTT_PROBES) { if (received_probes_ == 1) { // we got the first probe, wait at most INIT_RTT_PROBE_WAIT sec for the - // others + // others. main_path_ = path; setInitRttTimer(INIT_RTT_PROBE_WAIT); } @@ -393,11 +450,21 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) { return true; } -void RTCState::onNewRound(double round_len, bool in_sync) { - // XXX - // here we take into account only the single path case so we assume that we - // don't use two paths in parellel for this single flow +void RTCState::onJumpForward(uint32_t next_seq) { + for (uint32_t seq = highest_seq_received_in_order_ + 1; seq < next_seq; + seq++) { + auto it = pending_interests_.find(seq); + PacketState packet_state = getPacketState(seq); + if (it == pending_interests_.end() && + packet_state != PacketState::RECEIVED && + packet_state != PacketState::DEFINITELY_LOST) { + onLossDetected(seq); + onPacketLost(seq); + } + } +} +void RTCState::onNewRound(double round_len, bool in_sync) { if (path_table_.empty()) return; double bytes_per_sec = @@ -407,7 +474,24 @@ void RTCState::onNewRound(double round_len, bool in_sync) { else received_rate_ = (received_rate_ * MOVING_AVG_ALPHA) + ((1 - MOVING_AVG_ALPHA) * bytes_per_sec); + double fec_bytes_per_sec = + ((double)received_fec_bytes_ * (MILLI_IN_A_SEC / round_len)); + + if (fec_received_rate_ == 0) + fec_received_rate_ = fec_bytes_per_sec; + else + fec_received_rate_ = (fec_received_rate_ * 0.8) + (0.2 * fec_bytes_per_sec); + + double fec_recovered_bytes_per_sec = + ((double)recovered_bytes_with_fec_ * (MILLI_IN_A_SEC / round_len)); + if (fec_recovered_rate_ == 0) + fec_recovered_rate_ = fec_recovered_bytes_per_sec; + else + fec_recovered_rate_ = + (fec_recovered_rate_ * 0.8) + (0.2 * fec_recovered_bytes_per_sec); + +#if 0 // search for an active path. There should be only one active path (meaning a // path that leads to the producer socket -no cache- and from which we are // currently getting data packets) at any time. However it may happen that @@ -428,9 +512,36 @@ void RTCState::onNewRound(double round_len, bool in_sync) { } } } +#endif + + // search for an active path. Is it possible to have multiple path that are + // used at the same time. We use as reference path the one from where we gets + // more packets. This means that the path should have better lantecy or less + // channel losses + + uint32_t last_round_packets = 0; + std::shared_ptr<RTCDataPath> old_main_path = main_path_; + main_path_ = nullptr; + + for (auto it = path_table_.begin(); it != path_table_.end(); it++) { + if (it->second->isActive()) { + uint32_t pkt = it->second->getPacketsLastRound(); + if (pkt > last_round_packets) { + last_round_packets = pkt; + main_path_ = it->second; + } + } + it->second->roundEnd(); + } - // if (in_sync) updateLossRate(); - updateLossRate(); + if (main_path_ == nullptr) main_path_ = old_main_path; + + // in case we get a new main path we reset the stats of the old one. this is + // beacuse, in case we need to switch back we don't what to take decisions on + // old stats that may be outdated. + if (main_path_ != old_main_path) old_main_path->clearRtt(); + + updateLossRate(in_sync); // handle nacks if (!nack_on_last_round_ && received_bytes_ > 0) { @@ -460,6 +571,8 @@ void RTCState::onNewRound(double round_len, bool in_sync) { // reset counters received_bytes_ = 0; + received_fec_bytes_ = 0; + recovered_bytes_with_fec_ = 0; packets_lost_ = 0; definitely_lost_pkt_ = 0; losses_recovered_ = 0; @@ -516,20 +629,16 @@ void RTCState::updatePathStats(const core::ContentObject &content_object, // it means that we are processing an interest // that is not pending - uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); uint64_t RTT = now - interest_sent_time; - path->insertRttSample(RTT); + path->insertRttSample(utils::SteadyTime::Milliseconds(RTT), false); // compute OWD (the first part of the nack and data packet header are the // same, so we cast to data data packet) - struct data_packet_t *packet = - (struct data_packet_t *)content_object.getPayload()->data(); - uint64_t sender_timestamp = packet->getTimestamp(); - int64_t OWD = now - sender_timestamp; + core::ParamsRTC params = RTCState::getDataParams(content_object); + int64_t OWD = now - params.timestamp; path->insertOwdSample(OWD); // compute IAT or set path to producer @@ -543,59 +652,106 @@ void RTCState::updatePathStats(const core::ContentObject &content_object, } } -void RTCState::updateLossRate() { +void RTCState::updateLossRate(bool in_sync) { last_round_loss_rate_ = loss_rate_; loss_rate_ = 0.0; - residual_loss_rate_ = 0.0; uint32_t number_theorically_received_packets_ = highest_seq_received_ - first_seq_in_round_; - // in this case no new packet was recevied after the previuos round, avoid - // division by 0 - if (number_theorically_received_packets_ == 0) return; - // XXX this may be quite inefficient if the rate is high // maybe is better to iterate over the set? - for (uint32_t i = first_seq_in_round_; i < highest_seq_received_; i++) { - auto it = skipped_interests_.find(i); - if (it != skipped_interests_.end()) { + + uint32_t fec_packets = 0; + for (uint32_t i = (first_seq_in_round_ + 1); i < highest_seq_received_; i++) { + PacketState state = getPacketState(i); + if (state == PacketState::SKIPPED) { if (number_theorically_received_packets_ > 0) number_theorically_received_packets_--; - skipped_interests_.erase(it); } + if (indexer_->isFec(i)) fec_packets++; } + if (indexer_->isFec(highest_seq_received_)) fec_packets++; - loss_rate_ = (double)((double)(packets_lost_) / - (double)number_theorically_received_packets_); + // in this case no new packet was received after the previous round, avoid + // division by 0 + if (number_theorically_received_packets_ == 0 && packets_lost_ == 0) return; - if (rounds_ % 15 == 0) max_loss_rate_ = 0; // reset every 3 sec - if (loss_rate_ > max_loss_rate_) max_loss_rate_ = loss_rate_; + if (number_theorically_received_packets_ != 0) + loss_rate_ = (double)((double)(packets_lost_) / + (double)number_theorically_received_packets_); + else + // we didn't receive anything except NACKs that triggered losses + loss_rate_ = 1.0; - if (avg_loss_rate_ == 0) + if (avg_loss_rate_ == -1.0) avg_loss_rate_ = loss_rate_; else avg_loss_rate_ = avg_loss_rate_ * MOVING_AVG_ALPHA + loss_rate_ * (1 - MOVING_AVG_ALPHA); - residual_loss_rate_ = (double)((double)(packets_lost_ - losses_recovered_) / - (double)number_theorically_received_packets_); + // update counters for loss rate per second + total_expected_packets_ += number_theorically_received_packets_; + lost_per_sec_ += packets_lost_; + + if (in_sync) { + // update counters for residual losses + // fec packets are not sent to the app so we don't want to count them here + expected_packets_ += + ((highest_seq_received_ - first_seq_in_round_) - fec_packets); + } else { + packets_sent_to_app_ = 0; + } + + if (rounds_from_last_compute_ >= (MILLI_IN_A_SEC / ROUND_LEN)) { + // compute loss rate per second + if (lost_per_sec_ > total_expected_packets_) + lost_per_sec_ = total_expected_packets_; + + if (total_expected_packets_ == 0) + per_sec_loss_rate_ = 0; + else + per_sec_loss_rate_ = + (double)((double)(lost_per_sec_) / (double)total_expected_packets_); + + loss_history_.pushBack(per_sec_loss_rate_); + max_loss_rate_ = getMaxLoss(); + + if (in_sync && expected_packets_ != 0) { + // compute residual loss rate + if (packets_sent_to_app_ > expected_packets_) { + // this may happen if we get packet from the prev bin that get recovered + // on the current one + packets_sent_to_app_ = expected_packets_; + } + + residual_loss_rate_ = + 1.0 - ((double)packets_sent_to_app_ / (double)expected_packets_); + if (residual_loss_rate_ < 0.0) residual_loss_rate_ = 0.0; + } + + lost_per_sec_ = 0; + total_expected_packets_ = 0; + expected_packets_ = 0; + packets_sent_to_app_ = 0; + rounds_from_last_compute_ = 0; + } + + rounds_from_last_compute_++; +} - if (residual_loss_rate_ < 0) residual_loss_rate_ = 0; +void RTCState::dataToBeReceived(uint32_t seq) { + addToPacketCache(seq, PacketState::TO_BE_RECEIVED); } void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { - if (indexer_->isFec(seq)) { - pending_fec_pkt_--; + auto it = pending_interests_.find(seq); + if (it != pending_interests_.end()) { + pending_interests_.erase(it); + if (indexer_->isFec(seq)) pending_fec_pkt_--; } - pending_interests_.erase(seq); - if (received_or_lost_packets_.size() >= MAX_CACHED_PACKETS) { - received_or_lost_packets_.erase(received_or_lost_packets_.begin()); - } - // notice that it may happen that a packet that we consider lost arrives after - // some time, in this case we simply overwrite the packet state. - received_or_lost_packets_[seq] = state; + addToPacketCache(seq, state); // keep track of the last packet received/lost // without holes. @@ -608,16 +764,25 @@ void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { } else if (seq <= highest_seq_received_in_order_) { // here we do nothing } else if (seq > highest_seq_received_in_order_) { - // 1) there is a gap in the sequence so we do not update largest_in_seq_ - // 2) all the packets from largest_in_seq_ to seq are in - // received_or_lost_packets_ an we upate largest_in_seq_ - // or are FEC packets + // 1) there is a gap in the sequence so we do not update + // highest_seq_received_in_order_ + // 2) all the packets from highest_seq_received_in_order_ to seq are + // received or lost or are fec packetis. In this case we increase + // highest_seq_received_in_order_ until we find an hole in the sequence for (uint32_t i = highest_seq_received_in_order_ + 1; i <= seq; i++) { - if (received_or_lost_packets_.find(i) == - received_or_lost_packets_.end() && - !indexer_->isFec(i)) { - break; + PacketState state = getPacketState(i); + if ((state == PacketState::UNKNOWN || state == PacketState::LOST)) { + if (indexer_->isFec(i)) { + // this is a fec packet and we don't care to receive it + // however we may need to increse the number or lost packets + // XXX: in case we want to use rtx to recover fec packets, + // this may prevent to detect a packet loss and no rtx will be sent + onLossDetected(i); + } else { + // this is a data packet and we need to get it + break; + } } // this packet is in order so we can update the // highest_seq_received_in_order_ @@ -629,9 +794,14 @@ void RTCState::addRecvOrLost(uint32_t seq, PacketState state) { void RTCState::setInitRttTimer(uint32_t wait) { init_rtt_timer_->cancel(); init_rtt_timer_->expires_from_now(std::chrono::milliseconds(wait)); - init_rtt_timer_->async_wait([this](std::error_code ec) { + + std::weak_ptr<RTCState> self = shared_from_this(); + init_rtt_timer_->async_wait([self](const std::error_code &ec) { if (ec) return; - checkInitRttTimer(); + + if (auto ptr = self.lock()) { + ptr->checkInitRttTimer(); + } }); } @@ -639,19 +809,25 @@ void RTCState::checkInitRttTimer() { if (received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV) { // we didn't received enough probes, restart received_probes_ = 0; - rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); - rtt_probes_->sendProbes(); + probe_handler_->setSuffixRange(MIN_INIT_PROBE_SEQ, MAX_INIT_PROBE_SEQ); + probe_handler_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES); + probe_handler_->sendProbes(); setInitRttTimer(INIT_RTT_PROBE_RESTART); return; } + init_rtt_ = true; main_path_->roundEnd(); - rtt_probes_->setProbes(RTT_PROBE_INTERVAL, 0); - rtt_probes_->sendProbes(); + loss_history_.pushBack(probe_handler_->getProbeLossRate()); + max_loss_rate_ = getMaxLoss(); + + probe_handler_->setSuffixRange(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ); + probe_handler_->setProbes(RTT_PROBE_INTERVAL, 0); + probe_handler_->sendProbes(); // init last_seq_nacked_. skip packets that may come from the cache double prod_rate = getProducerRate(); - double rtt = (double)getRTT() / MILLI_IN_A_SEC; + double rtt = (double)getMinRTT() / MILLI_IN_A_SEC; double packet_size = getAveragePacketSize(); uint32_t pkt_in_rtt_ = std::floor(((prod_rate / packet_size) * rtt) * 0.8); last_seq_nacked_ = last_production_seq_ + pkt_in_rtt_; @@ -659,6 +835,68 @@ void RTCState::checkInitRttTimer() { discovered_rtt_callback_(); } +double RTCState::getMaxLoss() { + if (loss_history_.size() != 0) return loss_history_.begin(); + return 0; +} + +core::ParamsRTC RTCState::getProbeParams(const core::ContentObject &probe) { + uint32_t seq = probe.getName().getSuffix(); + core::ParamsRTC params; + + switch (ProbeHandler::getProbeType(seq)) { + case ProbeType::INIT: { + core::ContentObjectManifest manifest( + const_cast<core::ContentObject &>(probe)); + manifest.decode(); + params = manifest.getParamsRTC(); + break; + } + case ProbeType::RTT: { + struct nack_packet_t *probe_pkt = + (struct nack_packet_t *)probe.getPayload()->data(); + params = core::ParamsRTC{ + .timestamp = probe_pkt->getTimestamp(), + .prod_rate = probe_pkt->getProductionRate(), + .prod_seg = probe_pkt->getProductionSegment(), + }; + break; + } + default: + break; + } + + return params; +} + +core::ParamsRTC RTCState::getDataParams(const core::ContentObject &data) { + core::ParamsRTC params; + + switch (data.getPayloadType()) { + case core::PayloadType::DATA: { + struct data_packet_t *data_pkt = + (struct data_packet_t *)data.getPayload()->data(); + params = core::ParamsRTC{ + .timestamp = data_pkt->getTimestamp(), + .prod_rate = data_pkt->getProductionRate(), + .prod_seg = data.getName().getSuffix(), + }; + break; + } + case core::PayloadType::MANIFEST: { + core::ContentObjectManifest manifest( + const_cast<core::ContentObject &>(data)); + manifest.decode(); + params = manifest.getParamsRTC(); + break; + } + default: + break; + } + + return params; +} + } // namespace rtc } // namespace protocol diff --git a/libtransport/src/protocols/rtc/rtc_state.h b/libtransport/src/protocols/rtc/rtc_state.h index 729ba7a1b..8bf48ccc2 100644 --- a/libtransport/src/protocols/rtc/rtc_state.h +++ b/libtransport/src/protocols/rtc/rtc_state.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -14,13 +14,16 @@ */ #pragma once +#include <core/facade.h> #include <hicn/transport/config.h> #include <hicn/transport/core/asio_wrapper.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/name.h> +#include <hicn/transport/utils/rtc_quality_score.h> #include <protocols/indexer.h> #include <protocols/rtc/probe_handler.h> #include <protocols/rtc/rtc_data_path.h> +#include <utils/max_filter.h> #include <map> #include <set> @@ -31,25 +34,50 @@ namespace protocol { namespace rtc { -enum class PacketState : uint8_t { RECEIVED, LOST, UNKNOWN }; +// packet state +// RECEIVED: the packet was already received +// LOST: the packet is marked as lost but can be recovered +// DEFINITELY_LOST: the packet is lost and cannot be recovered +// TO_BE_RECEIVED: when a packet is received is sent to the FEC decoder. the fec +// decoder may decide to send the packet directly to the app. to avoid +// duplicated the packet is marked with this state +// SKIPPED: an interest that was not sent, only for FEC packets +// UNKNOWN: unknown state +enum class PacketState : uint8_t { + RECEIVED, + TO_BE_RECEIVED, + LOST, + DEFINITELY_LOST, + SKIPPED, + UNKNOWN +}; + +class RTCState : public std::enable_shared_from_this<RTCState> { + using PendingInterestsMap = std::map<uint32_t, uint64_t>; + + private: + const double MAX_CACHED_PACKETS = 8192; // XXX this value may be too small + // for high rate apps -class RTCState : std::enable_shared_from_this<RTCState> { public: using DiscoveredRttCallback = std::function<void()>; public: - RTCState(Indexer *indexer, - ProbeHandler::SendProbeCallback &&rtt_probes_callback, + RTCState(Indexer *indexer, ProbeHandler::SendProbeCallback &&probe_callback, DiscoveredRttCallback &&discovered_rtt_callback, asio::io_service &io_service); ~RTCState(); + // initialization + void initParams(); + // packet events void onSendNewInterest(const core::Name *interest_name); void onTimeout(uint32_t seq, bool lost); void onLossDetected(uint32_t seq); void onRetransmission(uint32_t seq); + void onPossibleLossWithNoRtx(uint32_t seq); void onDataPacketReceived(const core::ContentObject &content_object, bool compute_stats); void onFecPacketReceived(const core::ContentObject &content_object); @@ -57,8 +85,10 @@ class RTCState : std::enable_shared_from_this<RTCState> { bool compute_stats); void onPacketLost(uint32_t seq); void onPacketRecoveredRtx(uint32_t seq); - void onPacketRecoveredFec(uint32_t seq); + void onFecPacketRecoveredRtx(uint32_t seq); + void onPacketRecoveredFec(uint32_t seq, uint32_t size); bool onProbePacketReceived(const core::ContentObject &probe); + void onJumpForward(uint32_t next_seq); // protocol state void onNewRound(double round_len, bool in_sync); @@ -72,10 +102,21 @@ class RTCState : std::enable_shared_from_this<RTCState> { // delay metrics bool isRttDiscovered() const { return init_rtt_; } - uint64_t getRTT() const { + uint64_t getMinRTT() const { if (mainPathIsValid()) return main_path_->getMinRtt(); return 0; } + + uint64_t getAvgRTT() const { + if (mainPathIsValid()) return main_path_->getAvgRtt(); + return 0; + } + + uint64_t getMaxRTT() const { + if (mainPathIsValid()) return main_path_->getMaxRtt(); + return 0; + } + void resetRttStats() { if (mainPathIsValid()) main_path_->clearRtt(); } @@ -98,6 +139,7 @@ class RTCState : std::enable_shared_from_this<RTCState> { uint64_t getInterestSentTime(uint32_t seq) { auto it = pending_interests_.find(seq); if (it != pending_interests_.end()) return it->second; + return 0; } @@ -110,14 +152,15 @@ class RTCState : std::enable_shared_from_this<RTCState> { return pending_interests_.size(); } - PacketState isReceivedOrLost(uint32_t seq) { - auto it = received_or_lost_packets_.find(seq); - if (it != received_or_lost_packets_.end()) return it->second; + PacketState getPacketState(uint32_t seq) { + auto it = packet_cache_.find(seq); + if (it != packet_cache_.end()) return it->second; return PacketState::UNKNOWN; } // loss rate - double getLossRate() const { return loss_rate_; } + double getPerRoundLossRate() const { return loss_rate_; } + double getPerSecondLossRate() const { return per_sec_loss_rate_; } double getAvgLossRate() const { return avg_loss_rate_; } double getMaxLossRate() const { return max_loss_rate_; } double getLastRoundLossRate() const { return last_round_loss_rate_; } @@ -134,15 +177,22 @@ class RTCState : std::enable_shared_from_this<RTCState> { return highest_seq_received_in_order_; } + double getMaxLoss(); + // fec packets uint32_t getReceivedFecPackets() const { return received_fec_pkt_; } uint32_t getPendingFecPackets() const { return pending_fec_pkt_; } // generic stats uint32_t getReceivedBytesInRound() const { return received_bytes_; } + uint32_t getReceivedFecBytesInRound() const { return received_fec_bytes_; } + uint32_t getRecoveredFecBytesInRound() const { + return recovered_bytes_with_fec_; + } uint32_t getReceivedNacksInRound() const { return received_nacks_last_round_; } + uint32_t getReceivedDataInRound() const { return received_data_last_round_; } uint32_t getSentInterestInRound() const { return sent_interests_last_round_; } uint32_t getSentRtxInRound() const { return sent_rtx_last_round_; } @@ -150,6 +200,9 @@ class RTCState : std::enable_shared_from_this<RTCState> { double getAvailableBw() const { return 0.0; }; // TODO double getProducerRate() const { return production_rate_; } double getReceivedRate() const { return received_rate_; } + double getReceivedFecRate() const { return fec_received_rate_; } + double getRecoveredFecRate() const { return fec_recovered_rate_; } + double getAveragePacketSize() const { return avg_packet_size_; } // nacks @@ -162,22 +215,46 @@ class RTCState : std::enable_shared_from_this<RTCState> { // packets from cache double getPacketFromCacheRatio() const { return data_from_cache_rate_; } - std::map<uint32_t, uint64_t>::iterator getPendingInterestsMapBegin() { + PendingInterestsMap::iterator getPendingInterestsMapBegin() { return pending_interests_.begin(); } - std::map<uint32_t, uint64_t>::iterator getPendingInterestsMapEnd() { + PendingInterestsMap::iterator getPendingInterestsMapEnd() { return pending_interests_.end(); } + // quality + uint8_t getQualityScore() { + uint8_t qs = quality_score_.getQualityScore( + getMaxRTT(), std::round(getResidualLossRate() * 100)); + return qs; + } + + // We received a data pkt that will be set to RECEIVED, but first we have to + // go through FEC. We do not want to consider this pkt as recovered, thus we + // set it as TO_BE_RECEIVED. + void dataToBeReceived(uint32_t seq); + + // Extract RTC parameters from probes (init or RTT probes) and data packets. + static core::ParamsRTC getProbeParams(const core::ContentObject &probe); + static core::ParamsRTC getDataParams(const core::ContentObject &data); + private: - void initParams(); + void addToPacketCache(uint32_t seq, PacketState state) { + // this function adds or updates the current state + if (packet_cache_.size() >= MAX_CACHED_PACKETS) { + packet_cache_.erase(packet_cache_.begin()); + } + packet_cache_[seq] = state; + } + + void eraseFromPacketCache(uint32_t seq) { packet_cache_.erase(seq); } // update stats void updateState(); void updateReceivedBytes(const core::ContentObject &content_object); void updatePacketSize(const core::ContentObject &content_object); void updatePathStats(const core::ContentObject &content_object, bool is_nack); - void updateLossRate(); + void updateLossRate(bool in_sycn); void addRecvOrLost(uint32_t seq, PacketState state); @@ -211,22 +288,39 @@ class RTCState : std::enable_shared_from_this<RTCState> { double avg_loss_rate_; double max_loss_rate_; double last_round_loss_rate_; + utils::MaxFilter<double> loss_history_; + + // per second loss rate + uint32_t lost_per_sec_; + uint32_t total_expected_packets_; + double per_sec_loss_rate_; + + // conunters for residual losses + // residual losses are computed every second and are used + // as feedback to the upper levels (e.g application) + uint32_t expected_packets_; + uint32_t packets_sent_to_app_; + uint32_t rounds_from_last_compute_; double residual_loss_rate_; // bw counters uint32_t received_bytes_; + uint32_t received_fec_bytes_; + uint32_t recovered_bytes_with_fec_; double avg_packet_size_; - double production_rate_; // rate communicated by the producer using nacks - double received_rate_; // rate recevied by the consumer + double production_rate_; // rate communicated by the producer using nacks + double received_rate_; // rate recevied by the consumer (only data) + double fec_received_rate_; // fec rate recevied by the consumer + double fec_recovered_rate_; // rate recovered using fec - // nack counter + // nack counters // the bool takes tracks only about the valid nacks (no rtx) and it is used to // switch between the states. Instead received_nacks_last_round_ logs all the // nacks for statistics bool nack_on_last_round_; uint32_t received_nacks_last_round_; - // packets counter + // packets counters uint32_t received_packets_last_round_; uint32_t received_data_last_round_; uint32_t received_data_from_cache_; @@ -234,11 +328,11 @@ class RTCState : std::enable_shared_from_this<RTCState> { uint32_t sent_interests_last_round_; uint32_t sent_rtx_last_round_; - // fec counter + // fec counters uint32_t received_fec_pkt_; uint32_t pending_fec_pkt_; - // round conunters + // round counters uint32_t rounds_; uint32_t rounds_without_nacks_; uint32_t rounds_without_packets_; @@ -261,23 +355,27 @@ class RTCState : std::enable_shared_from_this<RTCState> { // packet received // cache where to store info about the last MAX_CACHED_PACKETS - std::map<uint32_t, PacketState> received_or_lost_packets_; + // these are packets that are received or lost or definitely lost and are not + // anymore in the pending intetest list + std::map<uint32_t, PacketState> packet_cache_; // pending interests - std::map<uint32_t, uint64_t> pending_interests_; + PendingInterestsMap pending_interests_; // indexer Indexer *indexer_; - // skipped interests + // used to keep track of the skipped interests uint32_t last_interest_sent_; - std::unordered_set<uint32_t> skipped_interests_; // probes - std::shared_ptr<ProbeHandler> rtt_probes_; + std::shared_ptr<ProbeHandler> probe_handler_; bool init_rtt_; std::unique_ptr<asio::steady_timer> init_rtt_timer_; + // quality score + RTCQualityScore quality_score_; + // callbacks DiscoveredRttCallback discovered_rtt_callback_; }; diff --git a/libtransport/src/protocols/rtc/rtc_verifier.cc b/libtransport/src/protocols/rtc/rtc_verifier.cc new file mode 100644 index 000000000..29968dd02 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.cc @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <core/facade.h> +#include <protocols/rtc/rtc_packet.h> +#include <protocols/rtc/rtc_verifier.h> + +namespace transport { +namespace protocol { +namespace rtc { + +RTCVerifier::RTCVerifier(std::shared_ptr<auth::Verifier> verifier, + uint32_t max_unverified_delay) + : verifier_(verifier), max_unverified_delay_(max_unverified_delay) {} + +void RTCVerifier::setState(std::shared_ptr<RTCState> rtc_state) { + rtc_state_ = rtc_state; +} + +void RTCVerifier::setVerifier(std::shared_ptr<auth::Verifier> verifier) { + verifier_ = verifier; +} + +void RTCVerifier::setMaxUnverifiedDelay(uint32_t max_unverified_delay) { + max_unverified_delay_ = max_unverified_delay; +} + +auth::VerificationPolicy RTCVerifier::verify( + core::ContentObject &content_object, bool is_fec) { + uint32_t suffix = content_object.getName().getSuffix(); + core::PayloadType payload_type = content_object.getPayloadType(); + + bool is_probe = ProbeHandler::getProbeType(suffix) != ProbeType::NOT_PROBE; + bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE; + bool is_manifest = !is_probe && !is_nack && !is_fec && + payload_type == core::PayloadType::MANIFEST; + bool is_data = !is_probe && !is_nack && !is_fec && + payload_type == core::PayloadType::DATA; + + if (is_probe) return verifyProbe(content_object); + if (is_nack) return verifyNack(content_object); + if (is_fec) return verifyFec(content_object); + if (is_data) return verifyData(content_object); + if (is_manifest) return verifyManifest(content_object); + + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; +} + +auth::VerificationPolicy RTCVerifier::verifyProbe( + core::ContentObject &content_object) { + switch (ProbeHandler::getProbeType(content_object.getName().getSuffix())) { + case ProbeType::INIT: { + auth::VerificationPolicy policy = verifyManifest(content_object); + if (policy != auth::VerificationPolicy::ACCEPT) { + return policy; + } + return processManifest(content_object); + } + case ProbeType::RTT: + return verifyNack(content_object); + default: + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback( + content_object.getName().getSuffix(), policy); + return policy; + } +} + +auth::VerificationPolicy RTCVerifier::verifyNack( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::verifyFec( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::verifyData( + core::ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + + if (_is_ah(content_object.getFormat())) { + return verifier_->verifyPackets(&content_object); + } + + unverified_bytes_[suffix] = + content_object.headerSize() + content_object.payloadSize(); + unverified_packets_[suffix] = + content_object.computeDigest(manifest_hash_algo_); + + // An alert is raised when too much packets remain unverified + if (getTotalUnverified() > max_unverified_bytes_) { + unverified_bytes_.clear(); + unverified_packets_.clear(); + + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; + } + + return auth::VerificationPolicy::ACCEPT; +} + +auth::VerificationPolicy RTCVerifier::verifyManifest( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::processManifest( + core::ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + + core::ContentObjectManifest manifest(content_object); + manifest.decode(); + + // Update last manifest + if (suffix > last_manifest_) { + last_manifest_ = suffix; + } + + // Extract parameters + manifest_hash_algo_ = manifest.getHashAlgorithm(); + core::ParamsRTC params = manifest.getParamsRTC(); + + if (params.prod_rate > 0) { + max_unverified_bytes_ = static_cast<uint64_t>( + (max_unverified_delay_ / 1000.0) * params.prod_rate); + } + + if (max_unverified_bytes_ == 0 || !rtc_state_) { + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; + } + + // Extract hashes + auth::Verifier::SuffixMap suffix_map = + core::ContentObjectManifest::getSuffixMap(&manifest); + + // Return early if the manifest is empty + if (suffix_map.empty()) { + return auth::VerificationPolicy::ACCEPT; + } + + // Remove lost packets from digest map + manifest_digests_.insert(suffix_map.begin(), suffix_map.end()); + for (auto it = manifest_digests_.begin(); it != manifest_digests_.end();) { + if (rtc_state_->getPacketState(it->first) == PacketState::DEFINITELY_LOST) { + unverified_packets_.erase(it->first); + unverified_bytes_.erase(it->first); + it = manifest_digests_.erase(it); + } else { + ++it; + } + } + + // Verify packets + auth::Verifier::PolicyMap policies = + verifier_->verifyHashes(unverified_packets_, manifest_digests_); + + for (const auto &policy : policies) { + switch (policy.second) { + case auth::VerificationPolicy::ACCEPT: { + manifest_digests_.erase(policy.first); + unverified_packets_.erase(policy.first); + unverified_bytes_.erase(policy.first); + break; + } + case auth::VerificationPolicy::UNKNOWN: + break; + case auth::VerificationPolicy::DROP: + case auth::VerificationPolicy::ABORT: + auth::VerificationPolicy p = policy.second; + verifier_->callVerificationFailedCallback(policy.first, p); + return p; + } + } + + return auth::VerificationPolicy::ACCEPT; +} + +void RTCVerifier::onDataRecoveredFec(uint32_t suffix) { + manifest_digests_.erase(suffix); +} + +void RTCVerifier::onJumpForward(uint32_t next_suffix) { + if (next_suffix <= last_manifest_ + 1) { + return; + } + + // When we jump forward in the suffix sequence, we remove packets that + // probably won't be verified. Those packets have a suffix in the range + // [last_manifest_ + 1, next_suffix[. + for (auto it = unverified_packets_.begin(); + it != unverified_packets_.end();) { + if (it->first > last_manifest_) { + unverified_bytes_.erase(it->first); + it = unverified_packets_.erase(it); + } else { + ++it; + } + } +} + +uint32_t RTCVerifier::getTotalUnverified() const { + uint32_t total = 0; + + for (auto bytes : unverified_bytes_) { + if (bytes.second > UINT32_MAX - total) { + total = UINT32_MAX; + break; + } + total += bytes.second; + } + + return total; +} + +uint32_t RTCVerifier::getMaxUnverified() const { return max_unverified_bytes_; } + +} // end namespace rtc +} // end namespace protocol +} // end namespace transport diff --git a/libtransport/src/protocols/rtc/rtc_verifier.h b/libtransport/src/protocols/rtc/rtc_verifier.h new file mode 100644 index 000000000..596bd8536 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <core/facade.h> +#include <hicn/transport/auth/verifier.h> +#include <hicn/transport/core/content_object.h> +#include <protocols/rtc/rtc_state.h> + +namespace transport { +namespace protocol { +namespace rtc { + +class RTCVerifier { + public: + RTCVerifier(std::shared_ptr<auth::Verifier> verifier, + uint32_t max_unverified_delay); + + virtual ~RTCVerifier() = default; + + void setState(std::shared_ptr<RTCState> rtc_state); + + void setVerifier(std::shared_ptr<auth::Verifier> verifier); + + void setMaxUnverifiedDelay(uint32_t max_unverified_delay); + + void onDataRecoveredFec(uint32_t suffix); + void onJumpForward(uint32_t next_suffix); + + uint32_t getTotalUnverified() const; + uint32_t getMaxUnverified() const; + + auth::VerificationPolicy verify(core::ContentObject &content_object, + bool is_fec = false); + auth::VerificationPolicy verifyProbe(core::ContentObject &content_object); + auth::VerificationPolicy verifyNack(core::ContentObject &content_object); + auth::VerificationPolicy verifyFec(core::ContentObject &content_object); + auth::VerificationPolicy verifyData(core::ContentObject &content_object); + auth::VerificationPolicy verifyManifest(core::ContentObject &content_object); + + auth::VerificationPolicy processManifest(core::ContentObject &content_object); + + protected: + // The RTC state. + std::shared_ptr<RTCState> rtc_state_; + // The verifier instance. + std::shared_ptr<auth::Verifier> verifier_; + // Hash algorithm used by manifests. + auth::CryptoHashType manifest_hash_algo_; + // The last manifest processed. + auth::Suffix last_manifest_; + // Hold digests extracted from all manifests received. + auth::Verifier::SuffixMap manifest_digests_; + // Hold hashes of all content objects received before they are verified. + auth::Verifier::SuffixMap unverified_packets_; + // Hold number of unverified bytes. + std::unordered_map<auth::Suffix, uint32_t> unverified_bytes_; + // Maximum delay (in ms) for an unverified byte to become verifed. + uint32_t max_unverified_delay_; + // Maximum number of unverified bytes before aborting the connection. + uint64_t max_unverified_bytes_; +}; + +} // end namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/protocols/transport_protocol.cc b/libtransport/src/protocols/transport_protocol.cc index d6954ac37..f1e49ec0b 100644 --- a/libtransport/src/protocols/transport_protocol.cc +++ b/libtransport/src/protocols/transport_protocol.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -25,7 +25,8 @@ using namespace interface; TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, Indexer *indexer, Reassembly *reassembly) - : socket_(icn_socket), + : Protocol(), + socket_(icn_socket), indexer_verifier_(indexer), reassembly_(reassembly), fec_decoder_(nullptr), @@ -36,90 +37,82 @@ TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket, on_interest_satisfied_(VOID_HANDLER), on_content_object_input_(VOID_HANDLER), stats_summary_(VOID_HANDLER), + on_fwd_strategy_(VOID_HANDLER), + on_rec_strategy_(VOID_HANDLER), on_payload_(VOID_HANDLER), - fec_type_(fec::FECType::UNKNOWN), - is_running_(false) { + fec_type_(fec::FECType::UNKNOWN) { socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_); socket_->getSocketOption(OtherOptions::STATISTICS, &stats_); - // Set this transport protocol as portal's consumer callback - portal_->setConsumerCallback(this); - indexer_verifier_->setReassembly(reassembly_.get()); reassembly->setIndexer(indexer_verifier_.get()); } +TransportProtocol::~TransportProtocol() {} + int TransportProtocol::start() { // If the protocol is already running, return otherwise set as running - if (is_running_) return -1; - - // Get all callbacks references - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION, - &on_interest_retransmission_); - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, - &on_interest_output_); - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED, - &on_interest_timeout_); - socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED, - &on_interest_satisfied_); - socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT, - &on_content_object_input_); - socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, - &stats_summary_); - socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, - &on_payload_); - - socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); - - // Set it is the first time we schedule an interest - is_first_ = true; - - // Reset the protocol state machine - reset(); - - // Schedule next interests - scheduleNextInterests(); - - is_first_ = false; - - // Set the protocol as running - is_running_ = true; - - if (!is_async_) { - // Start Event loop - portal_->runEventsLoop(); - - // Not running anymore - is_running_ = false; + if (isRunning()) { + return -1; } - return 0; -} - -void TransportProtocol::stop() { - is_running_ = false; + // Start protocol on its own thread + portal_->getThread().add([this]() { + // Get all callbacks references + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION, + &on_interest_retransmission_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + &on_interest_output_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED, + &on_interest_timeout_); + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED, + &on_interest_satisfied_); + socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT, + &on_content_object_input_); + socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY, + &stats_summary_); + socket_->getSocketOption(ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE, + &on_fwd_strategy_); + socket_->getSocketOption(ConsumerCallbacksOptions::REC_STRATEGY_CHANGE, + &on_rec_strategy_); + socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK, + &on_payload_); + + socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_); + + std::string fec_type_str = ""; + socket_->getSocketOption(GeneralTransportOptions::FEC_TYPE, fec_type_str); + if (fec_type_str != "") { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_type_str.c_str()); + } + + // Set it is the first time we schedule an interest + is_first_ = true; + + // Reset the protocol state machine + reset(); + + // Set this transport protocol as portal's consumer callback + portal_->registerTransportCallback(this); + + // Schedule next interests + scheduleNextInterests(); + + is_first_ = false; + + // Set the protocol as running + setRunning(); + }); - if (!is_async_) { - portal_->stopEventsLoop(); - } else { - portal_->clear(); - } + return 0; } void TransportProtocol::resume() { if (isRunning()) return; - is_running_ = true; + setRunning(); - scheduleNextInterests(); - - if (!is_async_) { - // Start Event loop - portal_->runEventsLoop(); - - // Not running anymore - is_running_ = false; - } + portal_->getThread().tryRunHandlerNow([this]() { scheduleNextInterests(); }); } void TransportProtocol::reset() { @@ -130,7 +123,7 @@ void TransportProtocol::reset() { } } -void TransportProtocol::onContentReassembled(std::error_code ec) { +void TransportProtocol::onContentReassembled(const std::error_code &ec) { stop(); if (!on_payload_) { @@ -153,7 +146,12 @@ void TransportProtocol::sendInterest( uint32_t len) { DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending interest for name " << interest_name; - auto interest = core::PacketManager<>::getInstance().getPacket<Interest>(); + Packet::Format format; + socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT, + format); + + auto interest = + core::PacketManager<>::getInstance().getPacket<Interest>(format); interest->setName(interest_name); for (uint32_t i = 0; i < len; i++) { @@ -176,6 +174,14 @@ void TransportProtocol::sendInterest( portal_->sendInterest(std::move(interest)); } +void TransportProtocol::onError(const std::error_code &ec) { + // error from portal: stop socket + stop(); + + // signal error to application + on_payload_->readError(ec); +} + void TransportProtocol::onTimeout(Interest::Ptr &i, const Name &n) { if (TRANSPORT_EXPECT_FALSE(!isRunning())) { return; diff --git a/libtransport/src/protocols/transport_protocol.h b/libtransport/src/protocols/transport_protocol.h index 1008a238b..ad8cf0346 100644 --- a/libtransport/src/protocols/transport_protocol.h +++ b/libtransport/src/protocols/transport_protocol.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -19,10 +19,10 @@ #include <hicn/transport/interfaces/socket_consumer.h> #include <hicn/transport/interfaces/statistics.h> #include <hicn/transport/utils/object_pool.h> -#include <implementation/socket.h> #include <protocols/data_processing_events.h> #include <protocols/fec_base.h> #include <protocols/indexer.h> +#include <protocols/protocol.h> #include <protocols/reassembly.h> #include <array> @@ -38,8 +38,10 @@ class IndexVerificationManager; using ReadCallback = interface::ConsumerSocket::ReadCallback; -class TransportProtocol : public core::Portal::ConsumerCallback, - public ContentObjectProcessingEventCallback { +class TransportProtocol + : public ContentObjectProcessingEventCallback, + public Protocol, + public std::enable_shared_from_this<TransportProtocol> { static constexpr std::size_t interest_pool_size = 4096; friend class ManifestIndexManager; @@ -48,13 +50,11 @@ class TransportProtocol : public core::Portal::ConsumerCallback, TransportProtocol(implementation::ConsumerSocket *icn_socket, Indexer *indexer, Reassembly *reassembly); - virtual ~TransportProtocol() = default; - - TRANSPORT_ALWAYS_INLINE bool isRunning() { return is_running_; } + virtual ~TransportProtocol(); virtual int start(); - virtual void stop(); + using Protocol::stop; virtual void resume(); @@ -69,7 +69,7 @@ class TransportProtocol : public core::Portal::ConsumerCallback, virtual void scheduleNextInterests() = 0; // Events generated by the indexing - virtual void onContentReassembled(std::error_code ec); + virtual void onContentReassembled(const std::error_code &ec); virtual void onPacketDropped(Interest &interest, ContentObject &content_object, const std::error_code &ec) override = 0; @@ -90,7 +90,8 @@ class TransportProtocol : public core::Portal::ConsumerCallback, AllocatorHandler &&allocator_handler) { if (!fec_decoder_) { // Try to get FEC from environment - if (const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE")) { + const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE"); + if (fec_str && (fec_type_ == fec::FECType::UNKNOWN)) { LOG(INFO) << "Using FEC " << fec_str; fec_type_ = fec::FECUtils::fecTypeFromString(fec_str); } @@ -114,14 +115,13 @@ class TransportProtocol : public core::Portal::ConsumerCallback, // Consumer Callback void onContentObject(Interest &i, ContentObject &c) override; void onTimeout(Interest::Ptr &i, const Name &n) override; - void onError(std::error_code ec) override {} + void onError(const std::error_code &ec) override; protected: implementation::ConsumerSocket *socket_; std::unique_ptr<Indexer> indexer_verifier_; std::unique_ptr<Reassembly> reassembly_; std::unique_ptr<fec::ConsumerFEC> fec_decoder_; - std::shared_ptr<core::Portal> portal_; // True if it si the first time we schedule an interest std::atomic<bool> is_first_; interface::TransportStatistics *stats_; @@ -134,14 +134,13 @@ class TransportProtocol : public core::Portal::ConsumerCallback, interface::ConsumerContentObjectCallback *on_content_object_input_; interface::ConsumerContentObjectCallback *on_content_object_; interface::ConsumerTimerCallback *stats_summary_; + interface::StrategyCallback *on_fwd_strategy_; + interface::StrategyCallback *on_rec_strategy_; ReadCallback *on_payload_; bool is_async_; fec::FECType fec_type_; - - private: - std::atomic<bool> is_running_; }; } // end namespace protocol diff --git a/libtransport/src/test/CMakeLists.txt b/libtransport/src/test/CMakeLists.txt index 26fe7aee1..e7018ceed 100644 --- a/libtransport/src/test/CMakeLists.txt +++ b/libtransport/src/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -11,42 +11,68 @@ # See the License for the specific language governing permissions and # limitations under the License. -include(BuildMacros) - +############################################################## +# Test sources +############################################################## list(APPEND TESTS_SRC main.cc + test_aggregated_header.cc test_auth.cc - test_consumer_producer_rtc.cc + # test_consumer_producer_rtc.cc test_core_manifest.cc - test_event_thread.cc + # test_event_thread.cc + test_fec_base_rs.cc test_fec_reedsolomon.cc + test_fixed_block_allocator.cc test_indexer.cc test_interest.cc test_packet.cc + test_packet_allocator.cc + test_quality_score.cc + test_sessions.cc + test_thread_pool.cc ) if (ENABLE_RELY) list(APPEND TESTS_SRC test_fec_rely_wrapper.cc + test_fec_base_rely.cc + ) +endif() + +if (UNIX AND NOT APPLE) + list(APPEND TESTS_SRC + test_memif_connector.cc ) endif() -build_executable(unit_tests + +############################################################## +# Link libraries +############################################################## +set(MEMIF_MODULE_LIBRARIES + ${LIBRARIES} + ${LIBTRANSPORT_SHARED} + ${GTEST_LIBRARIES} +) + + +############################################################## +# Build single unit test executable and add it to test list +############################################################## +build_executable(libtransport_tests NO_INSTALL SOURCES ${TESTS_SRC} LINK_LIBRARIES - ${LIBRARIES} - ${LIBTRANSPORT_STATIC} - ${GTEST_LIBRARIES} + ${MEMIF_MODULE_LIBRARIES} INCLUDE_DIRS - ${LIBTRANSPORT_INCLUDE_DIRS} - ${LIBHICN_INCLUDE_DIRS} - ${LIBTRANSPORT_INTERNAL_INCLUDE_DIRS} + $<TARGET_PROPERTY:${LIBTRANSPORT_SHARED},INCLUDE_DIRECTORIES> ${GTEST_INCLUDE_DIRS} DEPENDS gtest ${LIBTRANSPORT_SHARED} COMPONENT ${LIBTRANSPORT_COMPONENT} - DEFINITIONS "${COMPILER_DEFINITIONS}" + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} LINK_FLAGS ${LINK_FLAGS} ) -add_test_internal(unit_tests) +add_test_internal(libtransport_tests) diff --git a/libtransport/src/test/main.cc b/libtransport/src/test/main.cc index a4d7ce1b3..591ed0d5b 100644 --- a/libtransport/src/test/main.cc +++ b/libtransport/src/test/main.cc @@ -16,7 +16,6 @@ #include <gtest/gtest.h> int main(int argc, char **argv) { - srand(time(0)); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
\ No newline at end of file diff --git a/libtransport/src/test/test_aggregated_header.cc b/libtransport/src/test/test_aggregated_header.cc new file mode 100644 index 000000000..0d88af5ab --- /dev/null +++ b/libtransport/src/test/test_aggregated_header.cc @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <protocols/rtc/rtc_packet.h> +#include <test/packet_samples.h> + +#include <climits> +#include <random> +#include <vector> + +namespace transport { + +namespace core { + +namespace { +// The fixture for testing class Foo. +class AggregatedPktHeaderTest : public ::testing::Test { + protected: + AggregatedPktHeaderTest() { + // You can do set-up work for each test here. + } + + virtual ~AggregatedPktHeaderTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } +}; + +} // namespace + +TEST_F(AggregatedPktHeaderTest, Add2Packets8bit) { + uint8_t buf[1500]; + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt1_len, 2); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + // print + // for (uint16_t i = 0; i < 40; i++){ + // std::cout << (int) i << " " << (int) buf[i] << std::endl; + //} + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add2Packets8bit255) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14}; // 255 + uint16_t pkt2_len = 255; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt2_len, 2); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add2Packets8bit256) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15}; // 256 + uint16_t pkt2_len = 256; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt2_len, 2); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add4Packets8bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + uint16_t pkt3_len = 10; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt1_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + *(ptr + i + pkt1_len + pkt2_len) = pkt3[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + *(ptr + i + pkt1_len + pkt2_len + pkt3_len) = pkt4[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Add4Packets16bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; // 260 + uint16_t pkt3_len = 260; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt3_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + uint8_t* ptr = hdr.getPayloadAppendPtr(); + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + *(ptr + i) = pkt1[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + *(ptr + i + pkt1_len) = pkt2[i]; + } + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + *(ptr + i + pkt1_len + pkt2_len) = pkt3[i]; + } + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + *(ptr + i + pkt1_len + pkt2_len + pkt3_len) = pkt4[i]; + } + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + hdr.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Defrag4Packets8bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + uint16_t pkt3_len = 10; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt1_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + + uint16_t offset = protocol::rtc::DATA_HEADER_SIZE + 8; // 8 = aggr hdr + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + buf[i + offset] = pkt1[i]; + } + offset += pkt1_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + buf[i + offset] = pkt2[i]; + } + offset += pkt2_len; + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + buf[i + offset] = pkt3[i]; + } + offset += pkt3_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + buf[i + offset] = pkt4[i]; + } + + protocol::rtc::AggrPktHeader hdr2(buf + protocol::rtc::DATA_HEADER_SIZE); + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + uint8_t packet_number = hdr2.getNumberOfPackets(); + EXPECT_EQ(packet_number, 4); + + hdr2.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr2.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr2.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr2.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +TEST_F(AggregatedPktHeaderTest, Defrag4Packets16bit) { + uint8_t buf[1500]; + + std::vector<uint8_t> pkt1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t pkt1_len = 14; + + std::vector<uint8_t> pkt2 = {11, 12, 13, 14, 15, 16, 17}; + uint16_t pkt2_len = 7; + + std::vector<uint8_t> pkt3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 40 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 60 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 80 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 100 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 120 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 140 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 160 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 180 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 200 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 220 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 240 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; // 260 + uint16_t pkt3_len = 260; + + std::vector<uint8_t> pkt4 = {100, 110}; + uint16_t pkt4_len = 2; + + for (uint16_t i = 0; i < 1500; i++) { + buf[i] = 0; + } + + // skip protocol::rtc::DATA_HEADER_SIZE that will be the rtc header + protocol::rtc::AggrPktHeader hdr(buf + protocol::rtc::DATA_HEADER_SIZE, + pkt3_len, 4); + hdr.addPacketToHeader(0, pkt1_len); + hdr.addPacketToHeader(1, pkt2_len); + hdr.addPacketToHeader(2, pkt3_len); + hdr.addPacketToHeader(3, pkt4_len); + + uint16_t offset = protocol::rtc::DATA_HEADER_SIZE + 12; // 12 = aggr hdr + + // copy packet 1 + for (uint16_t i = 0; i < pkt1_len; i++) { + buf[i + offset] = pkt1[i]; + } + offset += pkt1_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt2_len; i++) { + buf[i + offset] = pkt2[i]; + } + offset += pkt2_len; + + // copy packet 3 + for (uint16_t i = 0; i < pkt3_len; i++) { + buf[i + offset] = pkt3[i]; + } + offset += pkt3_len; + + // copy packet 2 + for (uint16_t i = 0; i < pkt4_len; i++) { + buf[i + offset] = pkt4[i]; + } + + protocol::rtc::AggrPktHeader hdr2(buf + protocol::rtc::DATA_HEADER_SIZE); + + uint8_t* pkt_ptr = nullptr; + uint16_t pkt_len = 0; + + uint8_t packet_number = hdr2.getNumberOfPackets(); + EXPECT_EQ(packet_number, 4); + + hdr2.getPointerToPacket(0, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt1_len); + for (uint16_t i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt1[i]); + } + + hdr2.getPointerToPacket(1, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt2_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt2[i]); + } + + hdr2.getPointerToPacket(2, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt3_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt3[i]); + } + + hdr2.getPointerToPacket(3, &pkt_ptr, &pkt_len); + EXPECT_EQ(pkt_len, pkt4_len); + for (int i = 0; i < pkt_len; i++) { + EXPECT_EQ(*(pkt_ptr + i), pkt4[i]); + } +} + +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_auth.cc b/libtransport/src/test/test_auth.cc index db1c3b52f..d7fd55433 100644 --- a/libtransport/src/test/test_auth.cc +++ b/libtransport/src/test/test_auth.cc @@ -15,10 +15,15 @@ #include <gtest/gtest.h> #include <hicn/transport/auth/crypto_hash.h> -#include <hicn/transport/auth/identity.h> #include <hicn/transport/auth/signer.h> #include <hicn/transport/auth/verifier.h> #include <hicn/transport/core/content_object.h> +#include <openssl/rand.h> + +using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>; +using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>; +using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>; +using DSA_ptr = std::unique_ptr<DSA, decltype(&::DSA_free)>; namespace transport { namespace auth { @@ -50,11 +55,23 @@ TEST_F(AuthTest, VoidVerifier) { } TEST_F(AuthTest, AsymmetricRSA) { - // Create the RSA signer from an Identity object - Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, - 30, "RSAVerifier"); - - std::shared_ptr<Signer> signer = identity.getSigner(); + // Create the RSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + RSA_ptr rsa(RSA_new(), ::RSA_free); + BN_ptr pub_exp(BN_new(), ::BN_free); + + BN_set_word(pub_exp.get(), RSA_F4); + if (1 != RSA_generate_key_ex(rsa.get(), 2048u, pub_exp.get(), NULL)) + throw errors::RuntimeException("can't generate the key"); + RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free); + RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free); + if (1 != EVP_PKEY_set1_RSA(pubKey.get(), rsa_pub.get())) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa_priv.get())) + throw errors::RuntimeException("can't generate the key"); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::RSA_SHA256, privateKey, pubKey); // Create a content object core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); @@ -68,61 +85,112 @@ TEST_F(AuthTest, AsymmetricRSA) { // Create the RSA verifier std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); + std::make_shared<AsymmetricVerifier>(pubKey); EXPECT_EQ(packet.getFormat(), HF_INET6_TCP_AH); EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); EXPECT_EQ(signer->getSuite(), CryptoSuite::RSA_SHA256); - EXPECT_EQ(signer->getSignatureSize(), 128u); + EXPECT_EQ(signer->getSignatureSize(), 256u); EXPECT_EQ(verifier->verifyPackets(&packet), VerificationPolicy::ACCEPT); } TEST_F(AuthTest, AsymmetricBufferRSA) { - // Create the RSA signer from an Identity object - Identity identity("test_rsa.p12", PASSPHRASE, CryptoSuite::RSA_SHA256, 1024u, - 30, "RSAVerifier"); + // Create the RSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + RSA_ptr rsa(RSA_new(), ::RSA_free); + BN_ptr pub_exp(BN_new(), ::BN_free); + + BN_set_word(pub_exp.get(), RSA_F4); + if (1 != RSA_generate_key_ex(rsa.get(), 2048u, pub_exp.get(), NULL)) + throw errors::RuntimeException("can't generate the key"); + RSA_ptr rsa_pub(RSAPublicKey_dup(rsa.get()), ::RSA_free); + RSA_ptr rsa_priv(RSAPrivateKey_dup(rsa.get()), ::RSA_free); + if (1 != EVP_PKEY_set1_RSA(pubKey.get(), rsa_pub.get())) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_RSA(privateKey.get(), rsa_priv.get())) + throw errors::RuntimeException("can't generate the key"); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::RSA_SHA256, privateKey, pubKey); - std::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); } TEST_F(AuthTest, AsymmetricBufferDSA) { - // Create the DSA signer from an Identity object - Identity identity("test_dsa.p12", PASSPHRASE, CryptoSuite::DSA_SHA256, 1024u, - 30, "DSAVerifier"); + // Create the DSA keys + + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + + DSA_ptr dsa(DSA_new(), ::DSA_free); + unsigned char buf[32]; + if (RAND_bytes(buf, sizeof(buf)) != 1) { + throw errors::RuntimeException("can't generate the key"); + } + if (DSA_generate_parameters_ex(dsa.get(), 1024u, buf, sizeof(buf), NULL, NULL, + NULL) != 1) + throw errors::RuntimeException("can't generate the key"); + if (DSA_generate_key(dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_DSA(privateKey.get(), dsa.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<X509> cert(X509_new(), ::X509_free); + X509_set_pubkey(cert.get(), privateKey.get()); + std::shared_ptr<EVP_PKEY> pubKey(X509_get_pubkey(cert.get()), EVP_PKEY_free); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::DSA_SHA256, privateKey, pubKey); - std::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); } TEST_F(AuthTest, AsymmetricVerifierDSA) { - // Create the DSA signer from an Identity object - Identity identity("test_dsa.p12", PASSPHRASE, CryptoSuite::DSA_SHA256, 1024u, - 30, "DSAVerifier"); + // Create the DSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); - std::shared_ptr<Signer> signer = identity.getSigner(); + DSA_ptr dsa(DSA_new(), ::DSA_free); + unsigned char buf[32]; + if (RAND_bytes(buf, sizeof(buf)) != 1) { + throw errors::RuntimeException("can't generate the key"); + } + if (DSA_generate_parameters_ex(dsa.get(), 1024u, buf, sizeof(buf), NULL, NULL, + NULL) != 1) + throw errors::RuntimeException("can't generate the key"); + if (DSA_generate_key(dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (EVP_PKEY_set1_DSA(privateKey.get(), dsa.get()) != 1) + throw errors::RuntimeException("can't generate the key"); + if (1 != EVP_PKEY_set1_DSA(privateKey.get(), dsa.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<X509> cert(X509_new(), ::X509_free); + X509_set_pubkey(cert.get(), privateKey.get()); + std::shared_ptr<EVP_PKEY> pubKey(X509_get_pubkey(cert.get()), EVP_PKEY_free); + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::DSA_SHA256, privateKey, pubKey); // Create a content object core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); @@ -134,7 +202,7 @@ TEST_F(AuthTest, AsymmetricVerifierDSA) { // EXPECT_EQ(signer->getSignatureSize(), 256u); signer->signPacket(&packet); std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); + std::make_shared<AsymmetricVerifier>(cert); EXPECT_EQ(packet.getFormat(), HF_INET6_TCP_AH); EXPECT_EQ(signer->getHashType(), CryptoHashType::SHA256); @@ -143,33 +211,59 @@ TEST_F(AuthTest, AsymmetricVerifierDSA) { } TEST_F(AuthTest, AsymmetricBufferECDSA) { - // Create the ECDSA signer from an Identity object - Identity identity("test_ecdsa.p12", PASSPHRASE, CryptoSuite::ECDSA_SHA256, - 256u, 30, "ECDSAVerifier"); + // Create the ECDSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + EC_KEY_ptr ec_priv(EC_KEY_new_by_curve_name(NID_secp256k1), ::EC_KEY_free); + EC_KEY_ptr ec_pub(EC_KEY_new(), ::EC_KEY_free); + EC_KEY_set_asn1_flag(ec_priv.get(), OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_generate_key(ec_priv.get()) == 0) + throw errors::RuntimeException("can't generate the ecdsa key"); + if (1 != EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_priv.get())) + throw errors::RuntimeException("can't generate the key"); + EC_KEY_set_group(ec_pub.get(), EC_KEY_get0_group(ec_priv.get())); + EC_KEY_set_public_key(ec_pub.get(), EC_KEY_get0_public_key(ec_priv.get())); + if (1 != EVP_PKEY_set1_EC_KEY(pubKey.get(), ec_pub.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::ECDSA_SHA256, privateKey, pubKey); - std::shared_ptr<AsymmetricSigner> signer = identity.getSigner(); std::string payload = "bonjour"; std::vector<uint8_t> buffer(payload.begin(), payload.end()); signer->signBuffer(buffer); std::vector<uint8_t> sig = signer->getSignature(); - std::shared_ptr<X509> cert = identity.getCertificate(); - AsymmetricVerifier verif(cert); - bool res = verif.verifyBuffer( + std::shared_ptr<AsymmetricVerifier> verif = + std::make_shared<AsymmetricVerifier>(pubKey); + bool res = verif->verifyBuffer( buffer, std::vector<uint8_t>(sig.data(), sig.data() + sig.size()), CryptoHashType::SHA256); EXPECT_EQ(res, true); -} +} // namespace auth TEST_F(AuthTest, AsymmetricVerifierECDSA) { - Identity identity("test_ecdsa.p12", PASSPHRASE, CryptoSuite::ECDSA_SHA256, - 256u, 30, "ECDSAVerifier"); - - std::shared_ptr<Signer> signer = identity.getSigner(); - std::shared_ptr<Verifier> verifier = - std::make_shared<AsymmetricVerifier>(identity.getCertificate()); - // Create a content object + // Create the ECDSA keys + std::shared_ptr<EVP_PKEY> privateKey(EVP_PKEY_new(), EVP_PKEY_free); + std::shared_ptr<EVP_PKEY> pubKey(EVP_PKEY_new(), EVP_PKEY_free); + EC_KEY_ptr ec_priv(EC_KEY_new_by_curve_name(NID_secp256k1), ::EC_KEY_free); + EC_KEY_ptr ec_pub(EC_KEY_new(), ::EC_KEY_free); + EC_KEY_set_asn1_flag(ec_priv.get(), OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_generate_key(ec_priv.get()) == 0) + throw errors::RuntimeException("can't generate the ecdsa key"); + if (1 != EVP_PKEY_set1_EC_KEY(privateKey.get(), ec_priv.get())) + throw errors::RuntimeException("can't generate the key"); + EC_KEY_set_group(ec_pub.get(), EC_KEY_get0_group(ec_priv.get())); + EC_KEY_set_public_key(ec_pub.get(), EC_KEY_get0_public_key(ec_priv.get())); + if (1 != EVP_PKEY_set1_EC_KEY(pubKey.get(), ec_pub.get())) + throw errors::RuntimeException("can't generate the key"); + + std::shared_ptr<AsymmetricSigner> signer = std::make_shared<AsymmetricSigner>( + CryptoSuite::ECDSA_SHA256, privateKey, pubKey); + + std::shared_ptr<AsymmetricVerifier> verifier = + std::make_shared<AsymmetricVerifier>(pubKey); for (int i = 0; i < 100; i++) { core::ContentObject packet(HF_INET6_TCP_AH, signer->getSignatureSize()); diff --git a/libtransport/src/test/test_consumer_producer_rtc.cc b/libtransport/src/test/test_consumer_producer_rtc.cc index 8541a9e1a..b11a6a388 100644 --- a/libtransport/src/test/test_consumer_producer_rtc.cc +++ b/libtransport/src/test/test_consumer_producer_rtc.cc @@ -41,7 +41,7 @@ class ConsumerProducerTest : public ::testing::Test, rtc_timer_(io_service_), stop_timer_(io_service_), consumer_(TransportProtocolAlgorithms::RTC, io_service_), - producer_(ProductionProtocolAlgorithms::RTC_PROD, io_service_), + producer_(ProductionProtocolAlgorithms::RTC_PROD, thread_), producer_prefix_(prefix), consumer_name_(name), packets_sent_(0), @@ -120,14 +120,13 @@ class ConsumerProducerTest : public ::testing::Test, size_t maxBufferSize() const override { return receive_buffer_size; } - void readError(const std::error_code ec) noexcept override { - FAIL() << "Error while reading from RTC socket"; + void readError(const std::error_code &ec) noexcept override { io_service_.stop(); + FAIL() << "Error while reading from RTC socket"; } void readSuccess(std::size_t total_size) noexcept override { packets_received_++; - std::cout << "Received something" << std::endl; } asio::io_service io_service_; diff --git a/libtransport/src/test/test_core_manifest.cc b/libtransport/src/test/test_core_manifest.cc index 93f4e87cb..23fd5e342 100644 --- a/libtransport/src/test/test_core_manifest.cc +++ b/libtransport/src/test/test_core_manifest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -17,6 +17,8 @@ #include <core/manifest_inline.h> #include <gtest/gtest.h> #include <hicn/transport/auth/crypto_hash.h> +#include <hicn/transport/auth/signer.h> +#include <hicn/transport/auth/verifier.h> #include <test/packet_samples.h> #include <climits> @@ -33,7 +35,7 @@ class ManifestTest : public ::testing::Test { protected: using ContentObjectManifest = ManifestInline<ContentObject, Fixed>; - ManifestTest() : name_("b001::123|321"), manifest1_(name_) { + ManifestTest() : name_("b001::123|321"), manifest1_(HF_INET6_TCP_AH, name_) { // You can do set-up work for each test here. } @@ -97,8 +99,8 @@ TEST_F(ManifestTest, MoveConstructor) { TEST_F(ManifestTest, SetLastManifest) { manifest1_.clear(); - manifest1_.setFinalManifest(true); - bool fcn = manifest1_.isFinalManifest(); + manifest1_.setIsLast(true); + bool fcn = manifest1_.getIsLast(); ASSERT_TRUE(fcn == true); } @@ -109,13 +111,13 @@ TEST_F(ManifestTest, SetManifestType) { ManifestType type1 = ManifestType::INLINE_MANIFEST; ManifestType type2 = ManifestType::FLIC_MANIFEST; - manifest1_.setManifestType(type1); - ManifestType type_returned1 = manifest1_.getManifestType(); + manifest1_.setType(type1); + ManifestType type_returned1 = manifest1_.getType(); manifest1_.clear(); - manifest1_.setManifestType(type2); - ManifestType type_returned2 = manifest1_.getManifestType(); + manifest1_.setType(type2); + ManifestType type_returned2 = manifest1_.getType(); ASSERT_EQ(type1, type_returned1); ASSERT_EQ(type2, type_returned2); @@ -146,17 +148,80 @@ TEST_F(ManifestTest, SetHashAlgorithm) { ASSERT_EQ(hash3, type_returned3); } -TEST_F(ManifestTest, SetNextSegmentCalculationStrategy) { +TEST_F(ManifestTest, setParamsBytestream) { manifest1_.clear(); - NextSegmentCalculationStrategy strategy1 = - NextSegmentCalculationStrategy::INCREMENTAL; + ParamsBytestream params{ + .final_segment = 1, + }; + + manifest1_.setParamsBytestream(params); + manifest1_.encode(); + + ContentObjectManifest manifest(manifest1_); + manifest.decode(); + + ASSERT_EQ(interface::ProductionProtocolAlgorithms::BYTE_STREAM, + manifest.getTransportType()); + ASSERT_EQ(params, manifest.getParamsBytestream()); +} + +TEST_F(ManifestTest, SetParamsRTC) { + manifest1_.clear(); + + ParamsRTC params{ + .timestamp = 1, + .prod_rate = 2, + .prod_seg = 3, + .support_fec = 1, + }; + + manifest1_.setParamsRTC(params); + manifest1_.encode(); - manifest1_.setNextSegmentCalculationStrategy(strategy1); - NextSegmentCalculationStrategy type_returned1 = - manifest1_.getNextSegmentCalculationStrategy(); + ContentObjectManifest manifest(manifest1_); + manifest.decode(); + + ASSERT_EQ(interface::ProductionProtocolAlgorithms::RTC_PROD, + manifest.getTransportType()); + ASSERT_EQ(params, manifest.getParamsRTC()); +} - ASSERT_EQ(strategy1, type_returned1); +TEST_F(ManifestTest, SignManifest) { + Name name("b001::", 0); + auto signer = std::make_shared<auth::SymmetricSigner>( + auth::CryptoSuite::HMAC_SHA256, "hunter2"); + auto verifier = std::make_shared<auth::SymmetricVerifier>("hunter2"); + std::shared_ptr<ContentObjectManifest> manifest; + + // Instantiate Manifest + manifest.reset(ContentObjectManifest::createManifest( + HF_INET6_TCP_AH, name, ManifestVersion::VERSION_1, + ManifestType::INLINE_MANIFEST, false, name, signer->getHashType(), + signer->getSignatureFieldSize())); + + // Add Manifest entry + auth::CryptoHash hash(signer->getHashType()); + hash.computeDigest(std::vector<uint8_t>{0x01, 0x02, 0x03, 0x04}); + manifest->addSuffixHash(1, hash); + + // Encode manifest + manifest->encode(); + + // Sign manifest + signer->signPacket(manifest.get()); + + // Check size + ASSERT_EQ(manifest->payloadSize(), manifest->estimateManifestSize()); + ASSERT_EQ(manifest->length(), + manifest->headerSize() + manifest->payloadSize()); + ASSERT_EQ(ContentObjectManifest::manifestHeaderSize( + interface::ProductionProtocolAlgorithms::UNKNOWN), + manifest->manifestHeaderSize()); + + // Verify manifest + auth::VerificationPolicy policy = verifier->verifyPackets(manifest.get()); + ASSERT_EQ(auth::VerificationPolicy::ACCEPT, policy); } TEST_F(ManifestTest, SetBaseName) { @@ -198,23 +263,8 @@ TEST_F(ManifestTest, SetSuffixList) { } manifest1_.setBaseName(base_name); - core::Name ret_name = manifest1_.getBaseName(); - // auto & hash_list = manifest1_.getSuffixHashList(); - - // bool cond; - // int i = 0; - - // for (auto & item : manifest1_.getSuffixList()) { - // auto hash = manifest1_.getHash(suffixes[i]); - // cond = auth::CryptoHash::compareBinaryDigest(hash, - // entries[i].second.getDigest<uint8_t>().data(), - // entries[i].second.getType()); - // ASSERT_TRUE(cond); - // i++; - // } - ASSERT_EQ(base_name, ret_name); delete[] entries; diff --git a/libtransport/src/test/test_event_thread.cc b/libtransport/src/test/test_event_thread.cc index 549ff9c1a..324250717 100644 --- a/libtransport/src/test/test_event_thread.cc +++ b/libtransport/src/test/test_event_thread.cc @@ -14,6 +14,7 @@ */ #include <gtest/gtest.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <hicn/transport/utils/event_thread.h> #include <cmath> @@ -48,7 +49,7 @@ class EventThreadTest : public ::testing::Test { utils::EventThread event_thread_; }; -double average(const unsigned long samples[], int size) { +inline double average(const unsigned long samples[], int size) { double sum = 0; for (int i = 0; i < size; i++) { @@ -58,7 +59,7 @@ double average(const unsigned long samples[], int size) { return sum / size; } -double stdDeviation(const unsigned long samples[], int size) { +inline double stdDeviation(const unsigned long samples[], int size) { double avg = average(samples, size); double var = 0; @@ -72,26 +73,23 @@ double stdDeviation(const unsigned long samples[], int size) { } // namespace TEST_F(EventThreadTest, DISABLED_SchedulingDelay) { - using namespace std::chrono; - const size_t size = 1000000; - std::vector<unsigned long> samples(size); - - for (unsigned int i = 0; i < size; i++) { - auto t0 = steady_clock::now(); - event_thread_.add([t0, &samples, i]() { - auto t1 = steady_clock::now(); - samples[i] = duration_cast<nanoseconds>(t1 - t0).count(); - }); - } + // using namespace std::chrono; + // const size_t size = 1000000; + // std::vector<unsigned long> samples(size); + + // for (unsigned int i = 0; i < size; i++) { + // event_thread_.add([t0, &samples, i]() { + // }); + // } - event_thread_.stop(); + // event_thread_.stop(); - auto avg = average(&samples[0], size); - auto sd = stdDeviation(&samples[0], size); - (void)sd; + // auto avg = average(&samples[0], size); + // auto sd = stdDeviation(&samples[0], size); + // (void)sd; - // Expect average to be less that 1 ms - EXPECT_LT(avg, 1000000); + // // Expect average to be less that 1 ms + // EXPECT_LT(avg, 1000000); } } // namespace utils diff --git a/libtransport/src/test/test_fec_base_rely.cc b/libtransport/src/test/test_fec_base_rely.cc new file mode 100644 index 000000000..41e1eae49 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rely.cc @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/global_object_pool.h> +#include <protocols/fec_base.h> +#include <protocols/fec_utils.h> +#include <protocols/rtc/rtc_consts.h> + +#include <algorithm> +#include <iostream> +#include <queue> +#include <random> + +namespace transport { +namespace protocol { + +class PacketFactory { + public: + PacketFactory(){}; + + ~PacketFactory(){}; + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, uint32_t payload_size, + uint32_t payload_filler) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + // create payload + auto buff = packet_manager.getMemBuf(); + buff->append(payload_size); + std::fill(buff->writableData(), buff->writableTail(), payload_filler); + + // create data packet + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(buff->data(), buff->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, fec::buffer payload) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(payload->data(), payload->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } +}; + +class Encoder { + public: + Encoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + if (fec_type_ == fec::FECType::UNKNOWN) + std::cout << "x" << fec_str << "x" << std::endl; + encoder_ = fec::FECUtils::getEncoder(fec_type_, 1); + encoder_->setFECCallback( + std::bind(&Encoder::onFecPackets, this, std::placeholders::_1)); + encoder_->setBufferCallback( + std::bind(&Encoder::getBuffer, this, std::placeholders::_1)); + }; + + ~Encoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + fec_packets_.push(packet.getBuffer()); + } + } + + fec::buffer getBuffer(std::size_t size) { + auto ret = core::PacketManager<>::getInstance() + .getPacket<transport::core::ContentObject>(HF_INET6_TCP, 0); + ret->updateLength(rtc::DATA_HEADER_SIZE + size); + ret->append(rtc::DATA_HEADER_SIZE + size); + ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE); + + return ret; + } + + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata) { + encoder_->onPacketProduced(content_object, offset, metadata); + } + + public: + std::queue<fec::buffer> fec_packets_; + + private: + std::unique_ptr<fec::ProducerFEC> encoder_; + fec::FECType fec_type_; +}; + +class Decoder { + public: + Decoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + decoder_ = fec::FECUtils::getDecoder(fec_type_, 1); + decoder_->setFECCallback( + std::bind(&Decoder::onFecPackets, this, std::placeholders::_1)); + decoder_->setBufferCallback(fec::FECBase::BufferRequested(0)); + }; + + ~Decoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + hicn_packet_dump(packet.getBuffer()->data(), + packet.getBuffer()->length()); + recovered_packets_.push(packet.getBuffer()); + } + } + + void onPacketReceived(core::ContentObject &content_object, uint32_t offset) { + decoder_->onDataPacket(content_object, offset); + } + + public: + std::queue<fec::buffer> recovered_packets_; + + private: + std::unique_ptr<fec::ConsumerFEC> decoder_; + fec::FECType fec_type_; +}; + +TEST(FECtestRely, RelyTestInOrder1) { + // use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + // we cannot use the original data 1 to check it we recovered the packet + // correclty because Rely modifies the packet so here we create a copy + auto data1_copy = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 1 is lost + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + + bool eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRely, RelyTestInOrder2) { + // use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + auto data2_copy = pf.createData(name, 2, 45, 2); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 2 is lost + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data 1 + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + + bool eq_len = (data2_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +#if 0 +TEST(FECtestRely, RelyTestOutOfOrder1) { + //use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + auto data1_copy = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + + std::cout << "dump packet 2" << std::endl; + hicn_packet_dump(data2->data(), data2->length()); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + std::cout << "dump packet 2" << std::endl; + hicn_packet_dump(data2->data(), data2->length()); + + std::cout << "dump packet 3" << std::endl; + hicn_packet_dump(data3->data(), data3->length()); + + // decoding ooo packets. data 1 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t) 2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + bool eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if(eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int) 0); +} +#endif + +TEST(FECtestRely, RelyTestOutOfOrder2) { + // use Rely k = 2 N = 6 + std::string fec_str = "Rely_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + auto data2_copy = pf.createData(name, 2, 45, 2); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decoding ooo packets. data 2 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data 1 + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + bool eq_len = (data2_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRely, RelyTestLargerBlocks) { + // use Rely k = 4 N = 7 + std::string fec_str = "Rely_K4_N7"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + + auto data2 = pf.createData(name, 2, 45, 2); + + auto data3 = pf.createData(name, 3, 12, 3); + auto data3_copy = pf.createData(name, 3, 12, 3); + + auto data4 = pf.createData(name, 4, 20, 4); + auto data4_copy = pf.createData(name, 4, 20, 4); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data4, data4->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data5 = pf.createData(name, 5, encoder.fec_packets_.front()); + encoder.fec_packets_.pop(); // pop 5 + encoder.fec_packets_.pop(); // pop 6 + auto data7 = pf.createData(name, 7, encoder.fec_packets_.front()); + + // decoding packets: lost data 3 and data 4 + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data7, data7->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data5, data5->headerSize() + rtc::DATA_HEADER_SIZE); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)4); + decoder.recovered_packets_.pop(); // pop data 1 + decoder.recovered_packets_.pop(); // pop data 2 + auto recovered3 = pf.createData(name, 3, decoder.recovered_packets_.front()); + decoder.recovered_packets_.pop(); + auto recovered4 = pf.createData(name, 4, decoder.recovered_packets_.front()); + + bool eq_len = (data3_copy->length() == recovered3->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data3_copy->data(), recovered3->data(), recovered3->length()); + EXPECT_EQ(ret, (int)0); + + eq_len = (data4_copy->length() == recovered4->length()); + EXPECT_TRUE(eq_len); + ret = -1; + if (eq_len) + ret = memcmp(data4_copy->data(), recovered4->data(), recovered4->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRely, RelyTestK1_N3) { + // use Rely k = 1 N = 3 + std::string fec_str = "Rely_K1_N3"; + Encoder encoder(fec_str); + Decoder decoder1(fec_str); // recv 2 + Decoder decoder2(fec_str); // recv 3 + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + auto data1_copy = pf.createData(name, 1, 50, 1); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packets + EXPECT_TRUE(encoder.fec_packets_.size() == 2); + auto data2 = pf.createData(name, 2, encoder.fec_packets_.front()); + encoder.fec_packets_.pop(); // pop 2 + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // test1: recv data 2 + decoder1.onPacketReceived(*data2, + data2->headerSize() + rtc::DATA_HEADER_SIZE); + EXPECT_EQ(decoder1.recovered_packets_.size(), (size_t)1); + + auto recovered = pf.createData(name, 1, decoder1.recovered_packets_.front()); + bool eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); + + // test2: recv data 3 + decoder2.onPacketReceived(*data3, + data3->headerSize() + rtc::DATA_HEADER_SIZE); + EXPECT_EQ(decoder2.recovered_packets_.size(), (size_t)1); + + recovered = pf.createData(name, 1, decoder2.recovered_packets_.front()); + eq_len = (data1_copy->length() == recovered->length()); + EXPECT_TRUE(eq_len); + ret = -1; + if (eq_len) + ret = memcmp(data1_copy->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_fec_base_rs.cc b/libtransport/src/test/test_fec_base_rs.cc new file mode 100644 index 000000000..7d7bcebc3 --- /dev/null +++ b/libtransport/src/test/test_fec_base_rs.cc @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/global_object_pool.h> +#include <protocols/fec_base.h> +#include <protocols/fec_utils.h> +#include <protocols/rtc/rtc_consts.h> + +#include <algorithm> +#include <iostream> +#include <queue> +#include <random> + +namespace transport { +namespace protocol { + +class PacketFactory { + public: + PacketFactory(){}; + + ~PacketFactory(){}; + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, uint32_t payload_size, + uint32_t payload_filler) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + // create payload + auto buff = packet_manager.getMemBuf(); + buff->append(payload_size); + std::fill(buff->writableData(), buff->writableTail(), payload_filler); + + // create data packet + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(buff->data(), buff->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } + + std::shared_ptr<transport::core::ContentObject> createData( + core::Name &name, uint32_t suffix, fec::buffer payload) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + auto data = packet_manager.getPacket<transport::core::ContentObject>( + HF_INET6_TCP, 0); + struct rtc::data_packet_t header; + header.setTimestamp(1000); + header.setProductionRate(1); + data->appendPayload((const uint8_t *)&header, rtc::DATA_HEADER_SIZE); + data->appendPayload(payload->data(), payload->length()); + data->setName(name.setSuffix(suffix)); + data->setLifetime(500); + data->setPathLabel(12); + + return data; + } +}; + +class Encoder { + public: + Encoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + encoder_ = fec::FECUtils::getEncoder(fec_type_, 1); + encoder_->setFECCallback( + std::bind(&Encoder::onFecPackets, this, std::placeholders::_1)); + encoder_->setBufferCallback( + std::bind(&Encoder::getBuffer, this, std::placeholders::_1)); + }; + + ~Encoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + fec_packets_.push(packet.getBuffer()); + } + } + + fec::buffer getBuffer(std::size_t size) { + auto ret = core::PacketManager<>::getInstance() + .getPacket<transport::core::ContentObject>(HF_INET6_TCP, 0); + ret->updateLength(rtc::DATA_HEADER_SIZE + size); + ret->append(rtc::DATA_HEADER_SIZE + size); + ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE); + + return ret; + } + + void onPacketProduced(core::ContentObject &content_object, uint32_t offset, + uint32_t metadata) { + encoder_->onPacketProduced(content_object, offset, metadata); + } + + public: + std::queue<fec::buffer> fec_packets_; + + private: + std::unique_ptr<fec::ProducerFEC> encoder_; + fec::FECType fec_type_; +}; + +class Decoder { + public: + Decoder(std::string fec_str) { + fec_type_ = fec::FECUtils::fecTypeFromString(fec_str.c_str()); + decoder_ = fec::FECUtils::getDecoder(fec_type_, 1); + decoder_->setFECCallback( + std::bind(&Decoder::onFecPackets, this, std::placeholders::_1)); + decoder_->setBufferCallback(fec::FECBase::BufferRequested(0)); + }; + + ~Decoder(){}; + + void onFecPackets(fec::BufferArray &packets) { + for (auto &packet : packets) { + recovered_packets_.push(packet.getBuffer()); + } + } + + void onPacketReceived(core::ContentObject &content_object, uint32_t offset) { + decoder_->onDataPacket(content_object, offset); + } + + public: + std::queue<fec::buffer> recovered_packets_; + + private: + std::unique_ptr<fec::ConsumerFEC> decoder_; + fec::FECType fec_type_; +}; + +TEST(FECtestRS, RSTestInOrder1) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 1 is lost + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + bool eq_len = (data1->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestInOrder2) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decode in order, data 2 is lost + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // check recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data packet 1 + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + + bool eq_len = (data2->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestOutOfOrder1) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decoding ooo packets. data 1 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + auto recovered = pf.createData(name, 1, decoder.recovered_packets_.front()); + bool eq_len = (data1->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data1->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestOutOfOrder2) { + // use RS k = 2 N = 6 + std::string fec_str = "RS_K2_N6"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data3 = pf.createData(name, 3, encoder.fec_packets_.front()); + + // decoding ooo packets. data 2 is lost. + decoder.onPacketReceived(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data2->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)2); + decoder.recovered_packets_.pop(); // pop data packet 1 + + auto recovered = pf.createData(name, 2, decoder.recovered_packets_.front()); + bool eq_len = (data2->length() == recovered->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data2->data(), recovered->data(), recovered->length()); + EXPECT_EQ(ret, (int)0); +} + +TEST(FECtestRS, RSTestLargerBlocks) { + // use RS k = 4 N = 7 + std::string fec_str = "RS_K4_N7"; + Encoder encoder(fec_str); + Decoder decoder(fec_str); + + PacketFactory pf; + + core::Name name("b001::"); + + auto data1 = pf.createData(name, 1, 50, 1); + const uint8_t *data1_ptr = data1->data(); + + auto data2 = pf.createData(name, 2, 45, 2); + const uint8_t *data2_ptr = data2->data(); + + auto data3 = pf.createData(name, 3, 12, 3); + const uint8_t *data3_ptr = data3->data(); + + auto data4 = pf.createData(name, 4, 20, 4); + const uint8_t *data4_ptr = data4->data(); + + // encoding + uint32_t metadata = static_cast<uint32_t>(data1->getPayloadType()); + encoder.onPacketProduced(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data3, data3->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + encoder.onPacketProduced(*data4, data4->headerSize() + rtc::DATA_HEADER_SIZE, + metadata); + + // create fec packet + auto data5 = pf.createData(name, 5, encoder.fec_packets_.front()); + encoder.fec_packets_.pop(); // pop 5 + encoder.fec_packets_.pop(); // pop 6 + auto data7 = pf.createData(name, 7, encoder.fec_packets_.front()); + + // decoding packets: lost data 3 and data 4 + decoder.onPacketReceived(*data2, data2->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data7, data7->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data1, data1->headerSize() + rtc::DATA_HEADER_SIZE); + decoder.onPacketReceived(*data5, data5->headerSize() + rtc::DATA_HEADER_SIZE); + + // test payload pointers off the original packets + EXPECT_EQ((const uint8_t *)data1->data(), data1_ptr); + EXPECT_EQ((const uint8_t *)data2->data(), data2_ptr); + EXPECT_EQ((const uint8_t *)data3->data(), data3_ptr); + EXPECT_EQ((const uint8_t *)data4->data(), data4_ptr); + + // get recovered packet + EXPECT_EQ(decoder.recovered_packets_.size(), (size_t)4); + decoder.recovered_packets_.pop(); // pop data 1 + decoder.recovered_packets_.pop(); // pop data 2 + auto recovered3 = pf.createData(name, 3, decoder.recovered_packets_.front()); + decoder.recovered_packets_.pop(); // pop data 3 + auto recovered4 = pf.createData(name, 4, decoder.recovered_packets_.front()); + + bool eq_len = (data3->length() == recovered3->length()); + EXPECT_TRUE(eq_len); + int ret = -1; + if (eq_len) + ret = memcmp(data3->data(), recovered3->data(), recovered3->length()); + EXPECT_EQ(ret, (int)0); + + eq_len = (data4->length() == recovered4->length()); + EXPECT_TRUE(eq_len); + ret = -1; + if (eq_len) + ret = memcmp(data4->data(), recovered4->data(), recovered4->length()); + EXPECT_EQ(ret, (int)0); +} + +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_fec_reedsolomon.cc b/libtransport/src/test/test_fec_reedsolomon.cc index c7e10d111..0973069b1 100644 --- a/libtransport/src/test/test_fec_reedsolomon.cc +++ b/libtransport/src/test/test_fec_reedsolomon.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <glog/logging.h> #include <gtest/gtest.h> #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/global_object_pool.h> @@ -30,28 +31,35 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { fec::RSEncoder encoder(k, n, seq_offset); fec::RSDecoder decoder(k, n, seq_offset); - std::vector<fec::buffer> tx_block(k); - std::vector<fec::buffer> rx_block(k); + using BufferMetadata = std::pair<fec::buffer, uint32_t>; + + std::vector<BufferMetadata> tx_block(k); + std::vector<BufferMetadata> rx_block(k); int count = 0; int run = 0; + // Setup random engine + std::random_device + rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> dis(0, 99); + int iterations = 100; auto &packet_manager = core::PacketManager<>::getInstance(); - encoder.setFECCallback( - [&tx_block]( - std::vector<std::pair<uint32_t, fec::buffer>> &repair_packets) { - for (auto &p : repair_packets) { - // Append repair symbols to tx_block - tx_block.emplace_back(std::move(p).second); - } - }); + encoder.setFECCallback([&tx_block](fec::BufferArray &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + tx_block.emplace_back(p.getBuffer(), p.getMetadata()); + } + }); decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &source_packets) { + [&tx_block, &count, &k](fec::BufferArray &source_packets) { for (int i = 0; i < k; i++) { // Compare decoded source packets with original transmitted packets. - if (*tx_block[i] != *source_packets[i].second) { + if (*tx_block[i].first != *source_packets[i].getBuffer() || + tx_block[i].second != source_packets[i].getMetadata()) { count++; } } @@ -60,7 +68,7 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { do { // Discard eventual packet appended in previous callback call tx_block.erase(tx_block.begin() + k, tx_block.end()); - auto _seq_offet = seq_offset; + uint32_t _seq_offset = seq_offset; // Initialization. Feed encoder with first k source packets for (int i = 0; i < k; i++) { @@ -69,45 +77,46 @@ double ReedSolomonTest(int k, int n, int seq_offset, int size) { // Let's append a bit less than size, so that the FEC class will take care // of filling the rest with zeros - auto cur_size = size - (rand() % 100); + auto cur_size = size - dis(gen); // Set payload, saving 2 bytes at the beginning of the buffer for encoding // the length packet->append(cur_size); - packet->trimStart(2); - std::generate(packet->writableData(), packet->writableTail(), rand); std::fill(packet->writableData(), packet->writableTail(), i + 1); // Set first byte of payload to seq_offset, to reorder at receiver side - packet->writableData()[0] = uint8_t(_seq_offet++); + uint32_t *pkt_head = (uint32_t *)packet->writableData(); + *pkt_head = _seq_offset++; + + // Set a metadata integer + uint32_t metadata = dis(gen); // Store packet in tx buffer and clear rx buffer - tx_block[i] = std::move(packet); + tx_block[i] = std::make_pair(std::move(packet), metadata); } // Create the repair packets for (auto &tx : tx_block) { - encoder.consume(tx, tx->writableBuffer()[0]); + encoder.consume(tx.first, tx.first->writableBuffer()[0], 0, tx.second); } // Simulate transmission on lossy channel - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); std::vector<bool> losses(n, false); for (int i = 0; i < n - k; i++) losses[i] = true; int rxi = 0; - std::shuffle(losses.begin(), losses.end(), - std::default_random_engine(seed)); + std::shuffle(losses.begin(), losses.end(), gen); for (int i = 0; i < n && rxi < k; i++) if (losses[i] == false) { rx_block[rxi++] = tx_block[i]; if (i < k) { // Source packet - decoder.consumeSource(rx_block[rxi - 1], - rx_block[rxi - 1]->data()[0]); + uint32_t index = *((uint32_t *)rx_block[rxi - 1].first->data()); + decoder.consumeSource(rx_block[rxi - 1].first, index, 0, + rx_block[rxi - 1].second); } else { // Repair packet - decoder.consumeRepair(rx_block[rxi - 1]); + decoder.consumeRepair(rx_block[rxi - 1].first); } } @@ -126,6 +135,12 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { fec::RSEncoder encoder(k, n); fec::RSDecoder decoder(k, n); + // Setup random engine + std::random_device + rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> dis(0, 99); + auto &packet_manager = core::PacketManager<>::getInstance(); std::vector<std::pair<fec::buffer, uint32_t>> tx_block; @@ -136,33 +151,39 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { // Receiver will receive packet for n_sourceblocks in a random order. int total_packets = n * n_sourceblocks; int tx_packets = k * n_sourceblocks; - unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); - encoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &repair_packets) { - for (auto &p : repair_packets) { - // Append repair symbols to tx_block - tx_block.emplace_back(std::move(p.second), ++i); - } + encoder.setFECCallback([&tx_block, &rx_block, &i, &n, &k, + &encoder](fec::BufferArray &repair_packets) { + for (auto &p : repair_packets) { + // Append repair symbols to tx_block + ++i; + tx_block.emplace_back(std::move(p.getBuffer()), i); + } - EXPECT_EQ(tx_block.size(), size_t(n)); + EXPECT_EQ(tx_block.size(), size_t(n)); - // Select k packets to send, including at least one symbol. We start - // from the end for this reason. - for (int j = n - 1; j > n - k - 1; j--) { - rx_block.emplace_back(std::move(tx_block[j])); - } + // Select k packets to send, including at least one symbol. We start + // from the end for this reason. + for (int j = n - 1; j > n - k - 1; j--) { + rx_block.emplace_back(std::move(tx_block[j])); + } - // Clear tx block for next source block - tx_block.clear(); - encoder.clear(); - }); + // Clear tx block for next source block + tx_block.clear(); + encoder.clear(); + }); // The decode callback must be called exactly n_sourceblocks times - decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &source_packets) { - count++; - }); + decoder.setFECCallback([&count](fec::BufferArray &source_packets) { + // Check buffers + for (auto &packet : source_packets) { + auto packet_index = ((uint32_t *)packet.getBuffer()->writableData())[0]; + EXPECT_EQ(packet_index, packet.getIndex()) + << "Packet index: " << packet_index + << " -- FEC Index: " << packet.getIndex(); + } + count++; + }); // Produce n * n_sourceblocks // - ( k ) * n_sourceblocks source packets @@ -173,7 +194,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { // Let's append a bit less than size, so that the FEC class will take care // of filling the rest with zeros - auto cur_size = size - (rand() % 100); + auto cur_size = size - dis(gen); // Set payload, saving 2 bytes at the beginning of the buffer for encoding // the length @@ -182,7 +203,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { std::fill(packet->writableData(), packet->writableTail(), i + 1); // Set first byte of payload to i, to reorder at receiver side - packet->writableData()[0] = uint8_t(i); + ((uint32_t *)packet->writableData())[0] = uint32_t(i); // Store packet in tx buffer tx_block.emplace_back(packet, i); @@ -195,8 +216,7 @@ void ReedSolomonMultiBlockTest(int n_sourceblocks) { EXPECT_EQ(size_t(tx_packets), size_t(rx_block.size())); // Lets shuffle the rx_block before starting feeding the decoder. - std::shuffle(rx_block.begin(), rx_block.end(), - std::default_random_engine(seed)); + std::shuffle(rx_block.begin(), rx_block.end(), gen); for (auto &p : rx_block) { int index = p.second % n; @@ -231,7 +251,7 @@ foreach_rs_fec_type #undef _ TEST(ReedSolomonMultiBlockTest, RSMB10) { - int blocks = 10; + int blocks = 1; ReedSolomonMultiBlockTest(blocks); } diff --git a/libtransport/src/test/test_fec_rely_wrapper.cc b/libtransport/src/test/test_fec_rely_wrapper.cc index c5b73f8d2..764e4dd2d 100644 --- a/libtransport/src/test/test_fec_rely_wrapper.cc +++ b/libtransport/src/test/test_fec_rely_wrapper.cc @@ -13,18 +13,23 @@ * limitations under the License. */ +#include <glog/logging.h> #include <gtest/gtest.h> #include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/interfaces/socket_options_default_values.h> #include <hicn/transport/utils/log.h> #include <protocols/fec/rely.h> #include <queue> +#include <random> namespace transport { namespace protocol { -std::string printMissing( - const std::map<uint32_t, core::ContentObject::Ptr> &missing) { +using SavedPacketMap = + std::map<uint32_t, std::pair<core::ContentObject::Ptr, uint32_t>>; + +std::string printMissing(const SavedPacketMap &missing) { std::stringstream stream; for (auto &[seq, packet] : missing) { @@ -48,14 +53,15 @@ std::string printMissing( * @param loss_rate The loss rate */ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, - int64_t timeout, uint32_t max_iterations, + int64_t /* timeout */, uint32_t max_iterations, int loss_rate) { // Create 1 encoder and 1 decoder fec::RelyEncoder _encoder(k, n); fec::RelyDecoder _decoder(k, n); // Seed the pseudo-random with known value to always get same loss pattern - srand(k * n); + std::mt19937 gen(k * + n); // Standard mersenne_twister_engine seeded with rd(); // We will interact with rely encoder/decoder using the interface fec::ProducerFEC &encoder = _encoder; @@ -68,73 +74,78 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, auto &packet_manager = core::PacketManager<>::getInstance(); // Store packets to verify them in the decoder callback - std::map<uint32_t, core::ContentObject::Ptr> saved_packets; + SavedPacketMap saved_packets; // Save repair packets here in encoder callback std::queue<fec::buffer> pending_repair_packets; // Set callback called by encoder when a buffer is required. - encoder.setBufferCallback([](std::size_t size) -> fec::buffer { + encoder.setBufferCallback([](std::size_t size) { auto ret = - core::PacketManager<>::getInstance().getPacket<core::ContentObject>(); + core::PacketManager<>::getInstance().getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); ret->updateLength(size); ret->append(size); ret->trimStart(ret->headerSize()); - assert(ret->length() >= size); + DCHECK(ret->length() >= size); return ret; }); // Set callback to be called by encoder when repair packets are ready - encoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &packets) { - // We must get n - k symbols - EXPECT_EQ(packets.size(), n - k); - // TRANSPORT_LOGD("Got %zu symbols", packets.size()); - - // Save symbols in pending_repair_packets queue and increment iterations - for (auto &packet : packets) { - ++iterations; - pending_repair_packets.push(packet.second); - } - }); + encoder.setFECCallback([&iterations, &pending_repair_packets, &n, + &k](fec::BufferArray &packets) { + // We must get n - k symbols + EXPECT_EQ(packets.size(), n - k); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Got " << packets.size() << " symbols"; + + // Save symbols in pending_repair_packets queue and increment iterations + for (auto &packet : packets) { + ++iterations; + pending_repair_packets.push(packet.getBuffer()); + } + }); // Set callback to be called when decoder recover a packet - decoder.setFECCallback( - [&](std::vector<std::pair<uint32_t, fec::buffer>> &packets) { - for (auto &packet : packets) { - // TRANSPORT_LOGD("Recovering packet %u", packet.first); + decoder.setFECCallback([&saved_packets](fec::BufferArray &packets) { + for (auto &packet : packets) { + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Recovering packet " << packet.getIndex(); + + // Ensure recovered packet is in packets actually produced by encoder + auto original = saved_packets.find(packet.getIndex()); + ASSERT_TRUE(original != saved_packets.end()); + auto &original_packet = *original->second.first; - // Ensure recovered packet is in packets actually produced by encoder - auto original = saved_packets.find(packet.first); - ASSERT_TRUE(original != saved_packets.end()); - auto &original_packet = *original->second; + // Remove additional headers at the beginning of the packet. This may + // change in the future. + original_packet.trimStart(60 /* Ip + TCP */ + 32 /* Rely header */ + + 4 /* Packet size */); - // Remove additional headers at the beginning of the packet. This may - // change in the future. - original_packet.trimStart(60 /* Ip + TCP */ + 28 /* Rely header */ + - 4 /* Packet size */); + // Recovered packet should be equal to the original one + EXPECT_TRUE(original_packet == *packet.getBuffer()); - // Recovered packet should be equal to the original one - EXPECT_TRUE(original_packet == *packet.second); + // Also metadata should correspond + EXPECT_TRUE(original->second.second == packet.getMetadata()); - // Restore removed headers - original_packet.prepend(60 + 28 + 4); + // Restore removed headers + original_packet.prepend(60 + 32 + 4); - // Erase packet from saved packet list - saved_packets.erase(original); - } - }); + // Erase packet from saved packet list + saved_packets.erase(original); + } + }); // Send max_iterations packets from encoder to decoder + std::uniform_int_distribution<> dis(0, 1299); while (iterations < max_iterations) { // Create a payload, the size is between 50 and 1350 bytes. - auto payload_size = 50 + (rand() % 1300); + auto payload_size = 50 + (dis(gen)); uint8_t payload[max_packet_size]; - std::generate(payload, payload + payload_size, rand); + std::generate(payload, payload + payload_size, gen); // Get a packet from global pool and set name - auto buffer = packet_manager.getPacket<core::ContentObject>(); + auto buffer = packet_manager.getPacket<core::ContentObject>( + transport::interface::default_values::packet_format); buffer->setName(core::Name("b001::abcd", iterations)); // Get offset @@ -145,20 +156,23 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // its own header). buffer->appendPayload(payload, payload_size); + // Set an u32 metadata to pass altogether with the buffer + uint32_t metadata = dis(gen); + // Save packet in the saving_packets list - // TRANSPORT_LOGD("Saving packet with index %lu", iterations); - saved_packets.emplace(iterations, buffer); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Saving packet with index " << iterations; + saved_packets.emplace(iterations, std::make_pair(buffer, metadata)); // Feed buffer into the encoder. This will eventually trigger a call to the // FEC callback as soon as k packets are fed into the endocer. - encoder.onPacketProduced(*buffer, offset); + encoder.onPacketProduced(*buffer, offset, metadata); // Check returned packet. We calculate the difference in size and we compare // only the part of the returned packet corresponding to the original // payload. Rely should only add a header and should not modify the actual // payload content. If it does it, this check will fail. auto diff = buffer->length() - payload_size - offset; - // TRANSPORT_LOGD("Difference is %zu", diff); + DLOG_IF(INFO, VLOG_IS_ON(3)) << "Difference is " << diff; auto cmp = std::memcmp(buffer->data() + offset + diff, payload, payload_size); EXPECT_FALSE(cmp); @@ -170,29 +184,33 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // using future packets that are not created in the test. For this reason, // we ensure the test ends without losses. #define DROP_CONDITION(loss_rate, max_iterations) \ - (rand() % 100) >= loss_rate || iterations >= max_iterations * 0.9 + (dis(gen)) >= loss_rate || iterations >= max_iterations * 0.9 // Handle the source packet to the decoder, id drop condition returns true if (DROP_CONDITION(loss_rate, max_iterations)) { // Pass packet to decoder - // TRANSPORT_LOGD("Passing packet %u to decoder", - // buffer->getName().getSuffix()); - decoder.onDataPacket(*buffer, offset); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << buffer->getName().getSuffix() + << " to decoder"; + decoder.onDataPacket(*buffer, offset, metadata); } else { - // TRANSPORT_LOGD("Packet %u, dropped", buffer->getName().getSuffix()); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet " << buffer->getName().getSuffix() << " dropped"; } - // Check if previous call to encoder.consumer() generated repair packets, + // Check if previous call to encoder.consume() generated repair packets, // and if yes, feed them to the decoder. while (pending_repair_packets.size()) { // Also repair packets can be lost if (DROP_CONDITION(loss_rate, max_iterations)) { auto &packet = pending_repair_packets.front(); - // TRANSPORT_LOGD("Passing packet %u to decoder", iterations); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Passing packet " << iterations << " to decoder"; core::ContentObject &co = (core::ContentObject &)(*packet); decoder.onDataPacket(co, 0); } else { - // TRANSPORT_LOGD("Packet (repair) %u dropped", iterations); + DLOG_IF(INFO, VLOG_IS_ON(3)) + << "Packet (repair) " << iterations << " dropped"; } // Remove packet from the queue @@ -206,9 +224,6 @@ void testRelyEncoderDecoder(uint32_t k, uint32_t n, size_t max_packet_size, // 0.001 residual losses EXPECT_LE(saved_packets.size(), iterations * 0.001) << printMissing(saved_packets); - - // Reset seed - srand(time(0)); } /** diff --git a/libtransport/src/test/test_fixed_block_allocator.cc b/libtransport/src/test/test_fixed_block_allocator.cc new file mode 100644 index 000000000..33e048031 --- /dev/null +++ b/libtransport/src/test/test_fixed_block_allocator.cc @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/fixed_block_allocator.h> + +namespace utils { + +class FixedBlockAllocatorTest : public ::testing::Test { + protected: + static inline const std::size_t default_size = 2048; + static inline const std::size_t default_n_buffer = 1024; + + // Get fixed block allocator_ of 1024 buffers of size 2048 bytes + FixedBlockAllocatorTest() + : allocator_( + ::utils::FixedBlockAllocator<default_size, + default_n_buffer>::getInstance()) { + // You can do set-up work for each test here. + } + + virtual ~FixedBlockAllocatorTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + allocator_.reset(); + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + allocator_.reset(); + } + + static bool pointerIsAligned(const void *pointer, size_t byte_count) { + // Sanity check + EXPECT_THAT(reinterpret_cast<std::uintptr_t>(pointer) & + (alignof(std::max_align_t) - 1), + testing::Eq(std::uintptr_t(0))); + + return uintptr_t(pointer) % byte_count == 0; + } + + ::utils::FixedBlockAllocator<default_size, default_n_buffer> &allocator_; +}; + +TEST_F(FixedBlockAllocatorTest, DefaultChecks) { + EXPECT_EQ(allocator_.blockSize(), default_size); + EXPECT_EQ(allocator_.blockCount(), default_n_buffer); + EXPECT_EQ(allocator_.allocations(), 0UL); + EXPECT_EQ(allocator_.deallocations(), 0UL); + EXPECT_EQ(allocator_.blocksInUse(), 0UL); + + // Allocate one single block of memory + auto block = allocator_.allocateBlock(); + + ASSERT_THAT(block, testing::NotNull()); + + // Check statistics + EXPECT_EQ(allocator_.allocations(), 1UL); + EXPECT_EQ(allocator_.deallocations(), 0UL); + EXPECT_EQ(allocator_.blocksInUse(), 1UL); + + // Deallocate it + allocator_.deallocateBlock(block); + + // check statistics + EXPECT_EQ(allocator_.allocations(), 1UL); + EXPECT_EQ(allocator_.deallocations(), 1UL); + EXPECT_EQ(allocator_.blocksInUse(), 0UL); + + // Test reset + allocator_.reset(); + + EXPECT_EQ(allocator_.blockSize(), default_size); + EXPECT_EQ(allocator_.blockCount(), default_n_buffer); + EXPECT_EQ(allocator_.allocations(), 0UL); + EXPECT_EQ(allocator_.deallocations(), 0UL); + EXPECT_EQ(allocator_.blocksInUse(), 0UL); +} + +TEST_F(FixedBlockAllocatorTest, CheckMemoryIsReused) { + // Get one block. As it is the first one, it will be retrieved from the pool + auto block = allocator_.allocateBlock(); + + // Make sure block is valid + ASSERT_THAT(block, testing::NotNull()); + + // Release block + allocator_.deallocateBlock(block); + + // Get same memory block again + auto block2 = allocator_.allocateBlock(); + + // Make sure memory is reused + ASSERT_EQ(block, block2); + + // Get a third block + auto block3 = allocator_.allocateBlock(); + + // Make sure is different memory + ASSERT_NE(block2, block3); + + // Deallocate both and check we get back the laso one + allocator_.deallocateBlock(block2); + allocator_.deallocateBlock(block3); + + auto block4 = allocator_.allocateBlock(); + ASSERT_EQ(block3, block4); +} + +TEST_F(FixedBlockAllocatorTest, CheckMemoryIsContiguous) { + // Get one block. As it is the first one, it will be retrieved from the pool + auto block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + + // Make sure block is valid + ASSERT_THAT(block, testing::NotNull()); + + // Get another block + auto block2 = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + + // Make sure block is valid + ASSERT_THAT(block2, testing::NotNull()); + + // Check the 2 blocks come from contiguous memory + ASSERT_THAT(std::size_t(block2 - block), testing::Eq(default_size)); +} + +TEST_F(FixedBlockAllocatorTest, CheckPoolExpansion) { + // Get all the blocks we setup when constructing the allocator + std::array<uint8_t *, default_n_buffer> blocks; + blocks[0] = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + for (std::size_t i = 1; i < default_n_buffer; i++) { + blocks[i] = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + ASSERT_THAT(std::size_t(blocks[i] - blocks[i - 1]), + testing::Eq(default_size)); + } + + ASSERT_THAT(allocator_.blockCount(), testing::Eq(default_n_buffer)); + + // We should have finished all the blocks belonging to first pool. Let's get + // one additional block + auto new_block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + + // Make sure the block count doubled its size + ASSERT_THAT(allocator_.blockCount(), testing::Eq(2 * default_n_buffer)); + + // Check the new block is not contiguous with respect last block in blocks + ASSERT_THAT(std::size_t(new_block - blocks[default_n_buffer - 1]), + testing::Ne(default_size)); +} + +TEST_F(FixedBlockAllocatorTest, CheckMemoryIsAligned) { + for (std::size_t i = 0; i < default_n_buffer; i++) { + auto block = reinterpret_cast<uint8_t *>(allocator_.allocateBlock()); + ASSERT_THAT(pointerIsAligned(block, alignof(std::max_align_t)), + testing::IsTrue); + } +} + +TEST_F(FixedBlockAllocatorTest, Multithreading) { + // Create 4 threads + utils::EventThread threads[4]; + ::utils::FixedBlockAllocator<default_size, default_n_buffer> + *allocator_addresses[4] = {nullptr, nullptr, nullptr, nullptr}; + int i = 0; + for (auto &t : threads) { + t.add([&allocator_addresses, i]() { + auto &allocator = + ::utils::FixedBlockAllocator<default_size, + default_n_buffer>::getInstance(); + allocator_addresses[i] = &allocator; + }); + i++; + } + + // Stop threads + for (auto &t : threads) { + t.stop(); + } + + // Check the instance of allocator was different for each thread + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + ASSERT_NE(allocator_addresses[i], allocator_addresses[j]); + } + } +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/test/test_interest.cc b/libtransport/src/test/test_interest.cc index 8853563b0..d9c535881 100644 --- a/libtransport/src/test/test_interest.cc +++ b/libtransport/src/test/test_interest.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -30,7 +30,7 @@ namespace { // The fixture for testing class Foo. class InterestTest : public ::testing::Test { protected: - InterestTest() : name_("b001::123|321"), interest_() { + InterestTest() : name_("b001::123|321"), interest_(HF_INET6_TCP) { // You can do set-up work for each test here. } @@ -108,7 +108,7 @@ TEST_F(InterestTest, ConstructorWithName) { Name n("b001::1|123"); try { - Interest interest(n); + Interest interest(HF_INET6_TCP, n); } catch (...) { FAIL() << "ERROR: Unexpected exception thrown"; } @@ -176,27 +176,27 @@ TEST_F(InterestTest, SetGetLocator) { auto l = interest.getLocator(); ip_address_t address; - ip_address_pton("b006::ab:cdab:cdef", &address); - auto ret = !std::memcmp(&l, &address, sizeof(address)); + inet_pton(AF_INET6, "b006::ab:cdab:cdef", &address); + auto ret = !ip_address_cmp(&l, &address, AF_INET6); EXPECT_TRUE(ret); // Set different locator - ip_address_pton("2001::1234::4321::abcd::", &address); + inet_pton(AF_INET6, "2001::1234::4321::abcd::", &address); // Set it on interest interest.setLocator(address); // Check it was set l = interest.getLocator(); - ret = !std::memcmp(&l, &address, sizeof(address)); + ret = !ip_address_cmp(&l, &address, AF_INET6); EXPECT_TRUE(ret); } TEST_F(InterestTest, SetGetLifetime) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); const constexpr uint32_t lifetime = 10000; // Set lifetime @@ -211,7 +211,7 @@ TEST_F(InterestTest, SetGetLifetime) { TEST_F(InterestTest, HasManifest) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); // Let's expect anexception here try { @@ -232,7 +232,7 @@ TEST_F(InterestTest, HasManifest) { TEST_F(InterestTest, AppendSuffixesEncodeAndIterate) { // Create interest from buffer - Interest interest; + Interest interest(HF_INET6_TCP); // Appenad some suffixes, with some duplicates interest.appendSuffix(1); diff --git a/libtransport/src/test/test_memif_connector.cc b/libtransport/src/test/test_memif_connector.cc new file mode 100644 index 000000000..562a12c88 --- /dev/null +++ b/libtransport/src/test/test_memif_connector.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <core/memif_connector.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/global_object_pool.h> +#include <hicn/transport/utils/chrono_typedefs.h> + +namespace transport { +namespace core { + +namespace { + +using namespace std::placeholders; + +/** + * Master memif connector + */ +template <int Master> +class Memif { + static inline std::size_t counter = 256; + static inline std::size_t total_packets = counter * 4096; + static inline std::size_t packet_size = 64; + + public: + Memif(asio::io_service &io_service) + : io_service_(io_service), + memif_connector_(std::make_shared<MemifConnector>( + std::bind(&Memif::onPacketReceived, this, _1, _2, _3), + std::bind(&Memif::onPacketSent, this, _1, _2), + std::bind(&Memif::onClose, this, _1), + std::bind(&Memif::onReconnect, this, _1, _2), io_service_, + Master ? "test_master" : "test_slave")), + recv_counter_(0), + sent_counter_(0) { + memif_connector_->connect(0 /* Memif ID */, Master /* Is Master */, + "@hicntransport/test/memif"); + } + + void setStart() { t0_ = utils::SteadyTime::now(); } + + void startTest() { + if constexpr (!Master) { + auto &packet_manager = core::PacketManager<>::getInstance(); + + // Send in busrt of 256 packet per time + for (std::size_t i = 0; i < counter; i++) { + auto packet = packet_manager.getMemBuf(); + packet->append(packet_size); + memif_connector_->send(packet); + sent_counter_++; + } + + if (sent_counter_ < total_packets) { + asio::post(io_service_, std::bind(&Memif::startTest, this)); + } + } else { + setStart(); + } + } + + auto getRecvCounter() { return recv_counter_; } + auto getSentCounter() { return sent_counter_; } + + private: + void onPacketReceived(Connector *c, + const std::vector<utils::MemBuf::Ptr> &buffers, + const std::error_code &ec) { + if constexpr (Master) { + recv_counter_ += buffers.size(); + if (recv_counter_ == total_packets) { + auto t1 = utils::SteadyTime::now(); + auto delta = utils::SteadyTime::getDurationS(t0_, t1); + auto rate = recv_counter_ / delta.count(); + LOG(INFO) << "rate: " << rate << " packets/s"; + io_service_.stop(); + } + } else { + FAIL() << "Slave should not receive packets"; + } + } + void onPacketSent(Connector *c, const std::error_code &ec) {} + void onClose(Connector *c) {} + void onReconnect(Connector *c, const std::error_code &ec) {} + + private: + asio::io_service &io_service_; + std::shared_ptr<MemifConnector> memif_connector_; + std::size_t recv_counter_; + std::size_t sent_counter_; + utils::SteadyTime::TimePoint t0_; +}; + +using MemifMaster = Memif<1>; +using MemifSlave = Memif<0>; + +} // namespace + +class MemifTest : public ::testing::Test { + protected: + MemifTest() : io_service_(), master_(io_service_), slave_(io_service_) { + // You can do set-up work for each test here. + } + + void run() { + asio::post(io_service_, std::bind(&MemifSlave::startTest, &slave_)); + master_.startTest(); + io_service_.run(); + + EXPECT_THAT(master_.getRecvCounter(), + ::testing::Eq(slave_.getSentCounter())); + } + + virtual ~MemifTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + protected: + asio::io_service io_service_; + MemifMaster master_; + MemifSlave slave_; +}; + +TEST_F(MemifTest, Test) { run(); } +} // namespace core +} // namespace transport diff --git a/libtransport/src/test/test_packet.cc b/libtransport/src/test/test_packet.cc index 76ad352d6..ca20cdfb7 100644 --- a/libtransport/src/test/test_packet.cc +++ b/libtransport/src/test/test_packet.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,6 +16,7 @@ #include <gtest/gtest.h> #include <hicn/transport/core/packet.h> #include <hicn/transport/errors/not_implemented_exception.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <test/packet_samples.h> #include <climits> @@ -33,7 +34,7 @@ namespace core { class PacketForTest : public Packet { public: template <typename... Args> - PacketForTest(Args &&... args) : Packet(std::forward<Args>(args)...) {} + PacketForTest(Args &&...args) : Packet(std::forward<Args>(args)...) {} virtual ~PacketForTest() {} @@ -302,7 +303,7 @@ TEST_F(PacketTest, ConstructorWithNew) { auto &_packet = raw_packets_[HF_INET6_TCP]; auto packet_ptr = new PacketForTest(Packet::WRAP_BUFFER, &_packet[0], _packet.size(), _packet.size()); - (void)packet_ptr; + delete packet_ptr; } TEST_F(PacketTest, ConstructorWithRawBufferInet6Tcp) { @@ -682,9 +683,7 @@ TEST_F(PacketTest, SetGetTestSignatureTimestamp) { // Let's try to set the signature timestamp in a packet without AH header. We // expect an exception. using namespace std::chrono; - uint64_t now = - duration_cast<milliseconds>(system_clock::now().time_since_epoch()) - .count(); + uint64_t now = utils::SteadyTime::nowMs().count(); try { packet.setSignatureTimestamp(now); diff --git a/libtransport/src/test/test_packet_allocator.cc b/libtransport/src/test/test_packet_allocator.cc new file mode 100644 index 000000000..b63ddde8d --- /dev/null +++ b/libtransport/src/test/test_packet_allocator.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <glog/logging.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/interest.h> +#define ALLOCATION_CHECKS +#include <hicn/transport/core/global_object_pool.h> +#undef ALLOCATION_CHECKS +#include <hicn/transport/utils/event_thread.h> + +namespace transport { +namespace core { + +class PacketAllocatorTest : public ::testing::Test { + protected: + static inline const std::size_t default_size = 2048; + static inline const std::size_t default_n_buffer = 1024; + + // Get fixed block allocator_ of 1024 buffers of size 2048 bytes + PacketAllocatorTest() : allocator_(PacketManager<>::getInstance()) { + // You can do set-up work for each test here. + } + + virtual ~PacketAllocatorTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() {} + + virtual void TearDown() {} + + static bool pointerIsAligned(const void *pointer, size_t byte_count) { + return uintptr_t(pointer) % byte_count == 0; + } + + template <typename T, typename... Args> + void allocationTest(Args &&...args) { + // Create packet + auto packet = allocator_.getPacket<T>(std::forward<Args>(args)...); + + // Check boundaries + LOG(INFO) << "packet size: " << sizeof(*packet) + sizeof(packet) + << std::endl; + EXPECT_LE(sizeof(*packet) + sizeof(packet) + sizeof(std::max_align_t), + sizeof(PacketManager<>::PacketStorage::packet_and_shared_ptr)); + } + + PacketManager<> &allocator_; +}; + +TEST_F(PacketAllocatorTest, ContentObjectAllocation) { + allocationTest<core::ContentObject>(HF_INET_TCP); +} + +TEST_F(PacketAllocatorTest, InterestAllocation) { + allocationTest<core::Interest>(HF_INET_TCP); +} + +// TEST_F(PacketAllocatorTest, MemBufAllocation) { +// allocationTest<::utils::MemBuf>(); +// } + +TEST_F(PacketAllocatorTest, CheckAllocationIsCorrect) { + // Create packet + auto packet = allocator_.getPacket<core::ContentObject>(HF_INET_TCP); + + // Address of actual buffer + uint8_t *buffer_address = packet->writableData(); + + // Address of packet + uint8_t *packet_address = reinterpret_cast<uint8_t *>(packet.get()); + + uint8_t *start_address = + buffer_address - + sizeof(PacketManager<>::PacketStorage::packet_and_shared_ptr); + + // Check memory was allocated on correct positions + EXPECT_TRUE(pointerIsAligned(start_address, alignof(std::max_align_t))); + EXPECT_TRUE(packet_address > start_address && + packet_address < buffer_address); + EXPECT_TRUE(pointerIsAligned(buffer_address, alignof(std::max_align_t))); + EXPECT_THAT(std::size_t(buffer_address - start_address), + testing::Eq(sizeof( + PacketManager<>::PacketStorage::packet_and_shared_ptr))); +} + +} // namespace core +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/test/test_quality_score.cc b/libtransport/src/test/test_quality_score.cc new file mode 100644 index 000000000..9513c94a6 --- /dev/null +++ b/libtransport/src/test/test_quality_score.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <hicn/transport/utils/rtc_quality_score.h> + +#include <climits> +#include <random> +#include <vector> + +namespace transport { + +namespace protocol { + +namespace rtc { + +TEST(QualityScoreTest, testQS) { + RTCQualityScore qs; + uint8_t score; + + // 0 losses + score = qs.getQualityScore(0, 0); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(98, 0); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(188, 0); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(398, 0); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(400, 0); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(598, 0); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(600, 0); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(700, 0); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(50000, 0); + EXPECT_EQ(score, (uint8_t)1); + + // 0 delay + score = qs.getQualityScore(0, 2); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(0, 9); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(0, 29); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(0, 30); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(0, 39); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(0, 40); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(0, 50); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(0, 5000); + EXPECT_EQ(score, (uint8_t)1); + + // loss < 10 + score = qs.getQualityScore(0, 3); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(98, 9); + EXPECT_EQ(score, (uint8_t)4); + + score = qs.getQualityScore(100, 9); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(398, 5); + EXPECT_EQ(score, (uint8_t)2); + + score = qs.getQualityScore(400, 5); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(4000, 5); + EXPECT_EQ(score, (uint8_t)1); + + // loss < 20 + score = qs.getQualityScore(0, 10); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(30, 10); + EXPECT_EQ(score, (uint8_t)3); + + score = qs.getQualityScore(198, 15); + EXPECT_EQ(score, (uint8_t)2); + + score = qs.getQualityScore(200, 19); + EXPECT_EQ(score, (uint8_t)1); + + score = qs.getQualityScore(300, 10); + EXPECT_EQ(score, (uint8_t)1); + + // loss < 30 + + score = qs.getQualityScore(0, 29); + EXPECT_EQ(score, (uint8_t)5); + + score = qs.getQualityScore(10, 29); + EXPECT_EQ(score, (uint8_t)2); + + score = qs.getQualityScore(0, 100); + EXPECT_EQ(score, (uint8_t)1); +} + +} // namespace rtc +} // namespace protocol +} // namespace transport diff --git a/libtransport/src/test/test_sessions.cc b/libtransport/src/test/test_sessions.cc new file mode 100644 index 000000000..d2e8c27bb --- /dev/null +++ b/libtransport/src/test/test_sessions.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/interfaces/global_conf_interface.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/interfaces/socket_producer.h> + +namespace transport { +namespace interface { + +class SessionsTest : public ::testing::Test { + protected: + static inline const std::size_t default_size = 2048; + static inline const std::size_t default_n_buffer = 1024; + + // Get fixed block allocator_ of 1024 buffers of size 2048 bytes + SessionsTest() { + // You can do set-up work for each test here. + // Set io_module to local forwarder with no external connections + global_config::IoModuleConfiguration config; + config.name = "forwarder_module"; + config.set(); + } + + virtual ~SessionsTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + std::vector<ConsumerSocket> consumers_; + std::vector<ProducerSocket> producers_; +}; + +TEST_F(SessionsTest, SessionAllocations) { + // Create 1000 consumer sockets and 1000 producer sockets + int cprotocol = TransportProtocolAlgorithms::RAAQM; + int pprotocol = ProductionProtocolAlgorithms::BYTE_STREAM; + int offset = 0; + + for (int i = 0; i < 1000; i++) { + auto &c = consumers_.emplace_back(cprotocol + (offset % 3)); + auto &p = producers_.emplace_back(pprotocol + (offset % 2)); + c.connect(); + p.connect(); + offset++; + } +} + +} // namespace interface +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/test/test_thread_pool.cc b/libtransport/src/test/test_thread_pool.cc new file mode 100644 index 000000000..1b6b4cc81 --- /dev/null +++ b/libtransport/src/test/test_thread_pool.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hicn/transport/utils/thread_pool.h> + +namespace utils { + +class ThreadPoolTest : public ::testing::Test { + protected: + ThreadPoolTest() : thread_pool_() { + // You can do set-up work for each test here. + } + + virtual ~ThreadPoolTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + ::utils::ThreadPool thread_pool_; +}; + +TEST_F(ThreadPoolTest, DefaultConstructor) { + // EXPECT_EQ(thread_pool_.GetNumThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumIdleThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumBusyThreads(), 0); +} + +TEST_F(ThreadPoolTest, GetNThreads) { + auto n_threads = thread_pool_.getNThreads(); + EXPECT_GT(n_threads, std::size_t(0)); + EXPECT_EQ(n_threads, std::thread::hardware_concurrency()); + + ::utils::ThreadPool pool(64); + n_threads = pool.getNThreads(); + EXPECT_GT(n_threads, std::size_t(0)); + EXPECT_NE(n_threads, std::thread::hardware_concurrency()); + EXPECT_EQ(n_threads, std::size_t(64)); + + // EXPECT_EQ(thread_pool_.GetNumThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumIdleThreads(), 0); + // EXPECT_EQ(thread_pool_.GetNumBusyThreads(), 0); +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/transport.config b/libtransport/src/transport.config index 89db204d5..d5c2918a6 100644 --- a/libtransport/src/transport.config +++ b/libtransport/src/transport.config @@ -17,7 +17,7 @@ forwarder = { remote_port = 33436; } }; - + listeners = { l0 = { local_address = "127.0.0.1"; diff --git a/libtransport/src/utils/CMakeLists.txt b/libtransport/src/utils/CMakeLists.txt index a85ab16bf..5bb76303a 100644 --- a/libtransport/src/utils/CMakeLists.txt +++ b/libtransport/src/utils/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -23,6 +23,7 @@ list(APPEND SOURCE_FILES list(APPEND HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h + ${CMAKE_CURRENT_SOURCE_DIR}/max_filter.h ${CMAKE_CURRENT_SOURCE_DIR}/stream_buffer.h ${CMAKE_CURRENT_SOURCE_DIR}/suffix_strategy.h ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h @@ -34,10 +35,6 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.h ${CMAKE_CURRENT_SOURCE_DIR}/fd_deadline_timer.h ) - - list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.cc - ) endif() if(NOT WIN32) diff --git a/libtransport/src/utils/content_store.cc b/libtransport/src/utils/content_store.cc index 8ae7fd4d4..d9d4147f7 100644 --- a/libtransport/src/utils/content_store.cc +++ b/libtransport/src/utils/content_store.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -17,6 +17,7 @@ #include <hicn/transport/core/content_object.h> #include <hicn/transport/core/interest.h> #include <hicn/transport/core/name.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <utils/content_store.h> namespace utils { @@ -56,7 +57,7 @@ void ContentStore::insert( fifo_list_.push_front(std::cref(content_object->getName())); auto pos = fifo_list_.begin(); content_store_hash_table_[content_object->getName()] = ContentStoreEntry( - ObjectTimeEntry(content_object, std::chrono::steady_clock::now()), pos); + ObjectTimeEntry(content_object, utils::SteadyTime::now()), pos); } std::shared_ptr<ContentObject> ContentStore::find(const Name &name) { @@ -67,8 +68,8 @@ std::shared_ptr<ContentObject> ContentStore::find(const Name &name) { if (it != content_store_hash_table_.end()) { auto content_lifetime = it->second.first.first->getLifetime(); auto time_passed_since_creation = - std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now() - it->second.first.second) + utils::SteadyTime::getDurationMs(it->second.first.second, + utils::SteadyTime::now()) .count(); if (time_passed_since_creation > content_lifetime) { diff --git a/libtransport/src/utils/content_store.h b/libtransport/src/utils/content_store.h index abe5e7f6c..ecd62cc2c 100644 --- a/libtransport/src/utils/content_store.h +++ b/libtransport/src/utils/content_store.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -35,8 +35,7 @@ using Name = transport::core::Name; using ContentObject = transport::core::ContentObject; using Interest = transport::core::Interest; -typedef std::pair<std::shared_ptr<ContentObject>, - std::chrono::steady_clock::time_point> +typedef std::pair<std::shared_ptr<ContentObject>, utils::SteadyTime::TimePoint> ObjectTimeEntry; typedef std::pair<ObjectTimeEntry, std::list<std::reference_wrapper<const Name>>::iterator> diff --git a/libtransport/src/utils/daemonizator.cc b/libtransport/src/utils/daemonizator.cc index 6cb7d16ba..4c17aa96b 100644 --- a/libtransport/src/utils/daemonizator.cc +++ b/libtransport/src/utils/daemonizator.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/deadline_timer.h b/libtransport/src/utils/deadline_timer.h index 5187754f0..6c105ed3d 100644 --- a/libtransport/src/utils/deadline_timer.h +++ b/libtransport/src/utils/deadline_timer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -22,61 +22,6 @@ #include <cstring> #include <utility> -namespace std { -namespace chrono { -namespace detail { - -template <typename From, typename To> -struct posix_duration_cast; - -// chrono -> timespec caster -template <typename Rep, typename Period> -struct posix_duration_cast<std::chrono::duration<Rep, Period>, - struct timespec> { - static struct timespec cast(std::chrono::duration<Rep, Period> const &d) { - struct timespec tv; - - std::chrono::seconds const sec = - std::chrono::duration_cast<std::chrono::seconds>(d); - - tv.tv_sec = sec.count(); - tv.tv_nsec = - std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count(); - - return tv; - } -}; - -// timespec -> chrono caster -template <typename Rep, typename Period> -struct posix_duration_cast<struct timespec, - std::chrono::duration<Rep, Period>> { - static std::chrono::duration<Rep, Period> cast(struct timespec const &tv) { - return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>( - std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec)); - } -}; - -} // namespace detail - -// chrono -> timespec -template <typename T, typename Rep, typename Period> -auto duration_cast(std::chrono::duration<Rep, Period> const &d) -> - typename std::enable_if<std::is_same<T, struct timespec>::value, - struct timespec>::type { - return detail::posix_duration_cast<std::chrono::duration<Rep, Period>, - timespec>::cast(d); -} - -// timespec -> chrono -template <typename Duration> -Duration duration_cast(struct timespec const &tv) { - return detail::posix_duration_cast<struct timespec, Duration>::cast(tv); -} - -} // namespace chrono -} // namespace std - namespace utils { template <typename Implementation> diff --git a/libtransport/src/utils/epoll_event_reactor.cc b/libtransport/src/utils/epoll_event_reactor.cc deleted file mode 100644 index 457727bbe..000000000 --- a/libtransport/src/utils/epoll_event_reactor.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <glog/logging.h> -#include <hicn/transport/utils/branch_prediction.h> -#include <signal.h> -#include <unistd.h> -#include <utils/epoll_event_reactor.h> -#include <utils/fd_deadline_timer.h> - -#include <iostream> - -namespace utils { - -EpollEventReactor::EpollEventReactor() - : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {} - -EpollEventReactor::~EpollEventReactor() { close(epoll_fd_); } - -int EpollEventReactor::addFileDescriptor(int fd, uint32_t events) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - LOG(ERROR) << "invalid fd " << fd; - return -1; - } - - struct epoll_event evt; - std::memset(&evt, 0, sizeof(evt)); - evt.events = events; - evt.data.fd = fd; - - if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) < - 0)) { - LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; - return -1; - } - - return 0; -} - -int EpollEventReactor::modFileDescriptor(int fd, uint32_t events) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - LOG(ERROR) << "invalid fd " << fd; - return -1; - } - - struct epoll_event evt; - memset(&evt, 0, sizeof(evt)); - evt.events = events; - evt.data.fd = fd; - - if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) < - 0)) { - LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; - return -1; - } - - return 0; -} - -std::size_t EpollEventReactor::mapSize() { return event_callback_map_.size(); } - -int EpollEventReactor::delFileDescriptor(int fd) { - if (TRANSPORT_EXPECT_FALSE(fd < 0)) { - LOG(ERROR) << "invalid fd " << fd; - return -1; - } - - struct epoll_event evt; - memset(&evt, 0, sizeof(evt)); - - if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) < - 0)) { - LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; - return -1; - } - - utils::SpinLock::Acquire locked(event_callback_map_lock_); - event_callback_map_.erase(fd); - - return 0; -} - -void EpollEventReactor::runEventLoop(int timeout) { - Event evt[128]; - int en = 0; - EventCallbackMap::iterator it; - EventCallback callback; - - // evt.events = EPOLLIN | EPOLLOUT; - sigset_t sigset; - sigemptyset(&sigset); - - while (run_event_loop_) { - memset(&evt, 0, sizeof(evt)); - en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset); - - if (TRANSPORT_EXPECT_FALSE(en < 0)) { - LOG(ERROR) << "epoll_pwait: " << strerror(errno); - if (errno == EINTR) { - continue; - } else { - return; - } - } - - for (int i = 0; i < en; i++) { - if (evt[i].data.fd > 0) { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - it = event_callback_map_.find(evt[i].data.fd); - } - - if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { - LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; - } else { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - callback = event_callback_map_[evt[i].data.fd]; - } - - callback(evt[i]); - - // In the callback the epoll event reactor could have been stopped, - // then we need to check whether the event loop is still running. - if (TRANSPORT_EXPECT_FALSE(!run_event_loop_)) { - return; - } - } - } else { - LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; - } - } - } -} - -void EpollEventReactor::runOneEvent() { - Event evt; - int en = 0; - EventCallbackMap::iterator it; - EventCallback callback; - - // evt.events = EPOLLIN | EPOLLOUT; - sigset_t sigset; - sigemptyset(&sigset); - - memset(&evt, 0, sizeof(evt)); - - en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset); - - if (TRANSPORT_EXPECT_FALSE(en < 0)) { - LOG(ERROR) << "epoll_pwait: " << strerror(errno); - return; - } - - if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - it = event_callback_map_.find(evt.data.fd); - } - - if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { - LOG(ERROR) << "unexpected event. fd " << evt.data.fd; - } else { - { - utils::SpinLock::Acquire locked(event_callback_map_lock_); - callback = event_callback_map_[evt.data.fd]; - } - - callback(evt); - } - } else { - LOG(ERROR) << "unexpected event. fd " << evt.data.fd; - } -} - -void EpollEventReactor::stop() { run_event_loop_ = false; } - -} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/utils/epoll_event_reactor.h b/libtransport/src/utils/epoll_event_reactor.h index 9ebfca937..8e7681c20 100644 --- a/libtransport/src/utils/epoll_event_reactor.h +++ b/libtransport/src/utils/epoll_event_reactor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,6 +15,7 @@ #pragma once +#include <glog/logging.h> #include <hicn/transport/utils/spinlock.h> #include <sys/epoll.h> #include <utils/event_reactor.h> @@ -35,9 +36,10 @@ typedef std::unordered_map<int, EventCallback> EventCallbackMap; class EpollEventReactor : public EventReactor { public: - explicit EpollEventReactor(); + explicit EpollEventReactor() + : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {} - ~EpollEventReactor(); + ~EpollEventReactor() { close(epoll_fd_); } template <typename EventHandler> int addFileDescriptor(int fd, uint32_t events, EventHandler &&callback) { @@ -56,20 +58,163 @@ class EpollEventReactor : public EventReactor { return ret; } - int delFileDescriptor(int fd); + int delFileDescriptor(int fd) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + LOG(ERROR) << "invalid fd " << fd; + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) < + 0)) { + return -1; + } + + utils::SpinLock::Acquire locked(event_callback_map_lock_); + event_callback_map_.erase(fd); + + return 0; + } + + int modFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + LOG(ERROR) << "invalid fd " << fd; + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) < + 0)) { + LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; + return -1; + } + + return 0; + } + + void runEventLoop(int timeout = -1) override { + Event evt[128]; + int en = 0; + EventCallbackMap::iterator it; + EventCallback callback; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + while (run_event_loop_) { + memset(&evt, 0, sizeof(evt)); + en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset); + + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + LOG(ERROR) << "epoll_pwait: " << strerror(errno); + if (errno == EINTR) { + continue; + } else { + return; + } + } + + for (int i = 0; i < en; i++) { + if (evt[i].data.fd > 0) { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + it = event_callback_map_.find(evt[i].data.fd); + } + + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; + } else { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + callback = event_callback_map_[evt[i].data.fd]; + } + + callback(evt[i]); + + // In the callback the epoll event reactor could have been stopped, + // then we need to check whether the event loop is still running. + if (TRANSPORT_EXPECT_FALSE(!run_event_loop_)) { + return; + } + } + } else { + LOG(ERROR) << "unexpected event. fd " << evt[i].data.fd; + } + } + } + } + + void runOneEvent() override { + Event evt; + int en = 0; + EventCallbackMap::iterator it; + EventCallback callback; - int modFileDescriptor(int fd, uint32_t events); + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); - void runEventLoop(int timeout = -1) override; + memset(&evt, 0, sizeof(evt)); - void runOneEvent() override; + en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset); - void stop() override; + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + LOG(ERROR) << "epoll_pwait: " << strerror(errno); + return; + } + + if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + it = event_callback_map_.find(evt.data.fd); + } - std::size_t mapSize(); + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + LOG(ERROR) << "unexpected event. fd " << evt.data.fd; + } else { + { + utils::SpinLock::Acquire locked(event_callback_map_lock_); + callback = event_callback_map_[evt.data.fd]; + } + + callback(evt); + } + } else { + LOG(ERROR) << "unexpected event. fd " << evt.data.fd; + } + } + + void stop() override { run_event_loop_ = false; } + + std::size_t mapSize() { return event_callback_map_.size(); } private: - int addFileDescriptor(int fd, uint32_t events); + int addFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + LOG(ERROR) << "invalid fd " << fd; + return -1; + } + + struct epoll_event evt; + std::memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) < + 0)) { + LOG(ERROR) << "epoll_ctl: " << strerror(errno) << " fd " << fd; + return -1; + } + + return 0; + } int epoll_fd_; std::atomic_bool run_event_loop_; diff --git a/libtransport/src/utils/event_reactor.h b/libtransport/src/utils/event_reactor.h index 4f8b58296..09af6f84c 100644 --- a/libtransport/src/utils/event_reactor.h +++ b/libtransport/src/utils/event_reactor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/fd_deadline_timer.h b/libtransport/src/utils/fd_deadline_timer.h index 22c13a763..e15cd4d2a 100644 --- a/libtransport/src/utils/fd_deadline_timer.h +++ b/libtransport/src/utils/fd_deadline_timer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,6 +16,7 @@ #pragma once #include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/utils/chrono_typedefs.h> #include <sys/timerfd.h> #include <unistd.h> #include <utils/deadline_timer.h> @@ -37,6 +38,11 @@ class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> { } } + FdDeadlineTimer(const FdDeadlineTimer &other) + : reactor_(other.reactor_), + timer_fd_(other.timer_fd_), + flags_(other.flags_) {} + ~FdDeadlineTimer() { close(timer_fd_); } template <typename WaitHandler> diff --git a/libtransport/src/utils/log.cc b/libtransport/src/utils/log.cc index 755d5bafa..44acf4595 100644 --- a/libtransport/src/utils/log.cc +++ b/libtransport/src/utils/log.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -36,7 +36,7 @@ foreach_log_level static constexpr char log_config_section[] = "log"; #define LOG_NAME \ "Libhicntransport-" HICNTRANSPORT_VERSION_MAJOR \ - "." HICNTRANSPORT_VERSION_MINOR "." HICNTRANSPORT_VERSION_REVISION + "." HICNTRANSPORT_VERSION_MINOR "." HICNTRANSPORT_VERSION_PATCH static constexpr char log_name[] = LOG_NAME; #define foreach_log_config \ diff --git a/libtransport/src/utils/max_filter.h b/libtransport/src/utils/max_filter.h new file mode 100644 index 000000000..7a2c6aace --- /dev/null +++ b/libtransport/src/utils/max_filter.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License.sudo make instamake install + */ + +#pragma once + +#include <deque> +#include <iostream> +#include <set> +#include <type_traits> +#include <vector> + +namespace utils { + +template <typename T> +class MaxFilter { + public: + MaxFilter(std::size_t size) : size_(size) {} + + std::size_t size() { return by_arrival_.size(); } + + template <typename R> + void pushBack(R&& value) { + if (by_arrival_.size() >= size_) { + by_order_.erase(by_arrival_.back()); + by_arrival_.pop_back(); + } + + by_arrival_.push_front(by_order_.insert(std::forward<R>(value))); + } + + void clear() { + by_arrival_.clear(); + by_order_.clear(); + } + + const T& begin() { return *by_order_.crbegin(); } + + const T& rBegin() { return *by_order_.rbegin(); } + + private: + std::multiset<T> by_order_; + std::deque<typename std::multiset<T>::const_iterator> by_arrival_; + std::size_t size_; +}; + +} // namespace utils diff --git a/libtransport/src/utils/membuf.cc b/libtransport/src/utils/membuf.cc index 73c45cf6d..952116bb7 100644 --- a/libtransport/src/utils/membuf.cc +++ b/libtransport/src/utils/membuf.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Copyright 2013-present Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include <hicn/transport/portability/win_portability.h> #endif +#include <glog/logging.h> #include <hicn/transport/utils/membuf.h> #include <cassert> @@ -209,7 +210,7 @@ MemBuf::MemBuf(CopyBufferOp /* op */, const void* buf, std::size_t size, : MemBuf(CREATE, headroom + size + min_tailroom) { advance(headroom); if (size > 0) { - assert(buf != nullptr); + DCHECK(buf != nullptr); memcpy(writableData(), buf, size); append(size); } @@ -369,8 +370,8 @@ MemBuf::MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, length_(length), capacity_(capacity), flags_and_shared_info_(flagsAndSharedInfo) { - assert(data >= buf); - assert(data + length <= buf + capacity); + DCHECK(data >= buf); + DCHECK(data + length <= buf + capacity); } MemBuf::~MemBuf() { @@ -559,7 +560,7 @@ void MemBuf::unshareOneSlow() { // minimum capacity we also maintain at least the same amount of tailroom. std::size_t headlen = headroom(); if (length_ > 0) { - assert(data_ != nullptr); + DCHECK(data_ != nullptr); memcpy(buf + headlen, data_, length_); } @@ -576,7 +577,7 @@ void MemBuf::unshareOneSlow() { void MemBuf::unshareChained() { // unshareChained() should only be called if we are part of a chain of // multiple MemBufs. The caller should have already verified this. - assert(isChained()); + DCHECK(isChained()); MemBuf* current = this; while (true) { @@ -606,7 +607,7 @@ void MemBuf::markExternallyShared() { } void MemBuf::makeManagedChained() { - assert(isChained()); + DCHECK(isChained()); MemBuf* current = this; while (true) { @@ -677,15 +678,15 @@ void MemBuf::coalesceAndReallocate(size_t new_headroom, size_t new_length, size_t remaining = new_length; do { if (current->length_ > 0) { - assert(current->length_ <= remaining); - assert(current->data_ != nullptr); + DCHECK(current->length_ <= remaining); + DCHECK(current->data_ != nullptr); remaining -= current->length_; memcpy(p, current->data_, current->length_); p += current->length_; } current = current->next_; } while (current != end); - assert(remaining == 0); + DCHECK(remaining == 0); // Point at the new buffer decrementRefcount(); @@ -799,7 +800,7 @@ void MemBuf::reserveSlow(std::size_t min_headroom, std::size_t min_tailroom) { new_allocated_capacity = goodExtBufferSize(new_capacity); new_buffer = static_cast<uint8_t*>(malloc(new_allocated_capacity)); if (length_ > 0) { - assert(data_ != nullptr); + DCHECK(data_ != nullptr); memcpy(new_buffer + min_headroom, data_, length_); } if (sharedInfo()) { diff --git a/libtransport/src/utils/memory_pool_allocator.h b/libtransport/src/utils/memory_pool_allocator.h index a960b91bb..c2b34e7aa 100644 --- a/libtransport/src/utils/memory_pool_allocator.h +++ b/libtransport/src/utils/memory_pool_allocator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/min_filter.h b/libtransport/src/utils/min_filter.h index 092555ce0..4c7ae81f1 100644 --- a/libtransport/src/utils/min_filter.h +++ b/libtransport/src/utils/min_filter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/stream_buffer.h b/libtransport/src/utils/stream_buffer.h index adfb696f2..dde769a55 100644 --- a/libtransport/src/utils/stream_buffer.h +++ b/libtransport/src/utils/stream_buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/string_tokenizer.cc b/libtransport/src/utils/string_tokenizer.cc index a280a3c43..cb0a4a3ad 100644 --- a/libtransport/src/utils/string_tokenizer.cc +++ b/libtransport/src/utils/string_tokenizer.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/suffix_strategy.h b/libtransport/src/utils/suffix_strategy.h index ee016308e..96eaed662 100644 --- a/libtransport/src/utils/suffix_strategy.h +++ b/libtransport/src/utils/suffix_strategy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,72 +15,88 @@ #pragma once -#include <core/manifest_format.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/errors/runtime_exception.h> namespace utils { -using transport::core::NextSegmentCalculationStrategy; +/** + * INCREMENTAL: Manifests will be received inline with the data with no specific + * assumption regarding the manifest capacity. Consumers can send interests + * using a +1 heuristic. + * + * MANIFEST_CAPACITY_BASED: manifests with capacity N have a suffix multiple of + * N+1: 0, N+1, 2(N+1) etc. Contents have a suffix incremented by 1 except when + * it conflicts with a manifest: 1, 2, ..., N, N+2, N+3, ..., 2N+1, 2N+3 + */ +enum class NextSuffixStrategy : uint8_t { + INCREMENTAL = 1, +}; class SuffixStrategy { public: - static constexpr uint32_t INVALID_SUFFIX = - std::numeric_limits<uint32_t>::max(); + static constexpr uint32_t MAX_SUFFIX = std::numeric_limits<uint32_t>::max(); + static constexpr uint8_t MAX_MANIFEST_CAPACITY = + std::numeric_limits<uint8_t>::max(); - SuffixStrategy(NextSegmentCalculationStrategy strategy) + SuffixStrategy(NextSuffixStrategy strategy, uint32_t offset = 0, + uint32_t manifest_capacity = MAX_MANIFEST_CAPACITY) : suffix_stragegy_(strategy), + next_suffix_(offset), + manifest_capacity_(manifest_capacity), total_count_(0), - final_suffix_(INVALID_SUFFIX) {} + final_suffix_(MAX_SUFFIX) {} virtual ~SuffixStrategy() = default; - virtual uint32_t checkNextSuffix() = 0; - + virtual uint32_t checkNextSuffix() const = 0; virtual uint32_t getNextSuffix() = 0; - virtual uint32_t getFinalSuffix() { return final_suffix_; } - - virtual void setFinalSuffix(std::uint32_t final_suffix) { - if (final_suffix != INVALID_SUFFIX) { - final_suffix_ = final_suffix; - } - } - - virtual uint32_t checkNextManifestSuffix() = 0; - + virtual uint32_t checkNextManifestSuffix() const = 0; virtual uint32_t getNextManifestSuffix() = 0; - virtual uint32_t checkNextContentSuffix() = 0; - + virtual uint32_t checkNextContentSuffix() const = 0; virtual uint32_t getNextContentSuffix() = 0; - virtual void reset(uint32_t offset = 0) = 0; + virtual void reset(uint32_t offset = 0) { + next_suffix_ = offset; + total_count_ = 0; + } - virtual uint32_t getManifestCapacity() = 0; + virtual uint32_t getManifestCapacity() const { return manifest_capacity_; }; - virtual void setManifestCapacity(uint32_t capacity) = 0; + virtual void setManifestCapacity(uint8_t capacity) { + manifest_capacity_ = capacity; + } - virtual uint32_t getTotalCount() { return total_count_; }; + virtual uint32_t getFinalSuffix() const { return final_suffix_; } - NextSegmentCalculationStrategy getSuffixStrategy() { - return suffix_stragegy_; + virtual void setFinalSuffix(std::uint32_t final_suffix) { + if (final_suffix != MAX_SUFFIX) { + final_suffix_ = final_suffix; + } } - protected: - inline void incrementTotalCount() { total_count_++; }; + NextSuffixStrategy getSuffixStrategy() const { return suffix_stragegy_; } + + virtual uint32_t getTotalCount() const { return total_count_; } protected: - NextSegmentCalculationStrategy suffix_stragegy_; + NextSuffixStrategy suffix_stragegy_; + std::uint32_t next_suffix_; + std::uint8_t manifest_capacity_; std::uint32_t total_count_; std::uint32_t final_suffix_; + + inline void incrementTotalCount() { total_count_++; }; }; class IncrementalSuffixStrategy : public SuffixStrategy { public: IncrementalSuffixStrategy(std::uint32_t start_offset) - : SuffixStrategy(NextSegmentCalculationStrategy::INCREMENTAL), - next_suffix_(start_offset) {} + : SuffixStrategy(NextSuffixStrategy::INCREMENTAL, start_offset) {} - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() override { + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() const override { return next_suffix_; } @@ -89,7 +105,8 @@ class IncrementalSuffixStrategy : public SuffixStrategy { return next_suffix_++; } - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextContentSuffix() override { + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextContentSuffix() + const override { return checkNextSuffix(); } @@ -97,7 +114,8 @@ class IncrementalSuffixStrategy : public SuffixStrategy { return getNextSuffix(); } - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() override { + TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() + const override { return checkNextSuffix(); } @@ -105,90 +123,20 @@ class IncrementalSuffixStrategy : public SuffixStrategy { return getNextSuffix(); } - uint32_t getManifestCapacity() override { - throw errors::RuntimeException( - "No manifest capacity in IncrementalSuffixStrategy."); - } - - void setManifestCapacity(uint32_t capacity) override { - throw errors::RuntimeException( - "No manifest capacity in IncrementalSuffixStrategy."); - } - void reset(std::uint32_t offset = 0) override { next_suffix_ = offset; } - - protected: - std::uint32_t next_suffix_; -}; - -class CapacityBasedSuffixStrategy : public SuffixStrategy { - public: - CapacityBasedSuffixStrategy(std::uint32_t start_offset, - std::uint32_t manifest_capacity) - : SuffixStrategy(NextSegmentCalculationStrategy::INCREMENTAL), - next_suffix_(start_offset), - segments_in_manifest_(manifest_capacity), - current_manifest_iteration_(0) {} - - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextSuffix() override { - return next_suffix_; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextSuffix() override { - incrementTotalCount(); - return next_suffix_++; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextContentSuffix() override { - return next_suffix_ % segments_in_manifest_ == 0 ? next_suffix_ - : (next_suffix_ + 1); - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextContentSuffix() override { - incrementTotalCount(); - return next_suffix_ % segments_in_manifest_ == 0 ? next_suffix_++ - : ++next_suffix_; - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t checkNextManifestSuffix() override { - return (current_manifest_iteration_ + 1) * (segments_in_manifest_ + 1); - } - - TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override { - incrementTotalCount(); - return (current_manifest_iteration_++) * (segments_in_manifest_ + 1); - } - - TRANSPORT_ALWAYS_INLINE uint32_t getManifestCapacity() override { - return segments_in_manifest_; - } - - TRANSPORT_ALWAYS_INLINE void setManifestCapacity(uint32_t capacity) override { - segments_in_manifest_ = capacity; - } - - void reset(std::uint32_t offset = 0) override { next_suffix_ = offset; } - - protected: - std::uint32_t next_suffix_; - std::uint32_t segments_in_manifest_; - std::uint32_t current_manifest_iteration_; }; class SuffixStrategyFactory { public: static std::unique_ptr<SuffixStrategy> getSuffixStrategy( - NextSegmentCalculationStrategy strategy, uint32_t start_offset, - uint32_t manifest_capacity = 0) { + NextSuffixStrategy strategy, uint32_t start_offset = 0, + uint32_t manifest_capacity = SuffixStrategy::MAX_MANIFEST_CAPACITY) { switch (strategy) { - case NextSegmentCalculationStrategy::INCREMENTAL: + case NextSuffixStrategy::INCREMENTAL: return std::make_unique<IncrementalSuffixStrategy>(start_offset); - case NextSegmentCalculationStrategy::MANIFEST_CAPACITY_BASED: - return std::make_unique<CapacityBasedSuffixStrategy>(start_offset, - manifest_capacity); default: throw errors::RuntimeException( - "No valid NextSegmentCalculationStrategy specified."); + "No valid NextSuffixStrategy specified."); } } }; diff --git a/libtransport/src/utils/test.h b/libtransport/src/utils/test.h index e3dd619ac..b91c8cb1f 100644 --- a/libtransport/src/utils/test.h +++ b/libtransport/src/utils/test.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/libtransport/src/utils/uri.cc b/libtransport/src/utils/uri.cc index 33eb8b45b..12b11641c 100644 --- a/libtransport/src/utils/uri.cc +++ b/libtransport/src/utils/uri.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: |