aboutsummaryrefslogtreecommitdiffstats
path: root/libtransport/src/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'libtransport/src/protocols')
-rw-r--r--libtransport/src/protocols/CMakeLists.txt53
-rw-r--r--libtransport/src/protocols/byte_stream_reassembly.cc84
-rw-r--r--libtransport/src/protocols/byte_stream_reassembly.h13
-rw-r--r--libtransport/src/protocols/cbr.cc12
-rw-r--r--libtransport/src/protocols/cbr.h5
-rw-r--r--libtransport/src/protocols/congestion_window_protocol.h2
-rw-r--r--libtransport/src/protocols/data_processing_events.h6
-rw-r--r--libtransport/src/protocols/datagram_reassembly.cc18
-rw-r--r--libtransport/src/protocols/datagram_reassembly.h10
-rw-r--r--libtransport/src/protocols/errors.cc17
-rw-r--r--libtransport/src/protocols/errors.h6
-rw-r--r--libtransport/src/protocols/fec/CMakeLists.txt36
-rw-r--r--libtransport/src/protocols/fec/fec.cc722
-rw-r--r--libtransport/src/protocols/fec/fec.h65
-rw-r--r--libtransport/src/protocols/fec/fec_info.h62
-rw-r--r--libtransport/src/protocols/fec/rely.cc234
-rw-r--r--libtransport/src/protocols/fec/rely.h215
-rw-r--r--libtransport/src/protocols/fec/rs.cc452
-rw-r--r--libtransport/src/protocols/fec/rs.h497
-rw-r--r--libtransport/src/protocols/fec_base.h173
-rw-r--r--libtransport/src/protocols/fec_utils.h155
-rw-r--r--libtransport/src/protocols/incremental_indexer.cc55
-rw-r--r--libtransport/src/protocols/incremental_indexer.h143
-rw-r--r--libtransport/src/protocols/incremental_indexer_bytestream.cc65
-rw-r--r--libtransport/src/protocols/incremental_indexer_bytestream.h119
-rw-r--r--libtransport/src/protocols/index_manager_bytestream.cc72
-rw-r--r--libtransport/src/protocols/index_manager_bytestream.h97
-rw-r--r--libtransport/src/protocols/indexer.cc93
-rw-r--r--libtransport/src/protocols/indexer.h111
-rw-r--r--libtransport/src/protocols/manifest_incremental_indexer.cc236
-rw-r--r--libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc204
-rw-r--r--libtransport/src/protocols/manifest_incremental_indexer_bytestream.h (renamed from libtransport/src/protocols/manifest_incremental_indexer.h)56
-rw-r--r--libtransport/src/protocols/packet_manager.h65
-rw-r--r--libtransport/src/protocols/prod_protocol_bytestream.cc369
-rw-r--r--libtransport/src/protocols/prod_protocol_bytestream.h75
-rw-r--r--libtransport/src/protocols/prod_protocol_rtc.cc747
-rw-r--r--libtransport/src/protocols/prod_protocol_rtc.h150
-rw-r--r--libtransport/src/protocols/production_protocol.cc131
-rw-r--r--libtransport/src/protocols/production_protocol.h134
-rw-r--r--libtransport/src/protocols/protocol.cc137
-rw-r--r--libtransport/src/protocols/protocol.h129
-rw-r--r--libtransport/src/protocols/raaqm.cc243
-rw-r--r--libtransport/src/protocols/raaqm.h62
-rw-r--r--libtransport/src/protocols/raaqm_data_path.cc32
-rw-r--r--libtransport/src/protocols/raaqm_data_path.h17
-rw-r--r--libtransport/src/protocols/rate_estimation.cc104
-rw-r--r--libtransport/src/protocols/rate_estimation.h22
-rw-r--r--libtransport/src/protocols/reassembly.cc5
-rw-r--r--libtransport/src/protocols/reassembly.h37
-rw-r--r--libtransport/src/protocols/rtc.cc984
-rw-r--r--libtransport/src/protocols/rtc.h228
-rw-r--r--libtransport/src/protocols/rtc/CMakeLists.txt59
-rw-r--r--libtransport/src/protocols/rtc/probe_handler.cc141
-rw-r--r--libtransport/src/protocols/rtc/probe_handler.h93
-rw-r--r--libtransport/src/protocols/rtc/rtc.cc1101
-rw-r--r--libtransport/src/protocols/rtc/rtc.h151
-rw-r--r--libtransport/src/protocols/rtc/rtc_consts.h214
-rw-r--r--libtransport/src/protocols/rtc/rtc_data_path.cc251
-rw-r--r--libtransport/src/protocols/rtc/rtc_data_path.h114
-rw-r--r--libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc217
-rw-r--r--libtransport/src/protocols/rtc/rtc_forwarding_strategy.h82
-rw-r--r--libtransport/src/protocols/rtc/rtc_indexer.h174
-rw-r--r--libtransport/src/protocols/rtc/rtc_ldr.cc236
-rw-r--r--libtransport/src/protocols/rtc/rtc_ldr.h85
-rw-r--r--libtransport/src/protocols/rtc/rtc_packet.h256
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc.h62
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc74
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h47
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_iat.cc287
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_iat.h93
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_queue.cc106
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_queue.h48
-rw-r--r--libtransport/src/protocols/rtc/rtc_reassembly.cc109
-rw-r--r--libtransport/src/protocols/rtc/rtc_reassembly.h43
-rw-r--r--libtransport/src/protocols/rtc/rtc_recovery_strategy.cc420
-rw-r--r--libtransport/src/protocols/rtc/rtc_recovery_strategy.h181
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_delay.cc147
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_delay.h55
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_fec_only.cc143
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_fec_only.h53
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_low_rate.cc174
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_low_rate.h71
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc65
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_recovery_off.h46
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc67
-rw-r--r--libtransport/src/protocols/rtc/rtc_rs_rtx_only.h46
-rw-r--r--libtransport/src/protocols/rtc/rtc_state.cc900
-rw-r--r--libtransport/src/protocols/rtc/rtc_state.h410
-rw-r--r--libtransport/src/protocols/rtc/rtc_verifier.cc248
-rw-r--r--libtransport/src/protocols/rtc/rtc_verifier.h96
-rw-r--r--libtransport/src/protocols/rtc_data_path.cc156
-rw-r--r--libtransport/src/protocols/rtc_data_path.h80
-rw-r--r--libtransport/src/protocols/test/CMakeLists.txt10
-rw-r--r--libtransport/src/protocols/test/test_transport_producer.cc80
-rw-r--r--libtransport/src/protocols/transport_protocol.cc233
-rw-r--r--libtransport/src/protocols/transport_protocol.h150
-rw-r--r--libtransport/src/protocols/verification_manager.cc101
-rw-r--r--libtransport/src/protocols/verification_manager.h71
98 files changed, 12872 insertions, 2933 deletions
diff --git a/libtransport/src/protocols/CMakeLists.txt b/libtransport/src/protocols/CMakeLists.txt
index 8bfbdd6ad..51879a9ed 100644
--- a/libtransport/src/protocols/CMakeLists.txt
+++ b/libtransport/src/protocols/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Copyright (c) 2021 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -11,50 +11,56 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
-
list(APPEND HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/indexer.h
- ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer.h
- ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer_bytestream.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer_bytestream.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/index_manager_bytestream.h
${CMAKE_CURRENT_SOURCE_DIR}/reassembly.h
${CMAKE_CURRENT_SOURCE_DIR}/datagram_reassembly.h
${CMAKE_CURRENT_SOURCE_DIR}/byte_stream_reassembly.h
${CMAKE_CURRENT_SOURCE_DIR}/congestion_window_protocol.h
- ${CMAKE_CURRENT_SOURCE_DIR}/packet_manager.h
${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.h
- ${CMAKE_CURRENT_SOURCE_DIR}/protocol.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/transport_protocol.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/production_protocol.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_bytestream.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_rtc.h
${CMAKE_CURRENT_SOURCE_DIR}/raaqm.h
${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.h
${CMAKE_CURRENT_SOURCE_DIR}/cbr.h
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h
${CMAKE_CURRENT_SOURCE_DIR}/errors.h
- ${CMAKE_CURRENT_SOURCE_DIR}/verification_manager.h
${CMAKE_CURRENT_SOURCE_DIR}/data_processing_events.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fec_base.h
)
list(APPEND SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/indexer.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/incremental_indexer_bytestream.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/manifest_incremental_indexer_bytestream.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/index_manager_bytestream.cc
${CMAKE_CURRENT_SOURCE_DIR}/reassembly.cc
${CMAKE_CURRENT_SOURCE_DIR}/datagram_reassembly.cc
${CMAKE_CURRENT_SOURCE_DIR}/byte_stream_reassembly.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/protocol.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/transport_protocol.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/production_protocol.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_bytestream.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/prod_protocol_rtc.cc
${CMAKE_CURRENT_SOURCE_DIR}/raaqm.cc
${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.cc
${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.cc
${CMAKE_CURRENT_SOURCE_DIR}/cbr.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc
${CMAKE_CURRENT_SOURCE_DIR}/errors.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/verification_manager.cc
)
-set(RAAQM_CONFIG_INSTALL_PREFIX
- ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn
-)
+if (${CMAKE_SYSTEM_NAME} MATCHES Darwin OR ${CMAKE_SYSTEM_NAME} MATCHES Linux)
+ set(RAAQM_CONFIG_INSTALL_PREFIX
+ ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn
+ )
+else()
+ set(RAAQM_CONFIG_INSTALL_PREFIX
+ ${CMAKE_INSTALL_PREFIX}/etc/hicn
+ )
+endif()
set(raaqm_config_path
${RAAQM_CONFIG_INSTALL_PREFIX}/consumer.conf
@@ -67,9 +73,12 @@ set(TRANSPORT_CONFIG
install(
FILES ${TRANSPORT_CONFIG}
- DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn
- COMPONENT lib${LIBTRANSPORT}
+ DESTINATION ${RAAQM_CONFIG_INSTALL_PREFIX}
+ COMPONENT ${LIBTRANSPORT_COMPONENT}
)
+add_subdirectory(rtc)
+add_subdirectory(fec)
+
set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
-set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
diff --git a/libtransport/src/protocols/byte_stream_reassembly.cc b/libtransport/src/protocols/byte_stream_reassembly.cc
index 6662bec3f..b9eaf3bec 100644
--- a/libtransport/src/protocols/byte_stream_reassembly.cc
+++ b/libtransport/src/protocols/byte_stream_reassembly.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -20,7 +20,7 @@
#include <protocols/byte_stream_reassembly.h>
#include <protocols/errors.h>
#include <protocols/indexer.h>
-#include <protocols/protocol.h>
+#include <protocols/transport_protocol.h>
namespace transport {
@@ -33,31 +33,26 @@ ByteStreamReassembly::ByteStreamReassembly(
implementation::ConsumerSocket *icn_socket,
TransportProtocol *transport_protocol)
: Reassembly(icn_socket, transport_protocol),
- index_(IndexManager::invalid_index),
+ index_(Indexer::invalid_index),
download_complete_(false) {}
-void ByteStreamReassembly::reassemble(
- std::unique_ptr<ContentObjectManifest> &&manifest) {
- if (TRANSPORT_EXPECT_TRUE(manifest != nullptr) && read_buffer_->capacity()) {
+void ByteStreamReassembly::reassemble(ContentObject &content_object) {
+ if (TRANSPORT_EXPECT_TRUE(read_buffer_->capacity())) {
received_packets_.emplace(
- std::make_pair(manifest->getName().getSuffix(), nullptr));
+ std::make_pair(content_object.getName().getSuffix(),
+ content_object.shared_from_this()));
assembleContent();
}
}
-void ByteStreamReassembly::reassemble(ContentObject::Ptr &&content_object) {
- if (TRANSPORT_EXPECT_TRUE(content_object != nullptr) &&
- read_buffer_->capacity()) {
- received_packets_.emplace(std::make_pair(
- content_object->getName().getSuffix(), std::move(content_object)));
- assembleContent();
- }
+void ByteStreamReassembly::reassemble(utils::MemBuf &buffer, uint32_t suffix) {
+ throw errors::NotImplementedException();
}
void ByteStreamReassembly::assembleContent() {
- if (TRANSPORT_EXPECT_FALSE(index_ == IndexManager::invalid_index)) {
- index_ = index_manager_->getNextReassemblySegment();
- if (index_ == IndexManager::invalid_index) {
+ if (TRANSPORT_EXPECT_FALSE(index_ == Indexer::invalid_index)) {
+ index_ = indexer_verifier_->getNextReassemblySegment();
+ if (index_ == Indexer::invalid_index) {
return;
}
}
@@ -72,37 +67,44 @@ void ByteStreamReassembly::assembleContent() {
}
received_packets_.erase(it);
- index_ = index_manager_->getNextReassemblySegment();
+ index_ = indexer_verifier_->getNextReassemblySegment();
it = received_packets_.find((const unsigned int)index_);
}
- if (!download_complete_ && index_ != IndexManager::invalid_index) {
+ if (!download_complete_ && index_ != Indexer::invalid_index) {
transport_protocol_->onReassemblyFailed(index_);
}
}
-bool ByteStreamReassembly::copyContent(const ContentObject &content_object) {
+bool ByteStreamReassembly::copyContent(ContentObject &content_object) {
bool ret = false;
- auto payload = content_object.getPayloadReference();
- auto payload_length = payload.second;
- auto write_size = std::min(payload_length, read_buffer_->tailroom());
- auto additional_bytes = payload_length > read_buffer_->tailroom()
- ? payload_length - read_buffer_->tailroom()
- : 0;
+ content_object.trimStart(content_object.headerSize());
- std::memcpy(read_buffer_->writableTail(), payload.first, write_size);
- read_buffer_->append(write_size);
+ utils::MemBuf *current = &content_object;
- if (!read_buffer_->tailroom()) {
- notifyApplication();
- std::memcpy(read_buffer_->writableTail(), payload.first + write_size,
- additional_bytes);
- read_buffer_->append(additional_bytes);
- }
+ do {
+ auto payload_length = current->length();
+ auto write_size = std::min(payload_length, read_buffer_->tailroom());
+ auto additional_bytes = payload_length > read_buffer_->tailroom()
+ ? payload_length - read_buffer_->tailroom()
+ : 0;
- download_complete_ =
- index_manager_->getFinalSuffix() == content_object.getName().getSuffix();
+ std::memcpy(read_buffer_->writableTail(), current->data(), write_size);
+ read_buffer_->append(write_size);
+
+ if (!read_buffer_->tailroom()) {
+ notifyApplication();
+ std::memcpy(read_buffer_->writableTail(), current->data() + write_size,
+ additional_bytes);
+ read_buffer_->append(additional_bytes);
+ }
+
+ current = current->next();
+ } while (current != &content_object);
+
+ download_complete_ = indexer_verifier_->getFinalSuffix() ==
+ content_object.getName().getSuffix();
if (TRANSPORT_EXPECT_FALSE(download_complete_)) {
ret = download_complete_;
@@ -115,17 +117,19 @@ bool ByteStreamReassembly::copyContent(const ContentObject &content_object) {
}
void ByteStreamReassembly::reInitialize() {
- index_ = IndexManager::invalid_index;
+ index_ = Indexer::invalid_index;
download_complete_ = false;
received_packets_.clear();
// reset read buffer
ReadCallback *read_callback;
- reassembly_consumer_socket_->getSocketOption(
- interface::ConsumerCallbacksOptions::READ_CALLBACK, &read_callback);
- read_buffer_ = utils::MemBuf::create(read_callback->maxBufferSize());
+ if (reassembly_consumer_socket_) {
+ reassembly_consumer_socket_->getSocketOption(
+ interface::ConsumerCallbacksOptions::READ_CALLBACK, &read_callback);
+ read_buffer_ = utils::MemBuf::create(read_callback->maxBufferSize());
+ }
}
} // namespace protocol
diff --git a/libtransport/src/protocols/byte_stream_reassembly.h b/libtransport/src/protocols/byte_stream_reassembly.h
index e4f62b3a8..a1f965d5c 100644
--- a/libtransport/src/protocols/byte_stream_reassembly.h
+++ b/libtransport/src/protocols/byte_stream_reassembly.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -27,12 +27,11 @@ class ByteStreamReassembly : public Reassembly {
TransportProtocol *transport_protocol);
protected:
- virtual void reassemble(core::ContentObject::Ptr &&content_object) override;
+ void reassemble(core::ContentObject &content_object) override;
- virtual void reassemble(
- std::unique_ptr<core::ContentObjectManifest> &&manifest) override;
+ void reassemble(utils::MemBuf &buffer, uint32_t suffix) override;
- bool copyContent(const core::ContentObject &content_object);
+ bool copyContent(core::ContentObject &content_object);
virtual void reInitialize() override;
@@ -40,10 +39,6 @@ class ByteStreamReassembly : public Reassembly {
void assembleContent();
protected:
- // The consumer socket
- // std::unique_ptr<IncrementalIndexManager> incremental_index_manager_;
- // std::unique_ptr<ManifestIndexManager> manifest_index_manager_;
- // IndexVerificationManager *index_manager_;
std::unordered_map<std::uint32_t, core::ContentObject::Ptr> received_packets_;
uint32_t index_;
bool download_complete_;
diff --git a/libtransport/src/protocols/cbr.cc b/libtransport/src/protocols/cbr.cc
index 0bffd7d18..e3f0f1336 100644
--- a/libtransport/src/protocols/cbr.cc
+++ b/libtransport/src/protocols/cbr.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -26,8 +26,6 @@ CbrTransportProtocol::CbrTransportProtocol(
implementation::ConsumerSocket *icnet_socket)
: RaaqmTransportProtocol(icnet_socket) {}
-int CbrTransportProtocol::start() { return RaaqmTransportProtocol::start(); }
-
void CbrTransportProtocol::reset() {
RaaqmTransportProtocol::reset();
socket_->getSocketOption(GeneralTransportOptions::CURRENT_WINDOW_SIZE,
@@ -39,11 +37,11 @@ void CbrTransportProtocol::afterDataUnsatisfied(uint64_t segment) {}
void CbrTransportProtocol::afterContentReception(
const Interest &interest, const ContentObject &content_object) {
auto segment = content_object.getName().getSuffix();
- auto now = utils::SteadyClock::now();
- auto rtt = std::chrono::duration_cast<utils::Microseconds>(
- now - interest_timepoints_[segment & mask]);
+ auto now = utils::SteadyTime::Clock::now();
+ auto rtt = utils::SteadyTime::getDurationUs(
+ interest_timepoints_[segment & mask], now);
// Update stats
- updateStats(segment, rtt.count(), now);
+ updateStats(segment, rtt, now);
}
} // end namespace protocol
diff --git a/libtransport/src/protocols/cbr.h b/libtransport/src/protocols/cbr.h
index 20129f6a3..c178dbf60 100644
--- a/libtransport/src/protocols/cbr.h
+++ b/libtransport/src/protocols/cbr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -25,7 +25,8 @@ class CbrTransportProtocol : public RaaqmTransportProtocol {
public:
CbrTransportProtocol(implementation::ConsumerSocket *icnet_socket);
- int start() override;
+ using RaaqmTransportProtocol::start;
+ using RaaqmTransportProtocol::stop;
void reset() override;
diff --git a/libtransport/src/protocols/congestion_window_protocol.h b/libtransport/src/protocols/congestion_window_protocol.h
index 36ac6eb17..f9ff208cc 100644
--- a/libtransport/src/protocols/congestion_window_protocol.h
+++ b/libtransport/src/protocols/congestion_window_protocol.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
diff --git a/libtransport/src/protocols/data_processing_events.h b/libtransport/src/protocols/data_processing_events.h
index 8975c2b4a..182de3ed8 100644
--- a/libtransport/src/protocols/data_processing_events.h
+++ b/libtransport/src/protocols/data_processing_events.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -24,8 +24,8 @@ namespace protocol {
class ContentObjectProcessingEventCallback {
public:
virtual ~ContentObjectProcessingEventCallback() = default;
- virtual void onPacketDropped(core::Interest::Ptr &&i,
- core::ContentObject::Ptr &&c) = 0;
+ virtual void onPacketDropped(core::Interest &i, core::ContentObject &c,
+ const std::error_code &reason) = 0;
virtual void onReassemblyFailed(std::uint32_t missing_segment) = 0;
};
diff --git a/libtransport/src/protocols/datagram_reassembly.cc b/libtransport/src/protocols/datagram_reassembly.cc
index abd7e984d..a04b0eecf 100644
--- a/libtransport/src/protocols/datagram_reassembly.cc
+++ b/libtransport/src/protocols/datagram_reassembly.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -14,6 +14,7 @@
*/
#include <protocols/datagram_reassembly.h>
+#include <protocols/transport_protocol.h>
namespace transport {
@@ -24,8 +25,19 @@ DatagramReassembly::DatagramReassembly(
TransportProtocol* transport_protocol)
: Reassembly(icn_socket, transport_protocol) {}
-void DatagramReassembly::reassemble(core::ContentObject::Ptr&& content_object) {
- read_buffer_ = content_object->getPayload();
+void DatagramReassembly::reassemble(core::ContentObject& content_object) {
+ auto read_buffer = content_object.getPayload();
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Size of payload: " << read_buffer->length() << ". Trimming "
+ << transport_protocol_->transportHeaderLength(false);
+ // here we have only src data packet
+ read_buffer->trimStart(transport_protocol_->transportHeaderLength(false));
+ Reassembly::read_buffer_ = std::move(read_buffer);
+ Reassembly::notifyApplication();
+}
+
+void DatagramReassembly::reassemble(utils::MemBuf& buffer, uint32_t suffix) {
+ read_buffer_ = buffer.cloneOne();
Reassembly::notifyApplication();
}
diff --git a/libtransport/src/protocols/datagram_reassembly.h b/libtransport/src/protocols/datagram_reassembly.h
index 2427ae62f..cefdca93b 100644
--- a/libtransport/src/protocols/datagram_reassembly.h
+++ b/libtransport/src/protocols/datagram_reassembly.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -26,12 +26,10 @@ class DatagramReassembly : public Reassembly {
DatagramReassembly(implementation::ConsumerSocket *icn_socket,
TransportProtocol *transport_protocol);
- virtual void reassemble(core::ContentObject::Ptr &&content_object) override;
+ virtual void reassemble(core::ContentObject &content_object) override;
+ void reassemble(utils::MemBuf &buffer, uint32_t suffix) override;
virtual void reInitialize() override;
- virtual void reassemble(
- std::unique_ptr<core::ContentObjectManifest> &&manifest) override {
- return;
- }
+ bool reassembleUnverified() override { return true; }
};
} // namespace protocol
diff --git a/libtransport/src/protocols/errors.cc b/libtransport/src/protocols/errors.cc
index eefb6f957..a7dd26e16 100644
--- a/libtransport/src/protocols/errors.cc
+++ b/libtransport/src/protocols/errors.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -39,6 +39,9 @@ std::string protocol_category_impl::message(int ev) const {
case protocol_error::integrity_verification_failed: {
return "Integrity verification failed";
}
+ case protocol_error::verification_failed: {
+ return "Verification failed";
+ }
case protocol_error::no_verifier_provided: {
return "Transport cannot get any verifier for the given data.";
}
@@ -52,9 +55,17 @@ std::string protocol_category_impl::message(int ev) const {
case protocol_error::session_aborted: {
return "The session has been aborted by the application.";
}
- default: { return "Unknown protocol error"; }
+ case protocol_error::not_reassemblable: {
+ return "The session has been aborted by the application.";
+ }
+ case protocol_error::duplicated_content: {
+ return "The session has been aborted by the application.";
+ }
+ default: {
+ return "Unknown protocol error";
+ }
}
}
} // namespace protocol
-} // namespace transport \ No newline at end of file
+} // namespace transport
diff --git a/libtransport/src/protocols/errors.h b/libtransport/src/protocols/errors.h
index cb3d3474e..33d5fbee4 100644
--- a/libtransport/src/protocols/errors.h
+++ b/libtransport/src/protocols/errors.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -37,10 +37,14 @@ enum class protocol_error {
success = 0,
signature_verification_failed,
integrity_verification_failed,
+ verification_failed,
no_verifier_provided,
io_error,
max_retransmissions_error,
session_aborted,
+ not_reassemblable,
+ delayed_reassemble,
+ duplicated_content
};
/**
diff --git a/libtransport/src/protocols/fec/CMakeLists.txt b/libtransport/src/protocols/fec/CMakeLists.txt
new file mode 100644
index 000000000..8ae0a7360
--- /dev/null
+++ b/libtransport/src/protocols/fec/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (c) 2021 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/fec.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rs.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fec_info.h
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/fec.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rs.cc
+)
+
+if (ENABLE_RELY)
+ list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/rely.h
+ )
+
+ list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/rely.cc
+ )
+endif()
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
diff --git a/libtransport/src/protocols/fec/fec.cc b/libtransport/src/protocols/fec/fec.cc
new file mode 100644
index 000000000..d2105eb53
--- /dev/null
+++ b/libtransport/src/protocols/fec/fec.cc
@@ -0,0 +1,722 @@
+/*
+ * fec.c -- forward error correction based on Vandermonde matrices
+ * 980624
+ * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
+ *
+ * Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
+ * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
+ * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/*
+ * The following parameter defines how many bits are used for
+ * field elements. The code supports any value from 2 to 16
+ * but fastest operation is achieved with 8 bit elements
+ * This is the only parameter you may want to change.
+ */
+#ifndef GF_BITS
+#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */
+#endif
+
+#include "fec.h"
+
+#include <assert.h>
+#include <hicn/transport/portability/platform.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * XXX This disable a warning raising only in some platforms.
+ * TODO Check if this warning is a mistake or it is a real bug:
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83404
+ * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=88059
+ */
+#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+
+/*
+ * You should not need to change anything beyond this point.
+ * The first part of the file implements linear algebra in GF.
+ *
+ * gf is the type used to store an element of the Galois Field.
+ * Must constain at least GF_BITS bits.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium. We use int whenever have to deal with an
+ * index, since they are generally faster.
+ */
+#if (GF_BITS < 2 && GF_BITS > 16)
+#error "GF_BITS must be 2 .. 16"
+#endif
+
+#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */
+
+/*
+ * Primitive polynomials - see Lin & Costello, Appendix A,
+ * and Lee & Messerschmitt, p. 453.
+ */
+static const char *allPp[] = {
+ /* GF_BITS polynomial */
+ NULL, /* 0 no code */
+ NULL, /* 1 no code */
+ "111", /* 2 1+x+x^2 */
+ "1101", /* 3 1+x+x^3 */
+ "11001", /* 4 1+x+x^4 */
+ "101001", /* 5 1+x^2+x^5 */
+ "1100001", /* 6 1+x+x^6 */
+ "10010001", /* 7 1 + x^3 + x^7 */
+ "101110001", /* 8 1+x^2+x^3+x^4+x^8 */
+ "1000100001", /* 9 1+x^4+x^9 */
+ "10010000001", /* 10 1+x^3+x^10 */
+ "101000000001", /* 11 1+x^2+x^11 */
+ "1100101000001", /* 12 1+x+x^4+x^6+x^12 */
+ "11011000000001", /* 13 1+x+x^3+x^4+x^13 */
+ "110000100010001", /* 14 1+x+x^6+x^10+x^14 */
+ "1100000000000001", /* 15 1+x+x^15 */
+ "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */
+};
+
+/*
+ * To speed up computations, we have tables for logarithm, exponent
+ * and inverse of a number. If GF_BITS <= 8, we use a table for
+ * multiplication as well (it takes 64K, no big deal even on a PDA,
+ * especially because it can be pre-initialized an put into a ROM!),
+ * otherwhise we use a table of logarithms.
+ * In any case the macro gf_mul(x,y) takes care of multiplications.
+ */
+
+static gf gf_exp[2 * GF_SIZE]; /* index->poly form conversion table */
+static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */
+static gf inverse[GF_SIZE + 1]; /* inverse of field elem. */
+ /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */
+
+/*
+ * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1,
+ * without a slow divide.
+ */
+static inline gf modnn(int x) {
+ while (x >= GF_SIZE) {
+ x -= GF_SIZE;
+ x = (x >> GF_BITS) + (x & GF_SIZE);
+ }
+ return x;
+}
+
+#define SWAP(a, b, t) \
+ { \
+ t tmp; \
+ tmp = a; \
+ a = b; \
+ b = tmp; \
+ }
+
+/*
+ * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much
+ * faster to use a multiplication table.
+ *
+ * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying
+ * many numbers by the same constant. In this case the first
+ * call sets the constant, and others perform the multiplications.
+ * A value related to the multiplication is held in a local variable
+ * declared with USE_GF_MULC . See usage in addmul1().
+ */
+#if (GF_BITS <= 8)
+static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1];
+
+#define gf_mul(x, y) gf_mul_table[x][y]
+
+#define USE_GF_MULC gf *__gf_mulc_
+#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c]
+#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x]
+
+static void init_mul_table() {
+ int i, j;
+ for (i = 0; i < GF_SIZE + 1; i++)
+ for (j = 0; j < GF_SIZE + 1; j++)
+ gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j])];
+
+ for (j = 0; j < GF_SIZE + 1; j++) gf_mul_table[0][j] = gf_mul_table[j][0] = 0;
+}
+#else /* GF_BITS > 8 */
+static inline gf gf_mul(x, y) {
+ if ((x) == 0 || (y) == 0) return 0;
+
+ return gf_exp[gf_log[x] + gf_log[y]];
+}
+#define init_mul_table()
+
+#define USE_GF_MULC register gf *__gf_mulc_
+#define GF_MULC0(c) __gf_mulc_ = &gf_exp[gf_log[c]]
+#define GF_ADDMULC(dst, x) \
+ { \
+ if (x) dst ^= __gf_mulc_[gf_log[x]]; \
+ }
+#endif
+
+/*
+ * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
+ * Lookup tables:
+ * index->polynomial form gf_exp[] contains j= \alpha^i;
+ * polynomial form -> index form gf_log[ j = \alpha^i ] = i
+ * \alpha=x is the primitive element of GF(2^m)
+ *
+ * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple
+ * multiplication of two numbers can be resolved without calling modnn
+ */
+
+/*
+ * i use malloc so many times, it is easier to put checks all in
+ * one place.
+ */
+static void *my_malloc(int sz, const char *err_string) {
+ void *p = malloc(sz);
+ if (p == NULL) {
+ fprintf(stderr, "-- malloc failure allocating %s\n", err_string);
+ exit(1);
+ }
+ return p;
+}
+
+#define NEW_GF_MATRIX(rows, cols) \
+ (gf *)my_malloc(rows *cols * sizeof(gf), " ## __LINE__ ## ")
+
+/*
+ * initialize the data structures used for computations in GF.
+ */
+static void generate_gf(void) {
+ int i;
+ gf mask;
+ const char *Pp = allPp[GF_BITS];
+
+ mask = 1; /* x ** 0 = 1 */
+ gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */
+ /*
+ * first, generate the (polynomial representation of) powers of \alpha,
+ * which are stored in gf_exp[i] = \alpha ** i .
+ * At the same time build gf_log[gf_exp[i]] = i .
+ * The first GF_BITS powers are simply bits shifted to the left.
+ */
+ for (i = 0; i < GF_BITS; i++, mask <<= 1) {
+ gf_exp[i] = mask;
+ gf_log[gf_exp[i]] = i;
+ /*
+ * If Pp[i] == 1 then \alpha ** i occurs in poly-repr
+ * gf_exp[GF_BITS] = \alpha ** GF_BITS
+ */
+ if (Pp[i] == '1') gf_exp[GF_BITS] ^= mask;
+ }
+ /*
+ * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als
+ * compute its inverse.
+ */
+ gf_log[gf_exp[GF_BITS]] = GF_BITS;
+ /*
+ * Poly-repr of \alpha ** (i+1) is given by poly-repr of
+ * \alpha ** i shifted left one-bit and accounting for any
+ * \alpha ** GF_BITS term that may occur when poly-repr of
+ * \alpha ** i is shifted.
+ */
+ mask = 1 << (GF_BITS - 1);
+ for (i = GF_BITS + 1; i < GF_SIZE; i++) {
+ if (gf_exp[i - 1] >= mask)
+ gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1);
+ else
+ gf_exp[i] = gf_exp[i - 1] << 1;
+ gf_log[gf_exp[i]] = i;
+ }
+ /*
+ * log(0) is not defined, so use a special value
+ */
+ gf_log[0] = GF_SIZE;
+ /* set the extended gf_exp values for fast multiply */
+ for (i = 0; i < GF_SIZE; i++) gf_exp[i + GF_SIZE] = gf_exp[i];
+
+ /*
+ * again special cases. 0 has no inverse. This used to
+ * be initialized to GF_SIZE, but it should make no difference
+ * since noone is supposed to read from here.
+ */
+ inverse[0] = 0;
+ inverse[1] = 1;
+ for (i = 2; i <= GF_SIZE; i++) inverse[i] = gf_exp[GF_SIZE - gf_log[i]];
+}
+
+/*
+ * Various linear algebra operations that i use often.
+ */
+
+/*
+ * addmul() computes dst[] = dst[] + c * src[]
+ * This is used often, so better optimize it! Currently the loop is
+ * unrolled 16 times, a good value for 486 and pentium-class machines.
+ * The case c=0 is also optimized, whereas c=1 is not. These
+ * calls are unfrequent in my typical apps so I did not bother.
+ *
+ * Note that gcc on
+ */
+#define addmul(dst, src, c, sz) \
+ if (c != 0) addmul1(dst, src, c, sz)
+
+#define UNROLL 16 /* 1, 4, 8, 16 */
+static void addmul1(gf *dst1, gf *src1, gf c, int sz) {
+ USE_GF_MULC;
+ gf *dst = dst1, *src = src1;
+ gf *lim = &dst[sz - UNROLL + 1];
+
+ GF_MULC0(c);
+
+#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */
+ for (; dst < lim; dst += UNROLL, src += UNROLL) {
+ GF_ADDMULC(dst[0], src[0]);
+ GF_ADDMULC(dst[1], src[1]);
+ GF_ADDMULC(dst[2], src[2]);
+ GF_ADDMULC(dst[3], src[3]);
+#if (UNROLL > 4)
+ GF_ADDMULC(dst[4], src[4]);
+ GF_ADDMULC(dst[5], src[5]);
+ GF_ADDMULC(dst[6], src[6]);
+ GF_ADDMULC(dst[7], src[7]);
+#endif
+#if (UNROLL > 8)
+ GF_ADDMULC(dst[8], src[8]);
+ GF_ADDMULC(dst[9], src[9]);
+ GF_ADDMULC(dst[10], src[10]);
+ GF_ADDMULC(dst[11], src[11]);
+ GF_ADDMULC(dst[12], src[12]);
+ GF_ADDMULC(dst[13], src[13]);
+ GF_ADDMULC(dst[14], src[14]);
+ GF_ADDMULC(dst[15], src[15]);
+#endif
+ }
+#endif
+ lim += UNROLL - 1;
+ for (; dst < lim; dst++, src++) /* final components */
+ GF_ADDMULC(*dst, *src);
+}
+
+/*
+ * computes C = AB where A is n*k, B is k*m, C is n*m
+ */
+static void matmul(gf *a, gf *b, gf *c, int n, int k, int m) {
+ int row, col, i;
+
+ for (row = 0; row < n; row++) {
+ for (col = 0; col < m; col++) {
+ gf *pa = &a[row * k];
+ gf *pb = &b[col];
+ gf acc = 0;
+ for (i = 0; i < k; i++, pa++, pb += m) acc ^= gf_mul(*pa, *pb);
+ c[row * m + col] = acc;
+ }
+ }
+}
+
+/*
+ * invert_mat() takes a matrix and produces its inverse
+ * k is the size of the matrix.
+ * (Gauss-Jordan, adapted from Numerical Recipes in C)
+ * Return non-zero if singular.
+ */
+int pivloops = 0;
+int pivswaps = 0; /* diagnostic */
+static int invert_mat(gf *src, int k) {
+ assert(k > 0);
+
+ gf c, *p;
+ int irow, icol, row, col, i, ix;
+
+ int error = 1;
+ int *indxc = (int *)my_malloc(k * sizeof(int), "indxc");
+ int *indxr = (int *)my_malloc(k * sizeof(int), "indxr");
+ int *ipiv = (int *)my_malloc(k * sizeof(int), "ipiv");
+ gf *id_row = NEW_GF_MATRIX(1, k);
+ gf *temp_row = NEW_GF_MATRIX(1, k);
+ memset(id_row, '\0', k * sizeof(gf));
+ pivloops = 0;
+ pivswaps = 0; /* diagnostic */
+ /*
+ * ipiv marks elements already used as pivots.
+ */
+ for (i = 0; i < k; i++) ipiv[i] = 0;
+
+ for (col = 0; col < k; col++) {
+ gf *pivot_row;
+ /*
+ * Zeroing column 'col', look for a non-zero element.
+ * First try on the diagonal, if it fails, look elsewhere.
+ */
+ irow = icol = -1;
+ if (ipiv[col] != 1 && src[col * k + col] != 0) {
+ irow = col;
+ icol = col;
+ goto found_piv;
+ }
+ for (row = 0; row < k; row++) {
+ if (ipiv[row] != 1) {
+ for (ix = 0; ix < k; ix++) {
+ pivloops++;
+ if (ipiv[ix] == 0) {
+ if (src[row * k + ix] != 0) {
+ irow = row;
+ icol = ix;
+ goto found_piv;
+ }
+ } else if (ipiv[ix] > 1) {
+ fprintf(stderr, "singular matrix\n");
+ goto fail;
+ }
+ }
+ }
+ }
+ if (icol == -1) {
+ fprintf(stderr, "XXX pivot not found!\n");
+ goto fail;
+ }
+ found_piv:
+ ++(ipiv[icol]);
+ /*
+ * swap rows irow and icol, so afterwards the diagonal
+ * element will be correct. Rarely done, not worth
+ * optimizing.
+ */
+ if (irow != icol) {
+ for (ix = 0; ix < k; ix++) {
+ SWAP(src[irow * k + ix], src[icol * k + ix], gf);
+ }
+ }
+ indxr[col] = irow;
+ indxc[col] = icol;
+ pivot_row = &src[icol * k];
+ c = pivot_row[icol];
+ if (c == 0) {
+ fprintf(stderr, "singular matrix 2\n");
+ goto fail;
+ }
+
+ if (c != 1) {
+ pivswaps++;
+ c = inverse[c];
+ pivot_row[icol] = 1;
+ for (ix = 0; ix < k; ix++) pivot_row[ix] = gf_mul(c, pivot_row[ix]);
+ }
+ /*
+ * from all rows, remove multiples of the selected row
+ * to zero the relevant entry (in fact, the entry is not zero
+ * because we know it must be zero).
+ * (Here, if we know that the pivot_row is the identity,
+ * we can optimize the addmul).
+ */
+ id_row[icol] = 1;
+ if (memcmp(pivot_row, id_row, k * sizeof(gf)) != 0) {
+ for (p = src, ix = 0; ix < k; ix++, p += k) {
+ if (ix != icol) {
+ c = p[icol];
+ p[icol] = 0;
+ addmul(p, pivot_row, c, k);
+ }
+ }
+ }
+ id_row[icol] = 0;
+ } /* done all columns */
+ for (col = k - 1; col >= 0; col--) {
+ if (indxr[col] < 0 || indxr[col] >= k)
+ fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]);
+ else if (indxc[col] < 0 || indxc[col] >= k)
+ fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]);
+ else if (indxr[col] != indxc[col]) {
+ for (row = 0; row < k; row++) {
+ SWAP(src[row * k + indxr[col]], src[row * k + indxc[col]], gf);
+ }
+ }
+ }
+ error = 0;
+fail:
+ free(indxc);
+ free(indxr);
+ free(ipiv);
+ free(id_row);
+ free(temp_row);
+ return error;
+}
+
+/*
+ * fast code for inverting a vandermonde matrix.
+ * XXX NOTE: It assumes that the matrix
+ * is not singular and _IS_ a vandermonde matrix. Only uses
+ * the second column of the matrix, containing the p_i's.
+ *
+ * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but
+ * largely revised for my purposes.
+ * p = coefficients of the matrix (p_i)
+ * q = values of the polynomial (known)
+ */
+
+int invert_vdm(gf *src, int k) {
+ assert(k > 0);
+
+ int i, j, row, col;
+ gf *b, *c, *p;
+ gf t, xx;
+
+ if (k == 1) /* degenerate case, matrix must be p^0 = 1 */
+ return 0;
+ /*
+ * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
+ * b holds the coefficient for the matrix inversion
+ */
+ c = NEW_GF_MATRIX(1, k);
+ b = NEW_GF_MATRIX(1, k);
+
+ p = NEW_GF_MATRIX(1, k);
+
+ for (j = 1, i = 0; i < k; i++, j += k) {
+ c[i] = 0;
+ p[i] = src[j]; /* p[i] */
+ }
+ /*
+ * construct coeffs. recursively. We know c[k] = 1 (implicit)
+ * and start P_0 = x - p_0, then at each stage multiply by
+ * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1}
+ * After k steps we are done.
+ */
+ c[k - 1] = p[0]; /* really -p(0), but x = -x in GF(2^m) */
+ for (i = 1; i < k; i++) {
+ gf p_i = p[i]; /* see above comment */
+ for (j = k - 1 - (i - 1); j < k - 1; j++) c[j] ^= gf_mul(p_i, c[j + 1]);
+ c[k - 1] ^= p_i;
+ }
+
+ for (row = 0; row < k; row++) {
+ /*
+ * synthetic division etc.
+ */
+ xx = p[row];
+ t = 1;
+ b[k - 1] = 1; /* this is in fact c[k] */
+ for (i = k - 2; i >= 0; i--) {
+ b[i] = c[i + 1] ^ gf_mul(xx, b[i + 1]);
+ t = gf_mul(xx, t) ^ b[i];
+ }
+ for (col = 0; col < k; col++)
+ src[col * k + row] = gf_mul(inverse[t], b[col]);
+ }
+ free(c);
+ free(b);
+ free(p);
+ return 0;
+}
+
+static int fec_initialized = 0;
+static void init_fec() {
+ generate_gf();
+ init_mul_table();
+ fec_initialized = 1;
+}
+
+/*
+ * This section contains the proper FEC encoding/decoding routines.
+ * The encoding matrix is computed starting with a Vandermonde matrix,
+ * and then transforming it into a systematic matrix.
+ */
+
+#define FEC_MAGIC 0xFECC0DEC
+
+void fec_free(struct fec_parms *p) {
+ if (p == NULL || p->magic != (((FEC_MAGIC ^ p->k) ^ p->n) ^
+ (unsigned long)(p->enc_matrix))) {
+ fprintf(stderr, "bad parameters to fec_free\n");
+ return;
+ }
+ free(p->enc_matrix);
+ free(p);
+}
+
+/*
+ * create a new encoder, returning a descriptor. This contains k,n and
+ * the encoding matrix.
+ */
+struct fec_parms *fec_new(int k, int n) {
+ int row, col;
+ gf *p, *tmp_m;
+
+ struct fec_parms *retval;
+
+ if (fec_initialized == 0) init_fec();
+
+ if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n) {
+ fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n", k, n, GF_SIZE);
+ return NULL;
+ }
+ retval = (struct fec_parms *)my_malloc(sizeof(struct fec_parms), "new_code");
+ retval->k = k;
+ retval->n = n;
+ retval->enc_matrix = NEW_GF_MATRIX(n, k);
+ retval->magic = ((FEC_MAGIC ^ k) ^ n) ^ (unsigned long)(retval->enc_matrix);
+ tmp_m = NEW_GF_MATRIX(n, k);
+ /*
+ * fill the matrix with powers of field elements, starting from 0.
+ * The first row is special, cannot be computed with exp. table.
+ */
+ tmp_m[0] = 1;
+ for (col = 1; col < k; col++) tmp_m[col] = 0;
+ for (p = tmp_m + k, row = 0; row < n - 1; row++, p += k) {
+ for (col = 0; col < k; col++) p[col] = gf_exp[modnn(row * col)];
+ }
+
+ /*
+ * quick code to build systematic matrix: invert the top
+ * k*k vandermonde matrix, multiply right the bottom n-k rows
+ * by the inverse, and construct the identity matrix at the top.
+ */
+ invert_vdm(tmp_m, k); /* much faster than invert_mat */
+ matmul(tmp_m + k * k, tmp_m, retval->enc_matrix + k * k, n - k, k, k);
+ /*
+ * the upper matrix is I so do not bother with a slow multiply
+ */
+ memset(retval->enc_matrix, '\0', k * k * sizeof(gf));
+ for (p = retval->enc_matrix, col = 0; col < k; col++, p += k + 1) *p = 1;
+ free(tmp_m);
+ return retval;
+}
+
+/*
+ * fec_encode accepts as input pointers to n data packets of size sz,
+ * and produces as output a packet pointed to by fec, computed
+ * with index "index".
+ */
+void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) {
+ int i, k = code->k;
+ gf *p;
+
+ if (GF_BITS > 8) sz /= 2; // NOSONAR
+
+ if (index < k)
+ memcpy(fec, src[index], sz * sizeof(gf));
+ else if (index < code->n) {
+ p = &(code->enc_matrix[index * k]);
+ memset(fec, '\0', sz * sizeof(gf));
+ for (i = 0; i < k; i++) addmul(fec, src[i], p[i], sz);
+ } else
+ fprintf(stderr, "Invalid index %d (max %d)\n", index, code->n - 1);
+}
+
+/*
+ * shuffle move src packets in their position
+ */
+static int shuffle(gf *pkt[], int index[], int k) {
+ int i;
+
+ for (i = 0; i < k;) {
+ if (index[i] >= k || index[i] == i)
+ i++;
+ else {
+ /*
+ * put pkt in the right position (first check for conflicts).
+ */
+ int c = index[i];
+
+ if (index[c] == c) {
+ fprintf(stderr, "\nshuffle, error at %d\n", i);
+ return 1;
+ }
+ SWAP(index[i], index[c], int);
+ SWAP(pkt[i], pkt[c], gf *);
+ }
+ }
+ return 0;
+}
+
+/*
+ * build_decode_matrix constructs the encoding matrix given the
+ * indexes. The matrix must be already allocated as
+ * a vector of k*k elements, in row-major order
+ */
+static gf *build_decode_matrix(struct fec_parms *code, int index[]) {
+ int i, k = code->k;
+ gf *p, *matrix = NEW_GF_MATRIX(k, k);
+
+ for (i = 0, p = matrix; i < k; i++, p += k) {
+ if (index[i] < k) {
+ memset(p, '\0', k * sizeof(gf));
+ p[i] = 1;
+ } else if (index[i] < code->n)
+ memcpy(p, &(code->enc_matrix[index[i] * k]), k * sizeof(gf));
+ else {
+ fprintf(stderr, "decode: invalid index %d (max %d)\n", index[i],
+ code->n - 1);
+ free(matrix);
+ return NULL;
+ }
+ }
+ if (invert_mat(matrix, k)) {
+ free(matrix);
+ matrix = NULL;
+ }
+ return matrix;
+}
+
+/*
+ * fec_decode receives as input a vector of packets, the indexes of
+ * packets, and produces the correct vector as output.
+ *
+ * Input:
+ * code: pointer to code descriptor
+ * pkt: pointers to received packets. They are modified
+ * to store the output packets (in place)
+ * index: pointer to packet indexes (modified)
+ * sz: size of each packet
+ */
+int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) {
+ gf *m_dec;
+ gf **new_pkt = nullptr;
+ int row, col, k = code->k;
+ int i = 0;
+
+ if (GF_BITS > 8) sz /= 2; // NOSONAR
+
+ if (shuffle(pkt, index, k)) /* error if true */
+ return 1;
+ m_dec = build_decode_matrix(code, index);
+
+ if (m_dec == NULL) return 1; /* error */
+ /*
+ * do the actual decoding
+ */
+ new_pkt = pkt + k;
+ for (row = 0; row < k; row++) {
+ if (index[row] >= k) {
+ memset(new_pkt[i], '\0', sz * sizeof(gf));
+ for (col = 0; col < k; col++)
+ addmul(new_pkt[i], pkt[col], m_dec[row * k + col], sz);
+ i++;
+ }
+ }
+ free(m_dec);
+ return 0;
+} \ No newline at end of file
diff --git a/libtransport/src/protocols/fec/fec.h b/libtransport/src/protocols/fec/fec.h
new file mode 100644
index 000000000..7710bb7af
--- /dev/null
+++ b/libtransport/src/protocols/fec/fec.h
@@ -0,0 +1,65 @@
+/*
+ * fec.c -- forward error correction based on Vandermonde matrices
+ * 980614
+ * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
+ *
+ * Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
+ * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
+ * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/*
+ * The following parameter defines how many bits are used for
+ * field elements. The code supports any value from 2 to 16
+ * but fastest operation is achieved with 8 bit elements
+ * This is the only parameter you may want to change.
+ */
+#ifndef GF_BITS
+#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */
+#endif
+
+#if (GF_BITS <= 8)
+typedef unsigned char gf;
+#else
+typedef unsigned short gf;
+#endif
+
+#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */
+
+struct fec_parms {
+ unsigned long magic;
+ int k, n; /* parameters of the code */
+ gf *enc_matrix;
+};
+
+void fec_free(struct fec_parms *p);
+struct fec_parms *fec_new(int k, int n);
+
+void fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz);
+int fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz);
+
+/* end of file */
diff --git a/libtransport/src/protocols/fec/fec_info.h b/libtransport/src/protocols/fec/fec_info.h
new file mode 100644
index 000000000..bdfc4d3af
--- /dev/null
+++ b/libtransport/src/protocols/fec/fec_info.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/errors/not_implemented_exception.h>
+
+namespace transport {
+namespace protocol {
+
+namespace fec {
+
+template <typename T>
+struct FecInfo {
+ static bool isFec() { throw errors::NotImplementedException(); }
+ static uint32_t nextSymbol(uint32_t index) {
+ throw errors::NotImplementedException();
+ }
+ static uint32_t nextSource(uint32_t index) {
+ throw errors::NotImplementedException();
+ }
+};
+
+template <uint32_t K, uint32_t N>
+struct Code {};
+
+template <uint32_t K, uint32_t N>
+struct FecInfo<Code<K, N>> {
+ static bool isFec(uint32_t index) { return (index % N) >= K; }
+
+ static uint32_t nextSymbol(uint32_t index) {
+ if (isFec(index)) {
+ return index;
+ }
+
+ return index + (K - (index % N));
+ }
+
+ static uint32_t nextSource(uint32_t index) {
+ if (!isFec(index)) {
+ return index;
+ }
+
+ return index + (N - (index % N));
+ }
+};
+
+} // namespace fec
+} // namespace protocol
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/protocols/fec/rely.cc b/libtransport/src/protocols/fec/rely.cc
new file mode 100644
index 000000000..9e0a06dd8
--- /dev/null
+++ b/libtransport/src/protocols/fec/rely.cc
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <protocols/fec/rely.h>
+
+#include <rely/packet.hpp>
+
+namespace transport {
+namespace protocol {
+namespace fec {
+
+RelyEncoder::RelyEncoder(uint32_t k, uint32_t n, uint32_t /* seq_offset */)
+ : RelyBase(k, n) {
+ configure(kmtu, ktimeout, kmax_stream_size);
+ set_repair_trigger(k_, n_ - k_, n_ - k_);
+}
+
+void RelyEncoder::onPacketProduced(core::ContentObject &content_object,
+ uint32_t offset, uint32_t metadata) {
+ // Get pointer to payload, leaving space to insert FEC header.
+ // TODO Check if this additional header is really needed.
+ auto data = content_object.writableData() + offset - sizeof(fec_metadata);
+ auto length = content_object.length() - offset + sizeof(fec_metadata);
+
+ // Check packet length does not exceed maximum length supported by the
+ // encoder (otherwise segmentation would take place).
+ DCHECK(length < max_packet_bytes());
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Encoding packet of length " << length - sizeof(fec_metadata);
+
+ // Get the suffix. With rely we need to write it in the fec_metadata in order
+ // to be able to recognize the seq number upon recovery.
+ auto suffix = content_object.getName().getSuffix();
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Producing packet " << suffix
+ << " (index == " << current_index_ << ")";
+
+ // Consume payload. Add fec_metadata in front before feeding payload to
+ // encoder, and copy original content of packet
+ fec_metadata *h = reinterpret_cast<fec_metadata *>(data);
+ fec_metadata copy = *h;
+ h->setSeqNumberBase(suffix);
+ h->setMetadataBase(metadata);
+ auto packets = consume(data, length, getCurrentTime());
+ DCHECK(packets == 1);
+
+ // Update packet counter
+ current_index_ += packets;
+
+ // Restore original packet content and increment data pointer to the correct
+ // position
+ *h = copy;
+ data += sizeof(fec_metadata);
+
+ // Check position of this packet inside N size block
+ auto i = current_index_ % n_;
+
+ // encoder will produce a source packet
+ if (i <= k_) {
+ // Rely modifies the payload of the packet. We replace the packet with the
+ // one returned by rely.
+ // TODO Optimize it by copying only the RELY header
+
+ // Be sure encoder can produce
+ DCHECK(can_produce());
+
+ // Check new payload size and make sure it fits in packet buffer
+ auto new_payload_size = produce_bytes();
+ int difference = (int)(new_payload_size - length);
+
+ DCHECK(difference > 0);
+ DCHECK(content_object.ensureCapacity(difference));
+
+ // Update length
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "The packet length will be incremented by "
+ << difference + sizeof(fec_metadata);
+ content_object.append(difference + sizeof(fec_metadata));
+ content_object.updateLength();
+
+ // Make sure we got a source packet, otherwise we would put a repair symbol
+ // in a source packet
+ DCHECK(rely::packet_is_systematic(produce_data()));
+
+ // Copy rely packet replacing old source packet.
+ std::memcpy(data, produce_data(), new_payload_size);
+
+ // Advance the encoder to next symbol.
+ produce_next();
+ }
+
+#if 0
+ if (i == k_) {
+ // Ensure repair are generated after k source packets
+ flush_repair();
+ }
+#endif
+
+ // Here we should produce all the repair packets
+ while (can_produce()) {
+ // The current index MUST be k_, because we enforce n - k repair to be
+ // produced after k sources
+ DCHECK(current_index_ == k_);
+
+ buffer packet;
+ if (!buffer_callback_) {
+ // If no callback is installed, let's allocate a buffer from global pool
+ packet = core::PacketManager<>::getInstance().getMemBuf();
+ packet->append(produce_bytes());
+ } else {
+ // Otherwise let's ask a buffer to the caller.
+ packet = buffer_callback_(produce_bytes());
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Producing symbol of size " << produce_bytes();
+
+ // Copy symbol to packet buffer
+ std::memcpy(packet->writableData(), produce_data(), produce_bytes());
+
+ // Push symbol in repair_packets
+ packets_.emplace_back(0, metadata, std::move(packet));
+
+ // Advance the encoder
+ produce_next();
+ }
+
+ // Print number of unprotected symbols
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Number of unprotected symbols: " << unprotected_symbols();
+
+ // If we have generated repair symbols, let's notify caller via the installed
+ // callback
+ if (packets_.size()) {
+ DCHECK(packets_.size() == n_ - k_);
+ fec_callback_(packets_);
+ packets_.clear();
+ current_index_ = 0;
+ }
+}
+
+RelyDecoder::RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset)
+ : RelyBase(k, n, seq_offset) {
+ configure(kmtu, ktimeout, kmax_stream_size);
+}
+
+void RelyDecoder::onDataPacket(core::ContentObject &content_object,
+ uint32_t offset, uint32_t metadata) {
+ // Adjust pointers to point to packet payload
+ auto data = content_object.writableData() + offset;
+ auto size = content_object.length() - offset;
+
+ // Pass payload to decoder
+ consume(data, size, getCurrentTime());
+
+ producePackets();
+}
+
+void RelyDecoder::producePackets() {
+ // Drain decoder if possible
+ while (can_produce()) {
+ auto fec_header_size = sizeof(fec_metadata);
+ auto payload_size = produce_bytes() - sizeof(fec_metadata);
+
+ buffer packet;
+ if (!buffer_callback_) {
+ packet = core::PacketManager<>::getInstance().getMemBuf();
+ packet->append(payload_size);
+ } else {
+ packet = buffer_callback_(payload_size);
+ }
+
+ // Read seq number
+ const fec_metadata *h =
+ reinterpret_cast<const fec_metadata *>(produce_data());
+ uint32_t index = h->getSeqNumberBase();
+ uint32_t metadata = h->getMetadataBase();
+
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "The index written in the packet is " << index;
+
+ // Copy payload
+ std::memcpy(packet->writableData(), produce_data() + fec_header_size,
+ payload_size);
+
+ // Save packet in buffer
+ packets_.emplace_back(index, metadata, std::move(packet));
+
+ // Advance to next packet
+ produce_next();
+ }
+
+ // If we produced packets, lets notify the caller via the callback
+ if (packets_.size() > 0) {
+ fec_callback_(packets_);
+ packets_.clear();
+ }
+
+ flushOutOfOrder();
+}
+
+void RelyDecoder::flushOutOfOrder() {
+ if (flush_timer_ == nullptr) return;
+ flush_timer_->cancel();
+
+ if (has_upcoming_flush()) {
+ flush_timer_->expires_from_now(std::chrono::milliseconds(
+ std::max((int64_t)0, upcoming_flush(getCurrentTime()))));
+
+ flush_timer_->async_wait([this](const std::error_code &ec) {
+ if (ec) return;
+ if (has_upcoming_flush()) {
+ flush(getCurrentTime());
+ producePackets();
+ }
+ });
+ }
+}
+
+} // namespace fec
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/fec/rely.h b/libtransport/src/protocols/fec/rely.h
new file mode 100644
index 000000000..cc81222b2
--- /dev/null
+++ b/libtransport/src/protocols/fec/rely.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/portability/endianess.h>
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <hicn/transport/utils/membuf.h>
+#include <protocols/fec/fec_info.h>
+#include <protocols/fec_base.h>
+
+#include <rely/decoder.hpp>
+#include <rely/encoder.hpp>
+
+#define RELY_DEBUG 0
+
+namespace transport {
+namespace protocol {
+namespace fec {
+
+/**
+ * @brief Table of used codes.
+ */
+#define foreach_rely_fec_type \
+ _(Rely, 1, 2) \
+ _(Rely, 1, 3) \
+ _(Rely, 1, 4) \
+ _(Rely, 2, 3) \
+ _(Rely, 2, 6) \
+ _(Rely, 3, 9) \
+ _(Rely, 4, 5) \
+ _(Rely, 4, 6) \
+ _(Rely, 4, 7) \
+ _(Rely, 6, 10) \
+ _(Rely, 8, 10) \
+ _(Rely, 8, 11) \
+ _(Rely, 8, 12) \
+ _(Rely, 8, 14) \
+ _(Rely, 8, 16) \
+ _(Rely, 8, 32) \
+ _(Rely, 10, 30) \
+ _(Rely, 10, 40) \
+ _(Rely, 10, 90) \
+ _(Rely, 16, 21) \
+ _(Rely, 16, 23) \
+ _(Rely, 16, 24) \
+ _(Rely, 16, 27) \
+ _(Rely, 17, 21) \
+ _(Rely, 17, 34) \
+ _(Rely, 32, 41) \
+ _(Rely, 32, 46) \
+ _(Rely, 32, 54) \
+ _(Rely, 34, 42) \
+ _(Rely, 35, 70) \
+ _(Rely, 52, 62)
+
+/**
+ * @brief Base class to store common fields.
+ */
+class RelyBase : public virtual FECBase {
+ protected:
+ static const constexpr size_t kmax_stream_size = 125U;
+ static const constexpr size_t kmtu = 1500U;
+ static const constexpr size_t ktimeout = 100U;
+ /**
+ * @brief FEC Header, added to each packet to get sequence number upon
+ * decoding operations. It may be removed once we know the meaning of the
+ * fields in the rely header.
+ */
+ class fec_metadata {
+ public:
+ void setSeqNumberBase(uint32_t suffix) {
+ seq_number = portability::host_to_net(suffix);
+ }
+ uint32_t getSeqNumberBase() const {
+ return portability::net_to_host(seq_number);
+ }
+
+ void setMetadataBase(uint32_t value) {
+ metadata = portability::host_to_net(value);
+ }
+ uint32_t getMetadataBase() const {
+ return portability::net_to_host(metadata);
+ }
+
+ private:
+ uint32_t seq_number;
+ uint32_t metadata;
+ };
+
+ /**
+ * @brief Construct a new Rely Base object.
+ *
+ * @param k The number of source symbol needed to generate n - k repair
+ * symbols
+ * @param n The sum of source packets and repair packets in a `block`
+ * @param seq_offset offset to use if production suffixes starts from an index
+ * != 0
+ */
+ RelyBase(uint32_t k, uint32_t n, uint32_t seq_offset = 0)
+ : k_(k),
+ n_(n),
+ seq_offset_(seq_offset % n_),
+ current_index_(seq_offset)
+#if RELY_DEBUG
+ ,
+ time_(0)
+#endif
+ {
+ }
+
+ /**
+ * @brief Get the current time in milliseconds
+ *
+ * @return int64_t Current time in milliseconds
+ */
+ int64_t getCurrentTime() {
+ // Get the current time
+#if RELY_DEBUG
+ return time_++;
+#else
+ return utils::SteadyTime::nowMs().count();
+#endif
+ }
+
+ uint32_t k_;
+ uint32_t n_;
+
+ std::uint32_t seq_offset_;
+
+ /**
+ * @brief Vector of packets to be passed to caller callbacks. For encoder it
+ * will contain the repair packets, for decoder the recovered sources.
+ */
+ BufferArray packets_;
+
+ /**
+ * @brief Current index to be used for local packet count.
+ *
+ */
+ uint32_t current_index_;
+#if RELY_DEBUG
+ uint32_t time_;
+#endif
+};
+
+/**
+ * @brief The Rely Encoder implementation.
+ *
+ */
+class RelyEncoder : RelyBase, rely::encoder, public ProducerFEC {
+ public:
+ RelyEncoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+ /**
+ * Producers will call this function when they produce a data packet.
+ */
+ void onPacketProduced(core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata = FECBase::INVALID_METADATA) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ * there is not need to distinguish between source and FEC packets here
+ */
+ std::size_t getFecHeaderSize(bool isFEC) override {
+ return header_bytes() + sizeof(fec_metadata) + 4;
+ }
+
+ void reset() override {
+ // Nothing to do here
+ }
+};
+
+class RelyDecoder : RelyBase, rely::decoder, public ConsumerFEC {
+ public:
+ RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+
+ /**
+ * Consumers will call this function when they receive a data packet
+ */
+ void onDataPacket(core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata = FECBase::INVALID_METADATA) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ * there is not need to distinguish between source and FEC packets here
+ */
+ std::size_t getFecHeaderSize(bool isFEC) override {
+ return header_bytes() + sizeof(fec_metadata);
+ }
+
+ void reset() override {
+ // Nothing to do here
+ }
+
+ private:
+ void producePackets();
+ void flushOutOfOrder();
+};
+
+} // namespace fec
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/fec/rs.cc b/libtransport/src/protocols/fec/rs.cc
new file mode 100644
index 000000000..d42740c32
--- /dev/null
+++ b/libtransport/src/protocols/fec/rs.cc
@@ -0,0 +1,452 @@
+
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <protocols/fec/fec.h>
+#include <protocols/fec/rs.h>
+
+#include <cassert>
+
+namespace transport {
+namespace protocol {
+namespace fec {
+
+BlockCode::BlockCode(uint32_t k, uint32_t n, uint32_t seq_offset,
+ struct fec_parms *code, rs &params)
+ : Packets(),
+ k_(k),
+ n_(n),
+ seq_offset_(seq_offset),
+ code_(code),
+ max_buffer_size_(0),
+ current_block_size_(0),
+ to_decode_(false),
+ params_(params) {
+ sorted_index_.reserve(n);
+ UNUSED(seq_offset_);
+}
+
+bool BlockCode::addRepairSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset) {
+ // Get index
+ to_decode_ = true;
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding symbol of size " << packet->length();
+ return addSymbol(packet, i, offset,
+ packet->length() - sizeof(fec_header) - offset,
+ FECBase::INVALID_METADATA);
+}
+
+bool BlockCode::addSourceSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset, uint32_t metadata) {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding source symbol of size "
+ << packet->length() << ", offset " << offset;
+ return addSymbol(packet, i, offset, packet->length() - offset, metadata);
+}
+
+bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset, std::size_t size,
+ uint32_t metadata) {
+ if (size > max_buffer_size_) {
+ max_buffer_size_ = size;
+ }
+
+ operator[](current_block_size_) = RSBufferInfo(offset, i, metadata, packet);
+ current_block_size_++;
+
+ if (current_block_size_ >= k_) {
+ if (to_decode_) {
+ decode();
+ } else {
+ encode();
+ }
+
+ clear();
+ return false;
+ }
+
+ return true;
+}
+
+void BlockCode::encode() {
+ gf *data[n_];
+ uint32_t base = operator[](0).getIndex();
+
+ // Set packet length in first 2 bytes
+ for (uint32_t i = 0; i < k_; i++) {
+ auto &packet = operator[](i).getBuffer();
+ auto offset = operator[](i).getOffset();
+ auto metadata_base = operator[](i).getMetadata();
+
+ auto ret =
+ packet->ensureCapacityAndFillUnused(max_buffer_size_ + offset, 0);
+ if (TRANSPORT_EXPECT_FALSE(ret == false)) {
+ throw errors::RuntimeException(
+ "Provided packet is not suitable to be used as FEC source packet. "
+ "Aborting.");
+ }
+
+ // Buffers should hold 2 *after* the padding, in order to be
+ // able to set the length for the encoding operation.
+ // packet->trimStart(offset);
+ fec_metadata *metadata = reinterpret_cast<fec_metadata *>(
+ packet->writableData() + max_buffer_size_ + offset);
+ auto buffer_length = packet->length() - offset;
+ metadata->setPacketLength(buffer_length);
+ metadata->setMetadataBase(metadata_base);
+
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current buffer size: " << packet->length();
+
+ data[i] = packet->writableData() + offset;
+ }
+
+ // Finish to fill source block with the buffers to hold the repair symbols
+ auto length = max_buffer_size_ + sizeof(fec_header) + METADATA_BYTES;
+ for (uint32_t i = k_; i < n_; i++) {
+ buffer packet;
+ if (!params_.buffer_callback_) {
+ // If no callback is installed, let's allocate a buffer from global pool
+ packet = core::PacketManager<>::getInstance().getMemBuf();
+ packet->append(length);
+ } else {
+ // Otherwise let's ask a buffer to the caller.
+ packet = params_.buffer_callback_(length);
+ }
+
+ fec_header *fh = reinterpret_cast<fec_header *>(packet->writableData());
+
+ fh->setSeqNumberBase(base);
+ fh->setNFecSymbols(n_ - k_);
+ fh->setEncodedSymbolId(i);
+ fh->setSourceBlockLen(n_);
+
+ packet->trimStart(sizeof(fec_header));
+
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Current symbol size: " << packet->length();
+
+ data[i] = packet->writableData();
+ operator[](i) = RSBufferInfo(uint32_t(0), i, FECBase::INVALID_METADATA,
+ std::move(packet));
+ }
+
+ // Generate repair symbols and put them in corresponding buffers
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Calling encode with max_buffer_size_ = " << max_buffer_size_;
+ for (uint32_t i = k_; i < n_; i++) {
+ fec_encode(code_, data, data[i], i,
+ (int)(max_buffer_size_ + METADATA_BYTES));
+ }
+
+ // Re-include header in repair packets
+ for (uint32_t i = k_; i < n_; i++) {
+ auto &packet = operator[](i).getBuffer();
+ packet->prepend(sizeof(fec_header));
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Produced repair symbol of size = " << packet->length();
+ }
+}
+
+void BlockCode::decode() {
+ gf *data[n_];
+ uint32_t index[k_];
+ buffer aux_fec_packets[n_ - k_];
+ // FEC packet number k0
+ uint32_t k0 = 0;
+
+ // Reorder block by index with in-place sorting
+ for (uint32_t i = 0; i < k_;) {
+ uint32_t idx = operator[](i).getIndex();
+ if (idx >= k_ || idx == i) {
+ i++;
+ } else {
+ std::swap(operator[](i), operator[](idx));
+ }
+ }
+
+ for (uint32_t i = 0; i < k_; i++) {
+ auto &packet = operator[](i).getBuffer();
+ index[i] = operator[](i).getIndex();
+ auto offset = operator[](i).getOffset();
+ auto metadata_base = operator[](i).getMetadata();
+ sorted_index_[i] = index[i];
+
+ if (index[i] < k_) {
+ operator[](i).setReceived();
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "DECODE SOURCE - index " << index[i]
+ << " - Current buffer size: " << packet->length();
+ // This is a source packet. We need to fill
+ // additional space to 0 and append the length
+
+ // Buffers should hold 2 bytes at the end, in order to be
+ // able to set the length for the encoding operation
+ packet->trimStart(offset);
+ packet->ensureCapacityAndFillUnused(max_buffer_size_, 0);
+ fec_metadata *metadata = reinterpret_cast<fec_metadata *>(
+ packet->writableData() + max_buffer_size_ - METADATA_BYTES);
+ metadata->setPacketLength(packet->length());
+ metadata->setMetadataBase(metadata_base);
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "DECODE SYMBOL - index " << index[i]
+ << " - Current buffer size: " << packet->length();
+ packet->trimStart(sizeof(fec_header) + offset);
+ aux_fec_packets[k0] = core::PacketManager<>::getInstance().getMemBuf();
+ data[k_ + k0] = aux_fec_packets[k0]->writableData();
+ k0++;
+ }
+ data[i] = packet->writableData();
+ }
+ // We decode the source block
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Calling decode with max_buffer_size_ = " << max_buffer_size_;
+
+ fec_decode(code_, data, reinterpret_cast<int *>(index),
+ (int)max_buffer_size_);
+
+ // Find the index in the block for recovered packets
+ for (uint32_t i = 0, j = 0; i < k_; i++) {
+ if (index[i] >= k_) {
+ operator[](i).setBuffer(aux_fec_packets[j++]);
+ operator[](i).setIndex(i);
+ }
+ }
+
+ // Adjust length according to the one written in the source packet
+ for (uint32_t i = 0; i < k_; i++) {
+ auto &packet = operator[](i).getBuffer();
+ fec_metadata *metadata = reinterpret_cast<fec_metadata *>(
+ packet->writableData() + max_buffer_size_ - METADATA_BYTES);
+ DCHECK(metadata->getPacketLength() <= packet->capacity());
+ // Adjust buffer length
+ packet->setLength(metadata->getPacketLength());
+ // Adjust metadata
+ operator[](i).setMetadata(metadata->getMetadataBase());
+
+ // reset the point to the beginning of the packets for all received packets
+ if (operator[](i).getReceived()) {
+ auto &packet = operator[](i).getBuffer();
+ auto offset = operator[](i).getOffset();
+ packet->prepend(offset);
+ }
+ }
+}
+
+void BlockCode::clear() {
+ current_block_size_ = 0;
+ max_buffer_size_ = 0;
+ sorted_index_.clear();
+ to_decode_ = false;
+}
+
+void rs::MatrixDeleter::operator()(struct fec_parms *params) {
+ fec_free(params);
+}
+
+rs::Codes rs::createCodes() {
+ Codes ret;
+
+#define _(name, k, n) \
+ ret.emplace(std::make_pair(k, n), Matrix(fec_new(k, n), MatrixDeleter()));
+ foreach_rs_fec_type
+#undef _
+
+ return ret;
+}
+
+rs::Codes rs::codes_ = createCodes();
+
+rs::rs(uint32_t k, uint32_t n, uint32_t seq_offset)
+ : k_(k), n_(n), seq_offset_(seq_offset % n) {}
+
+RSEncoder::RSEncoder(uint32_t k, uint32_t n, uint32_t seq_offset)
+ : rs(k, n, seq_offset),
+ current_code_(codes_[std::make_pair(k, n)].get()),
+ source_block_(k_, n_, seq_offset_, current_code_, *this) {}
+
+void RSEncoder::consume(const fec::buffer &packet, uint32_t index,
+ uint32_t offset, uint32_t metadata) {
+ if (!source_block_.addSourceSymbol(packet, index, offset, metadata)) {
+ fec::BufferArray repair_packets;
+ for (uint32_t i = k_; i < n_; i++) {
+ repair_packets.emplace_back(std::move(source_block_[i]));
+ }
+
+ fec_callback_(repair_packets);
+ }
+}
+
+void RSEncoder::onPacketProduced(core::ContentObject &content_object,
+ uint32_t offset, uint32_t metadata) {
+ consume(content_object.shared_from_this(),
+ content_object.getName().getSuffix(), offset, metadata);
+}
+
+RSDecoder::RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset)
+ : rs(k, n, seq_offset) {}
+
+void RSDecoder::recoverPackets(SourceBlocks::iterator &src_block_it) {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "recoverPackets for " << k_;
+ auto &src_block = src_block_it->second;
+ auto base_index = src_block_it->first;
+ BufferArray source_packets(k_);
+
+ // Iterate over packets in the block and adjust indexed accordingly. This must
+ // be done because indexes are from 0 to (n - k - 1), but we need indexes from
+ // base_index to base_index + (n - k - 1)
+ for (uint32_t i = 0; i < src_block.getK(); i++) {
+ src_block[i].setIndex(base_index + src_block[i].getIndex());
+ source_packets[i] = FECBufferInfo(std::move(src_block[i]));
+ }
+
+ setProcessed(src_block_it->first);
+
+ fec_callback_(source_packets);
+ processed_source_blocks_.emplace(src_block_it->first);
+
+ auto it = parked_packets_.find(src_block_it->first);
+ if (it != parked_packets_.end()) {
+ parked_packets_.erase(it);
+ }
+
+ src_blocks_.erase(src_block_it);
+}
+
+void RSDecoder::consumeSource(const fec::buffer &packet, uint32_t index,
+ uint32_t offset, uint32_t metadata) {
+ // Normalize index
+ DCHECK(index >= seq_offset_);
+ auto i = (index - seq_offset_) % n_;
+
+ // Get base
+ uint32_t base = index - i;
+
+ if (processed(base)) {
+ return;
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Decoder consume called for source symbol. BASE = " << base
+ << ", index = " << index << " and i = " << i;
+
+ // check if a source block already exist for this symbol. If it does not
+ // exist, we lazily park this packet until we receive a repair symbol for the
+ // same block. This is done for 2 reason:
+ // 1) If we receive all the source packets of a block, we do not need to
+ // recover anything.
+ // 2) Sender may change n and k at any moment, so we construct the source
+ // block based on the (n, k) values written in the fec header. This is
+ // actually not used right now, since we use fixed value of n and k passed
+ // at construction time, but it paves the ground for a more dynamic
+ // protocol that may come in the future.
+ auto it = src_blocks_.find(base);
+ if (it != src_blocks_.end()) {
+ auto ret = it->second.addSourceSymbol(packet, i, offset, metadata);
+ if (!ret) {
+ recoverPackets(it);
+ }
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding to parked source packets";
+ auto ret = parked_packets_.emplace(base, BufferInfoArray());
+ ret.first->second.emplace_back(offset, i, metadata, packet);
+
+ /**
+ * If we reached k source packets, we do not have any missing packet to
+ * recover via FEC. Delete the block.
+ */
+ if (ret.first->second.size() >= k_) {
+ setProcessed(ret.first->first);
+ parked_packets_.erase(ret.first);
+ }
+ }
+}
+
+void RSDecoder::consumeRepair(const fec::buffer &packet, uint32_t offset) {
+ // Repair symbol! Get index and base source block.
+ fec_header *h =
+ reinterpret_cast<fec_header *>(packet->writableData() + offset);
+ auto i = h->getEncodedSymbolId();
+ auto base = h->getSeqNumberBase();
+ auto n = h->getSourceBlockLen();
+ auto k = n - h->getNFecSymbols();
+
+ if (processed(base)) {
+ return;
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Decoder consume called for repair symbol. BASE = " << base
+ << ", index = " << base + i << " and i = " << (int)i << ". K=" << (int)k
+ << ", N=" << (int)n;
+
+ // check if a source block already exist for this symbol
+ auto it = src_blocks_.find(base);
+ if (it == src_blocks_.end()) {
+ // Create new source block
+ auto code_it = codes_.find(std::make_pair(k, n));
+ if (code_it == codes_.end()) {
+ LOG(ERROR) << "Code for k = " << k << " and n = " << n
+ << " does not exist.";
+ return;
+ }
+
+ auto emplace_result = src_blocks_.emplace(
+ base, BlockCode(k, n, seq_offset_, code_it->second.get(), *this));
+ it = emplace_result.first;
+
+ // Check in the parked packets and insert any packet that is part of this
+ // source block
+
+ auto it2 = parked_packets_.find(base);
+ if (it2 != parked_packets_.end()) {
+ for (auto &packet_index : it2->second) {
+ auto ret = it->second.addSourceSymbol(
+ packet_index.getBuffer(), packet_index.getIndex(),
+ packet_index.getOffset(), packet_index.getMetadata());
+ if (!ret) {
+ recoverPackets(it);
+ // Finish to delete packets in same source block that were
+ // eventually not used
+ return;
+ }
+ }
+ }
+ }
+
+ auto ret = it->second.addRepairSymbol(packet, i, offset);
+ if (!ret) {
+ recoverPackets(it);
+ }
+}
+
+void RSDecoder::onDataPacket(core::ContentObject &content_object,
+ uint32_t offset, uint32_t metadata) {
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Calling fec for data packet " << content_object.getName()
+ << ". Offset: " << offset;
+
+ auto suffix = content_object.getName().getSuffix();
+
+ if (isSymbol(suffix)) {
+ consumeRepair(content_object.shared_from_this(), offset);
+ } else {
+ consumeSource(content_object.shared_from_this(), suffix, offset, metadata);
+ }
+}
+
+} // namespace fec
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/fec/rs.h b/libtransport/src/protocols/fec/rs.h
new file mode 100644
index 000000000..6672eaa6b
--- /dev/null
+++ b/libtransport/src/protocols/fec/rs.h
@@ -0,0 +1,497 @@
+
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <arpa/inet.h>
+#include <hicn/transport/portability/c_portability.h>
+#include <hicn/transport/portability/endianess.h>
+#include <hicn/transport/utils/membuf.h>
+#include <protocols/fec/fec_info.h>
+#include <protocols/fec_base.h>
+
+#include <array>
+#include <cstdint>
+#include <map>
+#include <unordered_set>
+#include <vector>
+
+namespace transport {
+namespace protocol {
+
+namespace fec {
+
+#define foreach_rs_fec_type \
+ _(RS, 1, 2) \
+ _(RS, 1, 3) \
+ _(RS, 1, 4) \
+ _(RS, 2, 3) \
+ _(RS, 2, 4) \
+ _(RS, 2, 5) \
+ _(RS, 2, 6) \
+ _(RS, 3, 6) \
+ _(RS, 3, 7) \
+ _(RS, 3, 8) \
+ _(RS, 3, 9) \
+ _(RS, 3, 10) \
+ _(RS, 3, 11) \
+ _(RS, 3, 12) \
+ _(RS, 4, 5) \
+ _(RS, 4, 6) \
+ _(RS, 4, 7) \
+ _(RS, 4, 8) \
+ _(RS, 4, 9) \
+ _(RS, 4, 10) \
+ _(RS, 4, 11) \
+ _(RS, 4, 12) \
+ _(RS, 6, 10) \
+ _(RS, 8, 10) \
+ _(RS, 8, 11) \
+ _(RS, 8, 12) \
+ _(RS, 8, 14) \
+ _(RS, 8, 16) \
+ _(RS, 8, 32) \
+ _(RS, 10, 20) \
+ _(RS, 10, 25) \
+ _(RS, 10, 30) \
+ _(RS, 10, 40) \
+ _(RS, 10, 60) \
+ _(RS, 10, 90) \
+ _(RS, 16, 18) \
+ _(RS, 16, 21) \
+ _(RS, 16, 23) \
+ _(RS, 16, 24) \
+ _(RS, 16, 27) \
+ _(RS, 17, 21) \
+ _(RS, 17, 34) \
+ _(RS, 20, 45) \
+ _(RS, 20, 50) \
+ _(RS, 20, 60) \
+ _(RS, 20, 70) \
+ _(RS, 30, 70) \
+ _(RS, 30, 75) \
+ _(RS, 30, 85) \
+ _(RS, 30, 95) \
+ _(RS, 32, 36) \
+ _(RS, 32, 41) \
+ _(RS, 32, 46) \
+ _(RS, 32, 54) \
+ _(RS, 34, 42) \
+ _(RS, 35, 70) \
+ _(RS, 40, 95) \
+ _(RS, 40, 100) \
+ _(RS, 40, 110) \
+ _(RS, 40, 120) \
+ _(RS, 52, 62)
+
+static const constexpr uint16_t MAX_SOURCE_BLOCK_SIZE = 128;
+
+/**
+ * We use a std::array in place of std::vector to avoid to allocate a new vector
+ * in the heap every time we build a new source block, which would be bad if
+ * the decoder has to allocate several source blocks for many concurrent bases.
+ * std::array allows to be constructed in place, saving the allocation at the
+ * price os knowing in advance its size.
+ */
+class RSBufferInfo : public FECBufferInfo {
+ public:
+ RSBufferInfo() : FECBufferInfo() {}
+
+ RSBufferInfo(uint32_t offset, uint32_t index, uint32_t metadata,
+ buffer buffer)
+ : FECBufferInfo(index, metadata, buffer), offset_(offset) {}
+
+ uint32_t getOffset() { return offset_; }
+ RSBufferInfo &setOffset(uint32_t offset) {
+ offset_ = offset;
+ return *this;
+ }
+
+ private:
+ uint32_t offset_ = 0;
+};
+using Packets = std::array<RSBufferInfo, MAX_SOURCE_BLOCK_SIZE>;
+
+/**
+ * FEC Header, prepended to symbol packets.
+ */
+struct fec_header {
+ /**
+ * The base source packet seq_number this FES symbol refers to
+ */
+ uint32_t seq_number;
+
+ /**
+ * The index of the symbol inside the source block, between k and n - 1
+ */
+ uint8_t encoded_symbol_id;
+
+ /**
+ * Total length of source block (n)
+ */
+ uint8_t source_block_len;
+
+ /**
+ * Total number of symbols (n - k)
+ */
+ uint8_t n_fec_symbols;
+
+ /**
+ * Align header to 64 bits
+ */
+ uint8_t padding;
+
+ void setSeqNumberBase(uint32_t suffix) {
+ seq_number = portability::host_to_net(suffix);
+ }
+ uint32_t getSeqNumberBase() { return portability::net_to_host(seq_number); }
+ void setEncodedSymbolId(uint8_t esi) { encoded_symbol_id = esi; }
+ uint8_t getEncodedSymbolId() { return encoded_symbol_id; }
+ void setSourceBlockLen(uint8_t k) { source_block_len = k; }
+ uint8_t getSourceBlockLen() { return source_block_len; }
+ void setNFecSymbols(uint8_t n_r) { n_fec_symbols = n_r; }
+ uint8_t getNFecSymbols() { return n_fec_symbols; }
+};
+
+static_assert(sizeof(fec_header) <= 8, "fec_header is too large");
+
+class rs;
+
+/**
+ * This class models the source block itself.
+ */
+class BlockCode : public Packets {
+ /**
+ * @brief Metadata to include when encoding the buffers. This does not need to
+ * be sent over the network, but just to be included in the FEC protected
+ * bytes.
+ *
+ */
+ class __attribute__((__packed__)) fec_metadata {
+ public:
+ void setPacketLength(uint16_t length) {
+ packet_length = portability::host_to_net(length);
+ }
+ uint32_t getPacketLength() {
+ return portability::net_to_host(packet_length);
+ }
+
+ void setMetadataBase(uint32_t value) {
+ metadata = portability::host_to_net(value);
+ }
+ uint32_t getMetadataBase() { return portability::net_to_host(metadata); }
+
+ private:
+ uint16_t packet_length; /* Used to get the real size of the packet after we
+ pad it */
+ uint32_t
+ metadata; /* Caller may specify an integer for storing additional
+ metadata that can be used when recovering the packet. */
+ };
+
+ /**
+ * For variable length packet we need to prepend to the padded payload the
+ * real length of the packet. This is *not* sent over the network.
+ */
+ static constexpr std::size_t METADATA_BYTES = sizeof(fec_metadata);
+
+ public:
+ BlockCode(uint32_t k, uint32_t n, uint32_t seq_offset, struct fec_parms *code,
+ rs &params);
+
+ /**
+ * Add a repair symbol to the dource block.
+ */
+ bool addRepairSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset = 0);
+
+ /**
+ * Add a source symbol to the source block.
+ */
+ bool addSourceSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset = 0,
+ uint32_t metadata = FECBase::INVALID_METADATA);
+
+ /**
+ * Get current length of source block.
+ */
+ std::size_t length() { return current_block_size_; }
+
+ /**
+ * Get N
+ */
+ uint32_t getN() { return n_; }
+
+ /**
+ * Get K
+ */
+ uint32_t getK() { return k_; }
+
+ /**
+ * Clear source block
+ */
+ void clear();
+
+ private:
+ /**
+ * Add symbol to source block
+ **/
+ bool addSymbol(const fec::buffer &packet, uint32_t i, uint32_t offset,
+ std::size_t size, uint32_t metadata);
+
+ /**
+ * Starting from k source symbols, get the n - k repair symbols
+ */
+ void encode();
+
+ /**
+ * Starting from k symbols (mixed repair and source), get k source symbols.
+ * NOTE: It does not make sense to retrieve the k source symbols using the
+ * very same k source symbols. With the current implementation that case can
+ * never happen.
+ */
+ void decode();
+
+ private:
+ uint32_t k_;
+ uint32_t n_;
+ uint32_t seq_offset_;
+ struct fec_parms *code_;
+ std::size_t max_buffer_size_;
+ std::size_t current_block_size_;
+ std::vector<uint32_t> sorted_index_;
+ bool to_decode_;
+ rs &params_;
+};
+
+/**
+ * This class contains common parameters between the fec encoder and decoder.
+ * In particular it contains:
+ * - The callback to be called when symbols are encoded / decoded
+ * - The reference to the static reed-solomon parameters, allocated at program
+ * startup
+ * - N and K. Ideally they are useful only for the encoder (the decoder can
+ * retrieve them from the FEC header). However right now we assume sender and
+ * receiver agreed on the parameters k and n to use. We will introduce a control
+ * message later to negotiate them, so that decoder cah dynamically change them
+ * during the download.
+ */
+class rs : public virtual FECBase {
+ friend class BlockCode;
+
+ /**
+ * Deleter for static preallocated reed-solomon parameters.
+ */
+ struct MatrixDeleter {
+ void operator()(struct fec_parms *params);
+ };
+
+ /**
+ * unique_ptr to reed-solomon parameters, with custom deleter to call fec_free
+ * at the end of the program
+ */
+ using Matrix = std::unique_ptr<struct fec_parms, MatrixDeleter>;
+
+ /**
+ * Key to retrieve static preallocated reed-solomon parameters. It is pair of
+ * k and n
+ */
+ using Code = std::pair<std::uint32_t /* k */, std::uint32_t /* n */>;
+
+ /**
+ * Custom hash function for (k, n) pair.
+ */
+ struct CodeHasher {
+ std::size_t operator()(const Code &code) const {
+ uint64_t ret = uint64_t(code.first) << 32 | uint64_t(code.second);
+ return std::hash<uint64_t>{}(ret);
+ }
+ };
+
+ protected:
+ /**
+ * Callback to be called after the encode or the decode operations. In the
+ * former case it will contain the symbols, while in the latter the sources.
+ */
+ using PacketsReady = std::function<void(std::vector<buffer> &)>;
+
+ /**
+ * The sequence number base.
+ */
+ using SNBase = std::uint32_t;
+
+ /**
+ * The map of source blocks, used at the decoder side. For the encoding
+ * operation we can use one source block only, since packet are produced in
+ * order.
+ */
+ using SourceBlocks = std::unordered_map<SNBase, BlockCode>;
+
+ /**
+ * Map (k, n) -> reed-solomon parameter
+ */
+ using Codes = std::unordered_map<Code, Matrix, CodeHasher>;
+
+ public:
+ rs(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+ ~rs() = default;
+
+ virtual void clear() { processed_source_blocks_.clear(); }
+
+ bool isSymbol(uint32_t index) { return ((index - seq_offset_) % n_) >= k_; }
+
+ private:
+ /**
+ * Create reed-solomon codes at program startup.
+ */
+ static Codes createCodes();
+
+ protected:
+ bool processed(SNBase seq_base) {
+ return processed_source_blocks_.find(seq_base) !=
+ processed_source_blocks_.end();
+ }
+
+ void setProcessed(SNBase seq_base) {
+ processed_source_blocks_.emplace(seq_base);
+ }
+
+ std::uint32_t k_;
+ std::uint32_t n_;
+ std::uint32_t seq_offset_;
+
+ /**
+ * Keep track of processed source blocks
+ */
+ std::unordered_set<SNBase> processed_source_blocks_;
+
+ static Codes codes_;
+};
+
+/**
+ * The reed-solomon encoder. It is feeded with source symbols and it provide
+ * repair-symbols through the fec_callback_
+ */
+class RSEncoder : public rs, public ProducerFEC {
+ public:
+ RSEncoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+ /**
+ * Always consume source symbols.
+ */
+ void consume(const fec::buffer &packet, uint32_t index, uint32_t offset = 0,
+ uint32_t metadata = FECBase::INVALID_METADATA);
+
+ void onPacketProduced(core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata = FECBase::INVALID_METADATA) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ * in RS the source packets do not transport any FEC header
+ */
+ std::size_t getFecHeaderSize(bool isFEC) override {
+ return isFEC ? sizeof(fec_header) : 0;
+ }
+
+ void clear() override {
+ rs::clear();
+ source_block_.clear();
+ }
+
+ void reset() override { clear(); }
+
+ private:
+ struct fec_parms *current_code_;
+ /**
+ * The source block. As soon as it is filled with k source symbols, the
+ * encoder calls the callback fec_callback_ and the resets the block 0, ready
+ * to accept another batch of k source symbols.
+ */
+ BlockCode source_block_;
+};
+
+/**
+ * The reed-solomon encoder. It is feeded with source/repair symbols and it
+ * provides the original source symbols through the fec_callback_
+ */
+class RSDecoder : public rs, public ConsumerFEC {
+ public:
+ RSDecoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+
+ /**
+ * Consume source symbol
+ */
+ void consumeSource(const fec::buffer &packet, uint32_t i, uint32_t offset = 0,
+ uint32_t metadata = FECBase::INVALID_METADATA);
+
+ /**
+ * Consume repair symbol
+ */
+ void consumeRepair(const fec::buffer &packet, uint32_t offset = 0);
+
+ /**
+ * Consumers will call this function when they receive a data packet
+ */
+ void onDataPacket(core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata = FECBase::INVALID_METADATA) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ * in RS the source packets do not transport any FEC header
+ */
+ std::size_t getFecHeaderSize(bool isFEC) override {
+ return isFEC ? sizeof(fec_header) : 0;
+ }
+
+ /**
+ * Clear decoder to reuse
+ */
+ void clear() override {
+ rs::clear();
+ src_blocks_.clear();
+ parked_packets_.clear();
+ }
+
+ void reset() override { clear(); }
+
+ private:
+ void recoverPackets(SourceBlocks::iterator &src_block_it);
+
+ private:
+ /**
+ * Map of source blocks. We use a map because we may receive symbols belonging
+ * to diffreent source blocks at the same time, so we need to be able to
+ * decode many source symbols at the same time.
+ */
+ SourceBlocks src_blocks_;
+
+ /**
+ * Unordered Map of source symbols for which we did not receive any repair
+ * symbol in the same source block. Notably this happens when:
+ *
+ * - We receive the source symbols first and the repair symbols after
+ * - We received only source symbols for a given block. In that case it does
+ * not make any sense to build the source block, since we received all the
+ * source packet of the block.
+ */
+ using BufferInfoArray = std::vector<RSBufferInfo>;
+ std::unordered_map<uint32_t, BufferInfoArray> parked_packets_;
+};
+
+} // namespace fec
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/fec_base.h b/libtransport/src/protocols/fec_base.h
new file mode 100644
index 000000000..28f6a820a
--- /dev/null
+++ b/libtransport/src/protocols/fec_base.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/core/asio_wrapper.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/errors/not_implemented_exception.h>
+
+#include <functional>
+
+namespace transport {
+namespace protocol {
+
+namespace fec {
+
+using buffer = utils::MemBuf::Ptr;
+
+class FECBufferInfo {
+ public:
+ FECBufferInfo()
+ : index_(~0), metadata_(~0), received_(false), buffer_(nullptr) {}
+
+ template <typename T>
+ FECBufferInfo(uint32_t index, uint32_t metadata, T &&buffer)
+ : index_(index),
+ metadata_(metadata),
+ received_(false),
+ buffer_(std::forward<T>(buffer)) {}
+
+ // Getters
+ uint32_t getIndex() const { return index_; }
+
+ uint32_t getMetadata() const { return metadata_; }
+
+ bool getReceived() const { return received_; }
+
+ buffer &getBuffer() { return buffer_; }
+
+ // Setters
+ void setReceived() { received_ = true; }
+
+ FECBufferInfo &setIndex(uint32_t index) {
+ index_ = index;
+ return *this;
+ }
+
+ FECBufferInfo &setMetadata(uint32_t metadata) {
+ metadata_ = metadata;
+ return *this;
+ }
+
+ FECBufferInfo &setBuffer(buffer &buffer) {
+ buffer_ = buffer;
+ return *this;
+ }
+
+ FECBufferInfo &setBuffer(buffer &&buffer) {
+ buffer_ = std::move(buffer);
+ return *this;
+ }
+
+ private:
+ uint32_t index_;
+ uint32_t metadata_;
+ bool received_;
+ buffer buffer_;
+};
+
+using BufferArray = typename std::vector<FECBufferInfo>;
+
+class FECBase {
+ public:
+ static inline uint32_t INVALID_METADATA = ~0;
+ static inline uint32_t INVALID_INDEX = ~0;
+
+ virtual ~FECBase() {}
+ /**
+ * Callback to be called after the encode or the decode operations. In the
+ * former case it will contain the symbols, while in the latter the sources.
+ */
+ using PacketsReady = std::function<void(BufferArray &)>;
+
+ /**
+ * Callback to be called when a new buffer (for encoding / decoding) needs to
+ * be allocated.
+ */
+ using BufferRequested = std::function<buffer(std::size_t size)>;
+
+ /**
+ * @brief Get size of FEC header.
+ * the fec header size may be different if a packet is a data packet or a FEC
+ * packet
+ */
+ virtual std::size_t getFecHeaderSize(bool isFEC) = 0;
+
+ /**
+ * Set callback to call after packet encoding / decoding
+ */
+ template <typename Handler>
+ void setFECCallback(Handler &&callback) {
+ fec_callback_ = std::forward<Handler>(callback);
+ }
+
+ /**
+ * Set a callback to request a buffer.
+ */
+ template <typename Handler>
+ void setBufferCallback(Handler &&buffer_callback) {
+ buffer_callback_ = buffer_callback;
+ }
+
+ /**
+ * Creates the timer to flush packets. So far needed only if using Rely and
+ * want to avoid expired packets blocked by missing pkts to wait for a new
+ * packet to arrive and trigger the flush
+ */
+ void setIOService(asio::io_service &io_service) {
+ flush_timer_ = std::make_unique<asio::steady_timer>(io_service);
+ }
+
+ virtual void reset() = 0;
+
+ protected:
+ PacketsReady fec_callback_{0};
+ BufferRequested buffer_callback_{0};
+ std::unique_ptr<asio::steady_timer> flush_timer_;
+};
+
+/**
+ * Interface classes to integrate FEC inside any producer transport protocol
+ */
+class ProducerFEC : public virtual FECBase {
+ public:
+ virtual ~ProducerFEC() = default;
+ /**
+ * Producers will call this function upon production of a new packet.
+ */
+ virtual void onPacketProduced(
+ core::ContentObject &content_object, uint32_t offset,
+ uint32_t metadata = FECBase::INVALID_METADATA) = 0;
+};
+
+/**
+ * Interface classes to integrate FEC inside any consumer transport protocol
+ */
+class ConsumerFEC : public virtual FECBase {
+ public:
+ virtual ~ConsumerFEC() = default;
+
+ /**
+ * Consumers will call this function when they receive a data packet
+ */
+ virtual void onDataPacket(core::ContentObject &content_object,
+ uint32_t offset,
+ uint32_t metadata = FECBase::INVALID_METADATA) = 0;
+};
+
+} // namespace fec
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/fec_utils.h b/libtransport/src/protocols/fec_utils.h
new file mode 100644
index 000000000..d70ff1c09
--- /dev/null
+++ b/libtransport/src/protocols/fec_utils.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/config.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/errors/not_implemented_exception.h>
+#include <protocols/fec/rs.h>
+
+#if ENABLE_RELY
+#include <protocols/fec/rely.h>
+#endif
+
+#include <functional>
+
+namespace transport {
+namespace protocol {
+
+namespace fec {
+
+#if ENABLE_RELY
+#define foreach_fec_type foreach_rs_fec_type foreach_rely_fec_type
+#else
+#define foreach_fec_type foreach_rs_fec_type
+#endif
+
+#define ENUM_FROM_MACRO(name, k, n) name##_K##k##_N##n
+#define ENUM_FROM_MACRO_STR(name, k, n) #name "_K" #k "_N" #n
+
+enum class FECType : uint8_t {
+#define _(name, k, n) ENUM_FROM_MACRO(name, k, n),
+ foreach_fec_type
+#undef _
+ UNKNOWN
+};
+
+#define ENUM_FROM_MACRO2(name, k, n) FECType::ENUM_FROM_MACRO(name, k, n)
+
+class FECUtils {
+ public:
+ static FECType fecTypeFromString(const char *fec_type) {
+#define _(name, k, n) \
+ do { \
+ if (strncmp(fec_type, ENUM_FROM_MACRO_STR(name, k, n), \
+ strlen(ENUM_FROM_MACRO_STR(name, k, n))) == 0) { \
+ return ENUM_FROM_MACRO2(name, k, n); \
+ } \
+ } while (0);
+ foreach_fec_type
+#undef _
+
+ return FECType::UNKNOWN;
+ }
+
+ static bool isFec(FECType fec_type, uint32_t index, uint32_t seq_offset = 0) {
+ switch (fec_type) {
+#define _(name, k, n) \
+ case ENUM_FROM_MACRO2(name, k, n): \
+ return FecInfo<Code<k, n>>::isFec(index - (seq_offset % n));
+
+ foreach_fec_type
+#undef _
+ default : return false;
+ }
+ }
+
+ static uint32_t nextSource(FECType fec_type, uint32_t index,
+ uint32_t seq_offset = 0) {
+ switch (fec_type) {
+#define _(name, k, n) \
+ case ENUM_FROM_MACRO2(name, k, n): \
+ return FecInfo<Code<k, n>>::nextSource(index) + (seq_offset % n);
+
+ foreach_fec_type
+#undef _
+ default : throw std::runtime_error("Unknown fec type");
+ }
+ }
+
+ static uint32_t getSourceSymbols(FECType fec_type) {
+ switch (fec_type) {
+#define _(name, k, n) \
+ case ENUM_FROM_MACRO2(name, k, n): \
+ return k;
+ foreach_fec_type
+#undef _
+ default : throw std::runtime_error("Unknown fec type");
+ }
+ }
+
+ static uint32_t getBlockSymbols(FECType fec_type) {
+ switch (fec_type) {
+#define _(name, k, n) \
+ case ENUM_FROM_MACRO2(name, k, n): \
+ return n;
+ foreach_fec_type
+#undef _
+ default : throw std::runtime_error("Unknown fec type");
+ }
+ }
+
+ static std::unique_ptr<ProducerFEC> getEncoder(FECType fec_type,
+ uint32_t seq_offset = 0) {
+ return factoryEncoder(fec_type, seq_offset);
+ }
+
+ static std::unique_ptr<ConsumerFEC> getDecoder(FECType fec_type,
+ uint32_t seq_offset = 0) {
+ return factoryDencoder(fec_type, seq_offset);
+ }
+
+ private:
+ static std::unique_ptr<ProducerFEC> factoryEncoder(FECType fec_type,
+ uint32_t seq_offset) {
+ switch (fec_type) {
+#define _(name, k, n) \
+ case ENUM_FROM_MACRO2(name, k, n): \
+ return std::make_unique<name##Encoder>(k, n, seq_offset);
+
+ foreach_fec_type
+#undef _
+ default : throw std::runtime_error("Unknown fec type");
+ }
+ }
+
+ static std::unique_ptr<ConsumerFEC> factoryDencoder(FECType fec_type,
+ uint32_t seq_offset) {
+ switch (fec_type) {
+#define _(name, k, n) \
+ case ENUM_FROM_MACRO2(name, k, n): \
+ return std::make_unique<name##Decoder>(k, n, seq_offset);
+
+ foreach_fec_type
+#undef _
+ default : throw std::runtime_error("Unknown fec type");
+ }
+ }
+}; // namespace fec
+
+} // namespace fec
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/incremental_indexer.cc b/libtransport/src/protocols/incremental_indexer.cc
deleted file mode 100644
index 0872c4554..000000000
--- a/libtransport/src/protocols/incremental_indexer.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <protocols/incremental_indexer.h>
-
-#include <hicn/transport/interfaces/socket_consumer.h>
-#include <protocols/protocol.h>
-
-namespace transport {
-namespace protocol {
-
-void IncrementalIndexer::onContentObject(
- core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) {
- using namespace interface;
-
- TRANSPORT_LOGD("Receive content %s", content_object->getName().toString().c_str());
-
- if (TRANSPORT_EXPECT_FALSE(content_object->testRst())) {
- final_suffix_ = content_object->getName().getSuffix();
- }
-
- auto ret = verification_manager_->onPacketToVerify(*content_object);
-
- switch (ret) {
- case VerificationPolicy::ACCEPT_PACKET: {
- reassembly_->reassemble(std::move(content_object));
- break;
- }
- case VerificationPolicy::DROP_PACKET: {
- transport_protocol_->onPacketDropped(std::move(interest),
- std::move(content_object));
- break;
- }
- case VerificationPolicy::ABORT_SESSION: {
- transport_protocol_->onContentReassembled(
- make_error_code(protocol_error::session_aborted));
- break;
- }
- }
-}
-
-} // namespace protocol
-} // namespace transport
diff --git a/libtransport/src/protocols/incremental_indexer.h b/libtransport/src/protocols/incremental_indexer.h
deleted file mode 100644
index 20c5e4759..000000000
--- a/libtransport/src/protocols/incremental_indexer.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <hicn/transport/errors/runtime_exception.h>
-#include <hicn/transport/errors/unexpected_manifest_exception.h>
-#include <hicn/transport/utils/literals.h>
-
-#include <protocols/indexer.h>
-#include <protocols/reassembly.h>
-#include <protocols/verification_manager.h>
-
-#include <deque>
-
-namespace transport {
-
-namespace interface {
-class ConsumerSocket;
-}
-
-namespace protocol {
-
-class Reassembly;
-class TransportProtocol;
-
-class IncrementalIndexer : public Indexer {
- public:
- IncrementalIndexer(implementation::ConsumerSocket *icn_socket,
- TransportProtocol *transport, Reassembly *reassembly)
- : socket_(icn_socket),
- reassembly_(reassembly),
- transport_protocol_(transport),
- final_suffix_(std::numeric_limits<uint32_t>::max()),
- first_suffix_(0),
- next_download_suffix_(0),
- next_reassembly_suffix_(0),
- verification_manager_(
- std::make_unique<SignatureVerificationManager>(icn_socket)) {
- if (reassembly_) {
- reassembly_->setIndexer(this);
- }
- }
-
- IncrementalIndexer(const IncrementalIndexer &) = delete;
-
- IncrementalIndexer(IncrementalIndexer &&other)
- : socket_(other.socket_),
- reassembly_(other.reassembly_),
- transport_protocol_(other.transport_protocol_),
- final_suffix_(other.final_suffix_),
- first_suffix_(other.first_suffix_),
- next_download_suffix_(other.next_download_suffix_),
- next_reassembly_suffix_(other.next_reassembly_suffix_),
- verification_manager_(std::move(other.verification_manager_)) {
- if (reassembly_) {
- reassembly_->setIndexer(this);
- }
- }
-
- /**
- *
- */
- virtual ~IncrementalIndexer() {}
-
- TRANSPORT_ALWAYS_INLINE virtual void reset(
- std::uint32_t offset = 0) override {
- final_suffix_ = std::numeric_limits<uint32_t>::max();
- next_download_suffix_ = offset;
- next_reassembly_suffix_ = offset;
- }
-
- /**
- * Retrieve from the manifest the next suffix to retrieve.
- */
- TRANSPORT_ALWAYS_INLINE virtual uint32_t getNextSuffix() override {
- return next_download_suffix_ <= final_suffix_ ? next_download_suffix_++
- : IndexManager::invalid_index;
- }
-
- TRANSPORT_ALWAYS_INLINE virtual void setFirstSuffix(
- uint32_t suffix) override {
- first_suffix_ = suffix;
- }
-
- /**
- * Retrive the next segment to be reassembled.
- */
- TRANSPORT_ALWAYS_INLINE virtual uint32_t getNextReassemblySegment() override {
- return next_reassembly_suffix_ <= final_suffix_
- ? next_reassembly_suffix_++
- : IndexManager::invalid_index;
- }
-
- TRANSPORT_ALWAYS_INLINE virtual bool isFinalSuffixDiscovered() override {
- return final_suffix_ != std::numeric_limits<uint32_t>::max();
- }
-
- TRANSPORT_ALWAYS_INLINE virtual uint32_t getFinalSuffix() override {
- return final_suffix_;
- }
-
- void onContentObject(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object) override;
-
- TRANSPORT_ALWAYS_INLINE void setReassembly(Reassembly *reassembly) {
- reassembly_ = reassembly;
-
- if (reassembly_) {
- reassembly_->setIndexer(this);
- }
- }
-
- TRANSPORT_ALWAYS_INLINE bool onKeyToVerify() override {
- return verification_manager_->onKeyToVerify();
- }
-
- protected:
- implementation::ConsumerSocket *socket_;
- Reassembly *reassembly_;
- TransportProtocol *transport_protocol_;
- uint32_t final_suffix_;
- uint32_t first_suffix_;
- uint32_t next_download_suffix_;
- uint32_t next_reassembly_suffix_;
- std::unique_ptr<VerificationManager> verification_manager_;
-};
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.cc b/libtransport/src/protocols/incremental_indexer_bytestream.cc
new file mode 100644
index 000000000..b94f229e5
--- /dev/null
+++ b/libtransport/src/protocols/incremental_indexer_bytestream.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <protocols/errors.h>
+#include <protocols/incremental_indexer_bytestream.h>
+#include <protocols/transport_protocol.h>
+
+namespace transport {
+namespace protocol {
+
+void IncrementalIndexer::onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly) {
+ using namespace interface;
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received content " << content_object.getName();
+
+ DCHECK(reassembly_);
+
+ if (TRANSPORT_EXPECT_FALSE(content_object.isLast())) {
+ final_suffix_ = content_object.getName().getSuffix();
+ }
+
+ auto ret = verifier_->verifyPackets(&content_object);
+
+ switch (ret) {
+ case auth::VerificationPolicy::ACCEPT: {
+ if (reassembly) {
+ reassembly_->reassemble(content_object);
+ }
+ break;
+ }
+
+ case auth::VerificationPolicy::UNKNOWN:
+ case auth::VerificationPolicy::DROP: {
+ transport_->onPacketDropped(
+ interest, content_object,
+ make_error_code(protocol_error::verification_failed));
+ break;
+ }
+
+ case auth::VerificationPolicy::ABORT: {
+ transport_->onContentReassembled(
+ make_error_code(protocol_error::session_aborted));
+ break;
+ }
+ }
+}
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.h b/libtransport/src/protocols/incremental_indexer_bytestream.h
new file mode 100644
index 000000000..4f9b6126f
--- /dev/null
+++ b/libtransport/src/protocols/incremental_indexer_bytestream.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/interfaces/callbacks.h>
+#include <hicn/transport/utils/literals.h>
+#include <implementation/socket_consumer.h>
+#include <protocols/indexer.h>
+#include <protocols/reassembly.h>
+
+#include <deque>
+
+namespace transport {
+
+namespace interface {
+class ConsumerSocket;
+}
+
+namespace protocol {
+
+class Reassembly;
+class TransportProtocol;
+
+class IncrementalIndexer : public Indexer {
+ public:
+ IncrementalIndexer(implementation::ConsumerSocket *icn_socket,
+ TransportProtocol *transport)
+ : Indexer(icn_socket, transport),
+ final_suffix_(Indexer::invalid_index),
+ first_suffix_(0),
+ next_download_suffix_(0),
+ next_reassembly_suffix_(0) {}
+
+ IncrementalIndexer(const IncrementalIndexer &other) = delete;
+
+ IncrementalIndexer(IncrementalIndexer &&other)
+ : Indexer(other),
+ final_suffix_(other.final_suffix_),
+ first_suffix_(other.first_suffix_),
+ next_download_suffix_(other.next_download_suffix_),
+ next_reassembly_suffix_(other.next_reassembly_suffix_) {}
+
+ virtual ~IncrementalIndexer() {}
+
+ virtual void reset() override {
+ final_suffix_ = Indexer::invalid_index;
+ next_download_suffix_ = first_suffix_;
+ next_reassembly_suffix_ = first_suffix_;
+ }
+
+ virtual uint32_t checkNextSuffix() const override {
+ return next_download_suffix_ <= final_suffix_ ? next_download_suffix_
+ : Indexer::invalid_index;
+ }
+
+ virtual uint32_t getNextSuffix() override {
+ return next_download_suffix_ <= final_suffix_ ? next_download_suffix_++
+ : Indexer::invalid_index;
+ }
+
+ virtual void setFirstSuffix(uint32_t suffix) override {
+ first_suffix_ = suffix;
+ }
+
+ uint32_t getFirstSuffix() const override { return first_suffix_; }
+
+ virtual uint32_t jumpToIndex(uint32_t index) override {
+ next_download_suffix_ = index;
+ return next_download_suffix_;
+ }
+
+ /**
+ * Retrive the next segment to be reassembled.
+ */
+ virtual uint32_t getNextReassemblySegment() override {
+ return next_reassembly_suffix_ <= final_suffix_ ? next_reassembly_suffix_++
+ : Indexer::invalid_index;
+ }
+
+ virtual bool isFinalSuffixDiscovered() override {
+ return final_suffix_ != Indexer::invalid_index;
+ }
+
+ virtual uint32_t getFinalSuffix() const override { return final_suffix_; }
+
+ void enableFec(fec::FECType fec_type) override {}
+
+ void disableFec() override {}
+
+ void setNFec(uint32_t n_fec) override {}
+ virtual uint32_t getNFec() const override { return 0; }
+
+ virtual void onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly) override;
+
+ protected:
+ uint32_t final_suffix_;
+ uint32_t first_suffix_;
+ uint32_t next_download_suffix_;
+ uint32_t next_reassembly_suffix_;
+};
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/index_manager_bytestream.cc b/libtransport/src/protocols/index_manager_bytestream.cc
new file mode 100644
index 000000000..952f36e0e
--- /dev/null
+++ b/libtransport/src/protocols/index_manager_bytestream.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <protocols/index_manager_bytestream.h>
+#include <protocols/manifest_incremental_indexer_bytestream.h>
+#include <protocols/transport_protocol.h>
+
+namespace transport {
+namespace protocol {
+
+IndexManager::IndexManager(implementation::ConsumerSocket *icn_socket,
+ TransportProtocol *transport)
+ : IncrementalIndexer(icn_socket, transport),
+ indexer_(std::make_unique<IncrementalIndexer>(icn_socket, transport)),
+ first_segment_received_(false) {}
+
+void IndexManager::onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly) {
+ if (first_segment_received_) {
+ return indexer_->onContentObject(interest, content_object, reassembly);
+ } else {
+ std::uint32_t segment_number = interest.getName().getSuffix();
+
+ if (segment_number == 0) {
+ // Check if manifest
+ if (content_object.getPayloadType() == core::PayloadType::MANIFEST) {
+ IncrementalIndexer *indexer =
+ static_cast<IncrementalIndexer *>(indexer_.release());
+ indexer_ =
+ std::make_unique<ManifestIncrementalIndexer>(std::move(*indexer));
+ delete indexer;
+ }
+
+ indexer_->onContentObject(interest, content_object);
+ auto it = interest_data_set_.begin();
+ while (it != interest_data_set_.end()) {
+ indexer_->onContentObject(*it->first, *it->second);
+ it = interest_data_set_.erase(it);
+ }
+
+ first_segment_received_ = true;
+ } else {
+ interest_data_set_.emplace(interest.shared_from_this(),
+ content_object.shared_from_this());
+ }
+ }
+}
+
+void IndexManager::reset() {
+ indexer_ = std::make_unique<IncrementalIndexer>(socket_, transport_);
+ indexer_->setReassembly(this->reassembly_);
+ indexer_->reset();
+ first_segment_received_ = false;
+ interest_data_set_.clear();
+}
+
+} // namespace protocol
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/protocols/index_manager_bytestream.h b/libtransport/src/protocols/index_manager_bytestream.h
new file mode 100644
index 000000000..7ea31dfa5
--- /dev/null
+++ b/libtransport/src/protocols/index_manager_bytestream.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <protocols/incremental_indexer_bytestream.h>
+
+#include <list>
+
+namespace transport {
+
+namespace implementation {
+class ConsumerSocket;
+}
+
+namespace protocol {
+
+class TransportProtocol;
+
+class IndexManager : public IncrementalIndexer {
+ public:
+ IndexManager(implementation::ConsumerSocket *icn_socket,
+ TransportProtocol *transport);
+
+ uint32_t getNextSuffix() override { return indexer_->getNextSuffix(); }
+
+ void setFirstSuffix(uint32_t suffix) override {
+ indexer_->setFirstSuffix(suffix);
+ }
+
+ uint32_t getFirstSuffix() const override {
+ return indexer_->getFirstSuffix();
+ }
+
+ uint32_t getNextReassemblySegment() override {
+ return indexer_->getNextReassemblySegment();
+ }
+
+ bool isFinalSuffixDiscovered() override {
+ return indexer_->isFinalSuffixDiscovered();
+ }
+
+ uint32_t getFinalSuffix() const override {
+ return indexer_->getFinalSuffix();
+ }
+
+ uint32_t jumpToIndex(uint32_t index) override {
+ return indexer_->jumpToIndex(index);
+ }
+
+ void setNFec(uint32_t n_fec) override { return indexer_->setNFec(n_fec); }
+ uint32_t getNFec() const override { return indexer_->getNFec(); }
+
+ void enableFec(fec::FECType fec_type) override {
+ return indexer_->enableFec(fec_type);
+ }
+
+ double getFecOverhead() const override { return indexer_->getFecOverhead(); }
+
+ double getMaxFecOverhead() const override {
+ return indexer_->getMaxFecOverhead();
+ }
+
+ void disableFec() override { return indexer_->disableFec(); }
+
+ void reset() override;
+
+ void setReassembly(Reassembly *reassembly) override {
+ Indexer::setReassembly(reassembly);
+ indexer_->setReassembly(reassembly);
+ }
+
+ void onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly) override;
+
+ private:
+ std::unique_ptr<Indexer> indexer_;
+ bool first_segment_received_;
+ std::set<std::pair<core::Interest::Ptr, core::ContentObject::Ptr>>
+ interest_data_set_;
+};
+
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/indexer.cc b/libtransport/src/protocols/indexer.cc
index ca12330a6..41465755b 100644
--- a/libtransport/src/protocols/indexer.cc
+++ b/libtransport/src/protocols/indexer.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -13,67 +13,58 @@
* limitations under the License.
*/
-#include <hicn/transport/utils/branch_prediction.h>
-
-#include <protocols/incremental_indexer.h>
+#include <implementation/socket_consumer.h>
+#include <protocols/errors.h>
#include <protocols/indexer.h>
-#include <protocols/manifest_incremental_indexer.h>
-#include <protocols/protocol.h>
namespace transport {
namespace protocol {
-IndexManager::IndexManager(implementation::ConsumerSocket *icn_socket,
- TransportProtocol *transport, Reassembly *reassembly)
- : indexer_(std::make_unique<IncrementalIndexer>(icn_socket, transport,
- reassembly)),
- first_segment_received_(false),
- icn_socket_(icn_socket),
- transport_(transport),
- reassembly_(reassembly) {}
-
-void IndexManager::onContentObject(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object) {
- if (first_segment_received_) {
- indexer_->onContentObject(std::move(interest), std::move(content_object));
- } else {
- std::uint32_t segment_number = interest->getName().getSuffix();
+using namespace interface;
- if (segment_number == 0) {
- // Check if manifest
- if (content_object->getPayloadType() == PayloadType::MANIFEST) {
- IncrementalIndexer *indexer =
- static_cast<IncrementalIndexer *>(indexer_.release());
- indexer_ =
- std::make_unique<ManifestIncrementalIndexer>(std::move(*indexer));
- delete indexer;
- }
+const constexpr uint32_t Indexer::invalid_index;
- indexer_->onContentObject(std::move(interest), std::move(content_object));
- auto it = interest_data_set_.begin();
- while (it != interest_data_set_.end()) {
- indexer_->onContentObject(
- std::move(const_cast<core::Interest::Ptr &&>(it->first)),
- std::move(const_cast<core::ContentObject::Ptr &&>(it->second)));
- it = interest_data_set_.erase(it);
- }
+Indexer::Indexer(implementation::ConsumerSocket *socket,
+ TransportProtocol *transport)
+ : socket_(socket), transport_(transport) {
+ setVerifier();
+}
- first_segment_received_ = true;
- } else {
- interest_data_set_.emplace(std::move(interest),
- std::move(content_object));
- }
+void Indexer::setVerifier() {
+ if (socket_) {
+ socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier_);
}
}
-bool IndexManager::onKeyToVerify() { return indexer_->onKeyToVerify(); }
+void Indexer::applyPolicy(core::Interest &interest,
+ core::ContentObject &content_object, bool reassembly,
+ auth::VerificationPolicy policy) const {
+ DCHECK(reassembly_ != nullptr);
-void IndexManager::reset(std::uint32_t offset) {
- indexer_ = std::make_unique<IncrementalIndexer>(icn_socket_, transport_,
- reassembly_);
- first_segment_received_ = false;
- interest_data_set_.clear();
+ switch (policy) {
+ case auth::VerificationPolicy::ACCEPT: {
+ if (reassembly) {
+ reassembly_->reassemble(content_object);
+ }
+ break;
+ }
+ case auth::VerificationPolicy::UNKNOWN:
+ if (reassembly && reassembly_->reassembleUnverified()) {
+ reassembly_->reassemble(content_object);
+ }
+ break;
+ case auth::VerificationPolicy::DROP:
+ transport_->onPacketDropped(
+ interest, content_object,
+ make_error_code(protocol_error::verification_failed));
+ break;
+ case auth::VerificationPolicy::ABORT: {
+ transport_->onContentReassembled(
+ make_error_code(protocol_error::session_aborted));
+ break;
+ }
+ }
}
-} // namespace protocol
-} // namespace transport
+} // end namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/indexer.h b/libtransport/src/protocols/indexer.h
index 8213a1503..1bacb13aa 100644
--- a/libtransport/src/protocols/indexer.h
+++ b/libtransport/src/protocols/indexer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -15,8 +15,10 @@
#pragma once
+#include <hicn/transport/auth/policies.h>
#include <hicn/transport/core/content_object.h>
#include <hicn/transport/core/interest.h>
+#include <protocols/fec_utils.h>
#include <set>
@@ -33,72 +35,87 @@ class TransportProtocol;
class Indexer {
public:
- /**
- *
- */
+ static const constexpr uint32_t invalid_index =
+ (std::numeric_limits<uint32_t>::max() - 1);
+
+ Indexer(implementation::ConsumerSocket *socket, TransportProtocol *transport);
+
virtual ~Indexer() = default;
+
/**
- * Retrieve from the manifest the next suffix to retrieve.
+ * Suffix getters
*/
+ virtual uint32_t checkNextSuffix() const = 0;
virtual uint32_t getNextSuffix() = 0;
+ virtual uint32_t getNextReassemblySegment() = 0;
+ /**
+ * Set first suffix from where to start.
+ */
virtual void setFirstSuffix(uint32_t suffix) = 0;
+ virtual uint32_t getFirstSuffix() const = 0;
/**
- * Retrive the next segment to be reassembled.
+ * Functions to set/enable/disable fec
*/
- virtual uint32_t getNextReassemblySegment() = 0;
+ virtual void setNFec(uint32_t n_fec) = 0;
+ virtual uint32_t getNFec() const = 0;
+ virtual void enableFec(fec::FECType fec_type) = 0;
+ virtual void disableFec() = 0;
+ virtual bool isFec(uint32_t index) { return false; }
+ virtual double getFecOverhead() const { return 0.0; }
+ virtual double getMaxFecOverhead() const { return 0.0; }
+ /**
+ * Final suffix helpers.
+ */
virtual bool isFinalSuffixDiscovered() = 0;
+ virtual uint32_t getFinalSuffix() const = 0;
- virtual uint32_t getFinalSuffix() = 0;
-
- virtual void reset(std::uint32_t offset = 0) = 0;
-
- virtual void onContentObject(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object) = 0;
-
- virtual bool onKeyToVerify() = 0;
-};
-
-class IndexManager : Indexer {
- public:
- static constexpr uint32_t invalid_index = ~0;
-
- IndexManager(implementation::ConsumerSocket *icn_socket,
- TransportProtocol *transport, Reassembly *reassembly);
-
- uint32_t getNextSuffix() override { return indexer_->getNextSuffix(); }
-
- void setFirstSuffix(uint32_t suffix) override {
- indexer_->setFirstSuffix(suffix);
- }
-
- uint32_t getNextReassemblySegment() override {
- return indexer_->getNextReassemblySegment();
- }
-
- bool isFinalSuffixDiscovered() override {
- return indexer_->isFinalSuffixDiscovered();
+ /**
+ * Set reassembly protocol
+ */
+ virtual void setReassembly(Reassembly *reassembly) {
+ reassembly_ = reassembly;
}
- uint32_t getFinalSuffix() override { return indexer_->getFinalSuffix(); }
+ /**
+ * Set verifier using socket
+ */
+ virtual void setVerifier();
- void reset(std::uint32_t offset = 0) override;
+ /**
+ * Apply a verification policy
+ */
+ virtual void applyPolicy(core::Interest &interest,
+ core::ContentObject &content_object, bool reassembly,
+ auth::VerificationPolicy policy) const;
+ /**
+ * Jump to suffix. This may be useful if, for any protocol dependent
+ * mechanism, we need to suddenly change current suffix. This does not
+ * modify the way suffixes re incremented/decremented (that's part of the
+ * implementation).
+ */
+ virtual uint32_t jumpToIndex(uint32_t index) = 0;
- void onContentObject(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object) override;
+ /**
+ * Reset the indexer.
+ */
+ virtual void reset() = 0;
- bool onKeyToVerify() override;
+ /**
+ * Process incoming content objects.
+ */
+ virtual void onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly = true) = 0;
- private:
- std::unique_ptr<Indexer> indexer_;
- bool first_segment_received_;
- std::set<std::pair<core::Interest::Ptr, core::ContentObject::Ptr>>
- interest_data_set_;
- implementation::ConsumerSocket *icn_socket_;
+ protected:
+ implementation::ConsumerSocket *socket_;
TransportProtocol *transport_;
Reassembly *reassembly_;
+ std::shared_ptr<auth::Verifier> verifier_;
+ auth::CryptoHashType manifest_hash_type_;
};
} // end namespace protocol
diff --git a/libtransport/src/protocols/manifest_incremental_indexer.cc b/libtransport/src/protocols/manifest_incremental_indexer.cc
deleted file mode 100644
index da835b577..000000000
--- a/libtransport/src/protocols/manifest_incremental_indexer.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <implementation/socket_consumer.h>
-
-#include <protocols/manifest_incremental_indexer.h>
-#include <protocols/protocol.h>
-
-#include <cmath>
-#include <deque>
-
-namespace transport {
-
-namespace protocol {
-
-using namespace interface;
-
-ManifestIncrementalIndexer::ManifestIncrementalIndexer(
- implementation::ConsumerSocket *icn_socket, TransportProtocol *transport,
- Reassembly *reassembly)
- : IncrementalIndexer(icn_socket, transport, reassembly),
- suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy(
- NextSegmentCalculationStrategy::INCREMENTAL, next_download_suffix_,
- 0)) {}
-
-void ManifestIncrementalIndexer::onContentObject(
- core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) {
- // Check if manifest or not
- if (content_object->getPayloadType() == PayloadType::MANIFEST) {
- TRANSPORT_LOGD("Receive content %s", content_object->getName().toString().c_str());
- onUntrustedManifest(std::move(interest), std::move(content_object));
- } else if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) {
- TRANSPORT_LOGD("Receive manifest %s", content_object->getName().toString().c_str());
- onUntrustedContentObject(std::move(interest), std::move(content_object));
- }
-}
-
-void ManifestIncrementalIndexer::onUntrustedManifest(
- core::Interest::Ptr &&interest, core::ContentObject::Ptr &&content_object) {
- auto ret = verification_manager_->onPacketToVerify(*content_object);
-
- switch (ret) {
- case VerificationPolicy::ACCEPT_PACKET: {
- processTrustedManifest(std::move(content_object));
- break;
- }
- case VerificationPolicy::DROP_PACKET:
- case VerificationPolicy::ABORT_SESSION: {
- transport_protocol_->onContentReassembled(
- make_error_code(protocol_error::session_aborted));
- break;
- }
- }
-}
-
-void ManifestIncrementalIndexer::processTrustedManifest(
- ContentObject::Ptr &&content_object) {
- auto manifest =
- std::make_unique<ContentObjectManifest>(std::move(*content_object));
- manifest->decode();
-
- if (TRANSPORT_EXPECT_FALSE(manifest->getVersion() !=
- core::ManifestVersion::VERSION_1)) {
- throw errors::RuntimeException("Received manifest with unknown version.");
- }
-
- switch (manifest->getManifestType()) {
- case core::ManifestType::INLINE_MANIFEST: {
- auto _it = manifest->getSuffixList().begin();
- auto _end = manifest->getSuffixList().end();
-
- suffix_strategy_->setFinalSuffix(manifest->getFinalBlockNumber());
-
- for (; _it != _end; _it++) {
- auto hash =
- std::make_pair(std::vector<uint8_t>(_it->second, _it->second + 32),
- manifest->getHashAlgorithm());
-
- if (!checkUnverifiedSegments(_it->first, hash)) {
- suffix_hash_map_[_it->first] = std::move(hash);
- }
- }
-
- reassembly_->reassemble(std::move(manifest));
-
- break;
- }
- case core::ManifestType::FLIC_MANIFEST: {
- throw errors::NotImplementedException();
- }
- case core::ManifestType::FINAL_CHUNK_NUMBER: {
- throw errors::NotImplementedException();
- }
- }
-}
-
-bool ManifestIncrementalIndexer::checkUnverifiedSegments(
- std::uint32_t suffix, const HashEntry &hash) {
- auto it = unverified_segments_.find(suffix);
-
- if (it != unverified_segments_.end()) {
- auto ret = verifyContentObject(hash, *it->second.second);
-
- switch (ret) {
- case VerificationPolicy::ACCEPT_PACKET: {
- reassembly_->reassemble(std::move(it->second.second));
- break;
- }
- case VerificationPolicy::DROP_PACKET: {
- transport_protocol_->onPacketDropped(std::move(it->second.first),
- std::move(it->second.second));
- break;
- }
- case VerificationPolicy::ABORT_SESSION: {
- transport_protocol_->onContentReassembled(
- make_error_code(protocol_error::session_aborted));
- break;
- }
- }
-
- unverified_segments_.erase(it);
- return true;
- }
-
- return false;
-}
-
-VerificationPolicy ManifestIncrementalIndexer::verifyContentObject(
- const HashEntry &manifest_hash, const ContentObject &content_object) {
- VerificationPolicy ret;
-
- auto hash_type = static_cast<utils::CryptoHashType>(manifest_hash.second);
- auto data_packet_digest = content_object.computeDigest(manifest_hash.second);
- auto data_packet_digest_bytes =
- data_packet_digest.getDigest<uint8_t>().data();
- const std::vector<uint8_t> &manifest_digest_bytes = manifest_hash.first;
-
- if (utils::CryptoHash::compareBinaryDigest(
- data_packet_digest_bytes, manifest_digest_bytes.data(), hash_type)) {
- ret = VerificationPolicy::ACCEPT_PACKET;
- } else {
- ConsumerContentObjectVerificationFailedCallback
- *verification_failed_callback = VOID_HANDLER;
- socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED,
- &verification_failed_callback);
- ret = (*verification_failed_callback)(
- *socket_->getInterface(), content_object,
- make_error_code(protocol_error::integrity_verification_failed));
- }
-
- return ret;
-}
-
-void ManifestIncrementalIndexer::onUntrustedContentObject(
- Interest::Ptr &&i, ContentObject::Ptr &&c) {
- auto suffix = c->getName().getSuffix();
- auto it = suffix_hash_map_.find(suffix);
-
- if (it != suffix_hash_map_.end()) {
- auto ret = verifyContentObject(it->second, *c);
-
- switch (ret) {
- case VerificationPolicy::ACCEPT_PACKET: {
- suffix_hash_map_.erase(it);
- reassembly_->reassemble(std::move(c));
- break;
- }
- case VerificationPolicy::DROP_PACKET: {
- transport_protocol_->onPacketDropped(std::move(i), std::move(c));
- break;
- }
- case VerificationPolicy::ABORT_SESSION: {
- transport_protocol_->onContentReassembled(
- make_error_code(protocol_error::session_aborted));
- break;
- }
- }
- } else {
- unverified_segments_[suffix] = std::make_pair(std::move(i), std::move(c));
- }
-}
-
-uint32_t ManifestIncrementalIndexer::getNextSuffix() {
- auto ret = suffix_strategy_->getNextSuffix();
-
- if (ret <= suffix_strategy_->getFinalSuffix() &&
- ret != utils::SuffixStrategy::INVALID_SUFFIX) {
- suffix_queue_.push(ret);
- return ret;
- }
-
- return IndexManager::invalid_index;
-}
-
-uint32_t ManifestIncrementalIndexer::getFinalSuffix() {
- return suffix_strategy_->getFinalSuffix();
-}
-
-bool ManifestIncrementalIndexer::isFinalSuffixDiscovered() {
- return IncrementalIndexer::isFinalSuffixDiscovered();
-}
-
-uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() {
- if (suffix_queue_.empty()) {
- return IndexManager::invalid_index;
- }
-
- auto ret = suffix_queue_.front();
- suffix_queue_.pop();
- return ret;
-}
-
-void ManifestIncrementalIndexer::reset(std::uint32_t offset) {
- IncrementalIndexer::reset(offset);
- suffix_hash_map_.clear();
- unverified_segments_.clear();
- SuffixQueue empty;
- std::swap(suffix_queue_, empty);
- suffix_strategy_->reset(offset);
-}
-
-} // namespace protocol
-
-} // namespace transport
diff --git a/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc
new file mode 100644
index 000000000..0b15559a4
--- /dev/null
+++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <implementation/socket_consumer.h>
+#include <protocols/errors.h>
+#include <protocols/manifest_incremental_indexer_bytestream.h>
+#include <protocols/transport_protocol.h>
+
+#include <cmath>
+#include <deque>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace interface;
+
+ManifestIncrementalIndexer::ManifestIncrementalIndexer(
+ implementation::ConsumerSocket *icn_socket, TransportProtocol *transport)
+ : IncrementalIndexer(icn_socket, transport),
+ suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy(
+ utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) {}
+
+void ManifestIncrementalIndexer::onContentObject(
+ core::Interest &interest, core::ContentObject &content_object,
+ bool reassembly) {
+ switch (content_object.getPayloadType()) {
+ case PayloadType::DATA: {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received content " << content_object.getName();
+ onUntrustedContentObject(interest, content_object, reassembly);
+ break;
+ }
+ case PayloadType::MANIFEST: {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received manifest " << content_object.getName();
+ onUntrustedManifest(interest, content_object, reassembly);
+ break;
+ }
+ default: {
+ return;
+ }
+ }
+}
+
+void ManifestIncrementalIndexer::onUntrustedManifest(
+ core::Interest &interest, core::ContentObject &content_object,
+ bool reassembly) {
+ auth::VerificationPolicy policy = verifier_->verifyPackets(&content_object);
+
+ if (policy != auth::VerificationPolicy::ACCEPT) {
+ transport_->onContentReassembled(
+ make_error_code(protocol_error::session_aborted));
+ return;
+ }
+
+ core::ContentObjectManifest manifest(content_object.shared_from_this());
+ manifest.decode();
+
+ processTrustedManifest(interest, manifest, reassembly);
+}
+
+void ManifestIncrementalIndexer::processTrustedManifest(
+ core::Interest &interest, core::ContentObjectManifest &manifest,
+ bool reassembly) {
+ switch (manifest.getType()) {
+ case core::ManifestType::INLINE_MANIFEST: {
+ suffix_strategy_->setFinalSuffix(
+ manifest.getParamsBytestream().final_segment);
+
+ // The packets to verify with the received manifest
+ std::vector<auth::PacketPtr> packets;
+
+ // Convert the received manifest to a map of packet suffixes to hashes
+ auth::Verifier::SuffixMap suffix_map = manifest.getSuffixMap();
+
+ // Update 'suffix_map_' with new hashes from the received manifest and
+ // build 'packets'
+ for (auto it = suffix_map.begin(); it != suffix_map.end();) {
+ if (unverified_segments_.find(it->first) ==
+ unverified_segments_.end()) {
+ suffix_map_[it->first] = std::move(it->second);
+ suffix_map.erase(it++);
+ continue;
+ }
+
+ packets.push_back(std::get<1>(unverified_segments_[it->first]).get());
+ it++;
+ }
+
+ // Verify unverified segments using the received manifest
+ auth::Verifier::PolicyMap policies =
+ verifier_->verifyPackets(packets, suffix_map);
+
+ for (unsigned int i = 0; i < packets.size(); ++i) {
+ auth::Suffix suffix = packets[i]->getName().getSuffix();
+
+ auto it = unverified_segments_.find(suffix);
+
+ if (policies[suffix] != auth::VerificationPolicy::UNKNOWN) {
+ unverified_segments_.erase(it);
+ continue;
+ }
+
+ applyPolicy(*std::get<0>(it->second), *std::get<1>(it->second),
+ std::get<2>(it->second), policies[suffix]);
+ }
+
+ if (reassembly) {
+ auto manifest_co =
+ std::dynamic_pointer_cast<ContentObject>(manifest.getPacket());
+ reassembly_->reassemble(*manifest_co);
+ }
+ break;
+ }
+ case core::ManifestType::FLIC_MANIFEST: {
+ throw errors::NotImplementedException();
+ }
+ case core::ManifestType::FINAL_CHUNK_NUMBER: {
+ throw errors::NotImplementedException();
+ }
+ }
+}
+
+void ManifestIncrementalIndexer::onUntrustedContentObject(
+ Interest &interest, ContentObject &content_object, bool reassembly) {
+ auth::Suffix suffix = content_object.getName().getSuffix();
+ auth::VerificationPolicy policy =
+ verifier_->verifyPackets(&content_object, suffix_map_);
+
+ switch (policy) {
+ case auth::VerificationPolicy::UNKNOWN: {
+ unverified_segments_[suffix] =
+ std::make_tuple(interest.shared_from_this(),
+ content_object.shared_from_this(), reassembly);
+ break;
+ }
+ default: {
+ suffix_map_.erase(suffix);
+ break;
+ }
+ }
+
+ applyPolicy(interest, content_object, reassembly, policy);
+}
+
+uint32_t ManifestIncrementalIndexer::checkNextSuffix() const {
+ return suffix_strategy_->checkNextSuffix();
+}
+
+uint32_t ManifestIncrementalIndexer::getNextSuffix() {
+ auto ret = suffix_strategy_->getNextSuffix();
+
+ if (ret <= suffix_strategy_->getFinalSuffix() &&
+ ret != utils::SuffixStrategy::MAX_SUFFIX) {
+ suffix_queue_.push(ret);
+ return ret;
+ }
+
+ return Indexer::invalid_index;
+}
+
+uint32_t ManifestIncrementalIndexer::getFinalSuffix() const {
+ return suffix_strategy_->getFinalSuffix();
+}
+
+bool ManifestIncrementalIndexer::isFinalSuffixDiscovered() {
+ return IncrementalIndexer::isFinalSuffixDiscovered();
+}
+
+uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() {
+ if (suffix_queue_.empty()) {
+ return Indexer::invalid_index;
+ }
+
+ auto ret = suffix_queue_.front();
+ suffix_queue_.pop();
+ return ret;
+}
+
+void ManifestIncrementalIndexer::reset() {
+ IncrementalIndexer::reset();
+ suffix_map_.clear();
+ unverified_segments_.clear();
+ SuffixQueue empty;
+ std::swap(suffix_queue_, empty);
+ suffix_strategy_->reset(first_suffix_);
+}
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/manifest_incremental_indexer.h b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h
index 38b01533e..8527b55c1 100644
--- a/libtransport/src/protocols/manifest_incremental_indexer.h
+++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -15,14 +15,14 @@
#pragma once
+#include <hicn/transport/auth/common.h>
#include <implementation/socket.h>
-#include <protocols/incremental_indexer.h>
+#include <protocols/incremental_indexer_bytestream.h>
#include <utils/suffix_strategy.h>
#include <list>
namespace transport {
-
namespace protocol {
class ManifestIncrementalIndexer : public IncrementalIndexer {
@@ -30,17 +30,16 @@ class ManifestIncrementalIndexer : public IncrementalIndexer {
public:
using SuffixQueue = std::queue<uint32_t>;
- using HashEntry = std::pair<std::vector<uint8_t>, utils::CryptoHashType>;
+ using InterestContentPair =
+ std::tuple<core::Interest::Ptr, core::ContentObject::Ptr, bool>;
ManifestIncrementalIndexer(implementation::ConsumerSocket *icn_socket,
- TransportProtocol *transport,
- Reassembly *reassembly);
+ TransportProtocol *transport);
ManifestIncrementalIndexer(IncrementalIndexer &&indexer)
: IncrementalIndexer(std::move(indexer)),
suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy(
- core::NextSegmentCalculationStrategy::INCREMENTAL,
- next_download_suffix_, 0)) {
+ utils::NextSuffixStrategy::INCREMENTAL, next_download_suffix_)) {
for (uint32_t i = first_suffix_; i < next_download_suffix_; i++) {
suffix_queue_.push(i);
}
@@ -48,10 +47,13 @@ class ManifestIncrementalIndexer : public IncrementalIndexer {
virtual ~ManifestIncrementalIndexer() = default;
- void reset(std::uint32_t offset = 0) override;
+ void reset() override;
+
+ void onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly) override;
- void onContentObject(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object) override;
+ uint32_t checkNextSuffix() const override;
uint32_t getNextSuffix() override;
@@ -59,32 +61,26 @@ class ManifestIncrementalIndexer : public IncrementalIndexer {
bool isFinalSuffixDiscovered() override;
- uint32_t getFinalSuffix() override;
-
- private:
- void onUntrustedManifest(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object);
- void onUntrustedContentObject(core::Interest::Ptr &&interest,
- core::ContentObject::Ptr &&content_object);
- void processTrustedManifest(core::ContentObject::Ptr &&content_object);
- void onManifestReceived(core::Interest::Ptr &&i,
- core::ContentObject::Ptr &&c);
- void onManifestTimeout(core::Interest::Ptr &&i);
- VerificationPolicy verifyContentObject(
- const HashEntry &manifest_hash,
- const core::ContentObject &content_object);
- bool checkUnverifiedSegments(std::uint32_t suffix, const HashEntry &hash);
+ uint32_t getFinalSuffix() const override;
protected:
std::unique_ptr<utils::SuffixStrategy> suffix_strategy_;
SuffixQueue suffix_queue_;
// Hash verification
- std::unordered_map<uint32_t, HashEntry> suffix_hash_map_;
+ auth::Verifier::SuffixMap suffix_map_;
+ std::unordered_map<auth::Suffix, InterestContentPair> unverified_segments_;
- std::unordered_map<uint32_t,
- std::pair<core::Interest::Ptr, core::ContentObject::Ptr>>
- unverified_segments_;
+ private:
+ void onUntrustedManifest(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly);
+ void processTrustedManifest(core::Interest &interest,
+ core::ContentObjectManifest &manifest,
+ bool reassembly);
+ void onUntrustedContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly);
};
} // end namespace protocol
diff --git a/libtransport/src/protocols/packet_manager.h b/libtransport/src/protocols/packet_manager.h
deleted file mode 100644
index a552607ea..000000000
--- a/libtransport/src/protocols/packet_manager.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <hicn/transport/utils/object_pool.h>
-
-namespace transport {
-
-namespace protocol {
-
-using namespace core;
-
-template <typename PacketType, std::size_t packet_pool_size = 4096>
-class PacketManager {
- static_assert(std::is_base_of<Packet, PacketType>::value,
- "The packet manager support just Interest and Data.");
-
- public:
- PacketManager(std::size_t size = packet_pool_size) : size_(0) {
- // Create pool of interests
- increasePoolSize(size);
- }
-
- TRANSPORT_ALWAYS_INLINE void increasePoolSize(std::size_t size) {
- for (std::size_t i = 0; i < size; i++) {
- interest_pool_.add(new PacketType());
- }
-
- size_ += size;
- }
-
- TRANSPORT_ALWAYS_INLINE typename PacketType::Ptr getPacket() {
- auto result = interest_pool_.get();
-
- while (TRANSPORT_EXPECT_FALSE(!result.first)) {
- // Add packets to the pool
- increasePoolSize(size_);
- result = interest_pool_.get();
- }
-
- result.second->resetPayload();
- return std::move(result.second);
- }
-
- private:
- utils::ObjectPool<PacketType> interest_pool_;
- std::size_t size_;
-};
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/prod_protocol_bytestream.cc b/libtransport/src/protocols/prod_protocol_bytestream.cc
new file mode 100644
index 000000000..7f103e12b
--- /dev/null
+++ b/libtransport/src/protocols/prod_protocol_bytestream.cc
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <implementation/socket_producer.h>
+#include <protocols/prod_protocol_bytestream.h>
+
+#include <atomic>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace core;
+using namespace implementation;
+
+ByteStreamProductionProtocol::ByteStreamProductionProtocol(
+ implementation::ProducerSocket *icn_socket)
+ : ProductionProtocol(icn_socket) {}
+
+ByteStreamProductionProtocol::~ByteStreamProductionProtocol() { stop(); }
+
+uint32_t ByteStreamProductionProtocol::produceDatagram(
+ const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) {
+ throw errors::NotImplementedException();
+}
+
+uint32_t ByteStreamProductionProtocol::produceDatagram(const Name &content_name,
+ const uint8_t *buffer,
+ size_t buffer_size) {
+ throw errors::NotImplementedException();
+}
+
+uint32_t ByteStreamProductionProtocol::produceStream(const Name &content_name,
+ const uint8_t *buffer,
+ size_t buffer_size,
+ bool is_last,
+ uint32_t start_offset) {
+ if (!buffer_size) {
+ return 0;
+ }
+
+ return produceStream(content_name,
+ utils::MemBuf::copyBuffer(buffer, buffer_size), is_last,
+ start_offset);
+}
+
+uint32_t ByteStreamProductionProtocol::produceStream(
+ const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer,
+ bool is_last, uint32_t start_offset) {
+ if (TRANSPORT_EXPECT_FALSE(buffer->length() == 0)) {
+ return 0;
+ }
+
+ // Total size of the data packet
+ uint32_t data_packet_size;
+ socket_->getSocketOption(GeneralTransportOptions::DATA_PACKET_SIZE,
+ data_packet_size);
+
+ // Maximum size of a segment
+ uint32_t max_segment_size;
+ socket_->getSocketOption(GeneralTransportOptions::MAX_SEGMENT_SIZE,
+ max_segment_size);
+
+ // Expiry time
+ uint32_t content_object_expiry_time;
+ socket_->getSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME,
+ content_object_expiry_time);
+
+ // Hash algorithm
+ auth::CryptoHashType hash_algo;
+ socket_->getSocketOption(GeneralTransportOptions::HASH_ALGORITHM, hash_algo);
+
+ // Suffix calculation strategy
+ std::shared_ptr<utils::SuffixStrategy> suffix_strategy;
+ socket_->getSocketOption(GeneralTransportOptions::SUFFIX_STRATEGY,
+ suffix_strategy);
+ suffix_strategy->reset(start_offset);
+
+ // Default format
+ core::Packet::Format default_format;
+ socket_->getSocketOption(GeneralTransportOptions::PACKET_FORMAT,
+ default_format);
+
+ Name name(content_name);
+ size_t buffer_size = buffer->length();
+ size_t signature_length = signer_->getSignatureFieldSize();
+ uint32_t final_block_number = start_offset;
+
+ // Content-related
+ core::Packet::Format content_format;
+ uint32_t content_header_size;
+ uint64_t content_free_space;
+ uint32_t nb_segments;
+ int bytes_segmented = 0;
+
+ // Manifest-related
+ core::Packet::Format manifest_format;
+ uint32_t manifest_header_size;
+ uint64_t manifest_free_space;
+ uint32_t nb_manifests;
+ std::shared_ptr<core::ContentObjectManifest> manifest;
+ uint32_t manifest_capacity = manifest_max_capacity_;
+ bool is_last_manifest = false;
+ ParamsBytestream transport_params;
+
+ manifest_format = Packet::toAHFormat(default_format);
+ content_format = !manifest_max_capacity_ ? Packet::toAHFormat(default_format)
+ : default_format;
+
+ content_header_size = (uint32_t)core::Packet::getHeaderSizeFromFormat(
+ content_format, signature_length);
+ manifest_header_size = (uint32_t)core::Packet::getHeaderSizeFromFormat(
+ manifest_format, signature_length);
+ content_free_space =
+ std::min(max_segment_size, data_packet_size - content_header_size);
+ manifest_free_space =
+ std::min(max_segment_size, data_packet_size - manifest_header_size);
+
+ // Compute the number of segments the data will be split into
+ nb_segments =
+ uint32_t(std::ceil(double(buffer_size) / double(content_free_space)));
+ if (content_free_space * nb_segments < buffer_size) {
+ nb_segments++;
+ }
+
+ if (manifest_max_capacity_) {
+ nb_manifests = static_cast<uint32_t>(
+ std::ceil(float(nb_segments) / manifest_capacity));
+ final_block_number += nb_segments + nb_manifests - 1;
+ transport_params.final_segment =
+ is_last ? final_block_number : utils::SuffixStrategy::MAX_SUFFIX;
+
+ manifest = ContentObjectManifest::createContentManifest(
+ manifest_format,
+ name.setSuffix(suffix_strategy->getNextManifestSuffix()),
+ signature_length);
+ manifest->setHeaders(core::ManifestType::INLINE_MANIFEST,
+ manifest_max_capacity_, hash_algo, is_last_manifest,
+ name);
+ manifest->setParamsBytestream(transport_params);
+ manifest->getPacket()->setLifetime(content_object_expiry_time);
+ }
+
+ auto self = shared_from_this();
+ for (unsigned int packaged_segments = 0; packaged_segments < nb_segments;
+ packaged_segments++) {
+ if (manifest_max_capacity_) {
+ if (manifest->Encoder::manifestSize(1) > manifest_free_space) {
+ manifest->encode();
+ auto manifest_co =
+ std::dynamic_pointer_cast<ContentObject>(manifest->getPacket());
+
+ signer_->signPacket(manifest_co.get());
+
+ // Send the current manifest
+ passContentObjectToCallbacks(manifest_co, self);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send manifest " << manifest_co->getName();
+
+ // Send content objects stored in the queue
+ while (!content_queue_.empty()) {
+ passContentObjectToCallbacks(content_queue_.front(), self);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content " << content_queue_.front()->getName();
+ content_queue_.pop();
+ }
+
+ // Create new manifest. The reference to the last manifest has been
+ // acquired in the passContentObjectToCallbacks function, so we can
+ // safely release this reference.
+ manifest = ContentObjectManifest::createContentManifest(
+ manifest_format,
+ name.setSuffix(suffix_strategy->getNextManifestSuffix()),
+ signature_length);
+ manifest->setHeaders(core::ManifestType::INLINE_MANIFEST,
+ manifest_max_capacity_, hash_algo,
+ is_last_manifest, name);
+ manifest->setParamsBytestream(transport_params);
+ manifest->getPacket()->setLifetime(content_object_expiry_time);
+ }
+ }
+
+ // Create content object
+ uint32_t content_suffix = suffix_strategy->getNextContentSuffix();
+ auto content_object = std::make_shared<ContentObject>(
+ name.setSuffix(content_suffix), content_format,
+ !manifest_max_capacity_ ? signature_length : 0);
+ content_object->setLifetime(content_object_expiry_time);
+
+ auto b = buffer->cloneOne();
+ b->trimStart(content_free_space * packaged_segments);
+ b->trimEnd(b->length());
+
+ // Segment the input data
+ if (TRANSPORT_EXPECT_FALSE(packaged_segments == nb_segments - 1)) {
+ b->append(buffer_size - bytes_segmented);
+ bytes_segmented += (int)(buffer_size - bytes_segmented);
+
+ if (is_last && manifest_max_capacity_) {
+ is_last_manifest = true;
+ } else if (is_last) {
+ content_object->setLast();
+ }
+
+ } else {
+ b->append(content_free_space);
+ bytes_segmented += (int)(content_free_space);
+ }
+
+ // Set the segmented data as payload
+ content_object->appendPayload(std::move(b));
+
+ // Either we sign the content object or we save its hash into the current
+ // manifest
+ if (manifest_max_capacity_) {
+ auth::CryptoHash hash = content_object->computeDigest(hash_algo);
+ manifest->addEntry(content_suffix, hash);
+ content_queue_.push(content_object);
+ } else {
+ signer_->signPacket(content_object.get());
+ passContentObjectToCallbacks(content_object, self);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content " << content_object->getName();
+ }
+ }
+
+ // We send the manifest that hasn't been fully filled yet
+ if (manifest_max_capacity_) {
+ if (is_last_manifest) {
+ manifest->setIsLast(is_last_manifest);
+ }
+
+ manifest->encode();
+ auto manifest_co =
+ std::dynamic_pointer_cast<ContentObject>(manifest->getPacket());
+
+ signer_->signPacket(manifest_co.get());
+
+ passContentObjectToCallbacks(manifest_co, self);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest_co->getName();
+
+ while (!content_queue_.empty()) {
+ passContentObjectToCallbacks(content_queue_.front(), self);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content " << content_queue_.front()->getName();
+ content_queue_.pop();
+ }
+ }
+
+ portal_->getThread().add([this, self]() {
+ std::shared_ptr<ContentObject> co;
+ while (object_queue_for_callbacks_.pop(co)) {
+ if (*on_new_segment_) {
+ on_new_segment_->operator()(*socket_->getInterface(), *co);
+ }
+
+ if (*on_content_object_to_sign_) {
+ on_content_object_to_sign_->operator()(*socket_->getInterface(), *co);
+ }
+
+ if (*on_content_object_in_output_buffer_) {
+ on_content_object_in_output_buffer_->operator()(
+ *socket_->getInterface(), *co);
+ }
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(), *co);
+ }
+ }
+ });
+
+ portal_->getThread().add([this, buffer_size, self]() {
+ if (*on_content_produced_) {
+ on_content_produced_->operator()(*socket_->getInterface(),
+ std::make_error_code(std::errc(0)),
+ buffer_size);
+ }
+ });
+
+ return suffix_strategy->getTotalCount();
+}
+
+void ByteStreamProductionProtocol::scheduleSendBurst(
+ const std::shared_ptr<ByteStreamProductionProtocol> &self) {
+ portal_->getThread().add([this, self]() {
+ ContentObject::Ptr co;
+
+ for (uint32_t i = 0; i < burst_size; i++) {
+ if (object_queue_for_callbacks_.pop(co)) {
+ if (*on_new_segment_) {
+ on_new_segment_->operator()(*socket_->getInterface(), *co);
+ }
+
+ if (*on_content_object_to_sign_) {
+ on_content_object_to_sign_->operator()(*socket_->getInterface(), *co);
+ }
+
+ output_buffer_.insert(co);
+
+ if (*on_content_object_in_output_buffer_) {
+ on_content_object_in_output_buffer_->operator()(
+ *socket_->getInterface(), *co);
+ }
+
+ portal_->sendContentObject(*co);
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(), *co);
+ }
+ } else {
+ break;
+ }
+ }
+ });
+}
+
+void ByteStreamProductionProtocol::passContentObjectToCallbacks(
+ const std::shared_ptr<ContentObject> &content_object,
+ const std::shared_ptr<ByteStreamProductionProtocol> &self) {
+ object_queue_for_callbacks_.push(std::move(content_object));
+
+ if (object_queue_for_callbacks_.size() >= burst_size) {
+ scheduleSendBurst(self);
+ }
+}
+
+void ByteStreamProductionProtocol::onInterest(Interest &interest) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received interest for " << interest.getName();
+ if (*on_interest_input_) {
+ on_interest_input_->operator()(*socket_->getInterface(), interest);
+ }
+
+ const std::shared_ptr<ContentObject> content_object =
+ output_buffer_.find(interest.getName());
+
+ if (content_object) {
+ if (*on_interest_satisfied_output_buffer_) {
+ on_interest_satisfied_output_buffer_->operator()(*socket_->getInterface(),
+ interest);
+ }
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(),
+ *content_object);
+ }
+
+ portal_->sendContentObject(*content_object);
+ } else {
+ if (*on_interest_process_) {
+ on_interest_process_->operator()(*socket_->getInterface(), interest);
+ }
+ }
+}
+
+} // namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/prod_protocol_bytestream.h b/libtransport/src/protocols/prod_protocol_bytestream.h
new file mode 100644
index 000000000..809ad8d5c
--- /dev/null
+++ b/libtransport/src/protocols/prod_protocol_bytestream.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/utils/ring_buffer.h>
+#include <protocols/production_protocol.h>
+
+#include <atomic>
+#include <queue>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace core;
+
+class ByteStreamProductionProtocol : public ProductionProtocol {
+ static constexpr uint32_t burst_size = 256;
+
+ public:
+ ByteStreamProductionProtocol(implementation::ProducerSocket *icn_socket);
+
+ ~ByteStreamProductionProtocol() override;
+
+ using ProductionProtocol::start;
+ using ProductionProtocol::stop;
+
+ uint32_t produceStream(const Name &content_name,
+ std::unique_ptr<utils::MemBuf> &&buffer,
+ bool is_last = true,
+ uint32_t start_offset = 0) override;
+ uint32_t produceStream(const Name &content_name, const uint8_t *buffer,
+ size_t buffer_size, bool is_last = true,
+ uint32_t start_offset = 0) override;
+ uint32_t produceDatagram(const Name &content_name,
+ std::unique_ptr<utils::MemBuf> &&buffer) override;
+ uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer,
+ size_t buffer_size) override;
+
+ auto shared_from_this() { return utils::shared_from(this); }
+
+ protected:
+ // Consumer Callback
+ // void reset() override;
+ void onInterest(core::Interest &i) override;
+
+ private:
+ void passContentObjectToCallbacks(
+ const std::shared_ptr<ContentObject> &content_object,
+ const std::shared_ptr<ByteStreamProductionProtocol> &self);
+ void scheduleSendBurst(
+ const std::shared_ptr<ByteStreamProductionProtocol> &self);
+
+ private:
+ // While manifests are being built, contents are stored in a queue
+ std::queue<std::shared_ptr<ContentObject>> content_queue_;
+ utils::CircularFifo<std::shared_ptr<ContentObject>, 2048>
+ object_queue_for_callbacks_;
+};
+
+} // end namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/prod_protocol_rtc.cc b/libtransport/src/protocols/prod_protocol_rtc.cc
new file mode 100644
index 000000000..83cd23ac6
--- /dev/null
+++ b/libtransport/src/protocols/prod_protocol_rtc.cc
@@ -0,0 +1,747 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <hicn/transport/core/global_object_pool.h>
+#include <implementation/socket_producer.h>
+#include <protocols/prod_protocol_rtc.h>
+#include <protocols/rtc/probe_handler.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <unordered_set>
+
+extern "C" {
+#include <hicn/util/bitmap.h>
+}
+
+namespace transport {
+namespace protocol {
+
+using Format = core::Packet::Format;
+
+RTCProductionProtocol::RTCProductionProtocol(
+ implementation::ProducerSocket *icn_socket)
+ : ProductionProtocol(icn_socket),
+ current_seg_(1),
+ prev_produced_bytes_(0),
+ prev_produced_packets_(0),
+ produced_bytes_(0),
+ produced_packets_(0),
+ max_packet_production_(UINT32_MAX),
+ bytes_production_rate_(UINT32_MAX),
+ packets_production_rate_(0),
+ last_produced_data_ts_(0),
+ last_round_(utils::SteadyTime::nowMs().count()),
+ allow_delayed_nacks_(false),
+ pending_fec_pace_(false),
+ max_len_(0),
+ queue_len_(0),
+ data_aggregation_(true),
+ data_aggregation_timer_switch_(false) {
+ std::uniform_int_distribution<> dis(0, 255);
+ prod_label_ = dis(gen_);
+ cache_label_ = (prod_label_ + 1) % 256;
+ round_timer_ =
+ std::make_unique<asio::steady_timer>(portal_->getThread().getIoService());
+ fec_pacing_timer_ =
+ std::make_unique<asio::steady_timer>(portal_->getThread().getIoService());
+ app_packets_timer_ =
+ std::make_unique<asio::steady_timer>(portal_->getThread().getIoService());
+ setOutputBufferSize(10000);
+}
+
+RTCProductionProtocol::~RTCProductionProtocol() {}
+
+void RTCProductionProtocol::setProducerParam() {
+ // Flow name: here we assume there is only one prefix registered in the portal
+ flow_name_ = portal_->getServedNamespaces().begin()->makeName();
+
+ // Default format
+ core::Packet::Format default_format;
+ socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT,
+ default_format);
+
+ // FEC
+ using namespace std::placeholders;
+ enableFEC(std::bind(&RTCProductionProtocol::onFecPackets, this, _1),
+ std::bind(&RTCProductionProtocol::getBuffer, this, _1));
+
+ // Aggregated data
+ socket_->getSocketOption(interface::RtcTransportOptions::AGGREGATED_DATA,
+ data_aggregation_);
+
+ size_t signature_size = signer_->getSignatureFieldSize();
+ data_header_format_ = {!manifest_max_capacity_
+ ? Packet::toAHFormat(default_format)
+ : default_format,
+ !manifest_max_capacity_ ? signature_size : 0};
+ manifest_header_format_ = {Packet::toAHFormat(default_format),
+ signature_size};
+ nack_header_format_ = {Packet::toAHFormat(default_format), signature_size};
+ fec_header_format_ = {Packet::toAHFormat(default_format), signature_size};
+
+ // Initialize verifier for aggregated interests
+ std::shared_ptr<auth::Verifier> verifier;
+ socket_->getSocketOption(implementation::GeneralTransportOptions::VERIFIER,
+ verifier);
+ verifier_ = std::make_shared<rtc::RTCVerifier>(verifier, 0, 0);
+
+ // Schedule round timer
+ scheduleRoundTimer();
+}
+
+void RTCProductionProtocol::scheduleRoundTimer() {
+ round_timer_->expires_from_now(
+ std::chrono::milliseconds(rtc::PRODUCER_STATS_INTERVAL));
+ std::weak_ptr<RTCProductionProtocol> self = shared_from_this();
+ round_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+
+ auto sp = self.lock();
+ if (sp && sp->isRunning()) {
+ sp->updateStats(true);
+ }
+ });
+}
+
+void RTCProductionProtocol::updateStats(bool new_round) {
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ uint64_t duration = now - last_round_;
+ if (!new_round) {
+ duration += rtc::PRODUCER_STATS_INTERVAL;
+ } else {
+ prev_produced_bytes_ = 0;
+ prev_produced_packets_ = 0;
+ }
+
+ double per_second = rtc::MILLI_IN_A_SEC / duration;
+
+ uint32_t prev_packets_production_rate = packets_production_rate_;
+
+ // bytes_production_rate_ does not take into account FEC!!! this is because
+ // each client requests a differen amount of FEC packet so the client itself
+ // increase the production rate in the right way
+ bytes_production_rate_ =
+ ceil((double)(produced_bytes_ + prev_produced_bytes_) * per_second);
+ packets_production_rate_ =
+ ceil((double)(produced_packets_ + prev_produced_packets_) * per_second);
+
+ if (fec_encoder_ && fec_type_ != fec::FECType::UNKNOWN) {
+ // add fec packets looking at the fec code. we don't use directly the number
+ // of fec packets produced in 1 round because it may happen that different
+ // numbers of blocks are generated during the rounds and this creates
+ // inconsistencies in the estimation of the production rate
+ uint32_t k = fec::FECUtils::getSourceSymbols(fec_type_);
+ uint32_t n = fec::FECUtils::getBlockSymbols(fec_type_);
+
+ packets_production_rate_ +=
+ ceil((double)packets_production_rate_ / (double)k) * (n - k);
+ }
+
+ // update the production rate as soon as it increases by 10% with respect to
+ // the last round
+ max_packet_production_ =
+ produced_packets_ + ceil((double)produced_packets_ * 0.10);
+ if (max_packet_production_ < rtc::WIN_MIN)
+ max_packet_production_ = rtc::WIN_MIN;
+
+ if (packets_production_rate_ <= rtc::MIN_PRODUCTION_RATE ||
+ prev_packets_production_rate <= rtc::MIN_PRODUCTION_RATE) {
+ allow_delayed_nacks_ = true;
+ } else {
+ // at least 2 rounds with enough packets
+ allow_delayed_nacks_ = false;
+ }
+
+ if (new_round) {
+ prev_produced_bytes_ = produced_bytes_;
+ prev_produced_packets_ = produced_packets_;
+ produced_bytes_ = 0;
+ produced_packets_ = 0;
+ last_round_ = now;
+ scheduleRoundTimer();
+ }
+}
+
+uint32_t RTCProductionProtocol::produceStream(
+ const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer,
+ bool is_last, uint32_t start_offset) {
+ throw errors::NotImplementedException();
+}
+
+uint32_t RTCProductionProtocol::produceStream(const Name &content_name,
+ const uint8_t *buffer,
+ size_t buffer_size, bool is_last,
+ uint32_t start_offset) {
+ throw errors::NotImplementedException();
+}
+
+void RTCProductionProtocol::produce(ContentObject &content_object) {
+ throw errors::NotImplementedException();
+}
+
+uint32_t RTCProductionProtocol::produceDatagram(
+ const Name &content_name, std::unique_ptr<utils::MemBuf> &&buffer) {
+ std::size_t buffer_size = buffer->length();
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Maybe Sending content object: " << content_name;
+
+ if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) return 0;
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending content object: " << content_name;
+
+ uint32_t data_packet_size;
+ socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE,
+ data_packet_size);
+ // this is a source packet but we check the fec header size of FEC packet in
+ // order to leave room for the header when FEC packets will be generated
+ uint32_t fec_header = 0;
+ if (fec_encoder_) fec_encoder_->getFecHeaderSize(true);
+ uint32_t headers_size =
+ (uint32_t)Packet::getHeaderSizeFromFormat(data_header_format_.first,
+ data_header_format_.second) +
+ rtc::DATA_HEADER_SIZE + fec_header;
+ if (TRANSPORT_EXPECT_FALSE((headers_size + buffer_size) > data_packet_size)) {
+ return 0;
+ }
+
+ if (!data_aggregation_) {
+ // if data aggregation is off emptyQueue will always return doing nothing
+ emptyQueue();
+
+ sendManifest(content_name);
+
+ // create content object
+ auto content_object =
+ core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ data_header_format_.first, data_header_format_.second);
+
+ // add rtc header to the payload
+ struct rtc::data_packet_t header;
+ content_object->appendPayload((const uint8_t *)&header,
+ rtc::DATA_HEADER_SIZE);
+ content_object->appendPayload(buffer->data(), buffer->length());
+
+ // schedule actual sending on internal thread
+ portal_->getThread().tryRunHandlerNow(
+ [this, content_object{std::move(content_object)},
+ content_name]() mutable {
+ produceInternal(std::move(content_object), content_name);
+ });
+ } else {
+ // XXX here we assume that all the packets that we push to the queue have
+ // the same name
+ auto app_pkt = utils::MemBuf::copyBuffer(buffer->data(), buffer->length());
+ addPacketToQueue(std::move(app_pkt));
+ }
+
+ return 1;
+}
+
+void RTCProductionProtocol::addPacketToQueue(
+ std::unique_ptr<utils::MemBuf> &&buffer) {
+ std::size_t buffer_size = buffer->length();
+ if ((queue_len_ + buffer_size) > rtc::MAX_RTC_PAYLOAD_SIZE) {
+ emptyQueue(); // this should guaranty that the generated packet will never
+ // be larger than an MTU
+ }
+
+ waiting_app_packets_.push(std::move(buffer));
+ if (max_len_ < buffer_size) max_len_ = buffer_size;
+ queue_len_ += buffer_size;
+
+ if (waiting_app_packets_.size() >= rtc::MAX_AGGREGATED_PACKETS) {
+ emptyQueue();
+ }
+
+ if (waiting_app_packets_.size() >= 1 && !data_aggregation_timer_switch_) {
+ data_aggregation_timer_switch_ = true;
+ app_packets_timer_->expires_from_now(
+ std::chrono::milliseconds(rtc::AGGREGATED_PACKETS_TIMER));
+ std::weak_ptr<RTCProductionProtocol> self = shared_from_this();
+ app_packets_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ if (!ptr->data_aggregation_timer_switch_) return;
+ ptr->emptyQueue();
+ }
+ });
+ }
+}
+
+void RTCProductionProtocol::emptyQueue() {
+ if (waiting_app_packets_.size() == 0) return; // queue is empty
+
+ Name n(flow_name_);
+
+ // cancel timer is scheduled
+ if (data_aggregation_timer_switch_) {
+ data_aggregation_timer_switch_ = false;
+ app_packets_timer_->cancel();
+ }
+
+ // send a manifest beforehand if the hash buffer if full
+ sendManifest(n);
+
+ // create content object
+ auto content_object =
+ core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ data_header_format_.first, data_header_format_.second);
+
+ // add rtc header to the payload
+ struct rtc::data_packet_t header;
+ content_object->appendPayload((const uint8_t *)&header,
+ rtc::DATA_HEADER_SIZE);
+
+ // init aggregated header
+ rtc::AggrPktHeader hdr(
+ (uint8_t *)(content_object->getPayload()->data() + rtc::DATA_HEADER_SIZE),
+ max_len_, waiting_app_packets_.size());
+ uint32_t header_size = hdr.getHeaderLen();
+ content_object->append(header_size); // leave space for the aggregated header
+
+ uint8_t index = 0;
+ while (waiting_app_packets_.size() != 0) {
+ std::unique_ptr<utils::MemBuf> pkt =
+ std::move(waiting_app_packets_.front());
+ waiting_app_packets_.pop();
+ // XXX for the moment we have a single name, so this works, otherwise we
+ // need to do something else
+ hdr.addPacketToHeader(index, pkt->length());
+ // append packet
+ content_object->appendPayload(pkt->data(), pkt->length());
+ index++;
+ }
+
+ // reset queue values
+ max_len_ = 0;
+ queue_len_ = 0;
+
+ // the packet is ready we need to send it
+ portal_->getThread().tryRunHandlerNow(
+ [this, content_object{std::move(content_object)}, n]() mutable {
+ produceInternal(std::move(content_object), n);
+ });
+}
+
+void RTCProductionProtocol::sendManifest(const Name &name) {
+ if (!manifest_max_capacity_) {
+ return;
+ }
+
+ Name manifest_name = name;
+
+ // If there is not enough hashes to fill a manifest, return early
+ if (manifest_entries_.size() < manifest_max_capacity_) {
+ return;
+ }
+
+ // Create a new manifest
+ std::shared_ptr<core::ContentObjectManifest> manifest =
+ createManifest(manifest_name.setSuffix(current_seg_));
+ auto manifest_co =
+ std::dynamic_pointer_cast<ContentObject>(manifest->getPacket());
+
+ // Fill the manifest with packet hashes that were previously saved
+ uint32_t nb_entries;
+ for (nb_entries = 0; nb_entries < manifest_max_capacity_; ++nb_entries) {
+ if (manifest_entries_.empty()) {
+ break;
+ }
+ std::pair<uint32_t, auth::CryptoHash> front = manifest_entries_.front();
+ manifest->addEntry(front.first, front.second);
+ manifest_entries_.pop();
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Sending manifest " << manifest_co->getName().getSuffix()
+ << " of size " << nb_entries;
+
+ // Encode and send the manifest
+ manifest->encode();
+ portal_->getThread().tryRunHandlerNow(
+ [this, content_object{std::move(manifest_co)}, manifest_name]() mutable {
+ produceInternal(std::move(content_object), manifest_name);
+ });
+}
+
+std::shared_ptr<core::ContentObjectManifest>
+RTCProductionProtocol::createManifest(const Name &content_name) const {
+ Name name(content_name);
+
+ auth::CryptoHashType hash_algo;
+ socket_->getSocketOption(interface::GeneralTransportOptions::HASH_ALGORITHM,
+ hash_algo);
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ // Create a new manifest
+ std::shared_ptr<core::ContentObjectManifest> manifest =
+ ContentObjectManifest::createContentManifest(
+ manifest_header_format_.first, name, manifest_header_format_.second);
+ manifest->setHeaders(core::ManifestType::INLINE_MANIFEST,
+ manifest_max_capacity_, hash_algo, false /* is_last */,
+ name);
+
+ // Set connection parameters
+ manifest->setParamsRTC(ParamsRTC{
+ .timestamp = now,
+ .prod_rate = bytes_production_rate_,
+ .prod_seg = current_seg_,
+ .fec_type = fec_type_,
+ });
+
+ return manifest;
+}
+
+void RTCProductionProtocol::produceInternal(
+ std::shared_ptr<ContentObject> &&content_object, const Name &content_name,
+ bool fec) {
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ if (fec && (now - last_produced_data_ts_) < rtc::FEC_PACING_TIME) {
+ paced_fec_packets_.push(std::pair<uint64_t, ContentObject::Ptr>(
+ now, std::move(content_object)));
+ postponeFecPacket();
+ } else {
+ // need to check if there are FEC packets waiting to be sent
+ flushFecPkts(current_seg_);
+ producePktInternal(std::move(content_object), content_name, fec);
+ }
+}
+
+void RTCProductionProtocol::producePktInternal(
+ std::shared_ptr<ContentObject> &&content_object, const Name &content_name,
+ bool fec) {
+ bool is_manifest = content_object->getPayloadType() == PayloadType::MANIFEST;
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ // set rtc header
+ if (!is_manifest) {
+ struct rtc::data_packet_t *data_pkt =
+ (struct rtc::data_packet_t *)content_object->getPayload()->data();
+ data_pkt->setTimestamp(now);
+ data_pkt->setProductionRate(bytes_production_rate_);
+ }
+
+ // set hicn stuff
+ Name n(content_name);
+ content_object->setName(n.setSuffix(current_seg_));
+
+ uint32_t expiry_time = 0;
+ socket_->getSocketOption(
+ interface::GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME,
+ expiry_time);
+ if (expiry_time == interface::default_values::content_object_expiry_time)
+ expiry_time = 500; // the data expiration time should be set by the App. if
+ // the App does not specify it the default is 500ms
+ content_object->setLifetime(expiry_time);
+ content_object->setPathLabel(prod_label_);
+
+ // update stats
+ if (!fec) {
+ produced_bytes_ +=
+ content_object->headerSize() + content_object->payloadSize();
+ produced_packets_++;
+ }
+
+ if (!data_aggregation_ && produced_packets_ >= max_packet_production_) {
+ // in this case all the pending interests may be used to accomodate the
+ // sudden increase in the production rate. calling the updateStats we will
+ // notify all the clients
+ updateStats(false);
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Sending content object: " << n << ", is fec: " << fec;
+
+ // pass packet to FEC encoder
+ if (fec_encoder_ && !fec) {
+ uint32_t offset = is_manifest ? (uint32_t)content_object->headerSize()
+ : (uint32_t)content_object->headerSize() +
+ rtc::DATA_HEADER_SIZE;
+ uint32_t metadata = static_cast<uint32_t>(content_object->getPayloadType());
+
+ fec_encoder_->onPacketProduced(*content_object, offset, metadata);
+ }
+
+ output_buffer_.insert(content_object);
+
+ if (*on_content_object_in_output_buffer_) {
+ on_content_object_in_output_buffer_->operator()(*socket_->getInterface(),
+ *content_object);
+ }
+
+ // TODO we may want to send FEC only if an interest is pending in the pit in
+ sendContentObject(content_object, false, fec);
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(),
+ *content_object);
+ }
+
+ if (!fec) last_produced_data_ts_ = now;
+
+ // Update current segment
+ current_seg_ = (current_seg_ + 1) % rtc::MIN_PROBE_SEQ;
+
+ // Publish FEC packets if available
+ if (fec_encoder_ && !fec) {
+ while (!fec && pending_fec_packets_.size()) {
+ auto &co = pending_fec_packets_.front();
+ produceInternal(std::move(co), flow_name_, true);
+ pending_fec_packets_.pop();
+ }
+ }
+}
+
+void RTCProductionProtocol::flushFecPkts(uint32_t current_seq_num) {
+ // Currently we immediately send all the pending fec packets
+ // A pacing policy may be helpful, but we do not want to delay too much
+ // the packets at this moment.
+ while (paced_fec_packets_.size() > 0) {
+ producePktInternal(std::move(paced_fec_packets_.front().second), flow_name_,
+ true);
+ paced_fec_packets_.pop();
+ }
+ fec_pacing_timer_->cancel();
+ pending_fec_pace_ = false;
+ postponeFecPacket();
+}
+
+void RTCProductionProtocol::postponeFecPacket() {
+ if (paced_fec_packets_.size() == 0) return;
+ if (pending_fec_pace_) {
+ return;
+ }
+
+ uint64_t produced_time = paced_fec_packets_.front().first;
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ uint64_t wait_time = 0;
+ if ((produced_time + rtc::FEC_PACING_TIME) > now)
+ wait_time = produced_time + rtc::FEC_PACING_TIME - now;
+
+ fec_pacing_timer_->expires_from_now(std::chrono::milliseconds(wait_time));
+ pending_fec_pace_ = true;
+
+ std::weak_ptr<RTCProductionProtocol> self = shared_from_this();
+ fec_pacing_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+
+ auto sp = self.lock();
+ if (sp && sp->isRunning()) {
+ if (!sp->pending_fec_pace_) return;
+
+ if (sp->paced_fec_packets_.size() > 0) {
+ sp->producePktInternal(std::move(sp->paced_fec_packets_.front().second),
+ sp->flow_name_, true);
+ sp->paced_fec_packets_.pop();
+ }
+ sp->pending_fec_pace_ = false;
+ sp->postponeFecPacket();
+ }
+ });
+}
+
+void RTCProductionProtocol::onInterest(Interest &interest) {
+ if (*on_interest_input_) {
+ on_interest_input_->operator()(*socket_->getInterface(), interest);
+ }
+
+ if (!interest.isValid()) throw std::runtime_error("Bad interest format");
+ if (interest.hasManifest() &&
+ verifier_->verify(interest) != auth::VerificationPolicy::ACCEPT)
+ throw std::runtime_error("Interset manifest verification failed");
+
+ uint32_t *suffix = interest.firstSuffix();
+ uint32_t n_suffixes_in_manifest = interest.numberOfSuffixes();
+ hicn_uword *request_bitmap = interest.getRequestBitmap();
+
+ Name name = interest.getName();
+ uint32_t pos = 0; // Position of current suffix in manifest
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received interest " << name << " (" << n_suffixes_in_manifest
+ << " suffixes in manifest)";
+
+ // Process the suffix in the interest header
+ // (first loop iteration), then suffixes in the manifest
+ do {
+ if (!interest.hasManifest() ||
+ bitmap_is_set_no_check(request_bitmap, pos)) {
+ const std::shared_ptr<ContentObject> content_object =
+ output_buffer_.find(name);
+
+ if (content_object) {
+ if (*on_interest_satisfied_output_buffer_) {
+ on_interest_satisfied_output_buffer_->operator()(
+ *socket_->getInterface(), interest);
+ }
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(),
+ *content_object);
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content %u (onInterest) " << content_object->getName();
+ content_object->setPathLabel(cache_label_);
+ sendContentObject(content_object);
+ } else {
+ if (*on_interest_process_) {
+ on_interest_process_->operator()(*socket_->getInterface(), interest);
+ }
+ processInterest(name.getSuffix(), interest.getLifetime());
+ }
+ }
+
+ // Retrieve next suffix in the manifest
+ if (interest.hasManifest()) {
+ uint32_t seq = *suffix;
+ suffix++;
+
+ name.setSuffix(seq);
+ interest.setName(name);
+ }
+ } while (pos++ < n_suffixes_in_manifest);
+}
+
+void RTCProductionProtocol::processInterest(uint32_t interest_seg,
+ uint32_t lifetime) {
+ switch (rtc::ProbeHandler::getProbeType(interest_seg)) {
+ case rtc::ProbeType::INIT:
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received init probe " << interest_seg;
+ sendManifestProbe(interest_seg);
+ return;
+ case rtc::ProbeType::RTT:
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received RTT probe " << interest_seg;
+ sendNack(interest_seg);
+ return;
+ default:
+ break;
+ }
+
+ if (interest_seg < current_seg_) sendNack(interest_seg);
+}
+
+void RTCProductionProtocol::sendManifestProbe(uint32_t sequence) {
+ Name manifest_name(flow_name_);
+ manifest_name.setSuffix(sequence);
+
+ std::shared_ptr<core::ContentObjectManifest> manifest_probe =
+ createManifest(manifest_name);
+ auto manifest_probe_co =
+ std::dynamic_pointer_cast<ContentObject>(manifest_probe->getPacket());
+
+ manifest_probe_co->setLifetime(0);
+ manifest_probe_co->setPathLabel(prod_label_);
+ manifest_probe->encode();
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(),
+ *manifest_probe_co);
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send init probe " << sequence;
+ sendContentObject(manifest_probe_co, true, false);
+}
+
+void RTCProductionProtocol::sendNack(uint32_t sequence) {
+ auto nack = core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ nack_header_format_.first, nack_header_format_.second);
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ uint32_t next_packet = current_seg_;
+ uint32_t prod_rate = bytes_production_rate_;
+
+ struct rtc::nack_packet_t header;
+ header.setTimestamp(now);
+ header.setProductionRate(prod_rate);
+ header.setProductionSegment(next_packet);
+ nack->appendPayload((const uint8_t *)&header, rtc::NACK_HEADER_SIZE);
+
+ Name n(flow_name_);
+ n.setSuffix(sequence);
+ nack->setName(n);
+ nack->setLifetime(0);
+ nack->setPathLabel(prod_label_);
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(), *nack);
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send nack " << sequence;
+ sendContentObject(nack, true, false);
+}
+
+void RTCProductionProtocol::sendContentObject(
+ std::shared_ptr<ContentObject> content_object, bool nack, bool fec) {
+ bool is_ah = HICN_PACKET_FORMAT_IS_AH(content_object->getFormat());
+
+ // Compute signature
+ if (is_ah) {
+ signer_->signPacket(content_object.get());
+ }
+
+ // Compute and save data packet digest
+ if (manifest_max_capacity_ && !is_ah) {
+ auth::CryptoHashType hash_algo;
+ socket_->getSocketOption(interface::GeneralTransportOptions::HASH_ALGORITHM,
+ hash_algo);
+ manifest_entries_.push({content_object->getName().getSuffix(),
+ content_object->computeDigest(hash_algo)});
+ }
+
+ portal_->sendContentObject(*content_object);
+}
+
+void RTCProductionProtocol::onFecPackets(fec::BufferArray &packets) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Produced " << packets.size() << " FEC packets";
+
+ for (auto &packet : packets) {
+ auto content_object =
+ std::static_pointer_cast<ContentObject>(packet.getBuffer());
+ content_object->prepend(content_object->headerSize() +
+ rtc::DATA_HEADER_SIZE);
+ pending_fec_packets_.push(std::move(content_object));
+ }
+}
+
+fec::buffer RTCProductionProtocol::getBuffer(std::size_t size) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Asked buffer for FEC symbol of size " << size;
+
+ auto ret = core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ fec_header_format_.first, fec_header_format_.second);
+
+ ret->updateLength(rtc::DATA_HEADER_SIZE + size);
+ ret->append(rtc::DATA_HEADER_SIZE + size);
+ ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE);
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Responding with buffer of length " << ret->length();
+ DCHECK(ret->length() >= size);
+
+ return ret;
+}
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/prod_protocol_rtc.h b/libtransport/src/protocols/prod_protocol_rtc.h
new file mode 100644
index 000000000..285ccb646
--- /dev/null
+++ b/libtransport/src/protocols/prod_protocol_rtc.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/core/name.h>
+#include <protocols/production_protocol.h>
+#include <protocols/rtc/rtc_verifier.h>
+
+#include <atomic>
+#include <map>
+#include <mutex>
+
+namespace transport {
+namespace protocol {
+
+class RTCProductionProtocol : public ProductionProtocol {
+ public:
+ RTCProductionProtocol(implementation::ProducerSocket *icn_socket);
+ ~RTCProductionProtocol() override;
+
+ using ProductionProtocol::start;
+ using ProductionProtocol::stop;
+ void setProducerParam() override;
+
+ void produce(ContentObject &content_object) override;
+ uint32_t produceStream(const Name &content_name,
+ std::unique_ptr<utils::MemBuf> &&buffer,
+ bool is_last = true,
+ uint32_t start_offset = 0) override;
+ uint32_t produceStream(const Name &content_name, const uint8_t *buffer,
+ size_t buffer_size, bool is_last = true,
+ uint32_t start_offset = 0) override;
+ uint32_t produceDatagram(const Name &content_name,
+ std::unique_ptr<utils::MemBuf> &&buffer) override;
+ uint32_t produceDatagram(const Name &content_name, const uint8_t *buffer,
+ size_t buffer_size) override {
+ return produceDatagram(content_name, utils::MemBuf::wrapBuffer(
+ buffer, buffer_size, buffer_size));
+ }
+
+ auto shared_from_this() { return utils::shared_from(this); }
+
+ private:
+ // packet handlers
+ void onInterest(Interest &interest) override;
+ void onError(const std::error_code &ec) override{};
+ void processInterest(uint32_t interest_seg, uint32_t lifetime);
+ void producePktInternal(std::shared_ptr<ContentObject> &&content_object,
+ const Name &content_name, bool fec = false);
+ void produceInternal(std::shared_ptr<ContentObject> &&content_object,
+ const Name &content_name, bool fec = false);
+ void sendNack(uint32_t sequence);
+ void sendContentObject(std::shared_ptr<ContentObject> content_object,
+ bool nac = false, bool fec = false);
+
+ // manifests
+ void sendManifestProbe(uint32_t sequence);
+ void sendManifest(const Name &content_name);
+ std::shared_ptr<core::ContentObjectManifest> createManifest(
+ const Name &name) const;
+
+ // stats
+ void updateStats(bool new_round);
+ void scheduleRoundTimer();
+
+ // FEC functions
+ void onFecPackets(fec::BufferArray &packets);
+ fec::buffer getBuffer(std::size_t size);
+ void postponeFecPacket();
+ void dispatchFecPacket();
+ void flushFecPkts(uint32_t current_seq_num);
+ // aggregated data functions
+ void emptyQueue();
+ void addPacketToQueue(std::unique_ptr<utils::MemBuf> &&buffer);
+
+ core::Name flow_name_;
+
+ std::pair<core::Packet::Format, size_t> data_header_format_;
+ std::pair<core::Packet::Format, size_t> manifest_header_format_;
+ std::pair<core::Packet::Format, size_t> fec_header_format_;
+ std::pair<core::Packet::Format, size_t> nack_header_format_;
+
+ uint32_t current_seg_; // seq id of the next packet produced
+ uint32_t prod_label_; // path label of the producer
+ uint32_t cache_label_; // path label for content from the producer cache
+
+ uint32_t prev_produced_bytes_; // XXX clearly explain all these new vars
+ uint32_t prev_produced_packets_;
+
+ uint32_t produced_bytes_; // bytes produced in the last round
+ uint32_t produced_packets_; // packet produed in the last round
+
+ uint32_t max_packet_production_; // never exceed this number of packets
+ // without update stats
+
+ uint32_t bytes_production_rate_; // bytes per sec
+ uint32_t packets_production_rate_; // pps
+
+ uint64_t last_produced_data_ts_; // ms
+
+ std::unique_ptr<asio::steady_timer> round_timer_;
+ std::unique_ptr<asio::steady_timer> fec_pacing_timer_;
+
+ uint64_t last_round_;
+
+ // delayed nacks are used by the producer to avoid to send too
+ // many nacks we the producer rate is 0. however, if the producer moves
+ // from a production rate higher than 0 to 0 the first round the dealyed
+ // should be avoided in order to notify the consumer as fast as possible
+ // of the new rate.
+ bool allow_delayed_nacks_;
+
+ // Save FEC packets here before sending them
+ std::queue<ContentObject::Ptr> pending_fec_packets_;
+ std::queue<std::pair<uint64_t, ContentObject::Ptr>> paced_fec_packets_;
+ bool pending_fec_pace_;
+
+ // Save application packets if they are small
+ std::queue<std::unique_ptr<utils::MemBuf>> waiting_app_packets_;
+ uint16_t max_len_; // len of the largest packet
+ uint16_t queue_len_; // total size of all packet in the queue
+ bool data_aggregation_; // turns on/off data aggregation
+ // timer to check the queue len
+ std::unique_ptr<asio::steady_timer> app_packets_timer_;
+ bool data_aggregation_timer_switch_; // bool to check if the timer is on
+
+ // Manifest
+ std::queue<std::pair<uint32_t, auth::CryptoHash>>
+ manifest_entries_; // map a packet suffix to a packet hash
+
+ // Verifier for aggregated interests
+ std::shared_ptr<rtc::RTCVerifier> verifier_;
+};
+
+} // namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/production_protocol.cc b/libtransport/src/protocols/production_protocol.cc
new file mode 100644
index 000000000..039a6a55a
--- /dev/null
+++ b/libtransport/src/protocols/production_protocol.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <implementation/socket_producer.h>
+#include <protocols/production_protocol.h>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace interface;
+
+ProductionProtocol::ProductionProtocol(
+ implementation::ProducerSocket *icn_socket)
+ : Protocol(),
+ socket_(icn_socket),
+ fec_encoder_(nullptr),
+ on_interest_input_(VOID_HANDLER),
+ on_interest_dropped_input_buffer_(VOID_HANDLER),
+ on_interest_inserted_input_buffer_(VOID_HANDLER),
+ on_interest_satisfied_output_buffer_(VOID_HANDLER),
+ on_interest_process_(VOID_HANDLER),
+ on_new_segment_(VOID_HANDLER),
+ on_content_object_to_sign_(VOID_HANDLER),
+ on_content_object_in_output_buffer_(VOID_HANDLER),
+ on_content_object_output_(VOID_HANDLER),
+ on_content_object_evicted_from_output_buffer_(VOID_HANDLER),
+ on_content_produced_(VOID_HANDLER),
+ producer_callback_(VOID_HANDLER),
+ fec_type_(fec::FECType::UNKNOWN) {
+ socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_);
+ // TODO add statistics for producer
+ // socket_->getSocketOption(OtherOptions::STATISTICS, &stats_);
+}
+
+ProductionProtocol::~ProductionProtocol() {}
+
+int ProductionProtocol::start() {
+ if (isRunning()) {
+ return -1;
+ }
+
+ portal_->getThread().addAndWaitForExecution([this]() {
+ socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_INPUT,
+ &on_interest_input_);
+ socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_DROP,
+ &on_interest_dropped_input_buffer_);
+ socket_->getSocketOption(ProducerCallbacksOptions::INTEREST_PASS,
+ &on_interest_inserted_input_buffer_);
+ socket_->getSocketOption(ProducerCallbacksOptions::CACHE_HIT,
+ &on_interest_satisfied_output_buffer_);
+ socket_->getSocketOption(ProducerCallbacksOptions::CACHE_MISS,
+ &on_interest_process_);
+ socket_->getSocketOption(ProducerCallbacksOptions::NEW_CONTENT_OBJECT,
+ &on_new_segment_);
+ socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_READY,
+ &on_content_object_in_output_buffer_);
+ socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT,
+ &on_content_object_output_);
+ socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_OBJECT_TO_SIGN,
+ &on_content_object_to_sign_);
+ socket_->getSocketOption(ProducerCallbacksOptions::CONTENT_PRODUCED,
+ &on_content_produced_);
+ socket_->getSocketOption(ProducerCallbacksOptions::PRODUCER_CALLBACK,
+ &producer_callback_);
+
+ socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_);
+ socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_);
+ socket_->getSocketOption(GeneralTransportOptions::MANIFEST_MAX_CAPACITY,
+ manifest_max_capacity_);
+
+ std::string fec_type_str = "";
+ socket_->getSocketOption(GeneralTransportOptions::FEC_TYPE, fec_type_str);
+ if (fec_type_str != "") {
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_type_str.c_str());
+ }
+
+ portal_->registerTransportCallback(this);
+ setProducerParam();
+
+ setRunning();
+ });
+
+ return 0;
+}
+
+void ProductionProtocol::produce(ContentObject &content_object) {
+ auto content_object_ptr = content_object.shared_from_this();
+ portal_->getThread().add([this, co = std::move(content_object_ptr)]() {
+ if (*on_content_object_in_output_buffer_) {
+ on_content_object_in_output_buffer_->operator()(*socket_->getInterface(),
+ *co);
+ }
+
+ output_buffer_.insert(co);
+
+ if (*on_content_object_output_) {
+ on_content_object_output_->operator()(*socket_->getInterface(), *co);
+ }
+
+ portal_->sendContentObject(*co);
+ });
+}
+
+void ProductionProtocol::sendMapme() { portal_->sendMapme(); }
+
+void ProductionProtocol::onError(const std::error_code &ec) {
+ // Stop production protocol
+ stop();
+
+ // Call error callback
+ if (producer_callback_) {
+ producer_callback_->produceError(ec);
+ }
+}
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/production_protocol.h b/libtransport/src/protocols/production_protocol.h
new file mode 100644
index 000000000..09718631f
--- /dev/null
+++ b/libtransport/src/protocols/production_protocol.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/interfaces/callbacks.h>
+#include <hicn/transport/interfaces/socket_producer.h>
+#include <hicn/transport/interfaces/statistics.h>
+#include <hicn/transport/utils/object_pool.h>
+#include <implementation/socket.h>
+#include <protocols/fec_base.h>
+#include <protocols/fec_utils.h>
+#include <protocols/protocol.h>
+#include <utils/content_store.h>
+
+#include <atomic>
+#include <thread>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace core;
+
+class ProductionProtocol
+ : public Protocol,
+ public std::enable_shared_from_this<ProductionProtocol> {
+ public:
+ ProductionProtocol(implementation::ProducerSocket *icn_socket);
+ virtual ~ProductionProtocol();
+
+ virtual int start();
+ using Protocol::stop;
+
+ virtual void setProducerParam(){};
+
+ virtual void produce(ContentObject &content_object);
+ virtual void sendMapme();
+ virtual uint32_t produceStream(const Name &content_name,
+ std::unique_ptr<utils::MemBuf> &&buffer,
+ bool is_last = true,
+ uint32_t start_offset = 0) = 0;
+ virtual uint32_t produceStream(const Name &content_name,
+ const uint8_t *buffer, size_t buffer_size,
+ bool is_last = true,
+ uint32_t start_offset = 0) = 0;
+ virtual uint32_t produceDatagram(const Name &content_name,
+ std::unique_ptr<utils::MemBuf> &&buffer) = 0;
+ virtual uint32_t produceDatagram(const Name &content_name,
+ const uint8_t *buffer,
+ size_t buffer_size) = 0;
+
+ void setOutputBufferSize(std::size_t size) { output_buffer_.setLimit(size); }
+ std::size_t getOutputBufferSize() { return output_buffer_.getLimit(); }
+
+ protected:
+ // Producer callback
+ virtual void onInterest(core::Interest &i) override = 0;
+ virtual void onError(const std::error_code &ec) override;
+
+ template <typename FECHandler, typename AllocatorHandler>
+ void enableFEC(FECHandler &&fec_handler,
+ AllocatorHandler &&allocator_handler) {
+ if (!fec_encoder_) {
+ // Try to get FEC from environment
+ const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE");
+ if (fec_str && (fec_type_ == fec::FECType::UNKNOWN)) {
+ LOG(INFO) << "Using FEC " << fec_str;
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str);
+ CHECK(fec_type_ != fec::FECType::UNKNOWN);
+ }
+
+ if (fec_type_ == fec::FECType::UNKNOWN) {
+ return;
+ }
+
+ fec_encoder_ = fec::FECUtils::getEncoder(fec_type_, 1);
+ fec_encoder_->setFECCallback(std::forward<FECHandler>(fec_handler));
+ fec_encoder_->setBufferCallback(
+ std::forward<AllocatorHandler>(allocator_handler));
+ }
+ }
+
+ protected:
+ implementation::ProducerSocket *socket_;
+
+ // Thread pool responsible for IO operations (send data / receive interests)
+ std::vector<utils::EventThread> io_threads_;
+ interface::ProductionStatistics *stats_;
+ std::unique_ptr<fec::ProducerFEC> fec_encoder_;
+
+ // Callbacks
+ interface::ProducerInterestCallback *on_interest_input_;
+ interface::ProducerInterestCallback *on_interest_dropped_input_buffer_;
+ interface::ProducerInterestCallback *on_interest_inserted_input_buffer_;
+ interface::ProducerInterestCallback *on_interest_satisfied_output_buffer_;
+ interface::ProducerInterestCallback *on_interest_process_;
+
+ interface::ProducerContentObjectCallback *on_new_segment_;
+ interface::ProducerContentObjectCallback *on_content_object_to_sign_;
+ interface::ProducerContentObjectCallback *on_content_object_in_output_buffer_;
+ interface::ProducerContentObjectCallback *on_content_object_output_;
+ interface::ProducerContentObjectCallback
+ *on_content_object_evicted_from_output_buffer_;
+
+ interface::ProducerContentCallback *on_content_produced_;
+
+ interface::ProducerSocket::Callback *producer_callback_;
+
+ // Output buffer
+ utils::ContentStore output_buffer_;
+
+ // Signature and manifest
+ std::shared_ptr<auth::Signer> signer_;
+ uint32_t manifest_max_capacity_;
+
+ bool is_async_;
+ fec::FECType fec_type_;
+};
+
+} // end namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/protocol.cc b/libtransport/src/protocols/protocol.cc
deleted file mode 100644
index 451fef80d..000000000
--- a/libtransport/src/protocols/protocol.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hicn/transport/interfaces/socket_consumer.h>
-#include <implementation/socket_consumer.h>
-#include <protocols/protocol.h>
-
-namespace transport {
-
-namespace protocol {
-
-using namespace interface;
-
-TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket,
- Reassembly *reassembly_protocol)
- : socket_(icn_socket),
- reassembly_protocol_(reassembly_protocol),
- index_manager_(
- std::make_unique<IndexManager>(socket_, this, reassembly_protocol)),
- is_running_(false),
- is_first_(false),
- on_interest_retransmission_(VOID_HANDLER),
- on_interest_output_(VOID_HANDLER),
- on_interest_timeout_(VOID_HANDLER),
- on_interest_satisfied_(VOID_HANDLER),
- on_content_object_input_(VOID_HANDLER),
- on_content_object_verification_(VOID_HANDLER),
- stats_summary_(VOID_HANDLER),
- verification_failed_callback_(VOID_HANDLER),
- on_payload_(VOID_HANDLER) {
- socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_);
- socket_->getSocketOption(OtherOptions::STATISTICS, &stats_);
-}
-
-int TransportProtocol::start() {
- // If the protocol is already running, return otherwise set as running
- if (is_running_) return -1;
-
- // Get all callbacks references before starting
- socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION,
- &on_interest_retransmission_);
- socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT,
- &on_interest_output_);
- socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED,
- &on_interest_timeout_);
- socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED,
- &on_interest_satisfied_);
- socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT,
- &on_content_object_input_);
- socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY,
- &on_content_object_verification_);
- socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY,
- &stats_summary_);
- socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED,
- &verification_failed_callback_);
- socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK,
- &on_payload_);
- socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_);
-
- // Set it is the first time we schedule an interest
- is_first_ = true;
-
- // Reset the protocol state machine
- reset();
- // Schedule next interests
- scheduleNextInterests();
-
- is_first_ = false;
-
- // Set the protocol as running
- is_running_ = true;
-
- if (!is_async_) {
- // Start Event loop
- portal_->runEventsLoop();
-
- // Not running anymore
- is_running_ = false;
- }
-
- return 0;
-}
-
-void TransportProtocol::stop() {
- is_running_ = false;
-
- if (!is_async_) {
- portal_->stopEventsLoop();
- } else {
- portal_->clear();
- }
-}
-
-void TransportProtocol::resume() {
- if (is_running_) return;
-
- is_running_ = true;
-
- scheduleNextInterests();
-
- portal_->runEventsLoop();
-
- is_running_ = false;
-}
-
-void TransportProtocol::onContentReassembled(std::error_code ec) {
- stop();
-
- if (!on_payload_) {
- throw errors::RuntimeException(
- "The read callback must be installed in the transport before "
- "starting "
- "the content retrieval.");
- }
-
- if (!ec) {
- on_payload_->readSuccess(stats_->getBytesRecv());
- } else {
- on_payload_->readError(ec);
- }
-}
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/protocol.h b/libtransport/src/protocols/protocol.h
index 73a0a2c64..a9f929db9 100644
--- a/libtransport/src/protocols/protocol.h
+++ b/libtransport/src/protocols/protocol.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -15,100 +15,59 @@
#pragma once
-#include <hicn/transport/interfaces/callbacks.h>
-#include <hicn/transport/interfaces/socket_consumer.h>
-#include <hicn/transport/interfaces/statistics.h>
-#include <hicn/transport/utils/object_pool.h>
-#include <implementation/socket.h>
-#include <protocols/data_processing_events.h>
-#include <protocols/indexer.h>
-#include <protocols/packet_manager.h>
-#include <protocols/reassembly.h>
+#include <core/portal.h>
+#include <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/noncopyable.h>
-#include <atomic>
+#include <random>
namespace transport {
namespace protocol {
-using namespace core;
-
-class IndexVerificationManager;
-
-using ReadCallback = interface::ConsumerSocket::ReadCallback;
-
-class TransportProtocolCallback {
- virtual void onContentObject(const core::Interest &interest,
- const core::ContentObject &content_object) = 0;
- virtual void onTimeout(const core::Interest &interest) = 0;
-};
-
-class TransportProtocol : public implementation::BasePortal::ConsumerCallback,
- public PacketManager<Interest>,
- public ContentObjectProcessingEventCallback {
- static constexpr std::size_t interest_pool_size = 4096;
-
- friend class ManifestIndexManager;
-
+class Protocol : public core::Portal::TransportCallback, utils::NonCopyable {
public:
- TransportProtocol(implementation::ConsumerSocket *icn_socket,
- Reassembly *reassembly_protocol);
-
- virtual ~TransportProtocol() = default;
-
- TRANSPORT_ALWAYS_INLINE bool isRunning() { return is_running_; }
-
- virtual int start();
-
- virtual void stop();
-
- virtual void resume();
-
- virtual bool verifyKeyPackets() = 0;
-
- virtual void scheduleNextInterests() = 0;
-
- // Events generated by the indexing
- virtual void onContentReassembled(std::error_code ec);
- virtual void onPacketDropped(
- Interest::Ptr &&interest,
- ContentObject::Ptr &&content_object) override = 0;
- virtual void onReassemblyFailed(std::uint32_t missing_segment) override = 0;
+ virtual void stop() {
+ portal_->getThread().addAndWaitForExecution([this]() {
+ unSetRunning();
+ portal_->unregisterTransportCallback();
+ portal_->clear();
+ });
+ }
+
+ virtual void onInterest(core::Interest &i) {
+ throw errors::RuntimeException("Not implemented");
+ }
+
+ virtual void onContentObject(core::Interest &i, core::ContentObject &c) {
+ throw errors::RuntimeException("Not implemented");
+ }
+
+ virtual void onTimeout(core::Interest::Ptr &i, const core::Name &n) {
+ throw errors::RuntimeException("Not implemented");
+ }
+
+ virtual void onError(const std::error_code &ec) {
+ throw errors::RuntimeException("Not implemented");
+ }
+
+ bool isRunning() { return is_running_; }
+ void setRunning() { is_running_ = true; }
+ void unSetRunning() { is_running_ = false; }
protected:
- // Consumer Callback
- virtual void reset() = 0;
- virtual void onContentObject(Interest::Ptr &&i,
- ContentObject::Ptr &&c) override = 0;
- virtual void onTimeout(Interest::Ptr &&i) override = 0;
- virtual void onError(std::error_code ec) override {}
+ Protocol() : portal_(nullptr), is_running_(false), gen_(rd_()) {}
+ virtual ~Protocol() {}
protected:
- implementation::ConsumerSocket *socket_;
- std::unique_ptr<Reassembly> reassembly_protocol_;
- std::unique_ptr<IndexManager> index_manager_;
- std::shared_ptr<implementation::BasePortal> portal_;
- std::atomic<bool> is_running_;
- // True if it si the first time we schedule an interest
- std::atomic<bool> is_first_;
- interface::TransportStatistics *stats_;
-
- // Callbacks
- interface::ConsumerInterestCallback *on_interest_retransmission_;
- interface::ConsumerInterestCallback *on_interest_output_;
- interface::ConsumerInterestCallback *on_interest_timeout_;
- interface::ConsumerInterestCallback *on_interest_satisfied_;
- interface::ConsumerContentObjectCallback *on_content_object_input_;
- interface::ConsumerContentObjectVerificationCallback
- *on_content_object_verification_;
- interface::ConsumerContentObjectCallback *on_content_object_;
- interface::ConsumerTimerCallback *stats_summary_;
- interface::ConsumerContentObjectVerificationFailedCallback
- *verification_failed_callback_;
- ReadCallback *on_payload_;
-
- bool is_async_;
+ std::shared_ptr<core::Portal> portal_;
+ std::atomic_bool is_running_;
+
+ // Random engine
+ std::random_device rd_;
+ std::mt19937 gen_;
};
-} // end namespace protocol
-} // end namespace transport
+} // namespace protocol
+
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/protocols/raaqm.cc b/libtransport/src/protocols/raaqm.cc
index 5023adf2e..bcbc15aef 100644
--- a/libtransport/src/protocols/raaqm.cc
+++ b/libtransport/src/protocols/raaqm.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -13,10 +13,11 @@
* limitations under the License.
*/
+#include <hicn/transport/core/global_object_pool.h>
#include <hicn/transport/interfaces/socket_consumer.h>
#include <implementation/socket_consumer.h>
#include <protocols/errors.h>
-#include <protocols/indexer.h>
+#include <protocols/index_manager_bytestream.h>
#include <protocols/raaqm.h>
#include <cstdlib>
@@ -30,12 +31,14 @@ using namespace interface;
RaaqmTransportProtocol::RaaqmTransportProtocol(
implementation::ConsumerSocket *icn_socket)
- : TransportProtocol(icn_socket, new ByteStreamReassembly(icn_socket, this)),
+ : TransportProtocol(icn_socket, new IndexManager(icn_socket, this),
+ new ByteStreamReassembly(icn_socket, this)),
current_window_size_(1),
interests_in_flight_(0),
cur_path_(nullptr),
- t0_(utils::SteadyClock::now()),
+ t0_(utils::SteadyTime::Clock::now()),
rate_estimator_(nullptr),
+ dis_(0, 1.0),
schedule_interests_(true) {
init();
}
@@ -46,11 +49,34 @@ RaaqmTransportProtocol::~RaaqmTransportProtocol() {
}
}
-int RaaqmTransportProtocol::start() {
+void RaaqmTransportProtocol::reset() {
+ // Set first segment to retrieve
+ TransportProtocol::reset();
+ core::Name *name;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
+ indexer_verifier_->setFirstSuffix(name->getSuffix());
+ std::queue<uint32_t> empty;
+ std::swap(interest_to_retransmit_, empty);
+ stats_->reset();
+
+ // Reset protocol variables
+ interests_in_flight_ = 0;
+ t0_ = utils::SteadyTime::Clock::now();
+
+ // Optionally reset congestion window
+ bool reset_window;
+ socket_->getSocketOption(RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET,
+ reset_window);
+ if (reset_window) {
+ current_window_size_ = 1;
+ }
+
+ // Reset rate estimator
if (rate_estimator_) {
rate_estimator_->onStart();
}
+ // If not cur_path exists, create one
if (!cur_path_) {
// RAAQM
double drop_factor;
@@ -93,41 +119,6 @@ int RaaqmTransportProtocol::start() {
cur_path_ = cur_path.get();
path_table_[default_values::path_id] = std::move(cur_path);
}
-
- portal_->setConsumerCallback(this);
- return TransportProtocol::start();
-}
-
-void RaaqmTransportProtocol::resume() { return TransportProtocol::resume(); }
-
-void RaaqmTransportProtocol::reset() {
- // Set first segment to retrieve
- core::Name *name;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
- index_manager_->reset();
- index_manager_->setFirstSuffix(name->getSuffix());
- std::queue<Interest::Ptr> empty;
- std::swap(interest_to_retransmit_, empty);
- stats_->reset();
-
- // Reset reassembly component
- reassembly_protocol_->reInitialize();
-
- // Reset protocol variables
- interests_in_flight_ = 0;
- t0_ = utils::SteadyClock::now();
-
- // Optionally reset congestion window
- bool reset_window;
- socket_->getSocketOption(RaaqmTransportOptions::PER_SESSION_CWINDOW_RESET,
- reset_window);
- if (reset_window) {
- current_window_size_ = 1;
- }
-}
-
-bool RaaqmTransportProtocol::verifyKeyPackets() {
- return index_manager_->onKeyToVerify();
}
void RaaqmTransportProtocol::increaseWindow() {
@@ -197,9 +188,8 @@ void RaaqmTransportProtocol::init() {
lte_delay_ = 15000;
if (!is) {
- TRANSPORT_LOGW(
- "WARNING: RAAQM parameters not found at %s, set default values",
- RAAQM_CONFIG_PATH);
+ LOG(WARNING) << "RAAQM parameters not found at " << RAAQM_CONFIG_PATH
+ << ", set default values";
return;
}
@@ -325,75 +315,66 @@ void RaaqmTransportProtocol::init() {
is.close();
}
-void RaaqmTransportProtocol::onContentObject(
- Interest::Ptr &&interest, ContentObject::Ptr &&content_object) {
- // Check whether makes sense to continue
- if (TRANSPORT_EXPECT_FALSE(!is_running_)) {
- return;
- }
+void RaaqmTransportProtocol::onContentObjectReceived(
+ Interest &interest, ContentObject &content_object, std::error_code &ec) {
+ uint32_t incremental_suffix = content_object.getName().getSuffix();
// Call application-defined callbacks
if (*on_content_object_input_) {
- (*on_content_object_input_)(*socket_->getInterface(), *content_object);
+ (*on_content_object_input_)(*socket_->getInterface(), content_object);
}
if (*on_interest_satisfied_) {
- (*on_interest_satisfied_)(*socket_->getInterface(), *interest);
- }
-
- if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) {
- stats_->updateBytesRecv(content_object->payloadSize());
+ (*on_interest_satisfied_)(*socket_->getInterface(), interest);
}
- onContentSegment(std::move(interest), std::move(content_object));
- scheduleNextInterests();
-}
+ ec = make_error_code(protocol_error::success);
-void RaaqmTransportProtocol::onContentSegment(
- Interest::Ptr &&interest, ContentObject::Ptr &&content_object) {
- uint32_t incremental_suffix = content_object->getName().getSuffix();
+ if (content_object.getPayloadType() == PayloadType::DATA) {
+ stats_->updateBytesRecv(content_object.payloadSize());
+ }
// Decrease in-flight interests
interests_in_flight_--;
// Update stats
if (!interest_retransmissions_[incremental_suffix & mask]) {
- afterContentReception(*interest, *content_object);
+ afterContentReception(interest, content_object);
}
- index_manager_->onContentObject(std::move(interest),
- std::move(content_object));
+ // Schedule next interests
+ scheduleNextInterests();
}
-void RaaqmTransportProtocol::onPacketDropped(
- Interest::Ptr &&interest, ContentObject::Ptr &&content_object) {
+void RaaqmTransportProtocol::onPacketDropped(Interest &interest,
+ ContentObject &content_object,
+ const std::error_code &reason) {
uint32_t max_rtx = 0;
socket_->getSocketOption(GeneralTransportOptions::MAX_INTEREST_RETX, max_rtx);
- uint64_t segment = interest->getName().getSuffix();
+ uint64_t segment = interest.getName().getSuffix();
if (TRANSPORT_EXPECT_TRUE(interest_retransmissions_[segment & mask] <
max_rtx)) {
stats_->updateRetxCount(1);
if (*on_interest_retransmission_) {
- (*on_interest_retransmission_)(*socket_->getInterface(), *interest);
+ (*on_interest_retransmission_)(*socket_->getInterface(), interest);
}
if (*on_interest_output_) {
- (*on_interest_output_)(*socket_->getInterface(), *interest);
+ (*on_interest_output_)(*socket_->getInterface(), interest);
}
- if (!is_running_) {
+ if (!isRunning()) {
return;
}
interest_retransmissions_[segment & mask]++;
- interest_to_retransmit_.push(std::move(interest));
+ interest_to_retransmit_.push((unsigned int)segment);
} else {
- TRANSPORT_LOGE(
- "Stop: received not trusted packet %llu times",
- (unsigned long long)interest_retransmissions_[segment & mask]);
+ LOG(ERROR) << "Stop: received not trusted packet "
+ << interest_retransmissions_[segment & mask] << " times";
onContentReassembled(
make_error_code(protocol_error::max_retransmissions_error));
}
@@ -403,23 +384,27 @@ void RaaqmTransportProtocol::onReassemblyFailed(std::uint32_t missing_segment) {
}
-void RaaqmTransportProtocol::onTimeout(Interest::Ptr &&interest) {
- checkForStalePaths();
-
- const Name &n = interest->getName();
-
- TRANSPORT_LOGW("Timeout on content %s", n.toString().c_str());
+void RaaqmTransportProtocol::sendInterest(
+ const Name &interest_name,
+ std::array<uint32_t, MAX_AGGREGATED_INTEREST> *additional_suffixes,
+ uint32_t len) {
+ interests_in_flight_++;
+ interest_retransmissions_[interest_name.getSuffix() & mask]++;
+ interest_timepoints_[interest_name.getSuffix() & mask] =
+ utils::SteadyTime::Clock::now();
+ TransportProtocol::sendInterest(interest_name, additional_suffixes, len);
+}
- if (TRANSPORT_EXPECT_FALSE(!is_running_)) {
- return;
- }
+void RaaqmTransportProtocol::onInterestTimeout(Interest::Ptr &interest,
+ const Name &n) {
+ checkForStalePaths();
interests_in_flight_--;
uint64_t segment = n.getSuffix();
// Do not retransmit interests asking contents that do not exist.
- if (segment > index_manager_->getFinalSuffix()) {
+ if (segment > indexer_verifier_->getFinalSuffix()) {
return;
}
@@ -440,32 +425,34 @@ void RaaqmTransportProtocol::onTimeout(Interest::Ptr &&interest) {
(*on_interest_retransmission_)(*socket_->getInterface(), *interest);
}
- if (!is_running_) {
+ if (!isRunning()) {
return;
}
- interest_retransmissions_[segment & mask]++;
- interest_to_retransmit_.push(std::move(interest));
-
+ interest_to_retransmit_.push((unsigned int)segment);
scheduleNextInterests();
} else {
- TRANSPORT_LOGE("Stop: reached max retx limit.");
+ LOG(ERROR) << "Stop: reached max retx limit.";
onContentReassembled(std::make_error_code(std::errc(std::errc::io_error)));
}
}
void RaaqmTransportProtocol::scheduleNextInterests() {
- bool cancel = (!is_running_ && !is_first_) || !schedule_interests_;
+ bool cancel = (!isRunning() && !is_first_) || !schedule_interests_;
if (TRANSPORT_EXPECT_FALSE(cancel)) {
schedule_interests_ = true;
return;
}
+ core::Name *name;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
+
if (TRANSPORT_EXPECT_FALSE(interests_in_flight_ >= current_window_size_ &&
interest_to_retransmit_.size() > 0)) {
// send at least one interest if there are retransmissions to perform and
// there is no space left in the window
- sendInterest(std::move(interest_to_retransmit_.front()));
+ auto suffix = interest_to_retransmit_.front();
+ sendInterest(name->setSuffix(suffix));
interest_to_retransmit_.pop();
}
@@ -474,58 +461,26 @@ void RaaqmTransportProtocol::scheduleNextInterests() {
// Send the interest needed for filling the window
while (interests_in_flight_ < current_window_size_) {
if (interest_to_retransmit_.size() > 0) {
- sendInterest(std::move(interest_to_retransmit_.front()));
+ auto suffix = interest_to_retransmit_.front();
+ sendInterest(name->setSuffix(suffix));
interest_to_retransmit_.pop();
} else {
- index = index_manager_->getNextSuffix();
+ if (TRANSPORT_EXPECT_FALSE(!isRunning() && !is_first_)) {
+ break;
+ }
+
+ index = indexer_verifier_->getNextSuffix();
if (index == IndexManager::invalid_index) {
break;
}
- sendInterest(index);
+ interest_retransmissions_[index & mask] = ~0;
+ sendInterest(name->setSuffix(index));
}
}
}
-bool RaaqmTransportProtocol::sendInterest(std::uint64_t next_suffix) {
- auto interest = getPacket();
- core::Name *name;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
- name->setSuffix((uint32_t)next_suffix);
- interest->setName(*name);
-
- uint32_t interest_lifetime;
- socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
- interest_lifetime);
- interest->setLifetime(interest_lifetime);
-
- if (*on_interest_output_) {
- on_interest_output_->operator()(*socket_->getInterface(), *interest);
- }
-
- if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) {
- return false;
- }
-
- // This is set to ~0 so that the next interest_retransmissions_ + 1,
- // performed by sendInterest, will result in 0
- interest_retransmissions_[next_suffix & mask] = ~0;
- interest_timepoints_[next_suffix & mask] = utils::SteadyClock::now();
-
- sendInterest(std::move(interest));
-
- return true;
-}
-
-void RaaqmTransportProtocol::sendInterest(Interest::Ptr &&interest) {
- interests_in_flight_++;
- interest_retransmissions_[interest->getName().getSuffix() & mask]++;
-
- TRANSPORT_LOGD("Send interest %s", interest->getName().toString().c_str());
- portal_->sendInterest(std::move(interest));
-}
-
-void RaaqmTransportProtocol::onContentReassembled(std::error_code ec) {
+void RaaqmTransportProtocol::onContentReassembled(const std::error_code &ec) {
rate_estimator_->onDownloadFinished();
TransportProtocol::onContentReassembled(ec);
schedule_interests_ = false;
@@ -535,18 +490,18 @@ void RaaqmTransportProtocol::updateRtt(uint64_t segment) {
if (TRANSPORT_EXPECT_FALSE(!cur_path_)) {
throw std::runtime_error("RAAQM ERROR: no current path found, exit");
} else {
- auto now = utils::SteadyClock::now();
- utils::Microseconds rtt = std::chrono::duration_cast<utils::Microseconds>(
- now - interest_timepoints_[segment & mask]);
+ auto now = utils::SteadyTime::Clock::now();
+ auto rtt = utils::SteadyTime::getDurationUs(
+ interest_timepoints_[segment & mask], now);
// Update stats
- updateStats((uint32_t)segment, rtt.count(), now);
+ updateStats((uint32_t)segment, rtt, now);
if (rate_estimator_) {
- rate_estimator_->onRttUpdate((double)rtt.count());
+ rate_estimator_->onRttUpdate(rtt);
}
- cur_path_->insertNewRtt(rtt.count(), now);
+ cur_path_->insertNewRtt(rtt, now);
cur_path_->smoothTimer();
if (cur_path_->newPropagationDelayAvailable()) {
@@ -558,27 +513,27 @@ void RaaqmTransportProtocol::updateRtt(uint64_t segment) {
void RaaqmTransportProtocol::RAAQM() {
if (!cur_path_) {
throw errors::RuntimeException("ERROR: no current path found, exit");
- exit(EXIT_FAILURE);
} else {
// Change drop probability according to RTT statistics
cur_path_->updateDropProb();
- double coin = ((double)rand() / (RAND_MAX));
+ double coin = dis_(gen_);
if (coin <= cur_path_->getDropProb()) {
decreaseWindow();
}
}
}
-void RaaqmTransportProtocol::updateStats(uint32_t suffix, uint64_t rtt,
- utils::TimePoint &now) {
+void RaaqmTransportProtocol::updateStats(
+ uint32_t suffix, const utils::SteadyTime::Microseconds &rtt,
+ utils::SteadyTime::TimePoint &now) {
// Update RTT statistics
stats_->updateAverageRtt(rtt);
stats_->updateAverageWindowSize(current_window_size_);
// Call statistics callback
if (*stats_summary_) {
- auto dt = std::chrono::duration_cast<utils::Milliseconds>(now - t0_);
+ auto dt = utils::SteadyTime::getDurationMs(t0_, now);
uint32_t timer_interval_milliseconds = 0;
socket_->getSocketOption(GeneralTransportOptions::STATS_INTERVAL,
diff --git a/libtransport/src/protocols/raaqm.h b/libtransport/src/protocols/raaqm.h
index fce4194d4..a7ef23b68 100644
--- a/libtransport/src/protocols/raaqm.h
+++ b/libtransport/src/protocols/raaqm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -18,11 +18,12 @@
#include <hicn/transport/utils/chrono_typedefs.h>
#include <protocols/byte_stream_reassembly.h>
#include <protocols/congestion_window_protocol.h>
-#include <protocols/protocol.h>
#include <protocols/raaqm_data_path.h>
#include <protocols/rate_estimation.h>
+#include <protocols/transport_protocol.h>
#include <queue>
+#include <random>
#include <vector>
namespace transport {
@@ -32,18 +33,15 @@ namespace protocol {
class RaaqmTransportProtocol : public TransportProtocol,
public CWindowProtocol {
public:
- RaaqmTransportProtocol(implementation::ConsumerSocket *icnet_socket);
+ RaaqmTransportProtocol(implementation::ConsumerSocket *icn_socket);
~RaaqmTransportProtocol();
- int start() override;
-
- void resume() override;
+ using TransportProtocol::start;
+ using TransportProtocol::stop;
void reset() override;
- virtual bool verifyKeyPackets() override;
-
protected:
static constexpr uint32_t buffer_size =
1 << interface::default_values::log_2_default_buffer_size;
@@ -58,52 +56,43 @@ class RaaqmTransportProtocol : public TransportProtocol,
const ContentObject &content_object);
virtual void afterDataUnsatisfied(uint64_t segment);
- virtual void updateStats(uint32_t suffix, uint64_t rtt,
- utils::TimePoint &now);
+ virtual void updateStats(uint32_t suffix,
+ const utils::SteadyTime::Microseconds &rtt,
+ utils::SteadyTime::TimePoint &now);
private:
void init();
- void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) override;
-
- void onContentSegment(Interest::Ptr &&interest,
- ContentObject::Ptr &&content_object);
-
- void onPacketDropped(Interest::Ptr &&interest,
- ContentObject::Ptr &&content_object) override;
-
+ void onContentObjectReceived(Interest &i, ContentObject &c,
+ std::error_code &ec) override;
+ void onPacketDropped(Interest &interest, ContentObject &content_object,
+ const std::error_code &reason) override;
void onReassemblyFailed(std::uint32_t missing_segment) override;
-
- void onTimeout(Interest::Ptr &&i) override;
-
+ void onInterestTimeout(Interest::Ptr &i, const Name &n) override;
virtual void scheduleNextInterests() override;
+ void sendInterest(const Name &interest_name,
+ std::array<uint32_t, MAX_AGGREGATED_INTEREST>
+ *additional_suffixes = nullptr,
+ uint32_t len = 0) override;
- bool sendInterest(std::uint64_t next_suffix);
-
- void sendInterest(Interest::Ptr &&interest);
-
- void onContentReassembled(std::error_code ec) override;
-
+ void onContentReassembled(const std::error_code &ec) override;
void updateRtt(uint64_t segment);
-
void RAAQM();
-
void updatePathTable(const ContentObject &content_object);
-
void checkDropProbability();
-
void checkForStalePaths();
-
void printRtt();
+ auto shared_from_this() { return utils::shared_from(this); }
+
protected:
// Congestion window management
double current_window_size_;
// Protocol management
uint64_t interests_in_flight_;
std::array<std::uint32_t, buffer_size> interest_retransmissions_;
- std::array<utils::TimePoint, buffer_size> interest_timepoints_;
- std::queue<Interest::Ptr> interest_to_retransmit_;
+ std::array<utils::SteadyTime::TimePoint, buffer_size> interest_timepoints_;
+ std::queue<uint32_t> interest_to_retransmit_;
private:
/**
@@ -117,13 +106,16 @@ class RaaqmTransportProtocol : public TransportProtocol,
PathTable path_table_;
// TimePoints for statistic
- utils::TimePoint t0_;
+ utils::SteadyTime::TimePoint t0_;
bool set_interest_filter_;
// for rate-estimation at packet level
IcnRateEstimator *rate_estimator_;
+ // Real distribution
+ std::uniform_real_distribution<> dis_;
+
// params for autotuning
bool raaqm_autotune_;
double default_beta_;
diff --git a/libtransport/src/protocols/raaqm_data_path.cc b/libtransport/src/protocols/raaqm_data_path.cc
index 8bbbadcf2..b8e6e6285 100644
--- a/libtransport/src/protocols/raaqm_data_path.cc
+++ b/libtransport/src/protocols/raaqm_data_path.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -14,7 +14,6 @@
*/
#include <hicn/transport/utils/chrono_typedefs.h>
-
#include <protocols/raaqm_data_path.h>
namespace transport {
@@ -24,16 +23,18 @@ namespace protocol {
RaaqmDataPath::RaaqmDataPath(double drop_factor,
double minimum_drop_probability,
unsigned new_timer, unsigned int samples,
- uint64_t new_rtt, uint64_t new_rtt_min,
- uint64_t new_rtt_max, unsigned new_pd)
+ const utils::SteadyTime::Milliseconds &new_rtt,
+ const utils::SteadyTime::Milliseconds &new_rtt_min,
+ const utils::SteadyTime::Milliseconds &new_rtt_max,
+ unsigned new_pd)
: drop_factor_(drop_factor),
minimum_drop_probability_(minimum_drop_probability),
timer_(new_timer),
samples_(samples),
- rtt_(new_rtt),
- rtt_min_(new_rtt_min),
- rtt_max_(new_rtt_max),
+ rtt_(new_rtt.count()),
+ rtt_min_(new_rtt_min.count()),
+ rtt_max_(new_rtt_max.count()),
prop_delay_(new_pd),
new_prop_delay_(false),
drop_prob_(0),
@@ -44,14 +45,15 @@ RaaqmDataPath::RaaqmDataPath(double drop_factor,
raw_data_bytes_received_(0),
last_raw_data_bytes_received_(0),
rtt_samples_(samples_),
- last_received_pkt_(utils::SteadyClock::now()),
+ last_received_pkt_(utils::SteadyTime::Clock::now()),
average_rtt_(0),
alpha_(ALPHA) {}
-RaaqmDataPath &RaaqmDataPath::insertNewRtt(uint64_t new_rtt,
- const utils::TimePoint &now) {
- rtt_ = new_rtt;
- rtt_samples_.pushBack(new_rtt);
+RaaqmDataPath &RaaqmDataPath::insertNewRtt(
+ const utils::SteadyTime::Microseconds &new_rtt,
+ const utils::SteadyTime::TimePoint &now) {
+ rtt_ = new_rtt.count() / 1000;
+ rtt_samples_.pushBack(rtt_);
rtt_max_ = rtt_samples_.rBegin();
rtt_min_ = rtt_samples_.begin();
@@ -144,10 +146,8 @@ unsigned int RaaqmDataPath::getPropagationDelay() {
}
bool RaaqmDataPath::isStale() {
- utils::TimePoint now = utils::SteadyClock::now();
- auto time =
- std::chrono::duration_cast<utils::Microseconds>(now - last_received_pkt_)
- .count();
+ utils::SteadyTime::TimePoint now = utils::SteadyTime::Clock::now();
+ auto time = utils::SteadyTime::getDurationUs(last_received_pkt_, now).count();
if (time > 2000000) {
return true;
}
diff --git a/libtransport/src/protocols/raaqm_data_path.h b/libtransport/src/protocols/raaqm_data_path.h
index 3f037bc76..dd24dad51 100644
--- a/libtransport/src/protocols/raaqm_data_path.h
+++ b/libtransport/src/protocols/raaqm_data_path.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -16,7 +16,6 @@
#pragma once
#include <hicn/transport/utils/chrono_typedefs.h>
-
#include <utils/min_filter.h>
#include <chrono>
@@ -35,8 +34,13 @@ class RaaqmDataPath {
public:
RaaqmDataPath(double drop_factor, double minimum_drop_probability,
unsigned new_timer, unsigned int samples,
- uint64_t new_rtt = 1000, uint64_t new_rtt_min = 1000,
- uint64_t new_rtt_max = 1000, unsigned new_pd = UINT_MAX);
+ const utils::SteadyTime::Milliseconds &new_rtt =
+ utils::SteadyTime::Milliseconds(1000),
+ const utils::SteadyTime::Milliseconds &new_rtt_min =
+ utils::SteadyTime::Milliseconds(1000),
+ const utils::SteadyTime::Milliseconds &new_rtt_max =
+ utils::SteadyTime::Milliseconds(1000),
+ unsigned new_pd = UINT_MAX);
public:
/*
@@ -45,7 +49,8 @@ class RaaqmDataPath {
* max of RTT.
* @param new_rtt is the value of the new RTT
*/
- RaaqmDataPath &insertNewRtt(uint64_t new_rtt, const utils::TimePoint &now);
+ RaaqmDataPath &insertNewRtt(const utils::SteadyTime::Microseconds &new_rtt,
+ const utils::SteadyTime::TimePoint &now);
/**
* @brief Update the path statistics
@@ -215,7 +220,7 @@ class RaaqmDataPath {
/**
* Time of the last call to the path reporter method
*/
- utils::TimePoint last_received_pkt_;
+ utils::SteadyTime::TimePoint last_received_pkt_;
double average_rtt_;
double alpha_;
diff --git a/libtransport/src/protocols/rate_estimation.cc b/libtransport/src/protocols/rate_estimation.cc
index a2cf1aefe..01c18c6cb 100644
--- a/libtransport/src/protocols/rate_estimation.cc
+++ b/libtransport/src/protocols/rate_estimation.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -13,9 +13,9 @@
* limitations under the License.
*/
+#include <glog/logging.h>
+#include <hicn/transport/errors/runtime_exception.h>
#include <hicn/transport/interfaces/socket_options_default_values.h>
-#include <hicn/transport/utils/log.h>
-
#include <protocols/rate_estimation.h>
#include <thread>
@@ -93,8 +93,8 @@ InterRttEstimator::InterRttEstimator(double alpha_arg) {
this->win_current_ = 1.0;
pthread_mutex_init(&(this->mutex_), NULL);
- this->start_time_ = std::chrono::steady_clock::now();
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->start_time_ = utils::SteadyTime::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
InterRttEstimator::~InterRttEstimator() {
@@ -106,33 +106,35 @@ InterRttEstimator::~InterRttEstimator() {
pthread_mutex_destroy(&(this->mutex_));
}
-void InterRttEstimator::onRttUpdate(double rtt) {
+void InterRttEstimator::onRttUpdate(
+ const utils::SteadyTime::Microseconds &rtt) {
pthread_mutex_lock(&(this->mutex_));
- this->rtt_ = rtt;
+ this->rtt_ = rtt.count() / 1000.0;
this->number_of_packets_++;
- this->avg_rtt_ += rtt;
+ this->avg_rtt_ += this->rtt_;
pthread_mutex_unlock(&(this->mutex_));
if (!thread_is_running_) {
my_th_ = (pthread_t *)malloc(sizeof(pthread_t));
if (!my_th_) {
- TRANSPORT_LOGE("Error allocating thread.");
- my_th_ = NULL;
+ throw errors::RuntimeException("Error allocating thread.");
}
- if (/*int err = */ pthread_create(my_th_, NULL, transport::protocol::Timer,
- (void *)this)) {
- TRANSPORT_LOGE("Error creating the thread");
+
+ if (pthread_create(my_th_, NULL, transport::protocol::Timer,
+ (void *)this)) {
+ free(my_th_);
my_th_ = NULL;
+ throw errors::RuntimeException("Error allocating thread.");
}
+
thread_is_running_ = true;
}
}
void InterRttEstimator::onWindowIncrease(double win_current) {
- TimePoint end = std::chrono::steady_clock::now();
+ auto end = utils::SteadyTime::now();
auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->begin_batch_)
- .count();
+ utils::SteadyTime::getDurationUs(this->begin_batch_, end).count();
pthread_mutex_lock(&(this->mutex_));
this->avg_win_ += this->win_current_ * delay;
@@ -140,14 +142,13 @@ void InterRttEstimator::onWindowIncrease(double win_current) {
this->win_change_ += delay;
pthread_mutex_unlock(&(this->mutex_));
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
void InterRttEstimator::onWindowDecrease(double win_current) {
- TimePoint end = std::chrono::steady_clock::now();
+ auto end = utils::SteadyTime::now();
auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->begin_batch_)
- .count();
+ utils::SteadyTime::getDurationUs(this->begin_batch_, end).count();
pthread_mutex_lock(&(this->mutex_));
this->avg_win_ += this->win_current_ * delay;
@@ -155,25 +156,24 @@ void InterRttEstimator::onWindowDecrease(double win_current) {
this->win_change_ += delay;
pthread_mutex_unlock(&(this->mutex_));
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
ALaTcpEstimator::ALaTcpEstimator() {
this->estimation_ = 0.0;
this->observer_ = NULL;
- this->start_time_ = std::chrono::steady_clock::now();
+ this->start_time_ = utils::SteadyTime::now();
this->totalSize_ = 0.0;
}
void ALaTcpEstimator::onStart() {
this->totalSize_ = 0.0;
- this->start_time_ = std::chrono::steady_clock::now();
+ this->start_time_ = utils::SteadyTime::now();
}
void ALaTcpEstimator::onDownloadFinished() {
- TimePoint end = std::chrono::steady_clock::now();
- auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->start_time_).count();
+ auto end = utils::SteadyTime::now();
+ auto delay = utils::SteadyTime::getDurationUs(this->start_time_, end).count();
this->estimation_ = this->totalSize_ * 8 * 1000000 / delay;
if (observer_) {
observer_->notifyStats(this->estimation_);
@@ -193,22 +193,21 @@ SimpleEstimator::SimpleEstimator(double alphaArg, int batching_param) {
this->number_of_packets_ = 0;
this->base_alpha_ = alphaArg;
this->alpha_ = alphaArg;
- this->start_time_ = std::chrono::steady_clock::now();
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->start_time_ = utils::SteadyTime::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
void SimpleEstimator::onStart() {
this->estimated_ = false;
this->number_of_packets_ = 0;
this->total_size_ = 0.0;
- this->start_time_ = std::chrono::steady_clock::now();
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->start_time_ = utils::SteadyTime::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
void SimpleEstimator::onDownloadFinished() {
- TimePoint end = std::chrono::steady_clock::now();
- auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->start_time_).count();
+ auto end = utils::SteadyTime::now();
+ auto delay = utils::SteadyTime::getDurationUs(this->start_time_, end).count();
if (observer_) {
observer_->notifyDownloadTime((double)delay);
}
@@ -230,8 +229,7 @@ void SimpleEstimator::onDownloadFinished() {
} else {
if (this->number_of_packets_ >=
(int)(75.0 * (double)this->batching_param_ / 100.0)) {
- delay = std::chrono::duration_cast<Microseconds>(end - this->begin_batch_)
- .count();
+ delay = utils::SteadyTime::getDurationUs(this->begin_batch_, end).count();
// Assuming all packets carry max_packet_size_ bytes of data
// (8*max_packet_size_ bits); 1000000 factor to convert us to seconds
if (this->estimation_) {
@@ -250,22 +248,21 @@ void SimpleEstimator::onDownloadFinished() {
}
this->number_of_packets_ = 0;
this->total_size_ = 0.0;
- this->start_time_ = std::chrono::steady_clock::now();
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->start_time_ = utils::SteadyTime::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
void SimpleEstimator::onDataReceived(int packet_size) {
this->total_size_ += packet_size;
}
-void SimpleEstimator::onRttUpdate(double rtt) {
+void SimpleEstimator::onRttUpdate(const utils::SteadyTime::Microseconds &rtt) {
this->number_of_packets_++;
if (this->number_of_packets_ == this->batching_param_) {
- TimePoint end = std::chrono::steady_clock::now();
+ auto end = utils::SteadyTime::now();
auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->begin_batch_)
- .count();
+ utils::SteadyTime::getDurationUs(this->begin_batch_, end).count();
// Assuming all packets carry max_packet_size_ bytes of data
// (8*max_packet_size_ bits); 1000000 factor to convert us to seconds
if (this->estimation_) {
@@ -281,7 +278,7 @@ void SimpleEstimator::onRttUpdate(double rtt) {
this->alpha_ = this->base_alpha_;
this->number_of_packets_ = 0;
this->total_size_ = 0.0;
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
}
@@ -298,13 +295,14 @@ BatchingPacketsEstimator::BatchingPacketsEstimator(double alpha_arg,
this->max_packet_size_ = 0;
this->estimation_ = 0.0;
this->win_current_ = 1.0;
- this->begin_batch_ = std::chrono::steady_clock::now();
- this->start_time_ = std::chrono::steady_clock::now();
+ this->begin_batch_ = utils::SteadyTime::now();
+ this->start_time_ = utils::SteadyTime::now();
}
-void BatchingPacketsEstimator::onRttUpdate(double rtt) {
+void BatchingPacketsEstimator::onRttUpdate(
+ const utils::SteadyTime::Microseconds &rtt) {
this->number_of_packets_++;
- this->avg_rtt_ += rtt;
+ this->avg_rtt_ += rtt.count() / 1000.0;
if (number_of_packets_ == this->batching_param_) {
if (estimation_ == 0) {
@@ -330,25 +328,23 @@ void BatchingPacketsEstimator::onRttUpdate(double rtt) {
}
void BatchingPacketsEstimator::onWindowIncrease(double win_current) {
- TimePoint end = std::chrono::steady_clock::now();
+ auto end = utils::SteadyTime::now();
auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->begin_batch_)
- .count();
+ utils::SteadyTime::getDurationUs(this->begin_batch_, end).count();
this->avg_win_ += this->win_current_ * delay;
this->win_current_ = win_current;
this->win_change_ += delay;
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
void BatchingPacketsEstimator::onWindowDecrease(double win_current) {
- TimePoint end = std::chrono::steady_clock::now();
+ auto end = utils::SteadyTime::now();
auto delay =
- std::chrono::duration_cast<Microseconds>(end - this->begin_batch_)
- .count();
+ utils::SteadyTime::getDurationUs(this->begin_batch_, end).count();
this->avg_win_ += this->win_current_ * delay;
this->win_current_ = win_current;
this->win_change_ += delay;
- this->begin_batch_ = std::chrono::steady_clock::now();
+ this->begin_batch_ = utils::SteadyTime::now();
}
} // end namespace protocol
diff --git a/libtransport/src/protocols/rate_estimation.h b/libtransport/src/protocols/rate_estimation.h
index 17f39e0b9..d809b2b7c 100644
--- a/libtransport/src/protocols/rate_estimation.h
+++ b/libtransport/src/protocols/rate_estimation.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -16,7 +16,7 @@
#pragma once
#include <hicn/transport/interfaces/statistics.h>
-
+#include <hicn/transport/utils/noncopyable.h>
#include <protocols/raaqm_data_path.h>
#include <chrono>
@@ -25,16 +25,13 @@ namespace transport {
namespace protocol {
-class IcnRateEstimator {
+class IcnRateEstimator : utils::NonCopyable {
public:
- using TimePoint = std::chrono::steady_clock::time_point;
- using Microseconds = std::chrono::microseconds;
-
IcnRateEstimator(){};
virtual ~IcnRateEstimator(){};
- virtual void onRttUpdate(double rtt){};
+ virtual void onRttUpdate(const utils::SteadyTime::Microseconds &rtt){};
virtual void onDataReceived(int packetSize){};
@@ -49,9 +46,10 @@ class IcnRateEstimator {
virtual void setObserver(interface::IcnObserver *observer) {
this->observer_ = observer;
};
+
interface::IcnObserver *observer_;
- TimePoint start_time_;
- TimePoint begin_batch_;
+ utils::SteadyTime::TimePoint start_time_;
+ utils::SteadyTime::TimePoint begin_batch_;
double base_alpha_;
double alpha_;
double estimation_;
@@ -68,7 +66,7 @@ class InterRttEstimator : public IcnRateEstimator {
~InterRttEstimator();
- void onRttUpdate(double rtt);
+ void onRttUpdate(const utils::SteadyTime::Microseconds &rtt);
void onDataReceived(int packet_size) {
if (packet_size > this->max_packet_size_) {
@@ -103,7 +101,7 @@ class BatchingPacketsEstimator : public IcnRateEstimator {
public:
BatchingPacketsEstimator(double alpha_arg, int batchingParam);
- void onRttUpdate(double rtt);
+ void onRttUpdate(const utils::SteadyTime::Microseconds &rtt);
void onDataReceived(int packet_size) {
if (packet_size > this->max_packet_size_) {
@@ -150,7 +148,7 @@ class SimpleEstimator : public IcnRateEstimator {
public:
SimpleEstimator(double alpha, int batching_param);
- void onRttUpdate(double rtt);
+ void onRttUpdate(const utils::SteadyTime::Microseconds &rtt);
void onDataReceived(int packet_size);
diff --git a/libtransport/src/protocols/reassembly.cc b/libtransport/src/protocols/reassembly.cc
index c6602153c..065458f7e 100644
--- a/libtransport/src/protocols/reassembly.cc
+++ b/libtransport/src/protocols/reassembly.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -16,7 +16,6 @@
#include <hicn/transport/interfaces/socket_consumer.h>
#include <hicn/transport/utils/array.h>
#include <hicn/transport/utils/membuf.h>
-
#include <implementation/socket_consumer.h>
#include <protocols/errors.h>
#include <protocols/indexer.h>
@@ -32,7 +31,7 @@ void Reassembly::notifyApplication() {
interface::ConsumerCallbacksOptions::READ_CALLBACK, &read_callback);
if (TRANSPORT_EXPECT_FALSE(!read_callback)) {
- TRANSPORT_LOGE("Read callback not installed!");
+ LOG(ERROR) << "Read callback not installed!";
return;
}
diff --git a/libtransport/src/protocols/reassembly.h b/libtransport/src/protocols/reassembly.h
index fdc9f2a05..c0c4de3d8 100644
--- a/libtransport/src/protocols/reassembly.h
+++ b/libtransport/src/protocols/reassembly.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright (c) 2021 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
@@ -36,7 +36,7 @@ class Reassembly {
public:
class ContentReassembledCallback {
public:
- virtual void onContentReassembled(std::error_code ec) = 0;
+ virtual void onContentReassembled(const std::error_code &ec) = 0;
};
Reassembly(implementation::ConsumerSocket *icn_socket,
@@ -46,19 +46,42 @@ class Reassembly {
virtual ~Reassembly() = default;
- virtual void reassemble(core::ContentObject::Ptr &&content_object) = 0;
- virtual void reassemble(
- std::unique_ptr<core::ContentObjectManifest> &&manifest) = 0;
+ /**
+ * Handle reassembly of content object.
+ */
+ virtual void reassemble(core::ContentObject &content_object) = 0;
+
+ /**
+ * Handle reassembly of content object.
+ */
+ virtual void reassemble(utils::MemBuf &buffer, uint32_t suffix) = 0;
+
+ /**
+ * Reset reassembler for new round
+ */
virtual void reInitialize() = 0;
- virtual void setIndexer(Indexer *indexer) { index_manager_ = indexer; }
+
+ /**
+ * Use indexer to get next segments to reassembly
+ */
+ virtual void setIndexer(Indexer *indexer) { indexer_verifier_ = indexer; }
+
+ /**
+ * Decide if it is required to pass to application buffers whose verification
+ * has been delayed (e.g. because the manifest is missing). False by default.
+ */
+ virtual bool reassembleUnverified() { return false; }
protected:
+ /**
+ * Notify application there is data to read.
+ */
virtual void notifyApplication();
protected:
implementation::ConsumerSocket *reassembly_consumer_socket_;
TransportProtocol *transport_protocol_;
- Indexer *index_manager_;
+ Indexer *indexer_verifier_;
std::unique_ptr<utils::MemBuf> read_buffer_;
};
diff --git a/libtransport/src/protocols/rtc.cc b/libtransport/src/protocols/rtc.cc
deleted file mode 100644
index a01b8daa5..000000000
--- a/libtransport/src/protocols/rtc.cc
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hicn/transport/interfaces/socket_consumer.h>
-#include <implementation/socket_consumer.h>
-#include <math.h>
-#include <protocols/rtc.h>
-
-#include <random>
-
-namespace transport {
-
-namespace protocol {
-
-using namespace interface;
-
-RTCTransportProtocol::RTCTransportProtocol(
- implementation::ConsumerSocket *icn_socket)
- : TransportProtocol(icn_socket, nullptr),
- DatagramReassembly(icn_socket, this),
- inflightInterests_(1 << default_values::log_2_default_buffer_size),
- modMask_((1 << default_values::log_2_default_buffer_size) - 1) {
- icn_socket->getSocketOption(PORTAL, portal_);
- rtx_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService());
- probe_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService());
- sentinel_timer_ =
- std::make_unique<asio::steady_timer>(portal_->getIoService());
- round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService());
- initParams();
-}
-
-RTCTransportProtocol::~RTCTransportProtocol() {}
-
-void RTCTransportProtocol::resume() {
- if (is_running_) return;
-
- is_running_ = true;
- inflightInterestsCount_ = 0;
-
- probeRtt();
- sentinelTimer();
- newRound();
- scheduleNextInterests();
-
- portal_->runEventsLoop();
- is_running_ = false;
-}
-
-// private
-void RTCTransportProtocol::initParams() {
- portal_->setConsumerCallback(this);
- // controller var
- currentState_ = HICN_RTC_SYNC_STATE;
-
- // cwin var
- currentCWin_ = HICN_INITIAL_CWIN;
- maxCWin_ = HICN_INITIAL_CWIN_MAX;
-
- // names/packets var
- actualSegment_ = 0;
- inflightInterestsCount_ = 0;
- interestRetransmissions_.clear();
- lastSegNacked_ = 0;
- lastReceived_ = 0;
- lastReceivedTime_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- lastEvent_ = lastReceivedTime_;
- highestReceived_ = 0;
- firstSequenceInRound_ = 0;
-
- rtx_timer_used_ = false;
- for (int i = 0; i < (1 << default_values::log_2_default_buffer_size); i++) {
- inflightInterests_[i] = {0};
- }
-
- // stats
- firstPckReceived_ = false;
- receivedBytes_ = 0;
- sentInterest_ = 0;
- receivedData_ = 0;
- packetLost_ = 0;
- lossRecovered_ = 0;
- avgPacketSize_ = HICN_INIT_PACKET_SIZE;
- gotNack_ = false;
- gotFutureNack_ = 0;
- rounds_ = 0;
- roundsWithoutNacks_ = 0;
- pathTable_.clear();
-
- // CC var
- estimatedBw_ = 0.0;
- lossRate_ = 0.0;
- queuingDelay_ = 0.0;
- protocolState_ = HICN_RTC_NORMAL_STATE;
-
- producerPathLabels_[0] = 0;
- producerPathLabels_[1] = 0;
- initied = false;
-
- socket_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
- (uint32_t)HICN_RTC_INTEREST_LIFETIME);
-}
-
-// private
-void RTCTransportProtocol::reset() {
- initParams();
- probeRtt();
- sentinelTimer();
- newRound();
-}
-
-uint32_t max(uint32_t a, uint32_t b) {
- if (a > b)
- return a;
- else
- return b;
-}
-
-uint32_t min(uint32_t a, uint32_t b) {
- if (a < b)
- return a;
- else
- return b;
-}
-
-void RTCTransportProtocol::newRound() {
- round_timer_->expires_from_now(std::chrono::milliseconds(HICN_ROUND_LEN));
- round_timer_->async_wait([this](std::error_code ec) {
- if (ec) return;
- updateStats(HICN_ROUND_LEN);
- newRound();
- });
-}
-
-void RTCTransportProtocol::updateDelayStats(
- const ContentObject &content_object) {
- uint32_t segmentNumber = content_object.getName().getSuffix();
- uint32_t pkt = segmentNumber & modMask_;
-
- if (inflightInterests_[pkt].state != sent_) return;
-
- if (interestRetransmissions_.find(segmentNumber) !=
- interestRetransmissions_.end())
- // this packet was rtx at least once
- return;
-
- uint32_t pathLabel = content_object.getPathLabel();
-
- if (pathTable_.find(pathLabel) == pathTable_.end()) {
- // found a new path
- std::shared_ptr<RTCDataPath> newPath = std::make_shared<RTCDataPath>();
- pathTable_[pathLabel] = newPath;
- }
-
- // RTT measurements are useful both from NACKs and data packets
- uint64_t RTT = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count() -
- inflightInterests_[pkt].transmissionTime;
-
- pathTable_[pathLabel]->insertRttSample(RTT);
- auto payload = content_object.getPayload();
-
- // we collect OWD only for datapackets
- if (payload->length() != HICN_NACK_HEADER_SIZE) {
- uint64_t *senderTimeStamp = (uint64_t *)payload->data();
- int64_t OWD = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count() -
- *senderTimeStamp;
-
- pathTable_[pathLabel]->insertOwdSample(OWD);
- pathTable_[pathLabel]->computeInterArrivalGap(segmentNumber);
- } else {
- pathTable_[pathLabel]->receivedNack();
- }
-}
-
-void RTCTransportProtocol::updateStats(uint32_t round_duration) {
- if (pathTable_.empty()) return;
-
- if (receivedBytes_ != 0) {
- double bytesPerSec =
- (double)(receivedBytes_ *
- ((double)HICN_MILLI_IN_A_SEC / (double)round_duration));
- estimatedBw_ = (estimatedBw_ * HICN_ESTIMATED_BW_ALPHA) +
- ((1 - HICN_ESTIMATED_BW_ALPHA) * bytesPerSec);
- }
-
- uint64_t minRtt = UINT_MAX;
- uint64_t maxRtt = 0;
-
- for (auto it = pathTable_.begin(); it != pathTable_.end(); it++) {
- it->second->roundEnd();
- if (it->second->isActive()) {
- if (it->second->getMinRtt() < minRtt) {
- minRtt = it->second->getMinRtt();
- producerPathLabels_[0] = it->first;
- }
- if (it->second->getMinRtt() > maxRtt) {
- maxRtt = it->second->getMinRtt();
- producerPathLabels_[1] = it->first;
- }
- }
- }
-
- if (pathTable_.find(producerPathLabels_[0]) == pathTable_.end() ||
- pathTable_.find(producerPathLabels_[1]) == pathTable_.end())
- return; // this should not happen
-
- // as a queuing delay we keep the lowest one among the two paths
- // if one path is congested the forwarder should decide to do not
- // use it so it does not make sense to inform the application
- // that maybe we have a problem
- if (pathTable_[producerPathLabels_[0]]->getQueuingDealy() <
- pathTable_[producerPathLabels_[1]]->getQueuingDealy())
- queuingDelay_ = pathTable_[producerPathLabels_[0]]->getQueuingDealy();
- else
- queuingDelay_ = pathTable_[producerPathLabels_[1]]->getQueuingDealy();
-
- if (sentInterest_ != 0 && currentState_ == HICN_RTC_NORMAL_STATE) {
- uint32_t numberTheoricallyReceivedPackets_ =
- highestReceived_ - firstSequenceInRound_;
- double lossRate = 0;
- if (numberTheoricallyReceivedPackets_ != 0)
- lossRate = (double)((double)(packetLost_ - lossRecovered_) /
- (double)numberTheoricallyReceivedPackets_);
-
- if (lossRate < 0) lossRate = 0;
-
- if (initied) {
- lossRate_ = lossRate_ * HICN_ESTIMATED_LOSSES_ALPHA +
- (lossRate * (1 - HICN_ESTIMATED_LOSSES_ALPHA));
- } else {
- lossRate_ = lossRate;
- initied = true;
- }
- }
-
- if (avgPacketSize_ == 0) avgPacketSize_ = HICN_INIT_PACKET_SIZE;
-
- // for the BDP we use the max rtt, so that we calibrate the window on the
- // RTT of the slowest path. In this way we are sure that the window will
- // never be too small
- uint32_t BDP = (uint32_t)ceil(
- (estimatedBw_ *
- (double)((double)pathTable_[producerPathLabels_[1]]->getMinRtt() /
- (double)HICN_MILLI_IN_A_SEC) *
- HICN_BANDWIDTH_SLACK_FACTOR) /
- avgPacketSize_);
- uint32_t BW = (uint32_t)ceil(estimatedBw_);
- computeMaxWindow(BW, BDP);
-
- if (*stats_summary_) {
- // Send the stats to the app
- stats_->updateQueuingDelay(queuingDelay_);
- stats_->updateLossRatio(lossRate_);
- stats_->updateAverageRtt(pathTable_[producerPathLabels_[1]]->getMinRtt());
- (*stats_summary_)(*socket_->getInterface(), *stats_);
- }
- // bound also by interest lifitime* production rate
- if (!gotNack_) {
- roundsWithoutNacks_++;
- if (currentState_ == HICN_RTC_SYNC_STATE &&
- roundsWithoutNacks_ >= HICN_ROUNDS_IN_SYNC_BEFORE_SWITCH) {
- currentState_ = HICN_RTC_NORMAL_STATE;
- }
- } else {
- roundsWithoutNacks_ = 0;
- }
-
- updateCCState();
- updateWindow();
-
- if (queuingDelay_ > 25.0) {
- // this indicates that the client will go soon out of synch,
- // switch to synch mode
- if (currentState_ == HICN_RTC_NORMAL_STATE) {
- currentState_ = HICN_RTC_SYNC_STATE;
- }
- computeMaxWindow(BW, 0);
- increaseWindow();
- }
-
- // in any case we reset all the counters
-
- gotNack_ = false;
- gotFutureNack_ = 0;
- receivedBytes_ = 0;
- sentInterest_ = 0;
- receivedData_ = 0;
- packetLost_ = 0;
- lossRecovered_ = 0;
- rounds_++;
- firstSequenceInRound_ = highestReceived_;
-}
-
-void RTCTransportProtocol::updateCCState() {
- // TODO
-}
-
-void RTCTransportProtocol::computeMaxWindow(uint32_t productionRate,
- uint32_t BDPWin) {
- if (productionRate ==
- 0) // we have no info about the producer, keep the previous maxCWin
- return;
-
- uint32_t interestLifetime = default_values::interest_lifetime;
- socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
- interestLifetime);
- uint32_t maxWaintingInterest = (uint32_t)ceil(
- (productionRate / avgPacketSize_) *
- (double)((double)(interestLifetime *
- HICN_INTEREST_LIFETIME_REDUCTION_FACTOR) /
- (double)HICN_MILLI_IN_A_SEC));
-
- if (currentState_ == HICN_RTC_SYNC_STATE) {
- // in this case we do not limit the window with the BDP, beacuse most
- // likely it is wrong
- maxCWin_ = maxWaintingInterest;
- return;
- }
-
- // currentState = RTC_NORMAL_STATE
- if (BDPWin != 0) {
- maxCWin_ = (uint32_t)ceil((double)BDPWin +
- (((double)BDPWin * 30.0) / 100.0)); // BDP + 30%
- } else {
- maxCWin_ = min(maxWaintingInterest, maxCWin_);
- }
-
- if (maxCWin_ < HICN_MIN_CWIN) maxCWin_ = HICN_MIN_CWIN;
-}
-
-void RTCTransportProtocol::updateWindow() {
- if (currentState_ == HICN_RTC_SYNC_STATE) return;
-
- if (estimatedBw_ == 0) return;
-
- if (currentCWin_ < maxCWin_ * 0.9) {
- currentCWin_ =
- min(maxCWin_, (uint32_t)(currentCWin_ * HICN_WIN_INCREASE_FACTOR));
- } else if (currentCWin_ > maxCWin_) {
- currentCWin_ =
- max((uint32_t)(currentCWin_ * HICN_WIN_DECREASE_FACTOR), HICN_MIN_CWIN);
- }
-}
-
-void RTCTransportProtocol::decreaseWindow() {
- // this is used only in SYNC mode
- if (currentState_ == HICN_RTC_NORMAL_STATE) return;
-
- if (gotFutureNack_ == 1)
- currentCWin_ = min((currentCWin_ - 1),
- (uint32_t)ceil((double)maxCWin_ * 0.66)); // 2/3
- else
- currentCWin_--;
-
- currentCWin_ = max(currentCWin_, HICN_MIN_CWIN);
-}
-
-void RTCTransportProtocol::increaseWindow() {
- // this is used only in SYNC mode
- if (currentState_ == HICN_RTC_NORMAL_STATE) return;
-
- // we need to be carefull to do not increase the window to much
- if (currentCWin_ < ((double)maxCWin_ * 0.7)) {
- currentCWin_ = currentCWin_ + 1; // exponential
- } else {
- currentCWin_ = min(
- maxCWin_,
- (uint32_t)ceil(currentCWin_ + (1.0 / (double)currentCWin_))); // linear
- }
-}
-
-void RTCTransportProtocol::probeRtt() {
- probe_timer_->expires_from_now(std::chrono::milliseconds(1000));
- probe_timer_->async_wait([this](std::error_code ec) {
- if (ec) return;
- probeRtt();
- });
-
- // To avoid sending the first probe, because the transport is not running yet
- if (is_first_ && !is_running_) return;
-
- time_sent_probe_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- Name *interest_name = nullptr;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
- &interest_name);
- // get a random numbe in the probe seq range
- std::default_random_engine eng((std::random_device())());
- std::uniform_int_distribution<uint32_t> idis(HICN_MIN_PROBE_SEQ,
- HICN_MAX_PROBE_SEQ);
- probe_seq_number_ = idis(eng);
- interest_name->setSuffix(probe_seq_number_);
-
- // we considere the probe as a rtx so that we do not incresea inFlightInt
- received_probe_ = false;
- TRANSPORT_LOGD("Send content interest %u (probeRtt)",
- interest_name->getSuffix());
- sendInterest(interest_name, true);
-}
-
-void RTCTransportProtocol::sendInterest(Name *interest_name, bool rtx) {
- auto interest = getPacket();
- interest->setName(*interest_name);
-
- uint32_t interestLifetime = default_values::interest_lifetime;
- socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
- interestLifetime);
- interest->setLifetime(uint32_t(interestLifetime));
-
- if (*on_interest_output_) {
- (*on_interest_output_)(*socket_->getInterface(), *interest);
- }
-
- if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) {
- return;
- }
-
- portal_->sendInterest(std::move(interest));
-
- sentInterest_++;
-
- if (!rtx) {
- packets_in_window_[interest_name->getSuffix()] = 0;
- inflightInterestsCount_++;
- }
-}
-
-void RTCTransportProtocol::scheduleNextInterests() {
- if (!is_running_ && !is_first_) return;
-
- TRANSPORT_LOGD("----- [window %u - inflight_interests %u = %d] -----",
- currentCWin_, inflightInterestsCount_,
- currentCWin_ - inflightInterestsCount_);
-
- while (inflightInterestsCount_ < currentCWin_) {
- Name *interest_name = nullptr;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
- &interest_name);
-
- interest_name->setSuffix(actualSegment_);
-
- // if the producer socket is not stated (does not reply even with nacks)
- // we keep asking for something without marking anything as lost (see
- // timeout). In this way when the producer socket will start the
- // consumer socket will not miss any packet
- if (TRANSPORT_EXPECT_FALSE(!firstPckReceived_)) {
- uint32_t pkt = actualSegment_ & modMask_;
- inflightInterests_[pkt].state = sent_;
- inflightInterests_[pkt].sequence = actualSegment_;
- actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ;
- TRANSPORT_LOGD(
- "Send content interest %u (scheduleNextInterests no replies)",
- interest_name->getSuffix());
- sendInterest(interest_name, false);
- return;
- }
-
- // we send the packet only if it is not pending yet
- // notice that this is not true for rtx packets
- if (portal_->interestIsPending(*interest_name)) {
- actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ;
- continue;
- }
-
- uint32_t pkt = actualSegment_ & modMask_;
- // if we already reacevied the content we don't ask it again
- if (inflightInterests_[pkt].state == received_ &&
- inflightInterests_[pkt].sequence == actualSegment_) {
- actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ;
- continue;
- }
-
- // same if the packet is lost
- if (inflightInterests_[pkt].state == lost_ &&
- inflightInterests_[pkt].sequence == actualSegment_) {
- actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ;
- continue;
- }
-
- inflightInterests_[pkt].transmissionTime =
- std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- // here the packet can be in any state except for lost or recevied
- inflightInterests_[pkt].state = sent_;
- inflightInterests_[pkt].sequence = actualSegment_;
- actualSegment_ = (actualSegment_ + 1) % HICN_MIN_PROBE_SEQ;
-
- TRANSPORT_LOGD("Send content interest %u (scheduleNextInterests)",
- interest_name->getSuffix());
- sendInterest(interest_name, false);
- }
-
- TRANSPORT_LOGD("----- end of scheduleNextInterest -----");
-}
-
-bool RTCTransportProtocol::verifyKeyPackets() {
- // Not yet implemented
- return false;
-}
-
-void RTCTransportProtocol::sentinelTimer() {
- uint32_t wait = 50;
-
- if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end() &&
- pathTable_.find(producerPathLabels_[1]) != pathTable_.end()) {
- // we have all the info to set the timers
- wait = round(pathTable_[producerPathLabels_[0]]->getInterArrivalGap());
- if (wait == 0) wait = 1;
- }
-
- sentinel_timer_->expires_from_now(std::chrono::milliseconds(wait));
- sentinel_timer_->async_wait([this](std::error_code ec) {
- if (ec) return;
-
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- if (pathTable_.find(producerPathLabels_[0]) == pathTable_.end() ||
- pathTable_.find(producerPathLabels_[1]) == pathTable_.end()) {
- // we have no info, so we send again
-
- for (auto it = packets_in_window_.begin(); it != packets_in_window_.end();
- it++) {
- uint32_t pkt = it->first & modMask_;
- if (inflightInterests_[pkt].sequence == it->first) {
- inflightInterests_[pkt].transmissionTime = now;
- Name *interest_name = nullptr;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
- &interest_name);
- interest_name->setSuffix(it->first);
- it->second++;
- sendInterest(interest_name, true);
- }
- }
- } else {
- uint64_t max_waiting_time = // wait at least 50ms
- (pathTable_[producerPathLabels_[1]]->getMinRtt() -
- pathTable_[producerPathLabels_[0]]->getMinRtt()) +
- (ceil(pathTable_[producerPathLabels_[0]]->getInterArrivalGap()) * 50);
-
- if ((currentState_ == HICN_RTC_NORMAL_STATE) &&
- (inflightInterestsCount_ >= currentCWin_) &&
- ((now - lastEvent_) > max_waiting_time) && (lossRate_ >= 0.05)) {
- uint64_t RTT = pathTable_[producerPathLabels_[1]]->getMinRtt();
-
- for (auto it = packets_in_window_.begin();
- it != packets_in_window_.end(); it++) {
- uint32_t pkt = it->first & modMask_;
- if (inflightInterests_[pkt].sequence == it->first &&
- ((now - inflightInterests_[pkt].transmissionTime) >= RTT)) {
- inflightInterests_[pkt].transmissionTime = now;
- Name *interest_name = nullptr;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
- &interest_name);
- interest_name->setSuffix(it->first);
- it->second++;
- sendInterest(interest_name, true);
- }
- }
- }
- }
-
- sentinelTimer();
- });
-}
-void RTCTransportProtocol::addRetransmissions(uint32_t val) {
- // add only val in the rtx list
- addRetransmissions(val, val + 1);
-}
-
-void RTCTransportProtocol::addRetransmissions(uint32_t start, uint32_t stop) {
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- bool new_rtx = false;
- for (uint32_t i = start; i < stop; i++) {
- auto it = interestRetransmissions_.find(i);
- if (it == interestRetransmissions_.end()) {
- uint32_t pkt = i & modMask_;
- if (lastSegNacked_ <= i && inflightInterests_[pkt].state != received_) {
- // it must be larger than the last past nack received
- packetLost_++;
- interestRetransmissions_[i] = 0;
- uint32_t pkt = i & modMask_;
- // we reset the transmission time setting to now, so that rtx will
- // happne in one RTT on waint one inter arrival gap
- inflightInterests_[pkt].transmissionTime = now;
- new_rtx = true;
- }
- } // if the retransmission is already there the rtx timer will
- // take care of it
- }
-
- // in case a new rtx is added to the map we need to run checkRtx()
- if (new_rtx) {
- if (rtx_timer_used_) {
- // if a timer is pending we need to delete it
- rtx_timer_->cancel();
- rtx_timer_used_ = false;
- }
- checkRtx();
- }
-}
-
-uint64_t RTCTransportProtocol::retransmit() {
- auto it = interestRetransmissions_.begin();
-
- // cut len to max HICN_MAX_RTX_SIZE
- // since we use a map, the smaller (and so the older) sequence number are at
- // the beginnin of the map
- while (interestRetransmissions_.size() > HICN_MAX_RTX_SIZE) {
- it = interestRetransmissions_.erase(it);
- }
-
- it = interestRetransmissions_.begin();
- uint64_t smallest_timeout = ULONG_MAX;
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- while (it != interestRetransmissions_.end()) {
- uint32_t pkt = it->first & modMask_;
-
- if (inflightInterests_[pkt].sequence != it->first) {
- // this packet is not anymore in the inflight buffer, erase it
- it = interestRetransmissions_.erase(it);
- continue;
- }
-
- // we retransmitted the packet too many times
- if (it->second >= HICN_MAX_RTX) {
- it = interestRetransmissions_.erase(it);
- continue;
- }
-
- // this packet is too old
- if ((lastReceived_ > it->first) &&
- (lastReceived_ - it->first) > HICN_MAX_RTX_MAX_AGE) {
- it = interestRetransmissions_.erase(it);
- continue;
- }
-
- uint64_t rtx_time = now;
-
- if (it->second == 0) {
- // first rtx
- if (producerPathLabels_[0] != producerPathLabels_[1]) {
- // multipath
- if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end() &&
- pathTable_.find(producerPathLabels_[1]) != pathTable_.end() &&
- (pathTable_[producerPathLabels_[0]]->getInterArrivalGap() <
- HICN_MIN_INTER_ARRIVAL_GAP)) {
- rtx_time = lastReceivedTime_ +
- (pathTable_[producerPathLabels_[1]]->getMinRtt() -
- pathTable_[producerPathLabels_[0]]->getMinRtt()) +
- pathTable_[producerPathLabels_[0]]->getInterArrivalGap();
- } // else low rate producer, send it immediatly
- } else {
- // single path
- if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end() &&
- (pathTable_[producerPathLabels_[0]]->getInterArrivalGap() <
- HICN_MIN_INTER_ARRIVAL_GAP)) {
- rtx_time = lastReceivedTime_ +
- pathTable_[producerPathLabels_[0]]->getInterArrivalGap();
- } // else low rate producer send immediatly
- }
- } else {
- // second or plus rtx, wait for the min rtt
- if (pathTable_.find(producerPathLabels_[0]) != pathTable_.end()) {
- uint64_t sent_time = inflightInterests_[pkt].transmissionTime;
- rtx_time = sent_time + pathTable_[producerPathLabels_[0]]->getMinRtt();
- } // if we don't have info we send it immediatly
- }
-
- if (now >= rtx_time) {
- inflightInterests_[pkt].transmissionTime = now;
- it->second++;
-
- Name *interest_name = nullptr;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
- &interest_name);
- interest_name->setSuffix(it->first);
- TRANSPORT_LOGD("Send content interest %u (retransmit)",
- interest_name->getSuffix());
- sendInterest(interest_name, true);
- } else if (rtx_time < smallest_timeout) {
- smallest_timeout = rtx_time;
- }
-
- ++it;
- }
- return smallest_timeout;
-}
-
-void RTCTransportProtocol::checkRtx() {
- if (interestRetransmissions_.empty()) {
- rtx_timer_used_ = false;
- return;
- }
-
- uint64_t next_timeout = retransmit();
- uint64_t wait = 1;
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- if (next_timeout != ULONG_MAX && now < next_timeout) {
- wait = next_timeout - now;
- }
- rtx_timer_used_ = true;
- rtx_timer_->expires_from_now(std::chrono::milliseconds(wait));
- rtx_timer_->async_wait([this](std::error_code ec) {
- if (ec) return;
- rtx_timer_used_ = false;
- checkRtx();
- });
-}
-
-void RTCTransportProtocol::onTimeout(Interest::Ptr &&interest) {
- uint32_t segmentNumber = interest->getName().getSuffix();
-
- if (segmentNumber >= HICN_MIN_PROBE_SEQ) {
- // this is a timeout on a probe, do nothing
- return;
- }
-
- uint32_t pkt = segmentNumber & modMask_;
-
- if (TRANSPORT_EXPECT_FALSE(!firstPckReceived_)) {
- // we do nothing, and we keep asking the same stuff over
- // and over until we get at least a packet
- inflightInterestsCount_--;
- lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- packets_in_window_.erase(segmentNumber);
- scheduleNextInterests();
- return;
- }
-
- if (inflightInterests_[pkt].state == sent_) {
- lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- packets_in_window_.erase(segmentNumber);
- inflightInterestsCount_--;
- }
-
- // check how many times we sent this packet
- auto it = interestRetransmissions_.find(segmentNumber);
- if (it != interestRetransmissions_.end() && it->second >= HICN_MAX_RTX) {
- inflightInterests_[pkt].state = lost_;
- }
-
- if (inflightInterests_[pkt].state == sent_) {
- inflightInterests_[pkt].state = timeout1_;
- } else if (inflightInterests_[pkt].state == timeout1_) {
- inflightInterests_[pkt].state = timeout2_;
- } else if (inflightInterests_[pkt].state == timeout2_) {
- inflightInterests_[pkt].state = lost_;
- }
-
- if (inflightInterests_[pkt].state == lost_) {
- interestRetransmissions_.erase(segmentNumber);
- } else {
- addRetransmissions(segmentNumber);
- }
-
- scheduleNextInterests();
-}
-
-bool RTCTransportProtocol::onNack(const ContentObject &content_object,
- bool rtx) {
- uint32_t *payload = (uint32_t *)content_object.getPayload()->data();
- uint32_t productionSeg = *payload;
- uint32_t productionRate = *(++payload);
- uint32_t nackSegment = content_object.getName().getSuffix();
-
- bool old_nack = false;
-
- // if we did not received anything between lastReceived_ + 1 and productionSeg
- // most likelly some packets got lost
- if (lastReceived_ != 0) {
- addRetransmissions(lastReceived_ + 1, productionSeg);
- }
-
- if (!rtx) {
- gotNack_ = true;
- // we synch the estimated production rate with the actual one
- estimatedBw_ = (double)productionRate;
- }
-
- if (productionSeg > nackSegment) {
- // we are asking for stuff produced in the past
- actualSegment_ = max(productionSeg, actualSegment_) % HICN_MIN_PROBE_SEQ;
-
- if (!rtx) {
- if (currentState_ == HICN_RTC_NORMAL_STATE) {
- currentState_ = HICN_RTC_SYNC_STATE;
- }
-
- computeMaxWindow(productionRate, 0);
- increaseWindow();
- }
-
- lastSegNacked_ = productionSeg;
- old_nack = true;
-
- } else if (productionSeg < nackSegment) {
- actualSegment_ = productionSeg % HICN_MIN_PROBE_SEQ;
-
- if (!rtx) {
- // we are asking stuff in the future
- gotFutureNack_++;
- computeMaxWindow(productionRate, 0);
- decreaseWindow();
-
- if (currentState_ == HICN_RTC_SYNC_STATE) {
- currentState_ = HICN_RTC_NORMAL_STATE;
- }
- }
- } else {
- // we are asking the right thing, but the producer is slow
- // keep doing the same until the packet is produced
- actualSegment_ = productionSeg % HICN_MIN_PROBE_SEQ;
- }
-
- return old_nack;
-}
-
-void RTCTransportProtocol::onContentObject(
- Interest::Ptr &&interest, ContentObject::Ptr &&content_object) {
- // as soon as we get a packet firstPckReceived_ will never be false
- firstPckReceived_ = true;
-
- auto payload = content_object->getPayload();
- uint32_t payload_size = (uint32_t)payload->length();
- uint32_t segmentNumber = content_object->getName().getSuffix();
- uint32_t pkt = segmentNumber & modMask_;
-
- if (*on_content_object_input_) {
- (*on_content_object_input_)(*socket_->getInterface(), *content_object);
- }
-
- if (segmentNumber >= HICN_MIN_PROBE_SEQ) {
- TRANSPORT_LOGD("Received probe %u", segmentNumber);
- if (segmentNumber == probe_seq_number_ && !received_probe_) {
- received_probe_ = true;
-
- uint32_t pathLabel = content_object->getPathLabel();
- if (pathTable_.find(pathLabel) == pathTable_.end()) {
- std::shared_ptr<RTCDataPath> newPath = std::make_shared<RTCDataPath>();
- pathTable_[pathLabel] = newPath;
- }
-
- // this is the expected probe, update the RTT and drop the packet
- uint64_t RTT = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count() -
- time_sent_probe_;
-
- pathTable_[pathLabel]->insertRttSample(RTT);
- pathTable_[pathLabel]->receivedNack();
- }
- return;
- }
-
- // check if the packet is a rtx
- bool is_rtx = false;
- if (interestRetransmissions_.find(segmentNumber) !=
- interestRetransmissions_.end()) {
- is_rtx = true;
- } else {
- auto it_win = packets_in_window_.find(segmentNumber);
- if (it_win != packets_in_window_.end() && it_win->second != 0)
- is_rtx = true;
- }
-
- if (payload_size == HICN_NACK_HEADER_SIZE) {
- TRANSPORT_LOGD("Received nack %u", segmentNumber);
-
- if (inflightInterests_[pkt].state == sent_) {
- lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- packets_in_window_.erase(segmentNumber);
- inflightInterestsCount_--;
- }
-
- bool old_nack = false;
-
- if (!is_rtx) {
- // this is not a retransmitted packet
- old_nack = onNack(*content_object, false);
- updateDelayStats(*content_object);
- } else {
- old_nack = onNack(*content_object, true);
- }
-
- // the nacked_ state is used only to avoid to decrease
- // inflightInterestsCount_ multiple times. In fact, every time that we
- // receive an event related to an interest (timeout, nacked, content) we
- // cange the state. In this way we are sure that we do not decrease twice
- // the counter
- if (old_nack) {
- inflightInterests_[pkt].state = lost_;
- interestRetransmissions_.erase(segmentNumber);
- } else {
- inflightInterests_[pkt].state = nacked_;
- }
-
- } else {
- TRANSPORT_LOGD("Received content %u", segmentNumber);
-
- avgPacketSize_ = (HICN_ESTIMATED_PACKET_SIZE * avgPacketSize_) +
- ((1 - HICN_ESTIMATED_PACKET_SIZE) * payload->length());
-
- receivedBytes_ += (uint32_t)(content_object->headerSize() +
- content_object->payloadSize());
-
- if (inflightInterests_[pkt].state == sent_) {
- lastEvent_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- packets_in_window_.erase(segmentNumber);
- inflightInterestsCount_--; // packet sent without timeouts
- }
-
- if (inflightInterests_[pkt].state == sent_ && !is_rtx) {
- // delay stats are computed only for non retransmitted data
- updateDelayStats(*content_object);
- }
-
- addRetransmissions(lastReceived_ + 1, segmentNumber);
- if (segmentNumber > highestReceived_) {
- highestReceived_ = segmentNumber;
- }
- if (segmentNumber > lastReceived_) {
- lastReceived_ = segmentNumber;
- lastReceivedTime_ =
- std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- }
- receivedData_++;
- inflightInterests_[pkt].state = received_;
-
- auto it = interestRetransmissions_.find(segmentNumber);
- if (it != interestRetransmissions_.end()) lossRecovered_++;
-
- interestRetransmissions_.erase(segmentNumber);
-
- reassemble(std::move(content_object));
- increaseWindow();
- }
-
- scheduleNextInterests();
-}
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc.h b/libtransport/src/protocols/rtc.h
deleted file mode 100644
index 9f1bcc25b..000000000
--- a/libtransport/src/protocols/rtc.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiTC_SYNC_STATE
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <protocols/datagram_reassembly.h>
-#include <protocols/protocol.h>
-#include <protocols/rtc_data_path.h>
-
-#include <map>
-#include <queue>
-#include <unordered_map>
-
-// algorithm state
-#define HICN_RTC_SYNC_STATE 0
-#define HICN_RTC_NORMAL_STATE 1
-#define HICN_ROUNDS_IN_SYNC_BEFORE_SWITCH 3
-
-// packet constants
-#define HICN_INIT_PACKET_SIZE 1300 // bytes
-#define HICN_PACKET_HEADER_SIZE 60 // bytes ipv6+tcp
-#define HICN_NACK_HEADER_SIZE 8 // bytes
-#define HICN_TIMESTAMP_SIZE 8 // bytes
-#define HICN_RTC_INTEREST_LIFETIME 1000 // ms
-
-// rtt measurement
-// normal interests for data goes from 0 to
-// HICN_MIN_PROBE_SEQ, the rest is reserverd for
-// probes
-#define HICN_MIN_PROBE_SEQ 0xefffffff
-#define HICN_MAX_PROBE_SEQ 0xffffffff
-
-// controller constant
-#define HICN_ROUND_LEN \
- 200 // ms interval of time on which
- // we take decisions / measurements
-#define HICN_MAX_RTX 10
-#define HICN_MAX_RTX_SIZE 1024
-#define HICN_MAX_RTX_MAX_AGE 10000
-#define HICN_MIN_RTT_WIN 30 // rounds
-#define HICN_MIN_INTER_ARRIVAL_GAP 100 // ms
-
-// cwin
-#define HICN_INITIAL_CWIN 1 // packets
-#define HICN_INITIAL_CWIN_MAX 100000 // packets
-#define HICN_MIN_CWIN 10 // packets
-#define HICN_WIN_INCREASE_FACTOR 1.5
-#define HICN_WIN_DECREASE_FACTOR 0.9
-
-// statistics constants
-#define HICN_BANDWIDTH_SLACK_FACTOR 1.8
-#define HICN_ESTIMATED_BW_ALPHA 0.7
-#define HICN_ESTIMATED_PACKET_SIZE 0.7
-#define HICN_ESTIMATED_LOSSES_ALPHA 0.8
-#define HICN_INTEREST_LIFETIME_REDUCTION_FACTOR 0.8
-
-// other constants
-#define HICN_NANO_IN_A_SEC 1000000000
-#define HICN_MICRO_IN_A_SEC 1000000
-#define HICN_MILLI_IN_A_SEC 1000
-
-namespace transport {
-
-namespace protocol {
-
-enum packetState { sent_, nacked_, received_, timeout1_, timeout2_, lost_ };
-
-typedef enum packetState packetState_t;
-
-struct sentInterest {
- uint64_t transmissionTime;
- uint32_t sequence; // sequence number of the interest sent
- // to handle seq % buffer_size
- packetState_t state; // see packet state
-};
-
-class RTCTransportProtocol : public TransportProtocol,
- public DatagramReassembly {
- public:
- RTCTransportProtocol(implementation::ConsumerSocket *icnet_socket);
-
- ~RTCTransportProtocol();
-
- using TransportProtocol::start;
-
- using TransportProtocol::stop;
-
- void resume() override;
-
- bool verifyKeyPackets() override;
-
- private:
- // algo functions
- void initParams();
- void reset() override;
-
- // CC functions
- void updateDelayStats(const ContentObject &content_object);
- void updateStats(uint32_t round_duration);
- void updateCCState();
- void computeMaxWindow(uint32_t productionRate, uint32_t BDPWin);
- void updateWindow();
- void decreaseWindow();
- void increaseWindow();
- void resetPreviousWindow();
-
- // packet functions
- void sendInterest(Name *interest_name, bool rtx);
- void scheduleNextInterests() override;
- void sentinelTimer();
- void addRetransmissions(uint32_t val);
- void addRetransmissions(uint32_t start, uint32_t stop);
- uint64_t retransmit();
- void checkRtx();
- void probeRtt();
- void newRound();
- void onTimeout(Interest::Ptr &&interest) override;
- bool onNack(const ContentObject &content_object, bool rtx);
- void onContentObject(Interest::Ptr &&interest,
- ContentObject::Ptr &&content_object) override;
- void onPacketDropped(Interest::Ptr &&interest,
- ContentObject::Ptr &&content_object) override {}
- void onReassemblyFailed(std::uint32_t missing_segment) override {}
-
- TRANSPORT_ALWAYS_INLINE virtual void reassemble(
- ContentObject::Ptr &&content_object) override {
- auto read_buffer = content_object->getPayload();
- read_buffer->trimStart(HICN_TIMESTAMP_SIZE);
- Reassembly::read_buffer_ = std::move(read_buffer);
- Reassembly::notifyApplication();
- }
-
- // controller var
- std::unique_ptr<asio::steady_timer> round_timer_;
- unsigned currentState_;
-
- // cwin var
- uint32_t currentCWin_;
- uint32_t maxCWin_;
-
- // names/packets var
- uint32_t actualSegment_;
- uint32_t inflightInterestsCount_;
- // map seq to rtx
- std::map<uint32_t, uint8_t> interestRetransmissions_;
- bool rtx_timer_used_;
- std::unique_ptr<asio::steady_timer> rtx_timer_;
- std::vector<sentInterest> inflightInterests_;
- uint32_t lastSegNacked_; // indicates the segment id in the last received
- // past Nack. we do not ask for retransmissions
- // for samething that is older than this value.
- uint32_t lastReceived_; // segment of the last content object received
- // indicates the base of the window on the client
- uint64_t lastReceivedTime_; // time at which we recevied the
- // lastReceived_ packet
-
- // sentinel
- // if all packets in the window get lost we need something that
- // wakes up our consumer socket. Interest timeouts set to 1 sec
- // expire too late. This timers expire much sooner and if it
- // detects that all the interest in the window may be lost
- // it sends all of them again
- std::unique_ptr<asio::steady_timer> sentinel_timer_;
- uint64_t lastEvent_; // time at which we removed a pending
- // interest from the window
- std::unordered_map<uint32_t, uint8_t> packets_in_window_;
-
- // rtt probes
- // the RTC transport tends to overestimate the RTT
- // du to the production time on the server side
- // once per second we send an interest for wich we know
- // we will get a nack. This nack will keep our estimation
- // close to the reality
- std::unique_ptr<asio::steady_timer> probe_timer_;
- uint64_t time_sent_probe_;
- uint32_t probe_seq_number_;
- bool received_probe_;
-
- uint32_t modMask_;
-
- // stats
- bool firstPckReceived_;
- uint32_t receivedBytes_;
- uint32_t sentInterest_;
- uint32_t receivedData_;
- int32_t packetLost_;
- int32_t lossRecovered_;
- uint32_t firstSequenceInRound_;
- uint32_t highestReceived_;
- double avgPacketSize_;
- bool gotNack_;
- uint32_t gotFutureNack_;
- uint32_t rounds_;
- uint32_t roundsWithoutNacks_;
-
- // we keep track of up two paths (if only one path is in use
- // the two values in the vector will be the same)
- // position 0 stores the path with minRTT
- // position 1 stores the path with maxRTT
- uint32_t producerPathLabels_[2];
-
- std::unordered_map<uint32_t, std::shared_ptr<RTCDataPath>> pathTable_;
- uint32_t roundCounter_;
-
- // CC var
- double estimatedBw_;
- double lossRate_;
- double queuingDelay_;
- unsigned protocolState_;
-
- bool initied;
-};
-
-} // namespace protocol
-
-} // namespace transport
diff --git a/libtransport/src/protocols/rtc/CMakeLists.txt b/libtransport/src/protocols/rtc/CMakeLists.txt
new file mode 100644
index 000000000..be8e0189c
--- /dev/null
+++ b/libtransport/src/protocols/rtc/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright (c) 2021 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_consts.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_forwarding_strategy.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_indexer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_packet.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_congestion_detection.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_iat.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_recovery_strategy.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_delay.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_fec_only.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_low_rate.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_recovery_off.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_rtx_only.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_verifier.h
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_forwarding_strategy.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_congestion_detection.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_iat.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_recovery_strategy.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_delay.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_fec_only.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_low_rate.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_recovery_off.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_rs_rtx_only.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_verifier.cc
+)
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
diff --git a/libtransport/src/protocols/rtc/probe_handler.cc b/libtransport/src/protocols/rtc/probe_handler.cc
new file mode 100644
index 000000000..60eceeb19
--- /dev/null
+++ b/libtransport/src/protocols/rtc/probe_handler.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <protocols/rtc/probe_handler.h>
+#include <protocols/rtc/rtc_consts.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+ProbeHandler::ProbeHandler(SendProbeCallback &&send_callback,
+ asio::io_service &io_service)
+ : probe_interval_(0),
+ max_probes_(0),
+ sent_probes_(0),
+ recv_probes_(0),
+ probe_timer_(std::make_unique<asio::steady_timer>(io_service)),
+ rand_eng_((std::random_device())()),
+ distr_(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ),
+ send_probe_callback_(std::move(send_callback)) {}
+
+ProbeHandler::~ProbeHandler() {}
+
+uint64_t ProbeHandler::getRtt(uint32_t seq, bool is_valid) {
+ auto it = pending_probes_.find(seq);
+
+ if (it == pending_probes_.end()) return 0;
+
+ if (!is_valid) {
+ // delete the probe anyway
+ pending_probes_.erase(it);
+ valid_batch_ = false;
+ return 0;
+ }
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ uint64_t rtt = now - it->second;
+ if (rtt < 1) rtt = 1;
+
+ pending_probes_.erase(it);
+ recv_probes_++;
+
+ return rtt;
+}
+
+double ProbeHandler::getProbeLossRate() {
+ if (!valid_batch_) return 1.0;
+ return 1.0 - ((double)recv_probes_ / (double)sent_probes_);
+}
+
+void ProbeHandler::setSuffixRange(uint32_t min, uint32_t max) {
+ DCHECK(min <= max && min >= MIN_PROBE_SEQ);
+ distr_ = std::uniform_int_distribution<uint32_t>(min, max);
+}
+
+void ProbeHandler::setProbes(uint32_t probe_interval, uint32_t max_probes) {
+ stopProbes();
+ probe_interval_ = probe_interval;
+ max_probes_ = max_probes;
+}
+
+void ProbeHandler::stopProbes() {
+ probe_interval_ = 0;
+ max_probes_ = 0;
+ sent_probes_ = 0;
+ recv_probes_ = 0;
+ valid_batch_ = true;
+ probe_timer_->cancel();
+}
+
+void ProbeHandler::sendProbes() {
+ if (probe_interval_ == 0) return;
+
+ std::weak_ptr<ProbeHandler> self(shared_from_this());
+ probe_timer_->expires_from_now(std::chrono::microseconds(probe_interval_));
+ probe_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+ auto s = self.lock();
+ if (s) {
+ s->generateProbe();
+ }
+ });
+}
+
+void ProbeHandler::generateProbe() {
+ if (probe_interval_ == 0) return;
+ if (max_probes_ != 0 && sent_probes_ >= max_probes_) return;
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ uint32_t seq = distr_(rand_eng_);
+ pending_probes_.insert(std::pair<uint32_t, uint64_t>(seq, now));
+ send_probe_callback_(seq);
+ sent_probes_++;
+
+ // clean up
+ // a probe may get lost. if the pending_probes_ size becomes bigger than
+ // MAX_PENDING_PROBES remove all the probes older than a seconds
+ if (pending_probes_.size() > MAX_PENDING_PROBES) {
+ for (auto it = pending_probes_.begin(); it != pending_probes_.end();) {
+ if ((now - it->second) > 1000)
+ it = pending_probes_.erase(it);
+ else
+ it++;
+ }
+ }
+
+ sendProbes();
+}
+
+ProbeType ProbeHandler::getProbeType(uint32_t seq) {
+ if (MIN_INIT_PROBE_SEQ <= seq && seq <= MAX_INIT_PROBE_SEQ) {
+ return ProbeType::INIT;
+ }
+ if (MIN_RTT_PROBE_SEQ <= seq && seq <= MAX_RTT_PROBE_SEQ) {
+ return ProbeType::RTT;
+ }
+ return ProbeType::NOT_PROBE;
+}
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/probe_handler.h b/libtransport/src/protocols/rtc/probe_handler.h
new file mode 100644
index 000000000..d989194d4
--- /dev/null
+++ b/libtransport/src/protocols/rtc/probe_handler.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#include <hicn/transport/config.h>
+#include <hicn/transport/core/asio_wrapper.h>
+
+#include <functional>
+#include <random>
+#include <unordered_map>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+enum class ProbeType {
+ NOT_PROBE,
+ INIT,
+ RTT,
+};
+
+class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> {
+ public:
+ using SendProbeCallback = std::function<void(uint32_t)>;
+
+ public:
+ ProbeHandler(SendProbeCallback &&send_callback, asio::io_service &io_service);
+
+ ~ProbeHandler();
+
+ // If the function returns 0 the probe is not valid.
+ uint64_t getRtt(uint32_t seq, bool is_valid);
+
+ // this function may return a residual loss rate higher than the real one if
+ // we don't wait enough time for the probes to come back
+ double getProbeLossRate();
+
+ // Set the probe suffix range [min, max]
+ void setSuffixRange(uint32_t min, uint32_t max);
+
+ // Reset the probes parameters and stops the current probing.
+ // probe_interval = 0 means that no event will be scheduled.
+ // max_probe = 0 means no limit to the number of probe to send.
+ void setProbes(uint32_t probe_interval, uint32_t max_probes);
+
+ void stopProbes();
+
+ void sendProbes();
+
+ static ProbeType getProbeType(uint32_t seq);
+
+ private:
+ void generateProbe();
+
+ uint32_t probe_interval_; // us
+ uint32_t max_probes_; // packets
+ uint32_t sent_probes_; // packets
+ uint32_t recv_probes_; // packets
+
+ bool valid_batch_; // if at least one probe in a batch is considered not
+ // valid (e.g. prod rate == ~0) the full batch is invalid.
+ // the bool is set to true when sendProbe is called
+
+ std::unique_ptr<asio::steady_timer> probe_timer_;
+
+ // Map from packet suffixes to timestamp
+ std::unordered_map<uint32_t, uint64_t> pending_probes_;
+
+ // Random generator
+ std::default_random_engine rand_eng_;
+ std::uniform_int_distribution<uint32_t> distr_;
+
+ SendProbeCallback send_probe_callback_;
+};
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc.cc b/libtransport/src/protocols/rtc/rtc.cc
new file mode 100644
index 000000000..9a56269f3
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc.cc
@@ -0,0 +1,1101 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/core/global_object_pool.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <implementation/socket_consumer.h>
+#include <math.h>
+#include <protocols/errors.h>
+#include <protocols/incremental_indexer_bytestream.h>
+#include <protocols/rtc/rtc.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_indexer.h>
+#include <protocols/rtc/rtc_rc_congestion_detection.h>
+
+#include <algorithm>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+using namespace interface;
+
+RTCTransportProtocol::RTCTransportProtocol(
+ implementation::ConsumerSocket *icn_socket)
+ : TransportProtocol(icn_socket, new RtcIndexer<>(icn_socket, this),
+ new RtcReassembly(icn_socket, this)),
+ max_aggregated_interest_(1),
+ number_(0) {
+ icn_socket->getSocketOption(PORTAL, portal_);
+ round_timer_ =
+ std::make_unique<asio::steady_timer>(portal_->getThread().getIoService());
+ scheduler_timer_ =
+ std::make_unique<asio::steady_timer>(portal_->getThread().getIoService());
+ pacing_timer_ =
+ std::make_unique<asio::steady_timer>(portal_->getThread().getIoService());
+}
+
+RTCTransportProtocol::~RTCTransportProtocol() {}
+
+void RTCTransportProtocol::resume() {
+ newRound();
+ TransportProtocol::resume();
+}
+
+std::size_t RTCTransportProtocol::transportHeaderLength(bool isFEC) {
+ return DATA_HEADER_SIZE +
+ (fec_decoder_ != nullptr ? fec_decoder_->getFecHeaderSize(isFEC) : 0);
+}
+
+// private
+void RTCTransportProtocol::initParams() {
+ TransportProtocol::reset();
+ std::weak_ptr<RTCTransportProtocol> self = shared_from_this();
+
+ fwd_strategy_.setCallback([self](notification::Strategy strategy) {
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ if (*ptr->on_fwd_strategy_) (*ptr->on_fwd_strategy_)(strategy);
+ }
+ });
+
+ std::shared_ptr<auth::Verifier> verifier;
+ socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier);
+
+ uint32_t factor_relevant;
+ socket_->getSocketOption(GeneralTransportOptions::MANIFEST_FACTOR_RELEVANT,
+ factor_relevant);
+
+ uint32_t factor_alert;
+ socket_->getSocketOption(GeneralTransportOptions::MANIFEST_FACTOR_ALERT,
+ factor_alert);
+
+ rc_ = std::make_shared<RTCRateControlCongestionDetection>();
+ ldr_ = std::make_shared<RTCLossDetectionAndRecovery>(
+ indexer_verifier_.get(), portal_->getThread().getIoService(),
+ interface::RtcTransportRecoveryStrategies::RTX_ONLY,
+ [self](uint32_t seq) {
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ ptr->sendRtxInterest(seq);
+ }
+ },
+ [self](notification::Strategy strategy) {
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ if (*ptr->on_rec_strategy_) (*ptr->on_rec_strategy_)(strategy);
+ }
+ });
+
+ verifier_ =
+ std::make_shared<RTCVerifier>(verifier, factor_relevant, factor_alert);
+
+ state_ = std::make_shared<RTCState>(
+ indexer_verifier_.get(),
+ [self](uint32_t seq) {
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ ptr->sendProbeInterest(seq);
+ }
+ },
+ [self]() {
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ ptr->discoveredRtt();
+ }
+ },
+ portal_->getThread().getIoService());
+
+ rc_->setState(state_);
+ rc_->turnOnRateControl();
+ ldr_->setState(state_.get());
+ ldr_->setRateControl(rc_.get());
+ verifier_->setState(state_);
+
+ // protocol state
+ start_send_interest_ = false;
+ current_state_ = SyncState::catch_up;
+
+ // Cancel timer
+ number_++;
+ round_timer_->cancel();
+
+ scheduler_timer_->cancel();
+ scheduler_timer_on_ = false;
+ last_interest_sent_time_ = 0;
+ last_interest_sent_seq_ = 0;
+
+ // Aggregated interests setup
+ bool aggregated_interests_on;
+ socket_->getSocketOption(RtcTransportOptions::AGGREGATED_INTERESTS,
+ aggregated_interests_on);
+ if (aggregated_interests_on) {
+ if (const char *max_aggr = std::getenv("MAX_AGGREGATED_INTERESTS"))
+ max_aggregated_interest_ = (uint32_t)std::stoul(std::string(max_aggr));
+ else
+ max_aggregated_interest_ = MAX_INTERESTS_IN_BATCH;
+
+ max_aggregated_interest_ = std::min<uint32_t>(max_aggregated_interest_,
+ 1 + MAX_SUFFIXES_IN_MANIFEST);
+ }
+ LOG(INFO) << "Max Aggregated: " << max_aggregated_interest_;
+
+ max_sent_int_ =
+ std::ceil((double)MAX_PACING_BATCH / (double)max_aggregated_interest_);
+
+ pacing_timer_->cancel();
+ pacing_timer_on_ = false;
+
+ // delete all timeouts and future nacks
+ timeouts_or_nacks_.clear();
+
+ // cwin vars
+ current_sync_win_ = INITIAL_WIN;
+ max_sync_win_ = INITIAL_WIN_MAX;
+
+ socket_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
+ RTC_INTEREST_LIFETIME);
+
+ // init state params
+ state_->initParams();
+}
+
+// private
+void RTCTransportProtocol::reset() {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "reset called";
+ initParams();
+ newRound();
+}
+
+void RTCTransportProtocol::inactiveProducer() {
+ // when the producer is inactive we reset the consumer state
+ // cwin vars
+ current_sync_win_ = INITIAL_WIN;
+ max_sync_win_ = INITIAL_WIN_MAX;
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Current window: " << current_sync_win_
+ << ", max_sync_win_: " << max_sync_win_;
+
+ // names/packets var
+ indexer_verifier_->reset();
+ indexer_verifier_->enableFec(fec_type_);
+ indexer_verifier_->setNFec(0);
+
+ ldr_->clear();
+}
+
+void RTCTransportProtocol::newRound() {
+ round_timer_->expires_from_now(std::chrono::milliseconds(ROUND_LEN));
+
+ std::weak_ptr<RTCTransportProtocol> self = shared_from_this();
+ round_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) {
+ return;
+ }
+
+ auto ptr = self.lock();
+
+ if (!ptr || !ptr->isRunning()) {
+ return;
+ }
+
+ auto &state = ptr->state_;
+
+ // saving counters that will be reset on new round
+ uint32_t sent_retx = state->getSentRtxInRound();
+ uint32_t received_bytes =
+ (state->getReceivedBytesInRound() + // data packets received
+ state->getReceivedFecBytesInRound()); // fec packets received
+ uint32_t sent_interest = state->getSentInterestInRound();
+ uint32_t lost_data = state->getLostData();
+ uint32_t definitely_lost = state->getDefinitelyLostPackets();
+ uint32_t recovered_losses = state->getRecoveredLosses();
+ uint32_t received_nacks = state->getReceivedNacksInRound();
+ uint32_t received_fec = state->getReceivedFecPackets();
+
+ // update sync state if needed
+ double cache_rate = state->getPacketFromCacheRatio();
+ uint32_t round_without_nacks = state->getRoundsWithoutNacks();
+
+ if (ptr->current_state_ == SyncState::in_sync) {
+ if (cache_rate > MAX_DATA_FROM_CACHE) {
+ ptr->current_state_ = SyncState::catch_up;
+ }
+ } else {
+ if (round_without_nacks >= ROUNDS_IN_SYNC_BEFORE_SWITCH &&
+ cache_rate < MAX_DATA_FROM_CACHE) {
+ ptr->current_state_ = SyncState::in_sync;
+ }
+ }
+
+ bool in_sync = (ptr->current_state_ == SyncState::in_sync);
+ ptr->ldr_->onNewRound(in_sync);
+ ptr->state_->onNewRound((double)ROUND_LEN, in_sync);
+ ptr->rc_->onNewRound((double)ROUND_LEN);
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Calling updateSyncWindow in newRound function";
+ ptr->updateSyncWindow();
+
+ ptr->sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data,
+ definitely_lost, recovered_losses, received_nacks,
+ received_fec);
+ ptr->fwd_strategy_.checkStrategy();
+ ptr->newRound();
+ });
+}
+
+void RTCTransportProtocol::discoveredRtt() {
+ start_send_interest_ = true;
+ uint32_t strategy;
+ socket_->getSocketOption(RtcTransportOptions::RECOVERY_STRATEGY, strategy);
+ ldr_->changeRecoveryStrategy(
+ (interface::RtcTransportRecoveryStrategies)strategy);
+
+ bool content_sharing_mode;
+ socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE,
+ content_sharing_mode);
+ if (content_sharing_mode) ldr_->setContentSharingMode();
+ ldr_->turnOnRecovery();
+ ldr_->onNewRound(false);
+
+ // set forwarding strategy switch if selected
+ Name *name = nullptr;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
+ Prefix prefix(*name, 128);
+ fwd_strategy_.initFwdStrategy(
+ portal_, prefix, state_.get(),
+ (interface::RtcTransportRecoveryStrategies)strategy);
+ updateSyncWindow();
+}
+
+void RTCTransportProtocol::computeMaxSyncWindow() {
+ double production_rate = state_->getProducerRate();
+ double packet_size = state_->getAveragePacketSize();
+ if (production_rate == 0.0 || packet_size == 0.0) {
+ // the consumer has no info about the producer,
+ // keep the previous maxCWin
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Returning in computeMaxSyncWindow because: prod_rate: "
+ << (production_rate == 0.0)
+ << " || packet_size: " << (packet_size == 0.0);
+ return;
+ }
+
+ bool content_sharing_mode;
+ socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE,
+ content_sharing_mode);
+ if (content_sharing_mode && (production_rate < MIN_PROD_RATE_SHARING_MODE))
+ production_rate = MIN_PROD_RATE_SHARING_MODE;
+
+ production_rate += (production_rate * indexer_verifier_->getMaxFecOverhead());
+
+ uint32_t lifetime = default_values::interest_lifetime;
+ socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
+ lifetime);
+ double lifetime_ms = (double)lifetime / MILLI_IN_A_SEC;
+
+ max_sync_win_ = (uint32_t)ceil(
+ (production_rate * lifetime_ms * INTEREST_LIFETIME_REDUCTION_FACTOR) /
+ packet_size);
+
+ max_sync_win_ = std::min(max_sync_win_, rc_->getCongestionWindow());
+}
+
+void RTCTransportProtocol::updateSyncWindow() {
+ computeMaxSyncWindow();
+
+ if (max_sync_win_ == INITIAL_WIN_MAX) {
+ if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) return;
+
+ current_sync_win_ = INITIAL_WIN;
+ scheduleNextInterests();
+ return;
+ }
+
+ double prod_rate = state_->getProducerRate();
+ double rtt = (double)state_->getMinRTT() / MILLI_IN_A_SEC;
+ double packet_size = state_->getAveragePacketSize();
+ bool content_sharing_mode;
+ socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE,
+ content_sharing_mode);
+ if (content_sharing_mode && (prod_rate < MIN_PROD_RATE_SHARING_MODE))
+ prod_rate = MIN_PROD_RATE_SHARING_MODE;
+
+ // if some of the info are not available do not update the current win
+ if (prod_rate != 0.0 && rtt != 0.0 && packet_size != 0.0) {
+ current_sync_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size);
+ uint32_t buffer = PRODUCER_BUFFER_MS + ((double)state_->getMinRTT() / 2.0);
+
+ current_sync_win_ +=
+ ceil(prod_rate * (buffer / MILLI_IN_A_SEC) / packet_size);
+
+ if (current_state_ == SyncState::catch_up) {
+ current_sync_win_ = current_sync_win_ * CATCH_UP_WIN_INCREMENT;
+ }
+
+ uint32_t min_win = WIN_MIN;
+ bool aggregated_data_on;
+ socket_->getSocketOption(RtcTransportOptions::AGGREGATED_DATA,
+ aggregated_data_on);
+ if (aggregated_data_on) {
+ min_win = WIN_MIN_WITH_AGGREGARED_DATA;
+ min_win += (min_win * (1 - (std::max(0.3, rtt) - rtt) / 0.3));
+ }
+
+ current_sync_win_ = std::min(current_sync_win_, max_sync_win_);
+ current_sync_win_ = std::max(current_sync_win_, min_win);
+ }
+
+ scheduleNextInterests();
+}
+
+void RTCTransportProtocol::sendRtxInterest(uint32_t seq) {
+ if (!isRunning() && !is_first_) return;
+
+ if (!start_send_interest_) return;
+
+ Name *interest_name = nullptr;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
+ &interest_name);
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "send rtx " << seq;
+ interest_name->setSuffix(seq);
+ sendInterest(*interest_name);
+}
+
+void RTCTransportProtocol::sendProbeInterest(uint32_t seq) {
+ if (!isRunning() && !is_first_) return;
+
+ Name *interest_name = nullptr;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
+ &interest_name);
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send probe " << seq;
+ interest_name->setSuffix(seq);
+ sendInterest(*interest_name);
+}
+
+void RTCTransportProtocol::sendInterestForTimeout(uint32_t seq) {
+ if (!isRunning() && !is_first_) return;
+
+ Name *interest_name = nullptr;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
+ &interest_name);
+
+ // we got a timeout for this packet so it is not pending anymore
+ interest_name->setSuffix(seq);
+ state_->onSendNewInterest(interest_name);
+ sendInterest(*interest_name);
+}
+
+void RTCTransportProtocol::scheduleNextInterests() {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Schedule next interests";
+
+ if (!isRunning() && !is_first_) {
+ return;
+ }
+
+ if (pacing_timer_on_) {
+ return; // wait pacing timer for the next send
+ }
+
+ if (!start_send_interest_) {
+ return; // RTT discovering phase is not finished so
+ // do not start to send interests
+ }
+
+ if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Inactive producer.";
+ // here we keep seding the same interest until the producer
+ // does not start again
+ if (indexer_verifier_->checkNextSuffix() != 0) {
+ // the producer just become inactive, reset the state
+ inactiveProducer();
+ }
+
+ Name *interest_name = nullptr;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
+ &interest_name);
+
+ uint32_t next_seg = 0;
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send interest " << next_seg;
+ interest_name->setSuffix(next_seg);
+
+ if (portal_->interestIsPending(*interest_name)) {
+ // if interest 0 is already pending we return
+ return;
+ }
+
+ sendInterest(*interest_name);
+ state_->onSendNewInterest(interest_name);
+ return;
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Pending interest number: " << state_->getPendingInterestNumber()
+ << " -- current_sync_win_: " << current_sync_win_;
+
+ uint32_t pending = state_->getPendingInterestNumber();
+ uint32_t pending_fec = state_->getPendingFecPackets();
+
+ if ((pending - pending_fec) >= current_sync_win_)
+ return; // no space in the window
+
+ // XXX double check if aggregated interests are still working here
+ if ((current_sync_win_ - (pending - pending_fec)) <
+ max_aggregated_interest_) {
+ if (scheduler_timer_on_) return; // timer already scheduled
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ uint64_t time = now - last_interest_sent_time_;
+ if (time < WAIT_FOR_INTEREST_BATCH) {
+ uint64_t next = WAIT_FOR_INTEREST_BATCH - time;
+ scheduler_timer_on_ = true;
+ scheduler_timer_->expires_from_now(std::chrono::milliseconds(next));
+
+ std::weak_ptr<RTCTransportProtocol> self = shared_from_this();
+ scheduler_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ if (!ptr->scheduler_timer_on_) return;
+ ptr->scheduler_timer_on_ = false;
+ ptr->scheduleNextInterests();
+ }
+ });
+ return; // wait for the timer
+ }
+ }
+
+ scheduler_timer_on_ = false;
+ scheduler_timer_->cancel();
+
+ // skip nacked pacekts
+ if (indexer_verifier_->checkNextSuffix() <= state_->getLastSeqNacked()) {
+ indexer_verifier_->jumpToIndex(state_->getLastSeqNacked() + 1);
+ }
+
+ // skip received packets
+ uint32_t max_received = state_->getHighestSeqReceivedInOrder();
+ if (indexer_verifier_->checkNextSuffix() <= max_received) {
+ indexer_verifier_->jumpToIndex(max_received + 1);
+ }
+
+ uint32_t sent_interests = 0;
+ uint32_t sent_packets = 0;
+ uint32_t aggregated_counter = 0;
+ Name *name = nullptr;
+ Name interest_name;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
+ std::array<uint32_t, MAX_AGGREGATED_INTEREST> additional_suffixes;
+
+ while (((state_->getPendingInterestNumber() -
+ state_->getPendingFecPackets()) < current_sync_win_) &&
+ (sent_interests < max_sent_int_)) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "In while loop. Window size: " << current_sync_win_;
+
+ uint32_t next_seg = indexer_verifier_->getNextSuffix();
+ name->setSuffix(next_seg);
+
+ // send the packet only if:
+ // 1) it is not pending yet (not true for rtx)
+ // 2) the packet is not received or def lost
+ // 3) is not in the rtx list
+ // 4) is fec and is not in order (!= last sent + 1)
+ PacketState packet_state = state_->getPacketState(next_seg);
+ if (portal_->interestIsPending(*name) ||
+ packet_state == PacketState::RECEIVED ||
+ packet_state == PacketState::DEFINITELY_LOST || ldr_->isRtx(next_seg) ||
+ (indexer_verifier_->isFec(next_seg) &&
+ next_seg != last_interest_sent_seq_ + 1)) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "skip interest " << next_seg << " because: pending "
+ << portal_->interestIsPending(*name) << ", recv or lost"
+ << (int)packet_state << ", rtx " << (ldr_->isRtx(next_seg))
+ << ", is old fec "
+ << ((indexer_verifier_->isFec(next_seg) &&
+ next_seg != last_interest_sent_seq_ + 1));
+ continue;
+ }
+
+ if (aggregated_counter == 0) {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "(name) send interest " << next_seg;
+ interest_name = *name;
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "(append) send interest " << next_seg;
+ additional_suffixes[aggregated_counter - 1] = next_seg;
+ }
+
+ last_interest_sent_seq_ = next_seg;
+ state_->onSendNewInterest(name);
+ aggregated_counter++;
+
+ if (aggregated_counter >= max_aggregated_interest_) {
+ sent_packets++;
+ sent_interests++;
+ sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1);
+ last_interest_sent_time_ = utils::SteadyTime::nowMs().count();
+ aggregated_counter = 0;
+ }
+ }
+
+ // exiting the while we may have some pending interest to send
+ if (aggregated_counter != 0) {
+ sent_packets++;
+ last_interest_sent_time_ = utils::SteadyTime::nowMs().count();
+ sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1);
+ }
+
+ if ((state_->getPendingInterestNumber() - state_->getPendingFecPackets()) <
+ current_sync_win_) {
+ // we still have space in the window but we already sent too many packets
+ // wait PACING_WAIT to avoid drops in the kernel
+
+ pacing_timer_on_ = true;
+ pacing_timer_->expires_from_now(std::chrono::microseconds(PACING_WAIT));
+
+ std::weak_ptr<RTCTransportProtocol> self = shared_from_this();
+ scheduler_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+
+ auto ptr = self.lock();
+ if (ptr && ptr->isRunning()) {
+ if (!ptr->pacing_timer_on_) return;
+
+ ptr->pacing_timer_on_ = false;
+ ptr->scheduleNextInterests();
+ }
+ });
+ }
+}
+
+void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest,
+ const Name &name) {
+ uint32_t segment_number = name.getSuffix();
+
+ if (ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE) {
+ // this is a timeout on a probe, do nothing
+ return;
+ }
+
+ PacketState state = state_->getPacketState(segment_number);
+ if (state == PacketState::RECEIVED || state == PacketState::DEFINITELY_LOST) {
+ // we may recover a packets using fec, ignore this timer
+ return;
+ }
+
+ timeouts_or_nacks_.insert(segment_number);
+ if (TRANSPORT_EXPECT_TRUE(state_->isProducerActive()) &&
+ segment_number <= state_->getHighestSeqReceived()) {
+ // we retransmit packets only if the producer is active, otherwise we
+ // use timeouts to avoid to send too much traffic
+ //
+ // a timeout is sent using RTX only if it is an old packet. if it is for a
+ // seq number that we didn't reach yet, we send the packet using the normal
+ // schedule next interest
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "handle timeout for packet " << segment_number << " using rtx";
+ if (ldr_->isRtxOn()) {
+ if (indexer_verifier_->isFec(segment_number)) {
+ // if this is a fec packet we do not recover it with rtx so we consider
+ // the packet to be lost
+ ldr_->onTimeout(segment_number, true);
+ state_->onTimeout(segment_number, true);
+ } else {
+ ldr_->onTimeout(segment_number, false);
+ state_->onTimeout(segment_number, false);
+ }
+ } else {
+ // in this case we wil never recover the timeout
+ ldr_->onTimeout(segment_number, true);
+ state_->onTimeout(segment_number, true);
+ }
+ scheduleNextInterests();
+ return;
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "handle timeout for packet " << segment_number
+ << " using normal interests";
+
+ if (segment_number < indexer_verifier_->checkNextSuffix()) {
+ // this is a timeout for a packet that will be generated in the future but
+ // we are asking for higher sequence numbers. we need to go back like in the
+ // case of future nacks
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "On timeout next seg = " << indexer_verifier_->checkNextSuffix()
+ << ", jump to " << segment_number;
+ // add an extra space in the window
+ indexer_verifier_->jumpToIndex(segment_number);
+ }
+
+ state_->onTimeout(segment_number, false);
+ sendInterestForTimeout(segment_number);
+ scheduleNextInterests();
+}
+
+void RTCTransportProtocol::onNack(const ContentObject &content_object) {
+ struct nack_packet_t *nack =
+ (struct nack_packet_t *)content_object.getPayload()->data();
+ uint32_t production_seg = nack->getProductionSegment();
+ uint32_t nack_segment = content_object.getName().getSuffix();
+ bool is_rtx = ldr_->isRtx(nack_segment);
+
+ // check if the packet got a timeout
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Nack received " << nack_segment
+ << ". Production segment: " << production_seg;
+
+ bool compute_stats = true;
+ auto tn_it = timeouts_or_nacks_.find(nack_segment);
+ if (tn_it != timeouts_or_nacks_.end() || is_rtx) {
+ compute_stats = false;
+ // remove packets from timeouts_or_nacks only in case of a past nack
+ }
+
+ state_->onNackPacketReceived(content_object, compute_stats);
+ ldr_->onNackPacketReceived(content_object);
+
+ // both in case of past and future nack we jump to the
+ // production segment in the nack. In case of past nack we will skip unneded
+ // interest (this is already done in the scheduleNextInterest in any case)
+ // while in case of future nacks we can go back in time and ask again for the
+ // content that generated the nack
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "On nack next seg = " << indexer_verifier_->checkNextSuffix()
+ << ", jump to " << production_seg;
+ indexer_verifier_->jumpToIndex(production_seg);
+
+ if (production_seg > nack_segment) {
+ // remove the nack is it exists
+ if (tn_it != timeouts_or_nacks_.end()) timeouts_or_nacks_.erase(tn_it);
+
+ state_->onJumpForward(production_seg);
+ // the client is asking for content in the past
+ // switch to catch up state and increase the window
+ // this is true only if the packet is not an RTX
+ if (!is_rtx) current_state_ = SyncState::catch_up;
+ } else {
+ // if production_seg == nack_segment we consider this a future nack, since
+ // production_seg is not yet created. this may happen in case of low
+ // production rate (e.g. ping at 1pps)
+
+ // if a future nack was also retransmitted add it to the timeout_or_nacks
+ // set
+ if (is_rtx) timeouts_or_nacks_.insert(nack_segment);
+
+ // the client is asking for content in the future
+ // switch to in sync state and decrease the window
+ current_state_ = SyncState::in_sync;
+ }
+ updateSyncWindow();
+}
+
+void RTCTransportProtocol::onProbe(const ContentObject &content_object) {
+ uint32_t suffix = content_object.getName().getSuffix();
+ ParamsRTC params = RTCState::getProbeParams(content_object);
+
+ if (ProbeHandler::getProbeType(suffix) == ProbeType::INIT) {
+ fec::FECType fec_type = params.fec_type;
+
+ if (fec_type != fec::FECType::UNKNOWN && !fec_decoder_) {
+ // Update FEC type
+ fec_type_ = fec_type;
+
+ // Enable FEC
+ enableFEC(std::bind(&RTCTransportProtocol::onFecPackets, this,
+ std::placeholders::_1),
+ fec::FECBase::BufferRequested(0));
+
+ // Update FEC parameters
+ indexer_verifier_->enableFec(fec_type);
+ indexer_verifier_->setNFec(0);
+ ldr_->setFecParams(fec::FECUtils::getBlockSymbols(fec_type),
+ fec::FECUtils::getSourceSymbols(fec_type));
+ fec_decoder_->setIOService(portal_->getThread().getIoService());
+ } else if (fec_type == fec::FECType::UNKNOWN) {
+ indexer_verifier_->disableFec();
+ }
+ }
+
+ if (!state_->onProbePacketReceived(content_object)) return;
+
+ // As for NACKs, set next_segment
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "on probe next seg = " << indexer_verifier_->checkNextSuffix()
+ << ", jump to " << params.prod_seg;
+ indexer_verifier_->jumpToIndex(params.prod_seg);
+
+ bool loss_detected = ldr_->onProbePacketReceived(content_object);
+ // we are not out of sync here but we are starting to download content from
+ // the cache, maybe beacuse the production rate increased suddenly. for this
+ // reason we put the state to catch up to increase the window
+ if (loss_detected) current_state_ = SyncState::catch_up;
+ updateSyncWindow();
+}
+
+void RTCTransportProtocol::onContentObjectReceived(
+ Interest &interest, ContentObject &content_object, std::error_code &ec) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received content object of size: " << content_object.payloadSize();
+
+ uint32_t segment_number = content_object.getName().getSuffix();
+ PayloadType payload_type = content_object.getPayloadType();
+ PacketState state;
+
+ ContentObject *content_ptr = &content_object;
+ ContentObject::Ptr manifest_ptr = nullptr;
+
+ bool is_probe =
+ ProbeHandler::getProbeType(segment_number) != ProbeType::NOT_PROBE;
+ bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE;
+ bool is_fec = indexer_verifier_->isFec(segment_number);
+ bool is_manifest =
+ !is_probe && !is_nack && !is_fec && payload_type == PayloadType::MANIFEST;
+ bool is_data =
+ !is_probe && !is_nack && !is_fec && payload_type == PayloadType::DATA;
+ bool compute_stats = is_data || is_manifest;
+
+ ec = make_error_code(protocol_error::not_reassemblable);
+
+ // A helper function to process manifests or data packets received
+ auto onDataPacketReceived = [this](ContentObject &content_object,
+ bool compute_stats) {
+ ldr_->onDataPacketReceived(content_object);
+ rc_->onDataPacketReceived(content_object, compute_stats);
+ updateSyncWindow();
+ };
+
+ // First verify the packet signature and apply the corresponding policy
+ auth::VerificationPolicy policy = verifier_->verify(content_object, is_fec);
+ indexer_verifier_->applyPolicy(interest, content_object, false, policy);
+
+ if (is_probe) {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << segment_number;
+ if (*on_content_object_input_) {
+ (*on_content_object_input_)(*socket_->getInterface(), content_object);
+ }
+ onProbe(content_object);
+ return;
+ }
+
+ if (is_nack) {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received nack " << segment_number;
+ if (*on_content_object_input_) {
+ (*on_content_object_input_)(*socket_->getInterface(), content_object);
+ }
+ onNack(content_object);
+ return;
+ }
+
+ // content_ptr will point either to the input data packet or to a manifest
+ // whose FEC header has been removed
+ if (is_manifest) {
+ manifest_ptr = removeFecHeader(content_object);
+ if (manifest_ptr) {
+ content_ptr = manifest_ptr.get();
+ }
+ }
+
+ // From there, the packet is either a FEC, a manifest or a data packet.
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content " << segment_number;
+
+ // Do not count timed out packets in stats
+ auto tn_it = timeouts_or_nacks_.find(segment_number);
+ if (tn_it != timeouts_or_nacks_.end()) {
+ compute_stats = false;
+ timeouts_or_nacks_.erase(tn_it);
+ }
+
+ // Do not count retransmissions or losses in stats
+ if (ldr_->isRtx(segment_number) ||
+ ldr_->isPossibleLossWithNoRtx(segment_number)) {
+ compute_stats = false;
+ }
+
+ // Fetch packet state
+ state = state_->getPacketState(segment_number);
+
+ // Check if the packet is a retransmission
+ if (ldr_->isRtx(segment_number) && state != PacketState::RECEIVED) {
+ if (is_data || is_manifest) {
+ uint64_t rtt = ldr_->getRtxRtt(segment_number);
+ state_->onPacketRecoveredRtx(content_object, rtt);
+
+ if (*on_content_object_input_) {
+ (*on_content_object_input_)(*socket_->getInterface(), content_object);
+ }
+
+ if (is_manifest) {
+ processManifest(interest, *content_ptr);
+ }
+
+ ec = is_manifest ? make_error_code(protocol_error::not_reassemblable)
+ : make_error_code(protocol_error::success);
+
+ // The packet is considered received, return early
+ onDataPacketReceived(*content_ptr, compute_stats);
+ // this is a rtx but we may need to feed it in the decoder
+ decodePacket(content_object, is_manifest);
+ return;
+ }
+
+ if (is_fec) {
+ state_->onFecPacketRecoveredRtx(content_object);
+ }
+ }
+
+ // Fetch packet state again; it may have changed
+ state = state_->getPacketState(segment_number);
+
+ // Check if the packet was already received
+ if (state == PacketState::RECEIVED || state == PacketState::TO_BE_RECEIVED) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received duplicated content " << segment_number << ", drop it";
+ ec = make_error_code(protocol_error::duplicated_content);
+ onDataPacketReceived(*content_ptr, compute_stats);
+ return;
+ }
+
+ if (!is_fec) {
+ state_->dataToBeReceived(segment_number);
+ }
+
+ // send packet to the decoder
+ decodePacket(content_object, is_manifest);
+
+ // We can return early if FEC
+ if (is_fec) {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received FEC " << segment_number;
+ state_->onFecPacketReceived(content_object);
+ onDataPacketReceived(*content_ptr, compute_stats);
+ return;
+ }
+
+ // The packet may have been already sent to the app by the decoder, check
+ // again if it is already received
+ state = state_->getPacketState(
+ segment_number); // state == RECEIVED or TO_BE_RECEIVED
+
+ if (state != PacketState::RECEIVED) {
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << (is_manifest ? "Received manifest " : "Received data ")
+ << segment_number;
+
+ if (is_manifest) {
+ processManifest(interest, *content_ptr);
+ }
+
+ state_->onDataPacketReceived(*content_ptr, compute_stats);
+
+ if (*on_content_object_input_) {
+ (*on_content_object_input_)(*socket_->getInterface(), content_object);
+ }
+
+ ec = is_manifest ? make_error_code(protocol_error::not_reassemblable)
+ : make_error_code(protocol_error::success);
+ }
+
+ onDataPacketReceived(*content_ptr, compute_stats);
+}
+
+void RTCTransportProtocol::sendStatsToApp(
+ uint32_t retx_count, uint32_t received_bytes, uint32_t sent_interests,
+ uint32_t lost_data, uint32_t definitely_lost, uint32_t recovered_losses,
+ uint32_t received_nacks, uint32_t received_fec) {
+ if (*stats_summary_) {
+ // Send the stats to the app
+ stats_->updateQueuingDelay(state_->getQueuing());
+
+ // stats_->updateInterestFecTx(0); //todo must be implemented
+ // stats_->updateBytesFecRecv(0); //todo must be implemented
+
+ stats_->updateRetxCount(retx_count);
+ stats_->updateBytesRecv(received_bytes);
+ stats_->updateInterestTx(sent_interests);
+ stats_->updateReceivedNacks(received_nacks);
+ stats_->updateReceivedFEC(received_fec);
+
+ stats_->updateAverageWindowSize(state_->getPendingInterestNumber());
+ stats_->updateLossRatio(state_->getPerSecondLossRate());
+ uint64_t rtt = state_->getAvgRTT();
+ stats_->updateAverageRtt(utils::SteadyTime::Microseconds(rtt * 1000));
+
+ stats_->updateQueuingDelay(state_->getQueuing());
+ stats_->updateLostData(lost_data);
+ stats_->updateDefinitelyLostData(definitely_lost);
+ stats_->updateRecoveredData(recovered_losses);
+ stats_->updateCCState((unsigned int)current_state_ ? 1 : 0);
+ (*stats_summary_)(*socket_->getInterface(), *stats_);
+ bool in_congestion = rc_->inCongestionState();
+ stats_->updateCongestionState(in_congestion);
+ double residual_losses = state_->getResidualLossRate();
+ stats_->updateResidualLossRate(residual_losses);
+ stats_->updateQualityScore(state_->getQualityScore());
+
+ // set alerts
+ if (rtt > MAX_RTT)
+ stats_->setAlert(interface::TransportStatistics::statsAlerts::LATENCY);
+ else
+ stats_->clearAlert(interface::TransportStatistics::statsAlerts::LATENCY);
+
+ if (in_congestion)
+ stats_->setAlert(interface::TransportStatistics::statsAlerts::CONGESTION);
+ else
+ stats_->clearAlert(
+ interface::TransportStatistics::statsAlerts::CONGESTION);
+
+ if (residual_losses > MAX_RESIDUAL_LOSSES)
+ stats_->setAlert(interface::TransportStatistics::statsAlerts::LOSSES);
+ else
+ stats_->clearAlert(interface::TransportStatistics::statsAlerts::LOSSES);
+ }
+}
+
+void RTCTransportProtocol::decodePacket(ContentObject &content_object,
+ bool is_manifest) {
+ if (!fec_decoder_) return;
+
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Send packet " << content_object.getName() << " to FEC decoder";
+
+ uint32_t offset =
+ is_manifest
+ ? (uint32_t)content_object.headerSize()
+ : (uint32_t)(content_object.headerSize() + rtc::DATA_HEADER_SIZE);
+ uint32_t metadata = static_cast<uint32_t>(content_object.getPayloadType());
+
+ fec_decoder_->onDataPacket(content_object, offset, metadata);
+}
+
+void RTCTransportProtocol::onFecPackets(fec::BufferArray &packets) {
+ Packet::Format format;
+ socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT,
+ format);
+
+ Name *name = nullptr;
+ socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, &name);
+
+ for (auto &packet : packets) {
+ uint32_t seq_number = packet.getIndex();
+ uint32_t metadata = packet.getMetadata();
+ fec::buffer buffer = packet.getBuffer();
+
+ PayloadType payload_type = static_cast<PayloadType>(metadata);
+ switch (payload_type) {
+ case PayloadType::DATA:
+ case PayloadType::MANIFEST:
+ break;
+ case PayloadType::UNSPECIFIED:
+ default:
+ payload_type = PayloadType::DATA;
+ break;
+ }
+
+ switch (state_->getPacketState(seq_number)) {
+ case PacketState::RECEIVED:
+ case PacketState::TO_BE_RECEIVED: {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Packet " << seq_number << " already received";
+ break;
+ }
+ default: {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Recovered packet " << seq_number << " through FEC";
+
+ if (payload_type == PayloadType::MANIFEST) {
+ name->setSuffix(seq_number);
+
+ auto interest =
+ core::PacketManager<>::getInstance().getPacket<Interest>(format);
+ interest->setName(*name);
+
+ auto content_object = toContentObject(
+ *name, format, payload_type, buffer->data(), buffer->length());
+
+ processManifest(*interest, *content_object);
+ }
+
+ state_->onPacketRecoveredFec(seq_number, (uint32_t)buffer->length());
+ ldr_->onPacketRecoveredFec(seq_number);
+
+ if (payload_type == PayloadType::DATA) {
+ verifier_->onDataRecoveredFec(seq_number);
+ reassembly_->reassemble(*buffer, seq_number);
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void RTCTransportProtocol::processManifest(Interest &interest,
+ ContentObject &manifest) {
+ auth::VerificationPolicy policy = verifier_->processManifest(manifest);
+ indexer_verifier_->applyPolicy(interest, manifest, false, policy);
+}
+
+ContentObject::Ptr RTCTransportProtocol::removeFecHeader(
+ const ContentObject &content_object) {
+ if (!fec_decoder_ || !fec_decoder_->getFecHeaderSize(false)) {
+ return nullptr;
+ }
+
+ size_t fec_header_size = fec_decoder_->getFecHeaderSize(false);
+ const uint8_t *payload =
+ content_object.data() + content_object.headerSize() + fec_header_size;
+ size_t payload_size = content_object.payloadSize() - fec_header_size;
+
+ ContentObject::Ptr co =
+ toContentObject(content_object.getName(), content_object.getFormat(),
+ content_object.getPayloadType(), payload, payload_size);
+
+ return co;
+}
+
+ContentObject::Ptr RTCTransportProtocol::toContentObject(
+ const Name &name, Packet::Format format, PayloadType payload_type,
+ const uint8_t *payload, std::size_t payload_size,
+ std::size_t additional_header_size) {
+ // Recreate ContentObject
+ ContentObject::Ptr co =
+ core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ format, additional_header_size);
+ co->updateLength(payload_size);
+ co->append(payload_size);
+ co->trimStart(co->headerSize());
+
+ // Copy payload
+ std::memcpy(co->writableData(), payload, payload_size);
+
+ // Restore network headers and some fields
+ co->prepend(co->headerSize());
+ co->setName(name);
+ co->setPayloadType(payload_type);
+
+ return co;
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc.h b/libtransport/src/protocols/rtc/rtc.h
new file mode 100644
index 000000000..a8a474216
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <protocols/rtc/rtc_forwarding_strategy.h>
+#include <protocols/rtc/rtc_ldr.h>
+#include <protocols/rtc/rtc_rc.h>
+#include <protocols/rtc/rtc_reassembly.h>
+#include <protocols/rtc/rtc_state.h>
+#include <protocols/rtc/rtc_verifier.h>
+#include <protocols/transport_protocol.h>
+
+#include <unordered_set>
+#include <vector>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RTCTransportProtocol : public TransportProtocol {
+ public:
+ RTCTransportProtocol(implementation::ConsumerSocket *icnet_socket);
+
+ ~RTCTransportProtocol();
+
+ using TransportProtocol::start;
+
+ using TransportProtocol::stop;
+
+ void resume() override;
+
+ std::size_t transportHeaderLength(bool isFEC) override;
+
+ auto shared_from_this() { return utils::shared_from(this); }
+
+ private:
+ enum class SyncState { catch_up = 0, in_sync = 1, last };
+
+ private:
+ // setup functions
+ void initParams();
+ void reset() override;
+
+ void inactiveProducer();
+
+ // protocol functions
+ void discoveredRtt();
+ void newRound();
+
+ // window functions
+ void computeMaxSyncWindow();
+ void updateSyncWindow();
+
+ // packet functions
+ void sendRtxInterest(uint32_t seq);
+ void sendProbeInterest(uint32_t seq);
+ void sendInterestForTimeout(uint32_t seq);
+ void scheduleNextInterests() override;
+ void onInterestTimeout(Interest::Ptr &interest, const Name &name) override;
+ void onNack(const ContentObject &content_object);
+ void onProbe(const ContentObject &content_object);
+ void onContentObjectReceived(Interest &interest,
+ ContentObject &content_object,
+ std::error_code &ec) override;
+ void onPacketDropped(Interest &interest, ContentObject &content_object,
+ const std::error_code &reason) override {}
+ void onReassemblyFailed(std::uint32_t missing_segment) override {}
+ void processManifest(Interest &interest, ContentObject &manifest);
+
+ // interaction with app functions
+ void sendStatsToApp(uint32_t retx_count, uint32_t received_bytes,
+ uint32_t sent_interests, uint32_t lost_data,
+ uint32_t definitely_lost, uint32_t recovered_losses,
+ uint32_t received_nacks, uint32_t received_fec);
+
+ // FEC functions
+ // send the received content object to the decoder
+ void decodePacket(ContentObject &content_object, bool is_manifest);
+ void onFecPackets(fec::BufferArray &packets);
+
+ // Utils
+ ContentObject::Ptr removeFecHeader(const ContentObject &content_object);
+ ContentObject::Ptr toContentObject(const Name &name, Packet::Format format,
+ PayloadType payload_type,
+ const uint8_t *payload,
+ std::size_t payload_size,
+ std::size_t additional_header_size = 0);
+
+ // protocol state
+ bool start_send_interest_;
+ SyncState current_state_;
+
+ // cwin vars
+ uint32_t current_sync_win_;
+ uint32_t max_sync_win_;
+
+ // round timer
+ std::unique_ptr<asio::steady_timer> round_timer_;
+
+ // scheduler timer (postpone interest sending to explot aggregated interests)
+ std::unique_ptr<asio::steady_timer> scheduler_timer_;
+ bool scheduler_timer_on_;
+ uint64_t last_interest_sent_time_;
+ uint64_t last_interest_sent_seq_;
+
+ // maximum aggregated interest. if the transport is connected to the forwarder
+ // we cannot use aggregated interests
+ uint32_t max_aggregated_interest_;
+ // maximum number of intereset that can be sent in a loop to avoid packets
+ // dropped by the kernel
+ uint32_t max_sent_int_;
+
+ // pacing timer (do not send too many interests in a short time to avoid
+ // packet drops in the kernel)
+ std::unique_ptr<asio::steady_timer> pacing_timer_;
+ bool pacing_timer_on_;
+
+ // timeouts
+ std::unordered_set<uint32_t> timeouts_or_nacks_;
+
+ std::shared_ptr<RTCState> state_;
+ std::shared_ptr<RTCRateControl> rc_;
+ std::shared_ptr<RTCLossDetectionAndRecovery> ldr_;
+ std::shared_ptr<RTCVerifier> verifier_;
+
+ // forwarding strategy selection
+ RTCForwardingStrategy fwd_strategy_;
+
+ uint32_t number_;
+};
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_consts.h b/libtransport/src/protocols/rtc/rtc_consts.h
new file mode 100644
index 000000000..29b5a3a12
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_consts.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <protocols/rtc/rtc_packet.h>
+#include <stdint.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+// used in rtc
+// protocol consts
+const uint32_t ROUND_LEN = 200;
+// ms interval of time on which
+// we take decisions / measurements
+const double INTEREST_LIFETIME_REDUCTION_FACTOR = 0.8;
+// how big (in ms) should be the buffer at the producer.
+// increasing this number we increase the time that an
+// interest will wait for the data packet to be produced
+// at the producer socket
+const uint32_t PRODUCER_BUFFER_MS = 300; // ms
+
+// interest scheduler
+// const uint32_t MAX_INTERESTS_IN_BATCH = 5;
+// const uint32_t WAIT_BETWEEN_INTEREST_BATCHES = 1000; // usec
+const uint32_t MAX_INTERESTS_IN_BATCH = 5; // number of seq numbers per
+ // aggregated interest packet
+ // considering the name itself
+const uint32_t WAIT_FOR_INTEREST_BATCH = 20; // msec. timer that we wait to try
+ // to aggregate interest in the
+ // same packet
+const uint32_t MAX_PACING_BATCH = 5; // number of interest that we can send
+ // inside the loop before they get dropped
+ // by the kernel.
+const uint32_t PACING_WAIT = 1000; // usec to wait betwing two pacing batch. As
+ // for MAX_PACING_BATCH this value was
+ // computed during tests
+const uint32_t MAX_RTX_IN_BATCH = 10; // max rtx to send in loop
+
+// packet const
+const uint32_t RTC_INTEREST_LIFETIME = 4000;
+
+// probes sequence range
+const uint32_t MIN_PROBE_SEQ = 0xefffffff;
+const uint32_t MIN_INIT_PROBE_SEQ = MIN_PROBE_SEQ;
+const uint32_t MAX_INIT_PROBE_SEQ = 0xf7ffffff - 1;
+const uint32_t MIN_RTT_PROBE_SEQ = MAX_INIT_PROBE_SEQ + 1;
+const uint32_t MAX_RTT_PROBE_SEQ = 0xffffffff - 1;
+// RTT_PROBE_INTERVAL will be used during the section while
+// INIT_RTT_PROBE_INTERVAL is used at the beginning to
+// quickily estimate the RTT
+const uint32_t RTT_PROBE_INTERVAL = 200000; // us
+const uint32_t INIT_RTT_PROBE_INTERVAL = 500; // us
+const uint32_t INIT_RTT_PROBES = 40; // number of probes to init RTT
+// if the produdcer is not yet started we need to probe multple times
+// to get an answer. we wait 100ms between each try
+const uint32_t INIT_RTT_PROBE_RESTART = 100; // ms
+// once we get the first probe we wait at most 60ms for the others
+const uint32_t INIT_RTT_PROBE_WAIT =
+ ((INIT_RTT_PROBES * INIT_RTT_PROBE_INTERVAL) / 1000) * 2; // ms
+// we reuires at least 5 probes to be recevied
+const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; // ms
+const uint32_t MAX_PENDING_PROBES = 10;
+
+// congestion
+const double MAX_QUEUING_DELAY = 50.0; // ms
+
+// data from cache
+const double MAX_DATA_FROM_CACHE = 0.10; // 10%
+
+// window const
+const uint32_t INITIAL_WIN = 5; // pkts
+const uint32_t INITIAL_WIN_MAX = 1000000; // pkts
+const uint32_t WIN_MIN = 5; // pkts
+const uint32_t WIN_MIN_WITH_AGGREGARED_DATA = 10; // pkts
+const double CATCH_UP_WIN_INCREMENT = 1.2;
+// used in rate control
+const double WIN_DECREASE_FACTOR = 0.5;
+const double WIN_INCREASE_FACTOR = 1.5;
+const uint32_t MIN_PROD_RATE_SHARING_MODE = 125000; // 1Mbps in bytes
+
+// round in congestion
+const double ROUNDS_BEFORE_TAKE_ACTION = 5;
+
+// used in state
+const uint8_t ROUNDS_IN_SYNC_BEFORE_SWITCH = 3;
+const double PRODUCTION_RATE_FRACTION = 0.8;
+
+const uint32_t INIT_PACKET_SIZE = 1200;
+
+const double MOVING_AVG_ALPHA = 0.8;
+
+const double MILLI_IN_A_SEC = 1000.0;
+const double MICRO_IN_A_SEC = 1000000.0;
+const uint32_t ROUNDS_PER_SEC = (uint32_t)(MILLI_IN_A_SEC / ROUND_LEN);
+const uint32_t ROUNDS_PER_MIN = (uint32_t)ROUNDS_PER_SEC * 60;
+
+const uint32_t MAX_ROUND_WHIOUT_PACKETS =
+ (20 * MILLI_IN_A_SEC) / ROUND_LEN; // 20 sec in rounds;
+
+// used in ldr
+const uint32_t RTC_MAX_RTX = 100;
+const uint32_t RTC_MAX_AGE = 60000; // in ms
+const uint64_t MAX_TIMER_RTX = ~0;
+const uint32_t SENTINEL_TIMER_INTERVAL = 100; // ms
+const uint32_t MAX_RTX_WITH_SENTINEL = 10; // packets
+const double CATCH_UP_RTT_INCREMENT = 1.2;
+const double MAX_RESIDUAL_LOSS_RATE = 1.0; // %
+const uint32_t WAIT_BEFORE_FEC_UPDATE = ROUNDS_PER_SEC;
+const uint32_t MAX_RTT_BEFORE_FEC = 60; // ms
+
+// used by producer
+const uint32_t PRODUCER_STATS_INTERVAL = 200; // ms
+const uint32_t MIN_PRODUCTION_RATE = 25; // pps, equal to min window *
+ // rounds in a second
+const uint32_t FEC_PACING_TIME = 5; // ms
+
+// aggregated data consts
+const uint16_t MAX_RTC_PAYLOAD_SIZE = 1200; // bytes
+const uint16_t MAX_AGGREGATED_PACKETS = 5; // pkt
+const uint32_t AGGREGATED_PACKETS_TIMER = 2; // ms
+
+// alert thresholds
+const uint32_t MAX_RTT = 200; // ms
+const double MAX_RESIDUAL_LOSSES = 0.05; // %
+
+const uint8_t FEC_MATRIX[64][10] = {
+ {1, 2, 2, 2, 3, 3, 4, 5, 5, 6}, // k = 1
+ {1, 2, 3, 3, 4, 5, 5, 6, 7, 9},
+ {2, 2, 3, 4, 5, 6, 7, 8, 9, 11},
+ {2, 3, 4, 5, 5, 7, 8, 9, 11, 13},
+ {2, 3, 4, 5, 6, 7, 9, 10, 12, 14}, // k = 5
+ {2, 3, 4, 6, 7, 8, 10, 12, 14, 16},
+ {2, 4, 5, 6, 8, 9, 11, 13, 15, 18},
+ {3, 4, 5, 7, 8, 10, 12, 14, 16, 19},
+ {3, 4, 6, 7, 9, 11, 13, 15, 18, 21},
+ {3, 4, 6, 8, 9, 11, 14, 16, 19, 23}, // k = 10
+ {3, 5, 6, 8, 10, 12, 14, 17, 20, 24},
+ {3, 5, 7, 8, 10, 13, 15, 18, 21, 26},
+ {3, 5, 7, 9, 11, 13, 16, 19, 23, 27},
+ {3, 5, 7, 9, 12, 14, 17, 20, 24, 28},
+ {4, 6, 8, 10, 12, 15, 18, 21, 25, 30}, // k = 15
+ {4, 6, 8, 10, 13, 15, 19, 22, 26, 31},
+ {4, 6, 8, 11, 13, 16, 19, 23, 27, 33},
+ {4, 6, 9, 11, 14, 17, 20, 24, 29, 34},
+ {4, 6, 9, 11, 14, 17, 21, 25, 30, 35},
+ {4, 7, 9, 12, 15, 18, 22, 26, 31, 37}, // k = 20
+ {4, 7, 9, 12, 15, 19, 22, 27, 32, 38},
+ {4, 7, 10, 13, 16, 19, 23, 28, 33, 40},
+ {5, 7, 10, 13, 16, 20, 24, 29, 34, 41},
+ {5, 7, 10, 13, 17, 20, 25, 30, 35, 42},
+ {5, 8, 11, 14, 17, 21, 26, 31, 37, 44}, // k = 25
+ {5, 8, 11, 14, 18, 22, 26, 31, 38, 45},
+ {5, 8, 11, 15, 18, 22, 27, 32, 39, 46},
+ {5, 8, 11, 15, 19, 23, 28, 33, 40, 48},
+ {5, 8, 12, 15, 19, 24, 28, 34, 41, 49},
+ {5, 9, 12, 16, 20, 24, 29, 35, 42, 50}, // k = 30
+ {5, 9, 12, 16, 20, 25, 30, 36, 43, 51},
+ {5, 9, 13, 16, 21, 25, 31, 37, 44, 53},
+ {6, 9, 13, 17, 21, 26, 31, 38, 45, 54},
+ {6, 9, 13, 17, 22, 26, 32, 39, 46, 55},
+ {6, 10, 13, 17, 22, 27, 33, 40, 47, 57}, // k = 35
+ {6, 10, 14, 18, 22, 28, 34, 40, 48, 58},
+ {6, 10, 14, 18, 23, 28, 34, 41, 49, 59},
+ {6, 10, 14, 19, 23, 29, 35, 42, 50, 60},
+ {6, 10, 14, 19, 24, 29, 36, 43, 52, 62},
+ {6, 10, 15, 19, 24, 30, 36, 44, 53, 63}, // k = 40
+ {6, 11, 15, 20, 25, 31, 37, 45, 54, 64},
+ {6, 11, 15, 20, 25, 31, 38, 46, 55, 65},
+ {7, 11, 15, 20, 26, 32, 39, 46, 56, 67},
+ {7, 11, 16, 21, 26, 32, 39, 47, 57, 68},
+ {7, 11, 16, 21, 27, 33, 40, 48, 58, 69}, // k = 45
+ {7, 11, 16, 21, 27, 33, 41, 49, 59, 70},
+ {7, 12, 16, 22, 27, 34, 41, 50, 60, 72},
+ {7, 12, 17, 22, 28, 34, 42, 51, 61, 73},
+ {7, 12, 17, 22, 28, 35, 43, 52, 62, 74},
+ {7, 12, 17, 23, 29, 36, 43, 52, 63, 75}, // k = 50
+ {7, 12, 17, 23, 29, 36, 44, 53, 64, 77},
+ {7, 12, 18, 23, 30, 37, 45, 54, 65, 78},
+ {7, 13, 18, 24, 30, 37, 45, 55, 66, 79},
+ {8, 13, 18, 24, 31, 38, 46, 56, 67, 80},
+ {8, 13, 18, 24, 31, 38, 47, 57, 68, 82}, // k = 55
+ {8, 13, 19, 25, 31, 39, 47, 57, 69, 83},
+ {8, 13, 19, 25, 32, 39, 48, 58, 70, 84},
+ {8, 13, 19, 25, 32, 40, 49, 59, 71, 85},
+ {8, 14, 19, 26, 33, 41, 50, 60, 72, 86},
+ {8, 14, 20, 26, 33, 41, 50, 61, 73, 88}, // k = 60
+ {8, 14, 20, 26, 34, 42, 51, 61, 74, 89},
+ {8, 14, 20, 27, 34, 42, 52, 62, 75, 90},
+ {8, 14, 20, 27, 34, 43, 52, 63, 76, 91},
+ {8, 14, 21, 27, 35, 43, 53, 64, 77, 92}, // k = 64
+};
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_data_path.cc b/libtransport/src/protocols/rtc/rtc_data_path.cc
new file mode 100644
index 000000000..a421396b1
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_data_path.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <protocols/rtc/rtc_data_path.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <cfloat>
+#include <chrono>
+#include <cmath>
+
+#define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec
+#define AVG_RTT_TIME 1000 // (ms) 1sec
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RTCDataPath::RTCDataPath(uint32_t path_id)
+ : path_id_(path_id),
+ min_rtt(UINT_MAX),
+ prev_min_rtt(UINT_MAX),
+ max_rtt(0),
+ prev_max_rtt(0),
+ min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the
+ // real OWD, but the measured one, that depends on the
+ // clock of sender and receiver. the only meaningful
+ // value is is the queueing delay. for this reason we
+ // keep both RTT (for the windowd calculation) and OWD
+ // (for congestion/quality control)
+ prev_min_owd(INT_MAX),
+ avg_owd(DBL_MAX),
+ queuing_delay(DBL_MAX),
+ jitter_(0.0),
+ last_owd_(0),
+ largest_recv_seq_(0),
+ largest_recv_seq_time_(0),
+ avg_inter_arrival_(DBL_MAX),
+ rtt_sum_(0),
+ last_avg_rtt_compute_(0),
+ rtt_samples_(0),
+ avg_rtt_(0.0),
+ received_nacks_(false),
+ received_packets_(0),
+ rounds_without_packets_(0),
+ last_received_data_packet_(0),
+ min_RTT_history_(HISTORY_LEN),
+ max_RTT_history_(HISTORY_LEN),
+ OWD_history_(HISTORY_LEN){};
+
+void RTCDataPath::insertRttSample(
+ const utils::SteadyTime::Milliseconds& rtt_milliseconds, bool is_probe) {
+ // compute min rtt
+ uint64_t rtt = rtt_milliseconds.count();
+ if (rtt < min_rtt) min_rtt = rtt;
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ last_received_data_packet_ = now;
+
+ // compute avg rtt
+ if (is_probe) {
+ // max rtt is computed only on probes to avoid to take into account the
+ // production time at the server
+ if (rtt > max_rtt) max_rtt = rtt;
+
+ rtt_sum_ += rtt;
+ rtt_samples_++;
+ }
+
+ if ((now - last_avg_rtt_compute_) >= AVG_RTT_TIME) {
+ // compute a new avg rtt
+ // if rtt_samples_ = 0 keep the same rtt
+ if (rtt_samples_ != 0) avg_rtt_ = (double)rtt_sum_ / (double)rtt_samples_;
+
+ rtt_sum_ = 0;
+ rtt_samples_ = 0;
+ last_avg_rtt_compute_ = now;
+ }
+
+ received_packets_++;
+}
+
+void RTCDataPath::insertOwdSample(int64_t owd) {
+ // for owd we use both min and avg
+ if (owd < min_owd) min_owd = owd;
+
+ if (avg_owd != DBL_MAX)
+ avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC);
+ else {
+ avg_owd = owd;
+ }
+
+ int64_t queueVal = owd - std::min(getMinOwd(), min_owd);
+
+ if (queuing_delay != DBL_MAX)
+ queuing_delay = (queuing_delay * (1 - ALPHA_RTC)) + (queueVal * ALPHA_RTC);
+ else {
+ queuing_delay = queueVal;
+ }
+
+ // keep track of the jitter computed as for RTP (RFC 3550)
+ int64_t diff = std::abs(owd - last_owd_);
+ last_owd_ = owd;
+ jitter_ += (1.0 / 16.0) * ((double)diff - jitter_);
+}
+
+void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) {
+ // got packet in sequence, compute gap
+ if (largest_recv_seq_ == (segment_number - 1)) {
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ uint64_t delta = now - largest_recv_seq_time_;
+ largest_recv_seq_ = segment_number;
+ largest_recv_seq_time_ = now;
+ if (avg_inter_arrival_ == DBL_MAX)
+ avg_inter_arrival_ = delta;
+ else
+ avg_inter_arrival_ =
+ (avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC);
+ return;
+ }
+
+ // ooo packet, update the stasts if needed
+ if (largest_recv_seq_ <= segment_number) {
+ largest_recv_seq_ = segment_number;
+ largest_recv_seq_time_ = utils::SteadyTime::nowMs().count();
+ }
+}
+
+void RTCDataPath::receivedNack() { received_nacks_ = true; }
+
+double RTCDataPath::getInterArrivalGap() {
+ if (avg_inter_arrival_ == DBL_MAX) return 0;
+ return avg_inter_arrival_;
+}
+
+bool RTCDataPath::isValidProducer() {
+ if (received_nacks_ && rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS)
+ return true;
+ return false;
+}
+
+bool RTCDataPath::isActive() {
+ if (rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS) return true;
+ return false;
+}
+
+bool RTCDataPath::pathToProducer() {
+ if (received_nacks_) return true;
+ return false;
+}
+
+void RTCDataPath::roundEnd() {
+ // reset min_rtt and add it to the history
+ if (min_rtt != UINT_MAX) {
+ prev_min_rtt = min_rtt;
+ } else {
+ // this may happen if we do not receive any packet
+ // from this path in the last round. in this case
+ // we use the measure from the previuos round
+ min_rtt = prev_min_rtt;
+ }
+
+ // same for max_rtt
+ if (max_rtt != 0) {
+ prev_max_rtt = max_rtt;
+ } else {
+ max_rtt = prev_max_rtt;
+ }
+
+ if (min_rtt == 0) min_rtt = 1;
+ if (max_rtt == 0) max_rtt = 1;
+
+ min_RTT_history_.pushBack(min_rtt);
+ max_RTT_history_.pushBack(max_rtt);
+ min_rtt = UINT_MAX;
+ max_rtt = 0;
+
+ // do the same for min owd
+ if (min_owd != INT_MAX) {
+ prev_min_owd = min_owd;
+ } else {
+ min_owd = prev_min_owd;
+ }
+
+ if (min_owd != INT_MAX) {
+ OWD_history_.pushBack(min_owd);
+ min_owd = INT_MAX;
+ }
+
+ if (received_packets_ == 0)
+ rounds_without_packets_++;
+ else
+ rounds_without_packets_ = 0;
+ received_packets_ = 0;
+}
+
+uint32_t RTCDataPath::getPathId() { return path_id_; }
+
+double RTCDataPath::getQueuingDealy() {
+ if (queuing_delay == DBL_MAX) return 0;
+ return queuing_delay;
+}
+
+uint64_t RTCDataPath::getMinRtt() {
+ if (min_RTT_history_.size() != 0) return min_RTT_history_.begin();
+ return 0;
+}
+
+uint64_t RTCDataPath::getAvgRtt() { return std::round(avg_rtt_); }
+
+uint64_t RTCDataPath::getMaxRtt() {
+ if (max_RTT_history_.size() != 0) return max_RTT_history_.begin();
+ return 0;
+}
+
+int64_t RTCDataPath::getMinOwd() {
+ if (OWD_history_.size() != 0) return OWD_history_.begin();
+ return INT_MAX;
+}
+
+double RTCDataPath::getJitter() { return jitter_; }
+
+uint64_t RTCDataPath::getLastPacketTS() { return last_received_data_packet_; }
+
+uint32_t RTCDataPath::getPacketsLastRound() { return received_packets_; }
+
+void RTCDataPath::clearRtt() {
+ min_RTT_history_.clear();
+ max_RTT_history_.clear();
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_data_path.h b/libtransport/src/protocols/rtc/rtc_data_path.h
new file mode 100644
index 000000000..ba5201fe8
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_data_path.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <stdint.h>
+#include <utils/max_filter.h>
+#include <utils/min_filter.h>
+
+#include <climits>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+const double ALPHA_RTC = 0.125;
+const uint32_t HISTORY_LEN = 20; // 4 sec
+
+class RTCDataPath {
+ public:
+ RTCDataPath(uint32_t path_id);
+
+ public:
+ void insertRttSample(const utils::SteadyTime::Milliseconds &rtt,
+ bool is_probe);
+ void insertOwdSample(int64_t owd);
+ void computeInterArrivalGap(uint32_t segment_number);
+ void receivedNack();
+
+ uint32_t getPathId();
+ uint64_t getMinRtt();
+ uint64_t getAvgRtt();
+ uint64_t getMaxRtt();
+ double getQueuingDealy();
+ double getInterArrivalGap();
+ double getJitter();
+ bool isActive(); // pakets recevied from this path in the last rounds
+ bool pathToProducer(); // path from a producer
+ bool isValidProducer(); // path from a producer that is also active
+ uint64_t getLastPacketTS();
+ uint32_t getPacketsLastRound();
+
+ void clearRtt();
+
+ void roundEnd();
+
+ private:
+ uint32_t path_id_;
+
+ int64_t getMinOwd();
+
+ uint64_t min_rtt;
+ uint64_t prev_min_rtt;
+
+ uint64_t max_rtt;
+ uint64_t prev_max_rtt;
+
+ int64_t min_owd;
+ int64_t prev_min_owd;
+
+ double avg_owd;
+
+ double queuing_delay;
+
+ double jitter_;
+ int64_t last_owd_;
+
+ uint32_t largest_recv_seq_;
+ uint64_t largest_recv_seq_time_;
+ double avg_inter_arrival_;
+
+ // compute the avg rtt over one sec
+ uint64_t rtt_sum_;
+ uint64_t last_avg_rtt_compute_;
+ uint32_t rtt_samples_;
+ double avg_rtt_;
+
+ // flags to check if a path is active
+ // we considere a path active if it reaches a producer
+ //(not a cache) --aka we got at least one nack on this path--
+ // and if we receives packets
+ bool received_nacks_;
+ uint32_t received_packets_;
+ uint32_t rounds_without_packets_; // if we don't get any packet
+ // for MAX_ROUNDS_WITHOUT_PKTS
+ // we consider the path inactive
+ uint64_t last_received_data_packet_; // timestamp for the last data received
+ // on this path
+
+ utils::MinFilter<uint64_t> min_RTT_history_;
+ utils::MaxFilter<uint64_t> max_RTT_history_;
+ utils::MinFilter<int64_t> OWD_history_;
+};
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc
new file mode 100644
index 000000000..4bbd7eac0
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.cc
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/interfaces/notification.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_forwarding_strategy.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+using namespace transport::interface;
+
+const double FWD_MAX_QUEUE = 30.0; // ms
+const double FWD_MAX_RTT = MAX_RTT_BEFORE_FEC; // ms
+const double FWD_MAX_LOSS_RATE = 0.1;
+
+RTCForwardingStrategy::RTCForwardingStrategy()
+ : low_rate_app_(false),
+ init_(false),
+ forwarder_set_(false),
+ selected_strategy_(NONE),
+ current_strategy_(NONE),
+ rounds_since_last_set_(0),
+ portal_(nullptr),
+ state_(nullptr) {}
+
+RTCForwardingStrategy::~RTCForwardingStrategy() {}
+
+void RTCForwardingStrategy::setCallback(
+ interface::StrategyCallback&& callback) {
+ callback_ = std::move(callback);
+}
+
+void RTCForwardingStrategy::initFwdStrategy(
+ std::shared_ptr<core::Portal> portal, core::Prefix& prefix, RTCState* state,
+ interface::RtcTransportRecoveryStrategies strategy) {
+ switch (strategy) {
+ case interface::RtcTransportRecoveryStrategies::LOW_RATE_AND_BESTPATH:
+ init_ = true;
+ low_rate_app_ = true;
+ selected_strategy_ = BEST_PATH;
+ current_strategy_ = BEST_PATH;
+ break;
+ case interface::RtcTransportRecoveryStrategies::LOW_RATE_AND_REPLICATION:
+ init_ = true;
+ low_rate_app_ = true;
+ selected_strategy_ = REPLICATION;
+ current_strategy_ = REPLICATION;
+ break;
+ case interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_ALL_FWD_STRATEGIES:
+ init_ = true;
+ low_rate_app_ = true;
+ selected_strategy_ = BEST_PATH;
+ current_strategy_ = BEST_PATH;
+ break;
+ case interface::RtcTransportRecoveryStrategies::DELAY_AND_BESTPATH:
+ init_ = true;
+ low_rate_app_ = false;
+ selected_strategy_ = BEST_PATH;
+ current_strategy_ = BEST_PATH;
+ break;
+ case interface::RtcTransportRecoveryStrategies::DELAY_AND_REPLICATION:
+ init_ = true;
+ low_rate_app_ = false;
+ selected_strategy_ = REPLICATION;
+ current_strategy_ = REPLICATION;
+ break;
+ case interface::RtcTransportRecoveryStrategies::RECOVERY_OFF:
+ case interface::RtcTransportRecoveryStrategies::RTX_ONLY:
+ case interface::RtcTransportRecoveryStrategies::FEC_ONLY:
+ case interface::RtcTransportRecoveryStrategies::DELAY_BASED:
+ case interface::RtcTransportRecoveryStrategies::LOW_RATE:
+ case interface::RtcTransportRecoveryStrategies::FEC_ONLY_LOW_RES_LOSSES:
+ default:
+ // fwd strategies are not used
+ init_ = false;
+ }
+
+ if (init_) {
+ rounds_since_last_set_ = 0;
+ prefix_ = prefix;
+ portal_ = portal;
+ state_ = state;
+ }
+}
+
+void RTCForwardingStrategy::checkStrategy() {
+ strategy_t used_strategy = selected_strategy_;
+ if (used_strategy == BOTH) used_strategy = current_strategy_;
+ assert(used_strategy == BEST_PATH || used_strategy == REPLICATION ||
+ used_strategy == NONE);
+
+ notification::ForwardingStrategy strategy =
+ notification::ForwardingStrategy::NONE;
+ switch (used_strategy) {
+ case BEST_PATH:
+ strategy = notification::ForwardingStrategy::BEST_PATH;
+ break;
+ case REPLICATION:
+ strategy = notification::ForwardingStrategy::REPLICATION;
+ break;
+ default:
+ break;
+ }
+ callback_(strategy);
+
+ if (!init_) return;
+
+ if (selected_strategy_ == NONE) return;
+
+ if (selected_strategy_ == BEST_PATH) {
+ checkStrategyBestPath();
+ return;
+ }
+
+ if (selected_strategy_ == REPLICATION) {
+ checkStrategyReplication();
+ return;
+ }
+
+ checkStrategyBoth();
+}
+
+void RTCForwardingStrategy::checkStrategyBestPath() {
+ if (!forwarder_set_) {
+ setStrategy(BEST_PATH);
+ forwarder_set_ = true;
+ return;
+ }
+
+ if (low_rate_app_) {
+ // this is used for gaming
+ uint8_t qs = state_->getQualityScore();
+
+ if (qs >= 4 || rounds_since_last_set_ < 25) { // wait a least 5 sec
+ // between each switch
+ rounds_since_last_set_++;
+ return;
+ }
+
+ // try to switch path
+ setStrategy(BEST_PATH);
+ } else {
+ if (rounds_since_last_set_ < 25) { // wait a least 5 sec
+ // between each switch
+ rounds_since_last_set_++;
+ return;
+ }
+
+ double queue = state_->getQueuing();
+ double rtt = state_->getAvgRTT();
+ double loss_rate = state_->getPerSecondLossRate();
+
+ if (queue >= FWD_MAX_QUEUE || rtt >= FWD_MAX_RTT ||
+ loss_rate > FWD_MAX_LOSS_RATE) {
+ // try to switch path
+ setStrategy(BEST_PATH);
+ }
+ }
+}
+
+void RTCForwardingStrategy::checkStrategyReplication() {
+ if (!forwarder_set_) {
+ setStrategy(REPLICATION);
+ forwarder_set_ = true;
+ return;
+ }
+
+ // here we have nothing to do for the moment
+ return;
+}
+
+void RTCForwardingStrategy::checkStrategyBoth() {
+ if (!forwarder_set_) {
+ setStrategy(current_strategy_);
+ forwarder_set_ = true;
+ return;
+ }
+
+ checkStrategyBestPath();
+
+ // TODO
+ // for the moment we use only best path.
+ // for later:
+ // 1. if both paths are bad use replication
+ // 2. while using replication compute the effectiveness. if the majority of
+ // the packets are coming from a single path, try to use bestpath
+}
+
+void RTCForwardingStrategy::setStrategy(strategy_t strategy) {
+ rounds_since_last_set_ = 0;
+ current_strategy_ = strategy;
+ portal_->setForwardingStrategy(prefix_,
+ string_strategies_[current_strategy_]);
+}
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h
new file mode 100644
index 000000000..c2227e09f
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_forwarding_strategy.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <core/portal.h>
+#include <hicn/transport/interfaces/callbacks.h>
+#include <protocols/rtc/rtc_state.h>
+
+#include <array>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RTCForwardingStrategy {
+ public:
+ enum strategy_t {
+ BEST_PATH,
+ REPLICATION,
+ BOTH,
+ NONE,
+ };
+
+ RTCForwardingStrategy();
+ ~RTCForwardingStrategy();
+
+ void initFwdStrategy(std::shared_ptr<core::Portal> portal,
+ core::Prefix& prefix, RTCState* state,
+ interface::RtcTransportRecoveryStrategies strategy);
+
+ void checkStrategy();
+ void setCallback(interface::StrategyCallback&& callback);
+
+ private:
+ void checkStrategyBestPath();
+ void checkStrategyReplication();
+ void checkStrategyBoth();
+
+ void setStrategy(strategy_t strategy);
+
+ std::array<std::string, 4> string_strategies_ = {"bestpath", "replication",
+ "both", "none"};
+
+ bool low_rate_app_; // if set to true the best path strategy will
+ // trigger a path switch based on the quality
+ // score, otherwise it will use the RTT,
+ // queuing delay and loss rate
+ bool init_; // true if all val are initializes
+ bool forwarder_set_; // true if the strategy is been set at least
+ // once
+ strategy_t selected_strategy_; // this is the strategy selected using socket
+ // options. this can also be equal to BOTH
+ strategy_t current_strategy_; // if both strategies can be used this
+ // indicates the one that is currently in use
+ // that can be only replication or best path
+ uint32_t rounds_since_last_set_;
+ core::Prefix prefix_;
+ std::shared_ptr<core::Portal> portal_;
+ RTCState* state_;
+ interface::StrategyCallback callback_;
+};
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_indexer.h b/libtransport/src/protocols/rtc/rtc_indexer.h
new file mode 100644
index 000000000..f87fcaaa2
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_indexer.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <protocols/errors.h>
+#include <protocols/fec_utils.h>
+#include <protocols/indexer.h>
+#include <protocols/rtc/probe_handler.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/transport_protocol.h>
+#include <utils/suffix_strategy.h>
+
+#include <deque>
+
+namespace transport {
+
+namespace interface {
+class ConsumerSocket;
+}
+
+namespace protocol {
+
+namespace rtc {
+
+template <uint32_t LIMIT = MIN_PROBE_SEQ>
+class RtcIndexer : public Indexer {
+ public:
+ RtcIndexer(implementation::ConsumerSocket *icn_socket,
+ TransportProtocol *transport)
+ : Indexer(icn_socket, transport),
+ first_suffix_(1),
+ next_suffix_(first_suffix_),
+ fec_type_(fec::FECType::UNKNOWN),
+ n_fec_(0),
+ n_current_fec_(n_fec_) {}
+
+ RtcIndexer(RtcIndexer &&other) : Indexer(other) {}
+
+ ~RtcIndexer() {}
+
+ void reset() override {
+ next_suffix_ = first_suffix_;
+ n_fec_ = 0;
+ }
+
+ uint32_t checkNextSuffix() const override { return next_suffix_; }
+
+ uint32_t getNextSuffix() override {
+ if (isFec(next_suffix_)) {
+ if (n_current_fec_) {
+ auto ret = next_suffix_++;
+ n_current_fec_--;
+ return ret;
+ } else {
+ n_current_fec_ = n_fec_;
+ next_suffix_ = nextSource(next_suffix_);
+ }
+ } else if (!n_current_fec_) {
+ n_current_fec_ = n_fec_;
+ }
+
+ return (next_suffix_++ % LIMIT);
+ }
+
+ void setFirstSuffix(uint32_t suffix) override {
+ first_suffix_ = suffix % LIMIT;
+ }
+
+ uint32_t getFirstSuffix() const override { return first_suffix_; }
+
+ uint32_t jumpToIndex(uint32_t index) override {
+ next_suffix_ = index % LIMIT;
+ return next_suffix_;
+ }
+
+ void onContentObject(core::Interest &interest,
+ core::ContentObject &content_object,
+ bool reassembly) override {
+ if (reassembly) {
+ reassembly_->reassemble(content_object);
+ }
+ }
+
+ /**
+ * Retrieve the next segment to be reassembled.
+ */
+ uint32_t getNextReassemblySegment() override {
+ throw errors::RuntimeException(
+ "Get reassembly segment called on rtc indexer. RTC indexer does not "
+ "provide reassembly.");
+ }
+
+ bool isFinalSuffixDiscovered() override { return true; }
+
+ uint32_t getFinalSuffix() const override { return LIMIT; }
+
+ void enableFec(fec::FECType fec_type) override { fec_type_ = fec_type; }
+
+ void disableFec() override { fec_type_ = fec::FECType::UNKNOWN; }
+
+ void setNFec(uint32_t n_fec) override {
+ n_fec_ = n_fec;
+ n_current_fec_ = n_fec_;
+ }
+
+ uint32_t getNFec() const override { return n_fec_; }
+
+ bool isFec(uint32_t index) override {
+ return isFec(fec_type_, index, first_suffix_);
+ }
+
+ double getFecOverhead() const override {
+ if (fec_type_ == fec::FECType::UNKNOWN) {
+ return 0;
+ }
+
+ double k = (double)fec::FECUtils::getSourceSymbols(fec_type_);
+ return (double)n_fec_ / k;
+ }
+
+ double getMaxFecOverhead() const override {
+ if (fec_type_ == fec::FECType::UNKNOWN) {
+ return 0;
+ }
+
+ double k = (double)fec::FECUtils::getSourceSymbols(fec_type_);
+ double n = (double)fec::FECUtils::getBlockSymbols(fec_type_);
+ return (double)(n - k) / k;
+ }
+
+ static bool isFec(fec::FECType fec_type, uint32_t index,
+ uint32_t first_suffix) {
+ if (index < LIMIT) {
+ return fec::FECUtils::isFec(fec_type, index, first_suffix);
+ }
+
+ return false;
+ }
+
+ static uint32_t nextSource(fec::FECType fec_type, uint32_t index,
+ uint32_t first_suffix) {
+ return fec::FECUtils::nextSource(fec_type, index, first_suffix) % LIMIT;
+ }
+
+ private:
+ uint32_t nextSource(uint32_t index) {
+ return nextSource(fec_type_, index, first_suffix_);
+ }
+
+ private:
+ uint32_t first_suffix_;
+ uint32_t next_suffix_;
+ fec::FECType fec_type_;
+ bool fec_enabled_;
+ uint32_t n_fec_;
+ uint32_t n_current_fec_;
+};
+
+} // namespace rtc
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_ldr.cc b/libtransport/src/protocols/rtc/rtc_ldr.cc
new file mode 100644
index 000000000..6e88a8636
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_ldr.cc
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_ldr.h>
+#include <protocols/rtc/rtc_rs_delay.h>
+#include <protocols/rtc/rtc_rs_fec_only.h>
+#include <protocols/rtc/rtc_rs_low_rate.h>
+#include <protocols/rtc/rtc_rs_recovery_off.h>
+#include <protocols/rtc/rtc_rs_rtx_only.h>
+#include <protocols/rtc/rtc_state.h>
+
+#include <algorithm>
+#include <unordered_set>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RTCLossDetectionAndRecovery::RTCLossDetectionAndRecovery(
+ Indexer *indexer, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies type,
+ RecoveryStrategy::SendRtxCallback &&callback,
+ interface::StrategyCallback &&external_callback) {
+ if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) {
+ rs_ = std::make_shared<RecoveryStrategyRecoveryOff>(
+ indexer, std::move(callback), io_service, type,
+ std::move(external_callback));
+ } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED ||
+ type == interface::RtcTransportRecoveryStrategies::
+ DELAY_AND_BESTPATH ||
+ type == interface::RtcTransportRecoveryStrategies::
+ DELAY_AND_REPLICATION) {
+ rs_ = std::make_shared<RecoveryStrategyDelayBased>(
+ indexer, std::move(callback), io_service, type,
+ std::move(external_callback));
+ } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY ||
+ type == interface::RtcTransportRecoveryStrategies::
+ FEC_ONLY_LOW_RES_LOSSES) {
+ rs_ = std::make_shared<RecoveryStrategyFecOnly>(
+ indexer, std::move(callback), io_service, type,
+ std::move(external_callback));
+ } else if (type == interface::RtcTransportRecoveryStrategies::LOW_RATE ||
+ type == interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_BESTPATH ||
+ type == interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_REPLICATION ||
+ type == interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_ALL_FWD_STRATEGIES) {
+ rs_ = std::make_shared<RecoveryStrategyLowRate>(
+ indexer, std::move(callback), io_service, type,
+ std::move(external_callback));
+ } else {
+ // default
+ type = interface::RtcTransportRecoveryStrategies::RTX_ONLY;
+ rs_ = std::make_shared<RecoveryStrategyRtxOnly>(
+ indexer, std::move(callback), io_service, type,
+ std::move(external_callback));
+ }
+}
+
+RTCLossDetectionAndRecovery::~RTCLossDetectionAndRecovery() {}
+
+void RTCLossDetectionAndRecovery::changeRecoveryStrategy(
+ interface::RtcTransportRecoveryStrategies type) {
+ if (type == rs_->getType()) return;
+
+ rs_->updateType(type);
+ if (type == interface::RtcTransportRecoveryStrategies::RECOVERY_OFF) {
+ rs_ =
+ std::make_shared<RecoveryStrategyRecoveryOff>(std::move(*(rs_.get())));
+ } else if (type == interface::RtcTransportRecoveryStrategies::DELAY_BASED ||
+ type == interface::RtcTransportRecoveryStrategies::
+ DELAY_AND_BESTPATH ||
+ type == interface::RtcTransportRecoveryStrategies::
+ DELAY_AND_REPLICATION) {
+ rs_ = std::make_shared<RecoveryStrategyDelayBased>(std::move(*(rs_.get())));
+ } else if (type == interface::RtcTransportRecoveryStrategies::FEC_ONLY ||
+ type == interface::RtcTransportRecoveryStrategies::
+ FEC_ONLY_LOW_RES_LOSSES) {
+ rs_ = std::make_shared<RecoveryStrategyFecOnly>(std::move(*(rs_.get())));
+ } else if (type == interface::RtcTransportRecoveryStrategies::LOW_RATE ||
+ type == interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_BESTPATH ||
+ type == interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_REPLICATION ||
+ type == interface::RtcTransportRecoveryStrategies::
+ LOW_RATE_AND_ALL_FWD_STRATEGIES) {
+ rs_ = std::make_shared<RecoveryStrategyLowRate>(std::move(*(rs_.get())));
+ } else {
+ // default
+ rs_ = std::make_shared<RecoveryStrategyRtxOnly>(std::move(*(rs_.get())));
+ }
+}
+
+void RTCLossDetectionAndRecovery::onNewRound(bool in_sync) {
+ rs_->incRoundId();
+ rs_->onNewRound(in_sync);
+}
+
+bool RTCLossDetectionAndRecovery::onTimeout(uint32_t seq, bool lost) {
+ if (!lost) {
+ return detectLoss(seq, seq + 1, false);
+ } else {
+ rs_->onLostTimeout(seq);
+ }
+ return false;
+}
+
+bool RTCLossDetectionAndRecovery::onPacketRecoveredFec(uint32_t seq) {
+ rs_->receivedPacket(seq);
+ return false;
+}
+
+bool RTCLossDetectionAndRecovery::onDataPacketReceived(
+ const core::ContentObject &content_object) {
+ uint32_t seq = content_object.getName().getSuffix();
+ bool is_rtx = rs_->isRtx(seq);
+ rs_->receivedPacket(seq);
+ bool ret = false;
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "received data. add from "
+ << rs_->getState()->getHighestSeqReceived() + 1 << " to " << seq;
+ if (!is_rtx)
+ ret = detectLoss(rs_->getState()->getHighestSeqReceived() + 1, seq, false);
+
+ rs_->getState()->updateHighestSeqReceived(seq);
+ return ret;
+}
+
+bool RTCLossDetectionAndRecovery::onNackPacketReceived(
+ const core::ContentObject &nack) {
+ struct nack_packet_t *nack_pkt =
+ (struct nack_packet_t *)nack.getPayload()->data();
+ uint32_t production_seq = nack_pkt->getProductionSegment();
+ uint32_t seq = nack.getName().getSuffix();
+
+ // received a nack. we can try to recover all data packets between the last
+ // received data and the production seq in the nack. this is similar to the
+ // recption of a probe
+ // e.g.: the client receives packets 10 11 12 20 where 20 is a nack
+ // with productionSeq = 18. this says that all the packets between 12 and 18
+ // may got lost and we should ask them
+
+ rs_->receivedPacket(seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "received nack. add from "
+ << rs_->getState()->getHighestSeqReceived() + 1
+ << " to " << production_seq;
+
+ // if it is a future nack store it in the list set of nacked seq
+ if (production_seq <= seq) rs_->receivedFutureNack(seq);
+
+ // call the detectLoss function using the probe flag = true. in fact the
+ // losses detected using nacks are the same as the one detected using probes,
+ // we should not increase the loss counter
+ return detectLoss(rs_->getState()->getHighestSeqReceived() + 1,
+ production_seq, true);
+}
+
+bool RTCLossDetectionAndRecovery::onProbePacketReceived(
+ const core::ContentObject &probe) {
+ // we don't log the reception of a probe packet for the sentinel timer because
+ // probes are not taken into account into the sync window. we use them as
+ // future nacks to detect possible packets lost
+
+ uint32_t production_seq = RTCState::getProbeParams(probe).prod_seg;
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "received probe. add from "
+ << rs_->getState()->getHighestSeqReceived() + 1
+ << " to " << production_seq;
+
+ return detectLoss(rs_->getState()->getHighestSeqReceived() + 1,
+ production_seq, true);
+}
+
+bool RTCLossDetectionAndRecovery::detectLoss(uint32_t start, uint32_t stop,
+ bool recv_probe) {
+ if (start >= stop) return false;
+
+ // skip nacked packets
+ if (start <= rs_->getState()->getLastSeqNacked()) {
+ start = rs_->getState()->getLastSeqNacked() + 1;
+ }
+
+ // skip received or lost packets
+ if (start <= rs_->getState()->getHighestSeqReceived()) {
+ start = rs_->getState()->getHighestSeqReceived() + 1;
+ }
+
+ bool loss_detected = false;
+ for (uint32_t seq = start; seq < stop; seq++) {
+ if (rs_->getState()->getPacketState(seq) == PacketState::UNKNOWN) {
+ if (rs_->lossDetected(seq)) {
+ loss_detected = true;
+ if ((recv_probe || rs_->wasNacked(seq)) && !rs_->isFecOn()) {
+ // these losses were detected using a probe and fec is off.
+ // in this case most likelly the procotol is about to go out of sync
+ // and the packets are not really lost (e.g. increase in prod rate).
+ // for this reason we do not
+ // count the losses in the stats. Instead we do the following
+ // 1. send RTX for the packets in case they were really lost
+ // 2. return to the RTC protocol that a loss was detected using a
+ // probe. the protocol will switch to catch_up mode to increase the
+ // size of the window
+ rs_->requestPossibleLostPacket(seq);
+ } else {
+ // if fec is on we don't need to mask pontetial losses, so increase
+ // the loss rate
+ rs_->notifyNewLossDetedcted(seq);
+ }
+ }
+ }
+ }
+ return loss_detected;
+}
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_ldr.h b/libtransport/src/protocols/rtc/rtc_ldr.h
new file mode 100644
index 000000000..24f22ffed
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_ldr.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <hicn/transport/config.h>
+#include <hicn/transport/interfaces/socket_options_keys.h>
+// RtcTransportRecoveryStrategies
+#include <hicn/transport/core/asio_wrapper.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/name.h>
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+#include <functional>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RTCLossDetectionAndRecovery
+ : public std::enable_shared_from_this<RTCLossDetectionAndRecovery> {
+ public:
+ RTCLossDetectionAndRecovery(Indexer *indexer, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies type,
+ RecoveryStrategy::SendRtxCallback &&callback,
+ interface::StrategyCallback &&external_callback);
+
+ ~RTCLossDetectionAndRecovery();
+
+ void setState(RTCState *state) { rs_->setState(state); }
+ void setRateControl(RTCRateControl *rateControl) {
+ rs_->setRateControl(rateControl);
+ }
+
+ void setFecParams(uint32_t n, uint32_t k) { rs_->setFecParams(n, k); }
+
+ void setContentSharingMode() { rs_->setContentSharingMode(); }
+ void turnOnRecovery() { rs_->turnOnRecovery(); }
+ bool isRtxOn() { return rs_->isRtxOn(); }
+
+ void changeRecoveryStrategy(interface::RtcTransportRecoveryStrategies type);
+
+ void onNewRound(bool in_sync);
+
+ // the following functions return true if a loss is detected, false otherwise
+ bool onTimeout(uint32_t seq, bool lost);
+ bool onPacketRecoveredFec(uint32_t seq);
+ bool onDataPacketReceived(const core::ContentObject &content_object);
+ bool onNackPacketReceived(const core::ContentObject &nack);
+ bool onProbePacketReceived(const core::ContentObject &probe);
+
+ void clear() { rs_->clear(); }
+
+ bool isRtx(uint32_t seq) { return rs_->isRtx(seq); }
+ bool isPossibleLossWithNoRtx(uint32_t seq) {
+ return rs_->isPossibleLossWithNoRtx(seq);
+ }
+
+ uint64_t getRtxRtt(uint32_t seq) { return rs_->getRtxRtt(seq); }
+
+ private:
+ // returns true if a loss is detected, false otherwise
+ bool detectLoss(uint32_t start, uint32_t stop, bool recv_probe);
+
+ std::shared_ptr<RecoveryStrategy> rs_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_packet.h b/libtransport/src/protocols/rtc/rtc_packet.h
new file mode 100644
index 000000000..ffbbd78fd
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_packet.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ */
+
+/* data packet
+ * +-----------------------------------------+
+ * | uint64_t: timestamp |
+ * | |
+ * +-----------------------------------------+
+ * | uint32_t: prod rate (bytes per sec) |
+ * +-----------------------------------------+
+ * | payload |
+ * | ... |
+ */
+
+/* nack packet
+ * +-----------------------------------------+
+ * | uint64_t: timestamp |
+ * | |
+ * +-----------------------------------------+
+ * | uint32_t: prod rate (bytes per sec) |
+ * +-----------------------------------------+
+ * | uint32_t: current seg in production |
+ * +-----------------------------------------+
+ */
+
+/* aggregated packets
+ * +---------------------------------+
+ * |c| #pkts | len1 | len2 | .... |
+ * +----------------------------------
+ *
+ * +---------------------------------+
+ * |c| #pkts | resv | len 1 |
+ * +----------------------------------
+ *
+ * aggregated packets header.
+ * header position. just after the data packet header
+ *
+ * c: 1 bit: 0 8bit encoding, 1 16bit encoding
+ * #pkts: 7 bits: number of application packets contained
+ * 8bits encoding:
+ * lenX: 8 bits: len in bites of packet X
+ * 16bits econding:
+ * resv: 8 bits: reserved field (unused)
+ * lenX: 16bits: len in bytes of packet X
+ */
+
+#pragma once
+#ifndef _WIN32
+#include <arpa/inet.h>
+#else
+#include <hicn/transport/portability/win_portability.h>
+#endif
+
+#include <hicn/transport/portability/endianess.h>
+
+#include <cstring>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+const uint32_t DATA_HEADER_SIZE = 12; // bytes
+ // XXX: sizeof(data_packet_t) is 16
+ // beacuse of padding
+const uint32_t NACK_HEADER_SIZE = 16;
+
+struct data_packet_t {
+ uint64_t timestamp;
+ uint32_t prod_rate;
+
+ inline uint64_t getTimestamp() const {
+ return portability::net_to_host(timestamp);
+ }
+ inline void setTimestamp(uint64_t time) {
+ timestamp = portability::host_to_net(time);
+ }
+
+ inline uint32_t getProductionRate() const {
+ return portability::net_to_host(prod_rate);
+ }
+ inline void setProductionRate(uint32_t rate) {
+ prod_rate = portability::host_to_net(rate);
+ }
+};
+
+struct nack_packet_t {
+ uint64_t timestamp;
+ uint32_t prod_rate;
+ uint32_t prod_seg;
+
+ inline uint64_t getTimestamp() const {
+ return portability::net_to_host(timestamp);
+ }
+ inline void setTimestamp(uint64_t time) {
+ timestamp = portability::host_to_net(time);
+ }
+
+ inline uint32_t getProductionRate() const {
+ return portability::net_to_host(prod_rate);
+ }
+ inline void setProductionRate(uint32_t rate) {
+ prod_rate = portability::host_to_net(rate);
+ }
+
+ inline uint32_t getProductionSegment() const {
+ return portability::net_to_host(prod_seg);
+ }
+ inline void setProductionSegment(uint32_t seg) {
+ prod_seg = portability::host_to_net(seg);
+ }
+};
+
+class AggrPktHeader {
+ public:
+ // XXX buf always point to the payload after the data header
+ AggrPktHeader(uint8_t *buf, uint16_t max_packet_len, uint16_t pkt_number)
+ : buf_(buf), pkt_num_(pkt_number) {
+ *buf_ = 0; // reset the first byte to correctly add the header
+ // encoding and the packet number
+ if (max_packet_len > 0xff) {
+ setAggrPktEncoding16bit();
+ } else {
+ setAggrPktEncoding8bit();
+ }
+ setAggrPktNUmber(pkt_number);
+ header_len_ = computeHeaderLen();
+ memset(buf_ + 1, 0, header_len_ - 1);
+ }
+
+ // XXX buf always point to the payload after the data header
+ AggrPktHeader(uint8_t *buf) : buf_(buf) {
+ encoding_ = getAggrPktEncoding();
+ pkt_num_ = getAggrPktNumber();
+ header_len_ = computeHeaderLen();
+ }
+
+ ~AggrPktHeader(){};
+
+ int addPacketToHeader(uint8_t index, uint16_t len) {
+ if (index > pkt_num_) return -1;
+
+ setAggrPktLen(index, len);
+ return 0;
+ }
+
+ int getPointerToPacket(uint8_t index, uint8_t **pkt_ptr, uint16_t *pkt_len) {
+ if (index > pkt_num_) return -1;
+
+ uint16_t len = 0;
+ for (int i = 0; i < index; i++)
+ len += getAggrPktLen(i); // sum the pkts len from 0 to index - 1
+
+ uint16_t offset = len + header_len_;
+ *pkt_ptr = buf_ + offset;
+ *pkt_len = getAggrPktLen(index);
+ return 0;
+ }
+
+ int getPacketOffsets(uint8_t index, uint16_t *pkt_offset, uint16_t *pkt_len) {
+ if (index > pkt_num_) return -1;
+
+ uint16_t len = 0;
+ for (int i = 0; i < index; i++)
+ len += getAggrPktLen(i); // sum the pkts len from 0 to index - 1
+
+ uint16_t offset = len + header_len_;
+ *pkt_offset = offset;
+ *pkt_len = getAggrPktLen(index);
+
+ return 0;
+ }
+
+ uint8_t *getPayloadAppendPtr() { return buf_ + header_len_; }
+
+ uint16_t getHeaderLen() { return header_len_; }
+
+ uint8_t getNumberOfPackets() { return pkt_num_; }
+
+ private:
+ inline uint16_t computeHeaderLen() const {
+ uint16_t len = 4; // min len in bytes
+ if (!encoding_) {
+ while (pkt_num_ >= len) {
+ len += 4;
+ }
+ } else {
+ while (pkt_num_ * 2 >= len) {
+ len += 4;
+ }
+ }
+ return len;
+ }
+
+ inline uint8_t getAggrPktEncoding() const {
+ // get the first bit of the first byte
+ return (*buf_ >> 7);
+ }
+
+ inline void setAggrPktEncoding8bit() {
+ // reset the first bit of the first byte
+ encoding_ = 0;
+ *buf_ &= 0x7f;
+ }
+
+ inline void setAggrPktEncoding16bit() {
+ // set the first bit of the first byte
+ encoding_ = 1;
+ *buf_ ^= 0x80;
+ }
+
+ inline uint8_t getAggrPktNumber() const {
+ // return the first byte with the first bit = 0
+ return (*buf_ & 0x7f);
+ }
+
+ inline void setAggrPktNUmber(uint8_t val) {
+ // set the val without modifying the first bit
+ *buf_ &= 0x80; // reset everithing but the first bit
+ val &= 0x7f; // reset the first bit
+ *buf_ |= val; // or the vals, done!
+ }
+
+ inline uint16_t getAggrPktLen(uint8_t pkt_index) const {
+ pkt_index++;
+ if (!encoding_) { // 8 bits
+ return (uint16_t) * (buf_ + pkt_index);
+ } else { // 16 bits
+ uint16_t *buf_16 = (uint16_t *)buf_;
+ return portability::net_to_host(*(buf_16 + pkt_index));
+ }
+ }
+
+ inline void setAggrPktLen(uint8_t pkt_index, uint16_t len) {
+ pkt_index++;
+ if (!encoding_) { // 8 bits
+ *(buf_ + pkt_index) = (uint8_t)len;
+ } else { // 16 bits
+ uint16_t *buf_16 = (uint16_t *)buf_;
+ *(buf_16 + pkt_index) = portability::host_to_net(len);
+ }
+ }
+
+ uint8_t *buf_;
+ uint8_t encoding_;
+ uint8_t pkt_num_;
+ uint16_t header_len_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc.h b/libtransport/src/protocols/rtc/rtc_rc.h
new file mode 100644
index 000000000..62636ce40
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <protocols/rtc/rtc_state.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RTCRateControl : public std::enable_shared_from_this<RTCRateControl> {
+ public:
+ RTCRateControl()
+ : rc_on_(false),
+ congestion_win_(1000000), // init the win to a large number
+ congestion_state_(CongestionState::Normal),
+ protocol_state_(nullptr) {}
+
+ virtual ~RTCRateControl() = default;
+
+ void turnOnRateControl() { rc_on_ = true; }
+ void setState(std::shared_ptr<RTCState> state) { protocol_state_ = state; };
+ uint32_t getCongestionWindow() { return congestion_win_; };
+ bool inCongestionState() {
+ if (congestion_state_ == CongestionState::Congested) return true;
+ return false;
+ }
+
+ virtual void onNewRound(double round_len) = 0;
+ virtual void onDataPacketReceived(const core::ContentObject &content_object,
+ bool compute_stats) = 0;
+
+ protected:
+ enum class CongestionState { Normal = 0, Underuse = 1, Congested = 2, Last };
+
+ protected:
+ bool rc_on_;
+ uint32_t congestion_win_;
+ CongestionState congestion_state_;
+
+ std::shared_ptr<RTCState> protocol_state_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc
new file mode 100644
index 000000000..6cd3094b5
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rc_congestion_detection.h>
+
+#include <algorithm>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RTCRateControlCongestionDetection::RTCRateControlCongestionDetection()
+ : rounds_without_congestion_(4), last_queue_(0) {} // must be > 3
+
+RTCRateControlCongestionDetection::~RTCRateControlCongestionDetection() {}
+
+void RTCRateControlCongestionDetection::onNewRound(double round_len) {
+ if (!rc_on_) return;
+
+ double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC;
+ double queue = protocol_state_->getQueuing();
+
+ if (rtt == 0.0) return; // no info from the producer
+
+ if (last_queue_ == queue) {
+ // if last_queue == queue the consumer didn't receive any
+ // packet from the producer. we do not change the current congestion state.
+ // we just increase the counter of rounds whithout congestion if needed
+ // (in case of congestion the counter is already set to 0)
+ if (congestion_state_ == CongestionState::Normal)
+ rounds_without_congestion_++;
+ } else {
+ if (queue > MAX_QUEUING_DELAY) {
+ // here we detect congestion.
+ congestion_state_ = CongestionState::Congested;
+ rounds_without_congestion_ = 0;
+ } else {
+ // wait 3 rounds before switch back to no congestion
+ if (rounds_without_congestion_ > 3) {
+ // nothing bad is happening
+ congestion_state_ = CongestionState::Normal;
+ }
+ rounds_without_congestion_++;
+ }
+ last_queue_ = queue;
+ }
+}
+
+void RTCRateControlCongestionDetection::onDataPacketReceived(
+ const core::ContentObject &content_object, bool compute_stats) {
+ // nothing to do
+ return;
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h
new file mode 100644
index 000000000..9afa6c39a
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc_congestion_detection.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <hicn/transport/utils/shared_ptr_utils.h>
+#include <protocols/rtc/rtc_rc.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RTCRateControlCongestionDetection : public RTCRateControl {
+ public:
+ RTCRateControlCongestionDetection();
+
+ ~RTCRateControlCongestionDetection();
+
+ void onNewRound(double round_len);
+ void onDataPacketReceived(const core::ContentObject &content_object,
+ bool compute_stats);
+
+ auto shared_from_this() { return utils::shared_from(this); }
+
+ private:
+ uint32_t rounds_without_congestion_;
+ double last_queue_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_iat.cc b/libtransport/src/protocols/rtc/rtc_rc_iat.cc
new file mode 100644
index 000000000..f06f377f3
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc_iat.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rc_iat.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RTCRateControlIAT::RTCRateControlIAT()
+ : rounds_since_last_drop_(0),
+ rounds_without_congestion_(0),
+ rounds_with_congestion_(0),
+ last_queue_(0),
+ last_rcv_time_(0),
+ last_prod_time_(0),
+ last_seq_number_(0),
+ target_rate_avg_(0),
+ round_index_(0),
+ congestion_cause_(CongestionCause::UNKNOWN) {}
+
+RTCRateControlIAT::~RTCRateControlIAT() {}
+
+void RTCRateControlIAT::onNewRound(double round_len) {
+ if (!rc_on_) return;
+
+ double received_rate = protocol_state_->getReceivedRate() +
+ protocol_state_->getRecoveredFecRate();
+
+ double target_rate =
+ protocol_state_->getProducerRate(); // * PRODUCTION_RATE_FRACTION;
+ double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC;
+ // double packet_size = protocol_state_->getAveragePacketSize();
+ double queue = protocol_state_->getQueuing();
+
+ if (rtt == 0.0) return; // no info from the producer
+
+ CongestionState prev_congestion_state = congestion_state_;
+
+ target_rate_avg_ = target_rate_avg_ * (1 - MOVING_AVG_ALPHA) +
+ target_rate * MOVING_AVG_ALPHA;
+
+ if (prev_congestion_state == CongestionState::Congested) {
+ if (queue > MAX_QUEUING_DELAY || last_queue_ == queue) {
+ congestion_state_ = CongestionState::Congested;
+
+ received_rate_.push_back(received_rate);
+ target_rate_.push_back(target_rate);
+
+ // We assume the cause does not change
+ // Note that the first assumption about the cause could be wrong
+ // the cause of congestion could change
+ if (congestion_cause_ == CongestionCause::UNKNOWN)
+ if (rounds_with_congestion_ >= 1)
+ congestion_cause_ = apply_classification_tree(
+ rounds_with_congestion_ > ROUND_TO_WAIT_FORCE_DECISION);
+
+ rounds_with_congestion_++;
+ } else {
+ congestion_state_ = CongestionState::Normal;
+
+ // clear past history
+ reset_congestion_statistics();
+
+ // TODO maybe we can use some of these values for the stdev of the
+ // congestion mode
+ for (int i = 0; i < ROUND_HISTORY_SIZE; i++) {
+ iat_on_hold_[i].clear();
+ }
+ }
+ } else if (queue > MAX_QUEUING_DELAY) {
+ if (prev_congestion_state == CongestionState::Normal) {
+ rounds_with_congestion_ = 0;
+
+ if (rounds_without_congestion_ > ROUND_TO_RESET_CAUSE)
+ congestion_cause_ = CongestionCause::UNKNOWN;
+ }
+ congestion_state_ = CongestionState::Congested;
+ received_rate_.push_back(received_rate);
+ target_rate_.push_back(target_rate);
+ } else {
+ // nothing bad is happening
+ congestion_state_ = CongestionState::Normal;
+ reset_congestion_statistics();
+
+ int past_index = (round_index_ + 1) % ROUND_HISTORY_SIZE;
+ for (std::vector<double>::iterator it = iat_on_hold_[past_index].begin();
+ it != iat_on_hold_[past_index].end(); ++it) {
+ congestion_free_iat_.push_back(*it);
+ if (congestion_free_iat_.size() > 50) {
+ congestion_free_iat_.erase(congestion_free_iat_.begin());
+ }
+ }
+ iat_on_hold_[past_index].clear();
+ round_index_ = (round_index_ + 1) % ROUND_HISTORY_SIZE;
+ }
+
+ last_queue_ = queue;
+
+ if (congestion_state_ == CongestionState::Congested) {
+ if (prev_congestion_state == CongestionState::Normal) {
+ // init the congetion window using the received rate
+ // disabling for the moment the congestion window setup
+ // congestion_win_ = (uint32_t)ceil(received_rate * rtt / packet_size);
+ rounds_since_last_drop_ = ROUNDS_BEFORE_TAKE_ACTION + 1;
+ }
+
+ if (rounds_since_last_drop_ >= ROUNDS_BEFORE_TAKE_ACTION) {
+ // disabling for the moment the congestion window setup
+ // uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR;
+ // congestion_win_ = std::max(win, WIN_MIN);
+ rounds_since_last_drop_ = 0;
+ return;
+ }
+
+ rounds_since_last_drop_++;
+ }
+
+ if (congestion_state_ == CongestionState::Normal) {
+ if (prev_congestion_state == CongestionState::Congested) {
+ rounds_without_congestion_ = 0;
+ }
+
+ rounds_without_congestion_++;
+ if (rounds_without_congestion_ < ROUNDS_BEFORE_TAKE_ACTION) return;
+
+ // disabling for the moment the congestion window setup
+ // congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR;
+ // congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX);
+ }
+
+ if (received_rate_.size() > 1000)
+ received_rate_.erase(received_rate_.begin());
+ if (target_rate_.size() > 1000) target_rate_.erase(target_rate_.begin());
+}
+
+void RTCRateControlIAT::onDataPacketReceived(
+ const core::ContentObject &content_object, bool compute_stats) {
+ core::ParamsRTC params = RTCState::getDataParams(content_object);
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ uint32_t segment_number = content_object.getName().getSuffix();
+
+ if (segment_number == (last_seq_number_ + 1) && compute_stats) {
+ uint64_t iat = now - last_rcv_time_;
+ uint64_t ist = params.timestamp - last_prod_time_;
+ if (now >= last_rcv_time_ && params.timestamp > last_prod_time_) {
+ if (iat >= ist && ist < MIN_IST_VALUE) {
+ if (congestion_state_ == CongestionState::Congested) {
+ iat_.push_back((iat - ist));
+ } else {
+ // no congestion, but we do not always add new values, but only when
+ // there is no sign of congestion
+ double queue = protocol_state_->getQueuing();
+ if (queue <= CONGESTION_FREE_QUEUEING_DELAY) {
+ iat_on_hold_[round_index_].push_back((iat - ist));
+ }
+ }
+ }
+ }
+ }
+
+ last_seq_number_ = segment_number;
+ last_rcv_time_ = now;
+ last_prod_time_ = params.timestamp;
+
+ if (iat_.size() > 1000) iat_.erase(iat_.begin());
+ return;
+}
+
+CongestionCause RTCRateControlIAT::apply_classification_tree(bool force_reply) {
+ if (iat_.size() <= 2 || received_rate_.size() < 2)
+ return CongestionCause::UNKNOWN;
+
+ double received_ratio = 0;
+ double iat_ratio = 0;
+ double iat_stdev = compute_iat_stdev(iat_);
+ double iat_congestion_free_stdev = compute_iat_stdev(congestion_free_iat_);
+
+ double iat_avg = 0.0;
+
+ double recv_avg = 0.0;
+ double recv_max = 0.0;
+
+ double target_rate_avg = 0.0;
+
+ int counter = 0;
+ std::vector<double>::reverse_iterator target_it = target_rate_.rbegin();
+ for (std::vector<double>::reverse_iterator it = received_rate_.rbegin();
+ it != received_rate_.rend(); ++it) {
+ recv_avg += *it;
+ target_rate_avg += *target_it;
+ if (counter < ROUND_HISTORY_SIZE)
+ if (recv_max < *it) {
+ recv_max = *it; // we consider only the last 2 seconds
+ }
+ counter++;
+ target_it++;
+ }
+ recv_avg = recv_avg / received_rate_.size();
+ target_rate_avg = target_rate_avg / target_rate_.size();
+
+ for (std::vector<double>::iterator it = iat_.begin(); it != iat_.end();
+ ++it) {
+ iat_avg += *it;
+ }
+ iat_avg = iat_avg / iat_.size();
+
+ double congestion_free_iat_avg = 0.0;
+ for (std::vector<double>::iterator it = congestion_free_iat_.begin();
+ it != congestion_free_iat_.end(); ++it) {
+ congestion_free_iat_avg += *it;
+ }
+ congestion_free_iat_avg =
+ congestion_free_iat_avg / congestion_free_iat_.size();
+
+ received_ratio = recv_avg / target_rate_avg;
+
+ iat_ratio = iat_stdev / iat_congestion_free_stdev;
+
+ CongestionCause congestion_cause = CongestionCause::UNKNOWN;
+ // applying classification tree model
+ if (received_ratio <= 0.87)
+ if (iat_stdev <= 6.48)
+ if (received_ratio <= 0.83)
+ congestion_cause = CongestionCause::LINK_CAPACITY;
+ else if (force_reply)
+ congestion_cause = CongestionCause::LINK_CAPACITY;
+ else
+ congestion_cause = CongestionCause::UNKNOWN; // accuracy is too low
+ else if (iat_ratio <= 2.46)
+ if (force_reply)
+ congestion_cause = CongestionCause::LINK_CAPACITY;
+ else
+ congestion_cause = CongestionCause::UNKNOWN; // accuracy is too low
+ else
+ congestion_cause = CongestionCause::COMPETING_CROSS_TRAFFIC;
+ else if (received_ratio <= 0.913 && iat_stdev <= 0.784)
+ congestion_cause = CongestionCause::LINK_CAPACITY;
+ else
+ congestion_cause = CongestionCause::COMPETING_CROSS_TRAFFIC;
+
+ return congestion_cause;
+}
+
+void RTCRateControlIAT::reset_congestion_statistics() {
+ iat_.clear();
+ received_rate_.clear();
+ target_rate_.clear();
+}
+
+double RTCRateControlIAT::compute_iat_stdev(std::vector<double> v) {
+ if (v.size() == 0) return 0;
+
+ float sum = 0.0, mean, standard_deviation = 0.0;
+ for (std::vector<double>::iterator it = v.begin(); it != v.end(); it++) {
+ sum += *it;
+ }
+
+ mean = sum / v.size();
+ for (std::vector<double>::iterator it = v.begin(); it != v.end(); it++) {
+ standard_deviation += pow(*it - mean, 2);
+ }
+ return sqrt(standard_deviation / v.size());
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_iat.h b/libtransport/src/protocols/rtc/rtc_rc_iat.h
new file mode 100644
index 000000000..715637807
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc_iat.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <hicn/transport/utils/shared_ptr_utils.h>
+#include <protocols/rtc/rtc_rc.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+const int ROUND_HISTORY_SIZE = 10; // equivalent to two seconds
+const int ROUND_TO_WAIT_FORCE_DECISION = 5;
+
+// once congestion is gone, we need to wait for k rounds before changing the
+// congestion cause in the case it appears again
+const int ROUND_TO_RESET_CAUSE = 5;
+
+const int MIN_IST_VALUE = 150; // samples of ist larger than 150ms are
+ // discarded
+const double CONGESTION_FREE_QUEUEING_DELAY = 10;
+
+enum class CongestionCause : uint8_t {
+ COMPETING_CROSS_TRAFFIC,
+ FRIENDLY_CROSS_TRAFFIC,
+ UNKNOWN_CROSS_TRAFFIC,
+ LINK_CAPACITY,
+ UNKNOWN
+};
+
+class RTCRateControlIAT : public RTCRateControl {
+ public:
+ RTCRateControlIAT();
+
+ ~RTCRateControlIAT();
+
+ void onNewRound(double round_len);
+ void onDataPacketReceived(const core::ContentObject &content_object,
+ bool compute_stats);
+
+ auto shared_from_this() { return utils::shared_from(this); }
+
+ private:
+ void reset_congestion_statistics();
+
+ double compute_iat_stdev(std::vector<double> v);
+
+ CongestionCause apply_classification_tree(bool force_reply);
+
+ private:
+ uint32_t rounds_since_last_drop_;
+ uint32_t rounds_without_congestion_;
+ uint32_t rounds_with_congestion_;
+ double last_queue_;
+ uint64_t last_rcv_time_;
+ uint64_t last_prod_time_;
+ uint32_t last_seq_number_;
+ double target_rate_avg_;
+
+ // Iat values are not immediately added to the congestion free set of values
+ std::array<std::vector<double>, ROUND_HISTORY_SIZE> iat_on_hold_;
+ uint32_t round_index_;
+
+ // with congestion statistics
+ std::vector<double> iat_;
+ std::vector<double> received_rate_;
+ std::vector<double> target_rate_;
+
+ // congestion free statistics
+ std::vector<double> congestion_free_iat_;
+
+ CongestionCause congestion_cause_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.cc b/libtransport/src/protocols/rtc/rtc_rc_queue.cc
new file mode 100644
index 000000000..ecabc5205
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc_queue.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rc_queue.h>
+
+#include <algorithm>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RTCRateControlQueue::RTCRateControlQueue()
+ : rounds_since_last_drop_(0),
+ rounds_without_congestion_(0),
+ last_queue_(0) {}
+
+RTCRateControlQueue::~RTCRateControlQueue() {}
+
+void RTCRateControlQueue::onNewRound(double round_len) {
+ if (!rc_on_) return;
+
+ double received_rate = protocol_state_->getReceivedRate();
+ double target_rate =
+ protocol_state_->getProducerRate() * PRODUCTION_RATE_FRACTION;
+ double rtt = (double)protocol_state_->getMinRTT() / MILLI_IN_A_SEC;
+ double packet_size = protocol_state_->getAveragePacketSize();
+ double queue = protocol_state_->getQueuing();
+
+ if (rtt == 0.0) return; // no info from the producer
+
+ CongestionState prev_congestion_state = congestion_state_;
+
+ if (prev_congestion_state == CongestionState::Normal &&
+ received_rate >= target_rate) {
+ // if the queue is high in this case we are most likelly fighting with
+ // a TCP flow and there is enough bandwidth to match the producer rate
+ congestion_state_ = CongestionState::Normal;
+ } else if (queue > MAX_QUEUING_DELAY || last_queue_ == queue) {
+ // here we detect congestion. in the case that last_queue == queue
+ // the consumer didn't receive any packet from the producer so we
+ // consider this case as congestion
+ // TODO: wath happen in case of high loss rate?
+ congestion_state_ = CongestionState::Congested;
+ } else {
+ // nothing bad is happening
+ congestion_state_ = CongestionState::Normal;
+ }
+
+ last_queue_ = queue;
+
+ if (congestion_state_ == CongestionState::Congested) {
+ if (prev_congestion_state == CongestionState::Normal) {
+ // init the congetion window using the received rate
+ congestion_win_ = (uint32_t)ceil(received_rate * rtt / packet_size);
+ rounds_since_last_drop_ = ROUNDS_BEFORE_TAKE_ACTION + 1;
+ }
+
+ if (rounds_since_last_drop_ >= ROUNDS_BEFORE_TAKE_ACTION) {
+ uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR;
+ congestion_win_ = std::max(win, WIN_MIN);
+ rounds_since_last_drop_ = 0;
+ return;
+ }
+
+ rounds_since_last_drop_++;
+ }
+
+ if (congestion_state_ == CongestionState::Normal) {
+ if (prev_congestion_state == CongestionState::Congested) {
+ rounds_without_congestion_ = 0;
+ }
+
+ rounds_without_congestion_++;
+ if (rounds_without_congestion_ < ROUNDS_BEFORE_TAKE_ACTION) return;
+
+ congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR;
+ congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX);
+ }
+}
+
+void RTCRateControlQueue::onDataPacketReceived(
+ const core::ContentObject &content_object, bool compute_stats) {
+ // nothing to do
+ return;
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.h b/libtransport/src/protocols/rtc/rtc_rc_queue.h
new file mode 100644
index 000000000..cdf78fd47
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rc_queue.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <hicn/transport/utils/shared_ptr_utils.h>
+#include <protocols/rtc/rtc_rc.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RTCRateControlQueue : public RTCRateControl {
+ public:
+ RTCRateControlQueue();
+
+ ~RTCRateControlQueue();
+
+ void onNewRound(double round_len);
+ void onDataPacketReceived(const core::ContentObject &content_object,
+ bool compute_stats);
+
+ auto shared_from_this() { return utils::shared_from(this); }
+
+ private:
+ uint32_t rounds_since_last_drop_;
+ uint32_t rounds_without_congestion_;
+ double last_queue_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.cc b/libtransport/src/protocols/rtc/rtc_reassembly.cc
new file mode 100644
index 000000000..b1b0fcaba
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_reassembly.cc
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <implementation/socket_consumer.h>
+#include <protocols/rtc/rtc_reassembly.h>
+#include <protocols/transport_protocol.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RtcReassembly::RtcReassembly(implementation::ConsumerSocket* icn_socket,
+ TransportProtocol* transport_protocol)
+ : DatagramReassembly(icn_socket, transport_protocol) {
+ is_setup_ = false;
+}
+
+void RtcReassembly::reassemble(core::ContentObject& content_object) {
+ if (!is_setup_) {
+ is_setup_ = true;
+ reassembly_consumer_socket_->getSocketOption(
+ interface::RtcTransportOptions::AGGREGATED_DATA, data_aggregation_);
+ }
+
+ auto read_buffer = content_object.getPayload();
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Size of payload: " << read_buffer->length();
+
+ read_buffer->trimStart(transport_protocol_->transportHeaderLength(false));
+
+ if (data_aggregation_) {
+ rtc::AggrPktHeader hdr((uint8_t*)read_buffer->data());
+
+ for (uint8_t i = 0; i < hdr.getNumberOfPackets(); i++) {
+ std::unique_ptr<utils::MemBuf> segment = read_buffer->clone();
+
+ uint16_t pkt_start = 0;
+ uint16_t pkt_len = 0;
+ int res = hdr.getPacketOffsets(i, &pkt_start, &pkt_len);
+ if (res == -1) {
+ // this should not happen
+ break;
+ }
+
+ segment->trimStart(pkt_start);
+ segment->trimEnd(segment->length() - pkt_len);
+
+ Reassembly::read_buffer_ = std::move(segment);
+ Reassembly::notifyApplication();
+ }
+ } else {
+ Reassembly::read_buffer_ = std::move(read_buffer);
+ Reassembly::notifyApplication();
+ }
+}
+
+void RtcReassembly::reassemble(utils::MemBuf& buffer, uint32_t suffix) {
+ if (!is_setup_) {
+ is_setup_ = true;
+ reassembly_consumer_socket_->getSocketOption(
+ interface::RtcTransportOptions::AGGREGATED_DATA, data_aggregation_);
+ }
+
+ if (data_aggregation_) {
+ rtc::AggrPktHeader hdr((uint8_t*)buffer.data());
+
+ for (uint8_t i = 0; i < hdr.getNumberOfPackets(); i++) {
+ std::unique_ptr<utils::MemBuf> segment = buffer.clone();
+
+ uint16_t pkt_start = 0;
+ uint16_t pkt_len = 0;
+ int res = hdr.getPacketOffsets(i, &pkt_start, &pkt_len);
+ if (res == -1) {
+ // this should not happen
+ break;
+ }
+
+ segment->trimStart(pkt_start);
+ segment->trimEnd(segment->length() - pkt_len);
+
+ Reassembly::read_buffer_ = std::move(segment);
+ Reassembly::notifyApplication();
+ }
+
+ } else {
+ Reassembly::read_buffer_ = buffer.cloneOne();
+ Reassembly::notifyApplication();
+ }
+}
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.h b/libtransport/src/protocols/rtc/rtc_reassembly.h
new file mode 100644
index 000000000..132004605
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_reassembly.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <glog/logging.h>
+#include <protocols/datagram_reassembly.h>
+#include <protocols/rtc/rtc_consts.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RtcReassembly : public DatagramReassembly {
+ public:
+ RtcReassembly(implementation::ConsumerSocket *icn_socket,
+ 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
+} // namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc
new file mode 100644
index 000000000..257fdd09b
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.cc
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <hicn/transport/interfaces/notification.h>
+#include <hicn/transport/interfaces/socket_options_keys.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+using namespace transport::interface;
+
+RecoveryStrategy::RecoveryStrategy(
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service,
+ bool use_rtx, bool use_fec,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback)
+ : rs_type_(rs_type),
+ recovery_on_(false),
+ content_sharing_mode_(false),
+ rtx_during_fec_(0),
+ next_rtx_timer_(MAX_TIMER_RTX),
+ send_rtx_callback_(std::move(callback)),
+ indexer_(indexer),
+ round_id_(0),
+ last_fec_used_(0),
+ callback_(std::move(external_callback)) {
+ setRtxFec(use_rtx, use_fec);
+ timer_ = std::make_unique<asio::steady_timer>(io_service);
+}
+
+RecoveryStrategy::RecoveryStrategy(RecoveryStrategy &&rs)
+ : rs_type_(rs.rs_type_),
+ content_sharing_mode_(rs.content_sharing_mode_),
+ rtx_during_fec_(0),
+ rtx_state_(std::move(rs.rtx_state_)),
+ rtx_timers_(std::move(rs.rtx_timers_)),
+ recover_with_fec_(std::move(rs.recover_with_fec_)),
+ timer_(std::move(rs.timer_)),
+ next_rtx_timer_(std::move(rs.next_rtx_timer_)),
+ send_rtx_callback_(std::move(rs.send_rtx_callback_)),
+ n_(std::move(rs.n_)),
+ k_(std::move(rs.k_)),
+ indexer_(std::move(rs.indexer_)),
+ state_(std::move(rs.state_)),
+ rc_(std::move(rs.rc_)),
+ round_id_(std::move(rs.round_id_)),
+ last_fec_used_(std::move(rs.last_fec_used_)),
+ callback_(std::move(rs.callback_)) {
+ setFecParams(n_, k_);
+}
+
+RecoveryStrategy::~RecoveryStrategy() {}
+
+void RecoveryStrategy::setFecParams(uint32_t n, uint32_t k) {
+ // if rs_type == FEC_ONLY_LOW_RES_LOSSES max k == 64
+ n_ = n;
+ k_ = k;
+
+ // XXX for the moment we go in steps of 5% loss rate.
+ uint32_t i = 0;
+ for (uint32_t loss_rate = 5; loss_rate < 100; loss_rate += 5) {
+ uint32_t fec_to_ask = 0;
+ if (n_ != 0 && k_ != 0) {
+ if (rs_type_ ==
+ interface::RtcTransportRecoveryStrategies::FEC_ONLY_LOW_RES_LOSSES) {
+ // the max loss rate in the matrix is 50%
+ uint32_t index = i;
+ if (i > 9) index = 9;
+ fec_to_ask = FEC_MATRIX[k_ - 1][index];
+ } else {
+ double dec_loss_rate = (double)(loss_rate + 5);
+ if (dec_loss_rate == 100.0) dec_loss_rate = 95.0;
+ dec_loss_rate = dec_loss_rate / 100.0;
+ double exp_losses = ceil((double)k_ * dec_loss_rate);
+ fec_to_ask = ceil((exp_losses / (1 - dec_loss_rate)) * 1.25);
+ }
+ }
+ fec_to_ask = std::min(fec_to_ask, (n_ - k_));
+ fec_per_loss_rate_.push_back(fec_to_ask);
+
+ i++;
+ }
+}
+
+uint64_t RecoveryStrategy::getRtxRtt(uint32_t seq) {
+ auto it = rtx_state_.find(seq);
+
+ if (it == rtx_state_.end()) return 0;
+
+ // we can compute the RTT of an RTX only if it was send once. Infact if the
+ // RTX was sent twice or more the data may be alredy in flight and the RTT
+ // will be underestimated. This may happen also for packets that we
+ // retransmitted too soon. in that case the RTT will be filtered out by
+ // checking the path label
+ if (it->second.rtx_count_ != 1) return 0;
+
+ // this a potentialy valid packet, compute the RTT
+ return (utils::SteadyTime::nowMs().count() - it->second.last_send_);
+}
+
+bool RecoveryStrategy::lossDetected(uint32_t seq) {
+ if (isRtx(seq)) {
+ // this packet is already in the list of rtx
+ return false;
+ }
+
+ auto it_fec = recover_with_fec_.find(seq);
+ if (it_fec != recover_with_fec_.end()) {
+ // this packet is already in list of packets to recover with fec
+ // this list contians also fec packets that will not be recovered with rtx
+ return false;
+ }
+
+ auto it_nack = nacked_seq_.find(seq);
+ if (it_nack != nacked_seq_.end()) {
+ // this packet was nacked so we do not use it to determine the loss rate
+ return false;
+ }
+
+ return true;
+}
+
+void RecoveryStrategy::notifyNewLossDetedcted(uint32_t seq) {
+ // new loss detected
+ // first record the loss. second do what is needed to recover it
+ state_->onLossDetected(seq);
+ newPacketLoss(seq);
+}
+
+void RecoveryStrategy::requestPossibleLostPacket(uint32_t seq) {
+ // these are packets for which we send a RTX but we do not increase the loss
+ // counter beacuse we don't know if they are lost or not
+ addNewRtx(seq, false);
+}
+
+void RecoveryStrategy::receivedFutureNack(uint32_t seq) {
+ nacked_seq_.insert(seq);
+}
+
+void RecoveryStrategy::clear() {
+ rtx_state_.clear();
+ rtx_timers_.clear();
+ recover_with_fec_.clear();
+
+ if (next_rtx_timer_ != MAX_TIMER_RTX) {
+ next_rtx_timer_ = MAX_TIMER_RTX;
+ timer_->cancel();
+ }
+}
+
+// rtx functions
+void RecoveryStrategy::addNewRtx(uint32_t seq, bool force) {
+ if (!indexer_->isFec(seq) || force) {
+ // this packet needs to be re-transmitted
+ rtxState state;
+ state.first_send_ = state_->getInterestSentTime(seq);
+ if (state.first_send_ == 0) // this interest was never sent before
+ state.first_send_ = getNow();
+ state.last_send_ = state.first_send_; // we didn't send an RTX for this
+ // packet yet
+ state.rtx_count_ = 0;
+ state.next_send_ = computeNextSend(seq, state.rtx_count_);
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Add " << seq << " to retransmissions. next rtx is in "
+ << state.next_send_ - getNow() << " ms";
+ rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state));
+ rtx_timers_.insert(std::pair<uint64_t, uint32_t>(state.next_send_, seq));
+
+ // if a new rtx is introduced, check the rtx timer
+ scheduleNextRtx();
+ } else {
+ // do not re-send fec packets but keep track of them
+ recover_with_fec_.insert(seq);
+ state_->onPossibleLossWithNoRtx(seq);
+ }
+}
+
+uint64_t RecoveryStrategy::computeNextSend(uint32_t seq, uint32_t rtx_counter) {
+ uint64_t now = getNow();
+ if (rtx_counter == 0) {
+ uint32_t wait = 1;
+ if (content_sharing_mode_) return now + wait;
+
+ uint32_t jitter = SENTINEL_TIMER_INTERVAL;
+ double prod_rate = state_->getProducerRate();
+ if (prod_rate != 0) jitter = ceil(state_->getJitter());
+
+ wait += jitter;
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "first rtx for " << seq << " in " << wait
+ << " ms, jitter = " << jitter;
+
+ return now + wait;
+ } else {
+ // wait one RTT. if an edge is known use the edge RTT for the first 5 rtx
+ double prod_rate = state_->getProducerRate();
+ if (prod_rate == 0) {
+ return now + SENTINEL_TIMER_INTERVAL;
+ }
+
+ uint64_t rtt = 0;
+ // if the transport detects an edge we try first to get the RTX from the
+ // edge. if no interest get a reply we move to the full RTT
+ if (rtx_counter < 5 && (state_->getEdgeRtt() != 0)) {
+ rtt = state_->getEdgeRtt();
+ } else {
+ rtt = state_->getAvgRTT();
+ }
+
+ if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL;
+
+ if (content_sharing_mode_) return now + rtt;
+
+ uint32_t wait = (uint32_t)rtt;
+
+ uint32_t jitter = ceil(state_->getJitter());
+ wait += jitter;
+
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "next rtx for " << seq << " in " << wait << " ms, rtt = " << rtt
+ << " jtter = " << jitter;
+
+ return now + wait;
+ }
+}
+
+void RecoveryStrategy::retransmit() {
+ if (rtx_timers_.size() == 0) return;
+
+ uint64_t now = getNow();
+
+ auto it = rtx_timers_.begin();
+ std::unordered_set<uint32_t> lost_pkt;
+ uint32_t sent_counter = 0;
+ while (it != rtx_timers_.end() && it->first <= now &&
+ sent_counter < MAX_RTX_IN_BATCH) {
+ uint32_t seq = it->second;
+ auto rtx_it =
+ rtx_state_.find(seq); // this should always return a valid iter
+ if (rtx_it->second.rtx_count_ >= RTC_MAX_RTX ||
+ (now - rtx_it->second.first_send_) >= RTC_MAX_AGE ||
+ seq < state_->getLastSeqNacked()) {
+ // max rtx reached or packet too old or packet nacked, this packet is lost
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "packet " << seq << " lost because 1) max rtx: "
+ << (rtx_it->second.rtx_count_ >= RTC_MAX_RTX) << " 2) max age: "
+ << ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE)
+ << " 3) nacked: " << (seq < state_->getLastSeqNacked());
+ lost_pkt.insert(seq);
+ it++;
+ } else {
+ // resend the packet
+ state_->onRetransmission(seq);
+ double prod_rate = state_->getProducerRate();
+ if (prod_rate != 0) rtx_it->second.rtx_count_++;
+ rtx_it->second.last_send_ = now;
+ rtx_it->second.next_send_ =
+ computeNextSend(seq, rtx_it->second.rtx_count_);
+ it = rtx_timers_.erase(it);
+ rtx_timers_.insert(
+ std::pair<uint64_t, uint32_t>(rtx_it->second.next_send_, seq));
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "send rtx for sequence " << seq << ", next send in "
+ << (rtx_it->second.next_send_ - now);
+
+ // if fec is on increase the number of RTX send during fec
+ if (fec_on_) rtx_during_fec_++;
+ send_rtx_callback_(seq);
+ sent_counter++;
+ }
+ }
+
+ // remove packets if needed
+ for (auto lost_it = lost_pkt.begin(); lost_it != lost_pkt.end(); lost_it++) {
+ uint32_t seq = *lost_it;
+ state_->onPacketLost(seq);
+ deleteRtx(seq);
+ }
+}
+
+void RecoveryStrategy::scheduleNextRtx() {
+ if (rtx_timers_.size() == 0) {
+ // all the rtx were removed, reset timer
+ next_rtx_timer_ = MAX_TIMER_RTX;
+ return;
+ }
+
+ // check if timer is alreay set
+ if (next_rtx_timer_ != MAX_TIMER_RTX) {
+ // a new check for rtx is already scheduled
+ if (next_rtx_timer_ > rtx_timers_.begin()->first) {
+ // we need to re-schedule it
+ timer_->cancel();
+ } else {
+ // wait for the next timer
+ return;
+ }
+ }
+
+ // set a new timer
+ next_rtx_timer_ = rtx_timers_.begin()->first;
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ uint64_t wait = 1;
+ if (next_rtx_timer_ != MAX_TIMER_RTX && next_rtx_timer_ > now)
+ wait = next_rtx_timer_ - now;
+
+ std::weak_ptr<RecoveryStrategy> self(shared_from_this());
+ timer_->expires_from_now(std::chrono::milliseconds(wait));
+ timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+ if (auto s = self.lock()) {
+ s->retransmit();
+ s->next_rtx_timer_ = MAX_TIMER_RTX;
+ s->scheduleNextRtx();
+ }
+ });
+}
+
+void RecoveryStrategy::deleteRtx(uint32_t seq) {
+ auto it_rtx = rtx_state_.find(seq);
+ if (it_rtx == rtx_state_.end()) return; // rtx not found
+
+ // remove the rtx from the timers list
+ uint64_t ts = it_rtx->second.next_send_;
+ auto it_timers = rtx_timers_.find(ts);
+ while (it_timers != rtx_timers_.end() && it_timers->first == ts) {
+ if (it_timers->second == seq) {
+ rtx_timers_.erase(it_timers);
+ break;
+ }
+ it_timers++;
+ }
+
+ // remove rtx
+ rtx_state_.erase(it_rtx);
+}
+
+// fec functions
+uint32_t RecoveryStrategy::computeFecPacketsToAsk() {
+ double loss_rate = state_->getMaxLossRate() * 100; // use loss rate in %
+
+ if (loss_rate > 95) loss_rate = 95; // max loss rate
+
+ if (loss_rate == 0) return 0;
+
+ // keep track of the last used fec. if we use a new bin on this round reset
+ // consecutive use and avg loss in the prev bin
+ uint32_t bin = ceil(loss_rate / 5.0) - 1;
+ if (bin > fec_per_loss_rate_.size() - 1)
+ bin = (uint32_t)fec_per_loss_rate_.size() - 1;
+
+ return fec_per_loss_rate_[bin];
+}
+
+void RecoveryStrategy::setRtxFec(std::optional<bool> rtx_on,
+ std::optional<bool> fec_on) {
+ if (rtx_on) rtx_on_ = *rtx_on;
+ if (fec_on) {
+ if (fec_on_ == false && (*fec_on) == true) { // turn on fec
+ // reset the number of RTX sent during fec
+ rtx_during_fec_ = 0;
+ }
+ fec_on_ = *fec_on;
+ }
+
+ notification::RecoveryStrategy strategy =
+ notification::RecoveryStrategy::RECOVERY_OFF;
+
+ if (rtx_on_ && fec_on_)
+ strategy = notification::RecoveryStrategy::RTX_AND_FEC;
+ else if (rtx_on_)
+ strategy = notification::RecoveryStrategy::RTX_ONLY;
+ else if (fec_on_)
+ strategy = notification::RecoveryStrategy::FEC_ONLY;
+
+ callback_(strategy);
+}
+
+// common functions
+void RecoveryStrategy::onLostTimeout(uint32_t seq) { removePacketState(seq); }
+
+void RecoveryStrategy::removePacketState(uint32_t seq) {
+ auto it_fec = recover_with_fec_.find(seq);
+ if (it_fec != recover_with_fec_.end()) {
+ recover_with_fec_.erase(it_fec);
+ return;
+ }
+
+ auto it_nack = nacked_seq_.find(seq);
+ if (it_nack != nacked_seq_.end()) {
+ nacked_seq_.erase(it_nack);
+ return;
+ }
+
+ deleteRtx(seq);
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_recovery_strategy.h b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h
new file mode 100644
index 000000000..405e1ebba
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_recovery_strategy.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <hicn/transport/interfaces/callbacks.h>
+#include <hicn/transport/utils/chrono_typedefs.h>
+#include <protocols/indexer.h>
+#include <protocols/rtc/rtc_rc.h>
+#include <protocols/rtc/rtc_state.h>
+
+#include <map>
+#include <optional>
+#include <unordered_map>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RecoveryStrategy : public std::enable_shared_from_this<RecoveryStrategy> {
+ protected:
+ struct rtx_state_ {
+ uint64_t first_send_; // first time this interest was sent
+ uint64_t last_send_; // last time this rtx was sent
+ uint64_t next_send_; // next retransmission time
+ uint32_t rtx_count_; // number or rtx
+ };
+
+ using rtxState = struct rtx_state_;
+
+ public:
+ using SendRtxCallback = std::function<void(uint32_t)>;
+
+ RecoveryStrategy(Indexer *indexer, SendRtxCallback &&callback,
+ asio::io_service &io_service, bool use_rtx, bool use_fec,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback);
+
+ RecoveryStrategy(RecoveryStrategy &&rs);
+
+ virtual ~RecoveryStrategy();
+
+ void setRtxFec(std::optional<bool> rtx_on = {},
+ std::optional<bool> fec_on = {});
+ void setState(RTCState *state) { state_ = state; }
+ void setRateControl(RTCRateControl *rateControl) { rc_ = rateControl; }
+ void setFecParams(uint32_t n, uint32_t k);
+ void setContentSharingMode() { content_sharing_mode_ = true; }
+
+ bool isRtx(uint32_t seq) {
+ if (rtx_state_.find(seq) != rtx_state_.end()) return true;
+ return false;
+ }
+
+ bool isPossibleLossWithNoRtx(uint32_t seq) {
+ if (recover_with_fec_.find(seq) != recover_with_fec_.end()) return true;
+ return false;
+ }
+
+ bool wasNacked(uint32_t seq) {
+ if (nacked_seq_.find(seq) != nacked_seq_.end()) return true;
+ return false;
+ }
+
+ interface::RtcTransportRecoveryStrategies getType() {
+ return rs_type_;
+ }
+ void updateType(interface::RtcTransportRecoveryStrategies type) {
+ rs_type_ = type;
+ }
+ bool isRtxOn() { return rtx_on_; }
+ bool isFecOn() { return fec_on_; }
+
+ RTCState *getState() { return state_; }
+
+ // if the function returns 0 it means that the packet is not an RTX or it is
+ // not a valid packet to safely compute the RTT
+ uint64_t getRtxRtt(uint32_t seq);
+ bool lossDetected(uint32_t seq);
+ void notifyNewLossDetedcted(uint32_t seq);
+ void requestPossibleLostPacket(uint32_t seq);
+ void receivedFutureNack(uint32_t seq);
+ void clear();
+
+ virtual void turnOnRecovery() = 0;
+ virtual void onNewRound(bool in_sync) = 0;
+ virtual void newPacketLoss(uint32_t seq) = 0;
+ virtual void receivedPacket(uint32_t seq) = 0;
+ void onLostTimeout(uint32_t seq);
+
+ void incRoundId() { round_id_++; }
+
+ // utils
+ uint64_t getNow() {
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ return now;
+ }
+
+ protected:
+ // rtx functions
+ void addNewRtx(uint32_t seq, bool force);
+ uint64_t computeNextSend(uint32_t seq, uint32_t rtx_counter);
+ void retransmit();
+ void scheduleNextRtx();
+ void deleteRtx(uint32_t seq);
+
+ // fec functions
+ uint32_t computeFecPacketsToAsk();
+
+ // common functons
+ void removePacketState(uint32_t seq);
+
+ interface::RtcTransportRecoveryStrategies rs_type_;
+ bool recovery_on_;
+ bool rtx_on_;
+ bool fec_on_;
+ bool content_sharing_mode_;
+
+ // number of RTX sent after fec turned on
+ // this is used to take into account jitter and out of order packets
+ // if we detect losses but we do not sent any RTX it means that the holes in
+ // the sequence are caused by the jitter
+ uint32_t rtx_during_fec_;
+
+ // this map keeps track of the retransmitted interest, ordered from the oldest
+ // to the newest one. the state contains the timer of the first send of the
+ // interest (from pendingIntetests_), the timer of the next send (key of the
+ // multimap) and the number of rtx
+ std::map<uint32_t, rtxState> rtx_state_;
+ // this map stored the rtx by timer. The key is the time at which the rtx
+ // should be sent, and the val is the interest seq number
+ std::multimap<uint64_t, uint32_t> rtx_timers_;
+
+ // lost packets that will be recovered with fec
+ std::unordered_set<uint32_t> recover_with_fec_;
+
+ // packet for which we recived a future nack
+ // in case we detect a loss for a nacked packet we send an RTX but we do not
+ // increase the loss counter. this is done because it may happen that the
+ // producer rate checkes over time and in flight interest may be satified by
+ // data packet after the reception of nacks
+ std::unordered_set<uint32_t> nacked_seq_;
+
+ // rtx vars
+ std::unique_ptr<asio::steady_timer> timer_;
+ uint64_t next_rtx_timer_;
+ SendRtxCallback send_rtx_callback_;
+
+ // fec vars
+ uint32_t n_;
+ uint32_t k_;
+ Indexer *indexer_;
+
+ RTCState *state_;
+ RTCRateControl *rc_;
+
+ private:
+ uint32_t round_id_; // number of rounds
+ uint32_t last_fec_used_;
+ std::vector<uint32_t> fec_per_loss_rate_;
+ interface::StrategyCallback callback_;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_delay.cc b/libtransport/src/protocols/rtc/rtc_rs_delay.cc
new file mode 100644
index 000000000..7d7a01133
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_delay.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rs_delay.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RecoveryStrategyDelayBased::RecoveryStrategyDelayBased(
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback)
+ : RecoveryStrategy(indexer, std::move(callback), io_service, true, false,
+ rs_type,
+ std::move(external_callback)), // start with rtx
+ congestion_state_(false),
+ probing_state_(false),
+ switch_rounds_(0) {}
+
+RecoveryStrategyDelayBased::RecoveryStrategyDelayBased(RecoveryStrategy &&rs)
+ : RecoveryStrategy(std::move(rs)) {
+ setRtxFec(true, false);
+ // we have to re-init congestion and
+ // probing
+ switch_rounds_ = 0;
+ congestion_state_ = false;
+ probing_state_ = false;
+}
+
+RecoveryStrategyDelayBased::~RecoveryStrategyDelayBased() {}
+
+void RecoveryStrategyDelayBased::turnOnRecovery() {
+ recovery_on_ = true;
+ uint64_t rtt = state_->getMinRTT();
+ uint32_t fec_to_ask = computeFecPacketsToAsk();
+ if (rtt > MAX_RTT_BEFORE_FEC && fec_to_ask > 0) {
+ // we need to start FEC (see fec only strategy for more details)
+ setRtxFec(true, true);
+ rtx_during_fec_ = 1; // avoid to stop fec
+ indexer_->setNFec(fec_to_ask);
+ } else {
+ // use RTX
+ setRtxFec(true, false);
+ switch_rounds_ = 0;
+ }
+}
+
+void RecoveryStrategyDelayBased::softSwitchToFec(uint32_t fec_to_ask) {
+ if (fec_to_ask == 0) {
+ setRtxFec(true, false);
+ switch_rounds_ = 0;
+ } else {
+ switch_rounds_++;
+ if (switch_rounds_ >= ((RTC_INTEREST_LIFETIME / ROUND_LEN) * 2) &&
+ rtx_during_fec_ != 0) { // go to fec only if it is needed (RTX are on)
+ setRtxFec(false, true);
+ } else {
+ setRtxFec(true, true);
+ }
+ }
+}
+
+void RecoveryStrategyDelayBased::onNewRound(bool in_sync) {
+ if (!recovery_on_) {
+ // disable fec so that no extra packet will be sent
+ // for rtx we check if recovery is on in newPacketLoss
+ setRtxFec(true, false);
+ indexer_->setNFec(0);
+ return;
+ }
+
+ uint64_t rtt = state_->getAvgRTT();
+
+ // XXX at the moment we are not looking at congestion events
+ // bool congestion = rc_->inCongestionState();
+
+ if ((!fec_on_ && rtt >= MAX_RTT_BEFORE_FEC) ||
+ (fec_on_ && rtt > (MAX_RTT_BEFORE_FEC - 10))) {
+ // switch from rtx to fec or keep use fec. Notice that if some rtx are
+ // waiting to be scheduled, they will be sent normally, but no new rtx will
+ // be created if the loss rate is 0 keep to use RTX.
+ uint32_t fec_to_ask = computeFecPacketsToAsk();
+ softSwitchToFec(fec_to_ask);
+ if (rtx_during_fec_ == 0) // if we do not send any RTX the losses
+ // registered may be due to jitter
+ indexer_->setNFec(0);
+ else
+ indexer_->setNFec(fec_to_ask);
+ return;
+ }
+
+ if ((fec_on_ && rtt <= (MAX_RTT_BEFORE_FEC - 10)) ||
+ (!rtx_on_ && rtt <= MAX_RTT_BEFORE_FEC)) {
+ // turn on rtx
+ softSwitchToFec(0);
+ indexer_->setNFec(0);
+ return;
+ }
+}
+
+void RecoveryStrategyDelayBased::newPacketLoss(uint32_t seq) {
+ if (rtx_on_ && recovery_on_) {
+ addNewRtx(seq, false);
+ } else {
+ if (!state_->isPending(seq) && !indexer_->isFec(seq)) {
+ addNewRtx(seq, true);
+ } else {
+ recover_with_fec_.insert(seq);
+ state_->onPossibleLossWithNoRtx(seq);
+ }
+ }
+}
+
+void RecoveryStrategyDelayBased::receivedPacket(uint32_t seq) {
+ removePacketState(seq);
+}
+
+void RecoveryStrategyDelayBased::probing() {
+ // TODO
+ // for the moment ask for all fec and exit the probing phase
+ probing_state_ = false;
+ setRtxFec(false, true);
+ indexer_->setNFec(computeFecPacketsToAsk());
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_delay.h b/libtransport/src/protocols/rtc/rtc_rs_delay.h
new file mode 100644
index 000000000..9e1c41388
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_delay.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RecoveryStrategyDelayBased : public RecoveryStrategy {
+ public:
+ RecoveryStrategyDelayBased(Indexer *indexer, SendRtxCallback &&callback,
+ asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback);
+
+ RecoveryStrategyDelayBased(RecoveryStrategy &&rs);
+
+ ~RecoveryStrategyDelayBased();
+
+ void turnOnRecovery();
+ void onNewRound(bool in_sync);
+ void newPacketLoss(uint32_t seq);
+ void receivedPacket(uint32_t seq);
+
+ private:
+ void softSwitchToFec(uint32_t fec_to_ask);
+
+ bool congestion_state_;
+ bool probing_state_;
+ uint32_t switch_rounds_;
+
+ void probing();
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc
new file mode 100644
index 000000000..5b10823ec
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rs_fec_only.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RecoveryStrategyFecOnly::RecoveryStrategyFecOnly(
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback)
+ : RecoveryStrategy(indexer, std::move(callback), io_service, true, false,
+ rs_type, std::move(external_callback)),
+ congestion_state_(false),
+ probing_state_(false),
+ switch_rounds_(0) {}
+
+RecoveryStrategyFecOnly::RecoveryStrategyFecOnly(RecoveryStrategy &&rs)
+ : RecoveryStrategy(std::move(rs)) {
+ setRtxFec(true, false);
+ switch_rounds_ = 0;
+ congestion_state_ = false;
+ probing_state_ = false;
+}
+
+RecoveryStrategyFecOnly::~RecoveryStrategyFecOnly() {}
+
+void RecoveryStrategyFecOnly::turnOnRecovery() {
+ recovery_on_ = true;
+ // init strategy
+ uint32_t fec_to_ask = computeFecPacketsToAsk();
+ if (fec_to_ask > 0) {
+ // the probing phase detected a lossy link. we immedialty start the fec and
+ // we disable the check to prevent to send fec packets before RTX. in fact
+ // here we know that we have losses and it is not a problem of OOO packets
+ setRtxFec(true, true);
+ rtx_during_fec_ = 1; // avoid to stop fec
+ indexer_->setNFec(fec_to_ask);
+ } else {
+ // keep only RTX on
+ setRtxFec(true, true);
+ }
+}
+
+void RecoveryStrategyFecOnly::onNewRound(bool in_sync) {
+ if (!recovery_on_) {
+ indexer_->setNFec(0);
+ return;
+ }
+
+ // XXX for the moment we are considering congestion events
+ // if(rc_->inCongestionState()){
+ // congestion_state_ = true;
+ // probing_state_ = false;
+ // indexer_->setNFec(0);
+ // return;
+ // }
+
+ // no congestion
+ if (congestion_state_) {
+ // this is the first round after congestion
+ // enter probing phase
+ probing_state_ = true;
+ congestion_state_ = false;
+ }
+
+ if (probing_state_) {
+ probing();
+ } else {
+ uint32_t fec_to_ask = computeFecPacketsToAsk();
+ // If fec_to_ask == 0 we use rtx even if in these strategy we use only fec.
+ // In this way the first packet loss that triggers the usage of fec can be
+ // recovered using rtx, otherwise it will always be lost
+ if (fec_to_ask == 0) {
+ setRtxFec(true, false);
+ switch_rounds_ = 0;
+ } else {
+ switch_rounds_++;
+ if (switch_rounds_ >= ((RTC_INTEREST_LIFETIME / ROUND_LEN) * 2) &&
+ rtx_during_fec_ !=
+ 0) { // go to fec only if it is needed (RTX are on)
+ setRtxFec(false, true);
+ } else {
+ setRtxFec(true, true); // keep both
+ }
+ }
+ if (rtx_during_fec_ == 0) // if we do not send any RTX the losses
+ // registered may be due to jitter
+ indexer_->setNFec(0);
+ else
+ indexer_->setNFec(fec_to_ask);
+ }
+}
+
+void RecoveryStrategyFecOnly::newPacketLoss(uint32_t seq) {
+ if (rtx_on_ && recovery_on_) {
+ addNewRtx(seq, false);
+ } else {
+ if (!state_->isPending(seq) && !indexer_->isFec(seq)) {
+ addNewRtx(seq, true);
+ } else {
+ // if not pending add to list to recover with fec
+ recover_with_fec_.insert(seq);
+ state_->onPossibleLossWithNoRtx(seq);
+ }
+ }
+}
+
+void RecoveryStrategyFecOnly::receivedPacket(uint32_t seq) {
+ removePacketState(seq);
+}
+
+void RecoveryStrategyFecOnly::probing() {
+ // TODO
+ // for the moment ask for all fec and exit the probing phase
+ probing_state_ = false;
+ uint32_t fec_to_ask = computeFecPacketsToAsk();
+ indexer_->setNFec(fec_to_ask);
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_fec_only.h b/libtransport/src/protocols/rtc/rtc_rs_fec_only.h
new file mode 100644
index 000000000..42df25bd9
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_fec_only.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RecoveryStrategyFecOnly : public RecoveryStrategy {
+ public:
+ RecoveryStrategyFecOnly(Indexer *indexer, SendRtxCallback &&callback,
+ asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback);
+
+ RecoveryStrategyFecOnly(RecoveryStrategy &&rs);
+
+ ~RecoveryStrategyFecOnly();
+
+ void turnOnRecovery();
+ void onNewRound(bool in_sync);
+ void newPacketLoss(uint32_t seq);
+ void receivedPacket(uint32_t seq);
+
+ private:
+ bool congestion_state_;
+ bool probing_state_;
+ uint32_t switch_rounds_;
+
+ void probing();
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc
new file mode 100644
index 000000000..dbad563cd
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rs_low_rate.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RecoveryStrategyLowRate::RecoveryStrategyLowRate(
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback)
+ : RecoveryStrategy(indexer, std::move(callback), io_service, false, true,
+ rs_type,
+ std::move(external_callback)), // start with fec
+ fec_consecutive_rounds_((MILLI_IN_A_SEC / ROUND_LEN) * 5), // 5 sec
+ rtx_allowed_consecutive_rounds_(0) {
+ initSwitchVector();
+}
+
+RecoveryStrategyLowRate::RecoveryStrategyLowRate(RecoveryStrategy &&rs)
+ : RecoveryStrategy(std::move(rs)),
+ fec_consecutive_rounds_((MILLI_IN_A_SEC / ROUND_LEN) * 5), // 5 sec
+ rtx_allowed_consecutive_rounds_(0) {
+ setRtxFec(false, true);
+ initSwitchVector();
+}
+
+RecoveryStrategyLowRate::~RecoveryStrategyLowRate() {}
+
+void RecoveryStrategyLowRate::initSwitchVector() {
+ // TODO adjust thresholds here when new data are collected
+ // see resutls in
+ // https://confluence-eng-gpk1.cisco.com/conf/display/SPT/dailyreports
+ thresholds_t t1;
+ t1.rtt = 15; // 15ms
+ t1.loss_rtx_to_fec = 15; // 15%
+ t1.loss_fec_to_rtx = 10; // 10%
+ thresholds_t t2;
+ t2.rtt = 35; // 35ms
+ t2.loss_rtx_to_fec = 5; // 5%
+ t2.loss_fec_to_rtx = 1; // 1%
+ switch_vector.push_back(t1);
+ switch_vector.push_back(t2);
+}
+
+void RecoveryStrategyLowRate::setRecoveryParameters(bool use_rtx, bool use_fec,
+ uint32_t fec_to_ask) {
+ setRtxFec(use_rtx, use_fec);
+ indexer_->setNFec(fec_to_ask);
+}
+
+void RecoveryStrategyLowRate::selectRecoveryStrategy(bool in_sync) {
+ uint32_t fec_to_ask = computeFecPacketsToAsk();
+ if (fec_to_ask == 0) {
+ // fec is off, turn on RTX immediatly to avoid packet losses
+ setRecoveryParameters(true, false, 0);
+ fec_consecutive_rounds_ = 0;
+ return;
+ }
+
+ uint32_t loss_rate = std::round(state_->getPerSecondLossRate() * 100);
+ uint32_t rtt = (uint32_t)state_->getAvgRTT();
+
+ bool use_rtx = false;
+ for (size_t i = 0; i < switch_vector.size(); i++) {
+ uint32_t max_loss_rate = 0;
+ if (fec_on_)
+ max_loss_rate = switch_vector[i].loss_fec_to_rtx;
+ else
+ max_loss_rate = switch_vector[i].loss_rtx_to_fec;
+
+ if (rtt < switch_vector[i].rtt && loss_rate < max_loss_rate) {
+ use_rtx = true;
+ rtx_allowed_consecutive_rounds_++;
+ break;
+ }
+ }
+
+ if (!use_rtx) rtx_allowed_consecutive_rounds_ = 0;
+
+ if (use_rtx) {
+ if (fec_on_) {
+ // here we should swtich from RTX to FEC
+ // wait 10sec where the switch is allowed before actually switch
+ if (rtx_allowed_consecutive_rounds_ >=
+ ((MILLI_IN_A_SEC / ROUND_LEN) * 10)) { // 10 sec
+ // use RTX
+ setRecoveryParameters(true, false, 0);
+ fec_consecutive_rounds_ = 0;
+ } else {
+ // keep using FEC (and maybe RTX)
+ setRecoveryParameters(true, true, fec_to_ask);
+ fec_consecutive_rounds_++;
+ }
+ } else {
+ // keep using RTX
+ setRecoveryParameters(true, false, 0);
+ fec_consecutive_rounds_ = 0;
+ }
+ } else {
+ // use FEC and RTX
+ setRecoveryParameters(true, true, fec_to_ask);
+ fec_consecutive_rounds_++;
+ }
+
+ // everytime that we anable FEC we keep also RTX on. in this way the first
+ // losses that are not covered by FEC are recovered using RTX. after 5 sec we
+ // disable fec
+ if (fec_consecutive_rounds_ >= ((MILLI_IN_A_SEC / ROUND_LEN) * 5)) {
+ // turn off RTX
+ setRtxFec(false);
+ }
+}
+
+void RecoveryStrategyLowRate::turnOnRecovery() {
+ recovery_on_ = 1;
+ // the stategy will be init in the new round function
+}
+
+void RecoveryStrategyLowRate::onNewRound(bool in_sync) {
+ if (!recovery_on_) {
+ // disable fec so that no extra packet will be sent
+ // for rtx we check if recovery is on in newPacketLoss
+ setRtxFec(true, false);
+ indexer_->setNFec(0);
+ return;
+ }
+
+ // XXX since this strategy will be used only for flow at low rate we do not
+ // consider congestion events like in other strategies
+
+ selectRecoveryStrategy(in_sync);
+}
+
+void RecoveryStrategyLowRate::newPacketLoss(uint32_t seq) {
+ if (rtx_on_ && recovery_on_) {
+ addNewRtx(seq, false);
+ } else {
+ if (!state_->isPending(seq) && !indexer_->isFec(seq)) {
+ addNewRtx(seq, true);
+ } else {
+ recover_with_fec_.insert(seq);
+ state_->onPossibleLossWithNoRtx(seq);
+ }
+ }
+}
+
+void RecoveryStrategyLowRate::receivedPacket(uint32_t seq) {
+ removePacketState(seq);
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_low_rate.h b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h
new file mode 100644
index 000000000..0e76efaca
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_low_rate.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+#include <vector>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+struct thresholds_t {
+ uint32_t rtt;
+ uint32_t loss_rtx_to_fec; // loss rate used to move from rtx to fec
+ uint32_t loss_fec_to_rtx; // loss rate used to move from fec to rtx
+};
+
+class RecoveryStrategyLowRate : public RecoveryStrategy {
+ public:
+ RecoveryStrategyLowRate(Indexer *indexer, SendRtxCallback &&callback,
+ asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback);
+
+ RecoveryStrategyLowRate(RecoveryStrategy &&rs);
+
+ ~RecoveryStrategyLowRate();
+
+ void turnOnRecovery();
+ void onNewRound(bool in_sync);
+ void newPacketLoss(uint32_t seq);
+ void receivedPacket(uint32_t seq);
+
+ private:
+ void initSwitchVector();
+ void setRecoveryParameters(bool use_rtx, bool use_fec, uint32_t fec_to_ask);
+ void selectRecoveryStrategy(bool in_sync);
+
+ uint32_t fec_consecutive_rounds_;
+ uint32_t rtx_allowed_consecutive_rounds_;
+
+ // this table contains the thresholds that indicates when to switch from RTX
+ // to FEC and viceversa. values in the vector are detected with a set of
+ // experiments. the vector is used in the following way: if rtt and loss rate
+ // are less than one of the values in the in the vector, losses are
+ // recovered using RTX. otherwive losses are recovered using FEC. as for FEC
+ // only and delay based strategy, the swith from RTX to FEC is smooth,
+ // meaning that FEC and RTX are used together for some rounds
+ std::vector<thresholds_t> switch_vector;
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc
new file mode 100644
index 000000000..00c6a0504
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rs_recovery_off.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff(
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback)
+ : RecoveryStrategy(indexer, std::move(callback), io_service, false, false,
+ rs_type, std::move(external_callback)) {}
+
+RecoveryStrategyRecoveryOff::RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs)
+ : RecoveryStrategy(std::move(rs)) {
+ setRtxFec(false, false);
+}
+
+RecoveryStrategyRecoveryOff::~RecoveryStrategyRecoveryOff() {}
+
+void RecoveryStrategyRecoveryOff::turnOnRecovery() {
+ // nothing to do
+ return;
+}
+void RecoveryStrategyRecoveryOff::onNewRound(bool in_sync) {
+ // nothing to do
+ return;
+}
+
+void RecoveryStrategyRecoveryOff::newPacketLoss(uint32_t seq) {
+ // here we only keep track of the lost packets to avoid to
+ // count them multple times in the counters. for this we
+ // use the recover_with_fec_ set
+ recover_with_fec_.insert(seq);
+ state_->onPossibleLossWithNoRtx(seq);
+}
+
+void RecoveryStrategyRecoveryOff::receivedPacket(uint32_t seq) {
+ removePacketState(seq);
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h
new file mode 100644
index 000000000..3d59cc473
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_recovery_off.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RecoveryStrategyRecoveryOff : public RecoveryStrategy {
+ public:
+ RecoveryStrategyRecoveryOff(Indexer *indexer, SendRtxCallback &&callback,
+ asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback);
+
+ RecoveryStrategyRecoveryOff(RecoveryStrategy &&rs);
+
+ ~RecoveryStrategyRecoveryOff();
+
+ void turnOnRecovery();
+ void onNewRound(bool in_sync);
+ void newPacketLoss(uint32_t seq);
+ void receivedPacket(uint32_t seq);
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc
new file mode 100644
index 000000000..4d7cf7a82
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_rs_rtx_only.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly(
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback)
+ : RecoveryStrategy(indexer, std::move(callback), io_service, true, false,
+ rs_type, std::move(external_callback)) {}
+
+RecoveryStrategyRtxOnly::RecoveryStrategyRtxOnly(RecoveryStrategy &&rs)
+ : RecoveryStrategy(std::move(rs)) {
+ setRtxFec(true, false);
+}
+
+RecoveryStrategyRtxOnly::~RecoveryStrategyRtxOnly() {}
+
+void RecoveryStrategyRtxOnly::turnOnRecovery() {
+ recovery_on_ = true;
+ setRtxFec(true, false);
+}
+
+void RecoveryStrategyRtxOnly::onNewRound(bool in_sync) {
+ // nothing to do
+ return;
+}
+
+void RecoveryStrategyRtxOnly::newPacketLoss(uint32_t seq) {
+ if (!recovery_on_) {
+ recover_with_fec_.insert(seq);
+ state_->onPossibleLossWithNoRtx(seq);
+ return;
+ }
+ addNewRtx(seq, false);
+}
+
+void RecoveryStrategyRtxOnly::receivedPacket(uint32_t seq) {
+ removePacketState(seq);
+}
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h
new file mode 100644
index 000000000..03dbed1c7
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_rs_rtx_only.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <protocols/rtc/rtc_recovery_strategy.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+class RecoveryStrategyRtxOnly : public RecoveryStrategy {
+ public:
+ RecoveryStrategyRtxOnly(Indexer *indexer, SendRtxCallback &&callback,
+ asio::io_service &io_service,
+ interface::RtcTransportRecoveryStrategies rs_type,
+ interface::StrategyCallback &&external_callback);
+
+ RecoveryStrategyRtxOnly(RecoveryStrategy &&rs);
+
+ ~RecoveryStrategyRtxOnly();
+
+ void turnOnRecovery();
+ void onNewRound(bool in_sync);
+ void newPacketLoss(uint32_t seq);
+ void receivedPacket(uint32_t seq);
+};
+
+} // end namespace rtc
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_state.cc b/libtransport/src/protocols/rtc/rtc_state.cc
new file mode 100644
index 000000000..82ac0b9c1
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_state.cc
@@ -0,0 +1,900 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/rtc/rtc_state.h>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+RTCState::RTCState(Indexer *indexer,
+ ProbeHandler::SendProbeCallback &&probe_callback,
+ DiscoveredRttCallback &&discovered_rtt_callback,
+ asio::io_service &io_service)
+ : loss_history_(10), // log 10sec history
+ indexer_(indexer),
+ probe_handler_(std::make_shared<ProbeHandler>(std::move(probe_callback),
+ io_service)),
+ discovered_rtt_callback_(std::move(discovered_rtt_callback)) {
+ init_rtt_timer_ = std::make_unique<asio::steady_timer>(io_service);
+}
+
+RTCState::~RTCState() {}
+
+void RTCState::initParams() {
+ // packets counters (total)
+ sent_interests_ = 0;
+ sent_rtx_ = 0;
+ received_data_ = 0;
+ received_nacks_ = 0;
+ received_timeouts_ = 0;
+ received_probes_ = 0;
+
+ // loss counters
+ packets_lost_ = 0;
+ definitely_lost_pkt_ = 0;
+ losses_recovered_ = 0;
+ first_seq_in_round_ = 0;
+ highest_seq_received_ = 0;
+ highest_seq_received_in_order_ = 0;
+ last_seq_nacked_ = 0;
+ loss_rate_ = 0.0;
+ avg_loss_rate_ = -1.0;
+ last_round_loss_rate_ = 0.0;
+
+ // loss rate per sec
+ lost_per_sec_ = 0;
+ total_expected_packets_ = 0;
+ per_sec_loss_rate_ = 0.0;
+
+ // residual losses counters
+ expected_packets_ = 0;
+ packets_sent_to_app_ = 0;
+ rounds_from_last_compute_ = 0;
+ residual_loss_rate_ = 0.0;
+
+ // fec counters
+ pending_fec_pkt_ = 0;
+ received_fec_pkt_ = 0;
+
+ // bw counters
+ received_bytes_ = 0;
+ received_fec_bytes_ = 0;
+ recovered_bytes_with_fec_ = 0;
+
+ avg_packet_size_ = INIT_PACKET_SIZE;
+ production_rate_ = 0.0;
+ received_rate_ = 0.0;
+ fec_recovered_rate_ = 0.0;
+
+ // nack counter
+ past_nack_on_last_round_ = false;
+ received_nacks_last_round_ = 0;
+
+ // packets counter
+ received_packets_last_round_ = 0;
+ received_data_last_round_ = 0;
+ received_data_from_cache_ = 0;
+ sent_interests_last_round_ = 0;
+ sent_rtx_last_round_ = 0;
+
+ // round conunters
+ rounds_ = 0;
+ rounds_without_nacks_ = 0;
+ rounds_without_packets_ = 0;
+
+ last_production_seq_ = 0;
+ producer_is_active_ = false;
+ last_prod_update_seq_ = 0;
+
+ // paths stats
+ path_table_.clear();
+ main_path_ = nullptr;
+ edge_path_ = nullptr;
+
+ // packet cache (not pending anymore)
+ packet_cache_.clear();
+
+ // pending interests
+ pending_interests_.clear();
+
+ // used to keep track of the skipped interest
+ last_interest_sent_ = 0;
+
+ // init rtt
+ first_interest_sent_time_ = ~0;
+ first_interest_sent_seq_ = 0;
+
+ // start probing the producer
+ init_rtt_ = false;
+ probe_handler_->setSuffixRange(MIN_INIT_PROBE_SEQ, MAX_INIT_PROBE_SEQ);
+ probe_handler_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES);
+ probe_handler_->sendProbes();
+ setInitRttTimer(INIT_RTT_PROBE_RESTART);
+}
+
+// packet events
+void RTCState::onSendNewInterest(const core::Name *interest_name) {
+ uint64_t now = utils::SteadyTime::nowMs().count();
+ uint32_t seq = interest_name->getSuffix();
+ pending_interests_.insert(std::pair<uint32_t, uint64_t>(seq, now));
+
+ if (sent_interests_ == 0) {
+ first_interest_sent_time_ = now;
+ first_interest_sent_seq_ = seq;
+ }
+
+ if (indexer_->isFec(seq)) {
+ pending_fec_pkt_++;
+ }
+
+ if (last_interest_sent_ == 0 && seq != 0) {
+ last_interest_sent_ = seq; // init last interest sent
+ }
+
+ // TODO what happen in case of jumps?
+ eraseFromPacketCache(
+ seq); // if we send this interest we don't know its state
+ for (uint32_t i = last_interest_sent_ + 1; i < seq; i++) {
+ if (indexer_->isFec(i)) {
+ // only fec packets can be skipped
+ addToPacketCache(i, PacketState::SKIPPED);
+ }
+ }
+
+ last_interest_sent_ = seq;
+
+ sent_interests_++;
+ sent_interests_last_round_++;
+}
+
+void RTCState::onTimeout(uint32_t seq, bool lost) {
+ auto it = pending_interests_.find(seq);
+ if (it != pending_interests_.end()) {
+ pending_interests_.erase(it);
+ if (indexer_->isFec(seq)) pending_fec_pkt_--;
+ }
+ received_timeouts_++;
+
+ if (lost) onPacketLost(seq);
+}
+
+void RTCState::onLossDetected(uint32_t seq) {
+ PacketState state = getPacketState(seq);
+
+ // if the packet is already marked with a state, do nothing
+ // to be considered lost the packet must be pending
+ if (state == PacketState::UNKNOWN &&
+ pending_interests_.find(seq) != pending_interests_.end()) {
+ packets_lost_++;
+ addToPacketCache(seq, PacketState::LOST);
+ }
+}
+
+void RTCState::onRetransmission(uint32_t seq) {
+ // remove the interest for the pendingInterest map only after the first rtx.
+ // in this way we can handle the ooo packets that come in late as normla
+ // packet. we consider a packet lost only if we sent at least an RTX for it.
+ // XXX this may become problematic if we stop the RTX transmissions
+ auto it = pending_interests_.find(seq);
+ if (it != pending_interests_.end()) {
+ pending_interests_.erase(it);
+ if (indexer_->isFec(seq)) pending_fec_pkt_--;
+ }
+ sent_rtx_++;
+ sent_rtx_last_round_++;
+}
+
+void RTCState::onPossibleLossWithNoRtx(uint32_t seq) {
+ // if fec is on or rtx is disable we don't need to do anything to recover a
+ // packet. however in both cases we need to remove possible missing packets
+ // from the window of pendinig interest in order to free space without wating
+ // for the timeout.
+ auto it = pending_interests_.find(seq);
+ if (it != pending_interests_.end()) {
+ pending_interests_.erase(it);
+ if (indexer_->isFec(seq)) pending_fec_pkt_--;
+ }
+}
+
+void RTCState::onDataPacketReceived(const core::ContentObject &content_object,
+ bool compute_stats) {
+ uint32_t seq = content_object.getName().getSuffix();
+
+ if (compute_stats) {
+ updatePathStats(content_object, false);
+ received_data_last_round_++;
+ }
+ received_data_++;
+ packets_sent_to_app_++;
+
+ core::ParamsRTC params = RTCState::getDataParams(content_object);
+
+ if (last_prod_update_seq_ < seq) {
+ last_prod_update_seq_ = seq;
+ production_rate_ = (double)params.prod_rate;
+ }
+
+ updatePacketSize(content_object);
+ updateReceivedBytes(content_object, false);
+ addRecvOrLost(seq, PacketState::RECEIVED);
+
+ // the producer is responding
+ // it is generating valid data packets so we consider it active
+ producer_is_active_ = true;
+
+ received_packets_last_round_++;
+}
+
+void RTCState::onFecPacketReceived(const core::ContentObject &content_object) {
+ uint32_t seq = content_object.getName().getSuffix();
+ updateReceivedBytes(content_object, true);
+
+ PacketState state = getPacketState(seq);
+ if (state != PacketState::LOST) {
+ // increase only for not lost packets
+ received_fec_pkt_++;
+ }
+ addRecvOrLost(seq, PacketState::RECEIVED);
+ // the producer is responding
+ // it is generating valid data packets so we consider it active
+ producer_is_active_ = true;
+}
+
+void RTCState::onNackPacketReceived(const core::ContentObject &nack,
+ bool compute_stats) {
+ uint32_t seq = nack.getName().getSuffix();
+ struct nack_packet_t *nack_pkt =
+ (struct nack_packet_t *)nack.getPayload()->data();
+ uint32_t production_seq = nack_pkt->getProductionSegment();
+ uint32_t production_rate = nack_pkt->getProductionRate();
+
+ if (TRANSPORT_EXPECT_FALSE(main_path_ == nullptr) ||
+ last_prod_update_seq_ < production_seq) {
+ // update production rate
+ last_production_seq_ = production_seq;
+ production_rate_ = (double)production_rate;
+ }
+
+ if (compute_stats) {
+ // this is not an RTX
+ updatePathStats(nack, true);
+ }
+
+ // for statistics pourpose we log all nacks, also the one received for
+ // retransmitted packets
+ received_nacks_++;
+ received_nacks_last_round_++;
+
+ bool to_delete = false;
+ if (production_seq > seq) {
+ // old nack, seq is lost
+ // update last nacked
+ if (last_seq_nacked_ < seq) last_seq_nacked_ = seq;
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "lost packet " << seq << " beacuse of a past nack";
+ if (compute_stats) past_nack_on_last_round_ = true;
+ onPacketLost(seq);
+ } else if (seq > production_seq) {
+ // future nack
+ // remove the nack from the pending interest map
+ // (the packet is not received/lost yet)
+ to_delete = true;
+ } else {
+ // this should be a quite rear event. simply remove the
+ // packet from the pending interest list
+ to_delete = true;
+ }
+
+ if (to_delete) {
+ auto it = pending_interests_.find(seq);
+ if (it != pending_interests_.end()) {
+ pending_interests_.erase(it);
+ if (indexer_->isFec(seq)) pending_fec_pkt_--;
+ }
+ }
+
+ received_packets_last_round_++;
+}
+
+void RTCState::onPacketLost(uint32_t seq) {
+ if (!indexer_->isFec(seq)) {
+ PacketState state = getPacketState(seq);
+ if (state == PacketState::LOST ||
+ (state == PacketState::UNKNOWN &&
+ pending_interests_.find(seq) != pending_interests_.end())) {
+ definitely_lost_pkt_++;
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost";
+ }
+ }
+
+ addRecvOrLost(seq, PacketState::DEFINITELY_LOST);
+}
+
+void RTCState::onPacketRecoveredRtx(const core::ContentObject &content_object,
+ uint64_t rtt) {
+ uint32_t seq = content_object.getName().getSuffix();
+ packets_sent_to_app_++;
+
+ // increase the recovered packet counter only if the packet was marked as LOST
+ // before.
+ PacketState state = getPacketState(seq);
+ if (state == PacketState::LOST) losses_recovered_++;
+
+ addRecvOrLost(seq, PacketState::RECEIVED);
+ updateReceivedBytes(content_object, false);
+
+ if (rtt == 0) return; // nothing to do
+
+ uint32_t path_label = content_object.getPathLabel();
+ auto path_it = path_table_.find(path_label);
+ if (path_it == path_table_.end()) {
+ // this is a new path and it must be a cache
+ std::shared_ptr<RTCDataPath> newPath =
+ std::make_shared<RTCDataPath>(path_label);
+ auto ret = path_table_.insert(
+ std::pair<uint32_t, std::shared_ptr<RTCDataPath>>(path_label, newPath));
+ path_it = ret.first;
+ }
+
+ auto path = path_it->second;
+ if (path->pathToProducer())
+ return; // this packet is coming from a producer
+ // even if we sent an RTX. this may happen
+ // for RTX that are sent too fast or in
+ // case of multipath
+
+ path->insertRttSample(utils::SteadyTime::Milliseconds(rtt), true);
+}
+
+void RTCState::onFecPacketRecoveredRtx(
+ const core::ContentObject &content_object) {
+ // This is the same as onPacketRecoveredRtx, but in this is case the
+ // pkt is also a FEC pkt, the addRecvOrLost will be called afterwards
+ losses_recovered_++;
+ updateReceivedBytes(content_object, true);
+}
+
+void RTCState::onPacketRecoveredFec(uint32_t seq, uint32_t size) {
+ losses_recovered_++;
+ packets_sent_to_app_++;
+ recovered_bytes_with_fec_ += size;
+
+ // adding header to the count
+ recovered_bytes_with_fec_ += 60; // XXX get header size some where
+
+ // the packet could be not marked as lost yet. onLossDetected checks if add in
+ // the packet in the lost count or not
+ onLossDetected(seq);
+
+ addRecvOrLost(seq, PacketState::RECEIVED);
+}
+
+bool RTCState::onProbePacketReceived(const core::ContentObject &probe) {
+ uint32_t seq = probe.getName().getSuffix();
+ core::ParamsRTC params = RTCState::getProbeParams(probe);
+
+ bool is_valid = true;
+ uint32_t max = UINT32_MAX;
+ if (params.prod_rate == max) is_valid = false;
+
+ uint64_t rtt;
+ rtt = probe_handler_->getRtt(seq, is_valid);
+ if (rtt == 0) return false; // this is not a valid probe
+
+ if (!is_valid) return false; // not a valid probe
+
+ // if we are here the producer is active
+ producer_is_active_ = true;
+
+ // Like for data and nacks update the path stats. Here the RTT is computed
+ // by the probe handler. Both probes for rtt and bw are good to estimate
+ // info on the path.
+ uint32_t path_label = probe.getPathLabel();
+ auto path_it = path_table_.find(path_label);
+
+ if (path_it == path_table_.end()) {
+ // found a new path
+ std::shared_ptr<RTCDataPath> newPath =
+ std::make_shared<RTCDataPath>(path_label);
+ auto ret = path_table_.insert(
+ std::pair<uint32_t, std::shared_ptr<RTCDataPath>>(path_label, newPath));
+ path_it = ret.first;
+ }
+
+ auto path = path_it->second;
+
+ path->insertRttSample(utils::SteadyTime::Milliseconds(rtt), true);
+ path->receivedNack();
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ int64_t OWD = now - params.timestamp;
+ path->insertOwdSample(OWD);
+
+ if (last_prod_update_seq_ < params.prod_seg) {
+ last_production_seq_ = params.prod_seg;
+ production_rate_ = (double)params.prod_rate;
+ }
+
+ // check for init RTT. if received_probes_ is equal to 0 schedule a timer to
+ // wait for the INIT_RTT_PROBES. in this way if some probes get lost we don't
+ // wait forever
+ received_probes_++;
+
+ if (!init_rtt_ && received_probes_ <= INIT_RTT_PROBES) {
+ if (received_probes_ == 1) {
+ // we got the first probe, wait at most INIT_RTT_PROBE_WAIT sec for the
+ // others.
+ main_path_ = path;
+ setInitRttTimer(INIT_RTT_PROBE_WAIT);
+ }
+ if (received_probes_ == INIT_RTT_PROBES) {
+ // we are done
+ init_rtt_timer_->cancel();
+ checkInitRttTimer();
+ }
+ }
+
+ received_packets_last_round_++;
+
+ // ignore probes sent before the first interest
+ if ((now - rtt) <= first_interest_sent_time_) return false;
+ return true;
+}
+
+void RTCState::onJumpForward(uint32_t next_seq) {
+ for (uint32_t seq = highest_seq_received_in_order_ + 1; seq < next_seq;
+ seq++) {
+ PacketState packet_state = getPacketState(seq);
+ if (packet_state != PacketState::RECEIVED &&
+ packet_state != PacketState::DEFINITELY_LOST) {
+ // here we considere the packet as definitely lost whitout increase the
+ // lost packet counter because this loss is not due to the network
+ // condition but the transport wants to skip the packet
+ onPacketLost(seq);
+ }
+ }
+}
+
+void RTCState::onNewRound(double round_len, bool in_sync) {
+ if (path_table_.empty()) return;
+
+ double bytes_per_sec =
+ ((double)received_bytes_ * (MILLI_IN_A_SEC / round_len));
+ if (received_rate_ == 0)
+ received_rate_ = bytes_per_sec;
+ else
+ received_rate_ = (received_rate_ * MOVING_AVG_ALPHA) +
+ ((1 - MOVING_AVG_ALPHA) * bytes_per_sec);
+ double fec_bytes_per_sec =
+ ((double)received_fec_bytes_ * (MILLI_IN_A_SEC / round_len));
+
+ if (fec_received_rate_ == 0)
+ fec_received_rate_ = fec_bytes_per_sec;
+ else
+ fec_received_rate_ = (fec_received_rate_ * 0.8) + (0.2 * fec_bytes_per_sec);
+
+ double fec_recovered_bytes_per_sec =
+ ((double)recovered_bytes_with_fec_ * (MILLI_IN_A_SEC / round_len));
+
+ if (fec_recovered_rate_ == 0)
+ fec_recovered_rate_ = fec_recovered_bytes_per_sec;
+ else
+ fec_recovered_rate_ =
+ (fec_recovered_rate_ * 0.8) + (0.2 * fec_recovered_bytes_per_sec);
+
+ // search for an active path. Is it possible to have multiple path that are
+ // used at the same time. We use as reference path the one from where we gets
+ // more packets. This means that the path should have better lantecy or less
+ // channel losses
+
+ uint32_t last_round_packets = 0;
+ uint64_t min_edge_rtt = UINT_MAX;
+ std::shared_ptr<RTCDataPath> old_main_path = main_path_;
+ main_path_ = nullptr;
+ edge_path_ = nullptr;
+
+ for (auto it = path_table_.begin(); it != path_table_.end(); it++) {
+ if (it->second->isValidProducer()) {
+ uint32_t pkt = it->second->getPacketsLastRound();
+ if (pkt > last_round_packets) {
+ last_round_packets = pkt;
+ main_path_ = it->second;
+ }
+ } else if (it->second->isActive() && !it->second->pathToProducer()) {
+ // this is a path to a cache from where we are receiving content
+ if (it->second->getMinRtt() < min_edge_rtt) {
+ min_edge_rtt = it->second->getMinRtt();
+ edge_path_ = it->second;
+ }
+ }
+ it->second->roundEnd();
+ }
+
+ if (main_path_ == nullptr) main_path_ = old_main_path;
+ if (edge_path_ == nullptr) edge_path_ = main_path_;
+ if (edge_path_->getMinRtt() >= main_path_->getMinRtt())
+ edge_path_ = main_path_;
+
+ // in case we get a new main path we reset the stats of the old one. this is
+ // beacuse, in case we need to switch back we don't what to take decisions on
+ // old stats that may be outdated.
+ if (main_path_ != old_main_path) old_main_path->clearRtt();
+
+ updateLossRate(in_sync);
+
+ // handle nacks
+ if (!past_nack_on_last_round_ && received_bytes_ > 0) {
+ rounds_without_nacks_++;
+ } else {
+ rounds_without_nacks_ = 0;
+ }
+
+ // check if the producer is active
+ if (received_packets_last_round_ != 0) {
+ rounds_without_packets_ = 0;
+ } else {
+ rounds_without_packets_++;
+ if (rounds_without_packets_ >= MAX_ROUND_WHIOUT_PACKETS &&
+ producer_is_active_ != false) {
+ initParams();
+ }
+ }
+
+ // reset counters
+ received_bytes_ = 0;
+ received_fec_bytes_ = 0;
+ recovered_bytes_with_fec_ = 0;
+ packets_lost_ = 0;
+ definitely_lost_pkt_ = 0;
+ losses_recovered_ = 0;
+ first_seq_in_round_ = highest_seq_received_;
+
+ past_nack_on_last_round_ = false;
+ received_nacks_last_round_ = 0;
+
+ received_packets_last_round_ = 0;
+ received_data_last_round_ = 0;
+ received_data_from_cache_ = 0;
+ sent_interests_last_round_ = 0;
+ sent_rtx_last_round_ = 0;
+
+ received_fec_pkt_ = 0;
+
+ rounds_++;
+}
+
+void RTCState::updateReceivedBytes(const core::ContentObject &content_object,
+ bool isFec) {
+ if (isFec) {
+ received_fec_bytes_ +=
+ (uint32_t)(content_object.headerSize() + content_object.payloadSize());
+ } else {
+ received_bytes_ +=
+ (uint32_t)(content_object.headerSize() + content_object.payloadSize());
+ }
+}
+
+void RTCState::updatePacketSize(const core::ContentObject &content_object) {
+ uint32_t pkt_size =
+ (uint32_t)(content_object.headerSize() + content_object.payloadSize());
+ avg_packet_size_ = (MOVING_AVG_ALPHA * avg_packet_size_) +
+ ((1 - MOVING_AVG_ALPHA) * pkt_size);
+}
+
+void RTCState::updatePathStats(const core::ContentObject &content_object,
+ bool is_nack) {
+ // get packet path
+ uint32_t path_label = content_object.getPathLabel();
+ auto path_it = path_table_.find(path_label);
+
+ if (path_it == path_table_.end()) {
+ // found a new path
+ std::shared_ptr<RTCDataPath> newPath =
+ std::make_shared<RTCDataPath>(path_label);
+ auto ret = path_table_.insert(
+ std::pair<uint32_t, std::shared_ptr<RTCDataPath>>(path_label, newPath));
+ path_it = ret.first;
+ }
+
+ auto path = path_it->second;
+
+ // compute rtt
+ uint32_t seq = content_object.getName().getSuffix();
+ uint64_t interest_sent_time = getInterestSentTime(seq);
+ if (interest_sent_time == 0)
+ return; // this should not happen,
+ // it means that we are processing an interest
+ // that is not pending
+
+ uint64_t now = utils::SteadyTime::nowMs().count();
+
+ uint64_t RTT = now - interest_sent_time;
+
+ path->insertRttSample(utils::SteadyTime::Milliseconds(RTT), false);
+
+ // compute OWD (the first part of the nack and data packet header are the
+ // same, so we cast to data data packet)
+ core::ParamsRTC params = RTCState::getDataParams(content_object);
+ int64_t OWD = now - params.timestamp;
+ path->insertOwdSample(OWD);
+
+ // compute IAT or set path to producer
+ if (!is_nack) {
+ // compute the iat only for the content packets
+ uint32_t segment_number = content_object.getName().getSuffix();
+ path->computeInterArrivalGap(segment_number);
+ if (!path->pathToProducer()) received_data_from_cache_++;
+ } else {
+ path->receivedNack();
+ }
+}
+
+void RTCState::updateLossRate(bool in_sync) {
+ last_round_loss_rate_ = loss_rate_;
+ loss_rate_ = 0.0;
+
+ uint32_t number_theorically_received_packets_ =
+ highest_seq_received_ - first_seq_in_round_;
+
+ // XXX this may be quite inefficient if the rate is high
+ // maybe is better to iterate over the set?
+
+ uint32_t fec_packets = 0;
+ for (uint32_t i = (first_seq_in_round_ + 1); i < highest_seq_received_; i++) {
+ PacketState state = getPacketState(i);
+ if (state == PacketState::SKIPPED) {
+ if (number_theorically_received_packets_ > 0)
+ number_theorically_received_packets_--;
+ }
+ if (indexer_->isFec(i)) fec_packets++;
+ }
+ if (indexer_->isFec(highest_seq_received_)) fec_packets++;
+
+ // in this case no new packet was received after the previous round, avoid
+ // division by 0
+ if (number_theorically_received_packets_ == 0 && packets_lost_ == 0) return;
+
+ if (number_theorically_received_packets_ != 0)
+ loss_rate_ = (double)((double)(packets_lost_) /
+ (double)number_theorically_received_packets_);
+ else
+ // we didn't receive anything except NACKs that triggered losses
+ loss_rate_ = 1.0;
+
+ if (avg_loss_rate_ == -1.0)
+ avg_loss_rate_ = loss_rate_;
+ else
+ avg_loss_rate_ =
+ avg_loss_rate_ * MOVING_AVG_ALPHA + loss_rate_ * (1 - MOVING_AVG_ALPHA);
+
+ // update counters for loss rate per second
+ total_expected_packets_ += number_theorically_received_packets_;
+ lost_per_sec_ += packets_lost_;
+
+ if (in_sync) {
+ // update counters for residual losses
+ // fec packets are not sent to the app so we don't want to count them here
+ expected_packets_ +=
+ ((highest_seq_received_ - first_seq_in_round_) - fec_packets);
+ } else {
+ expected_packets_ = 0;
+ packets_sent_to_app_ = 0;
+ }
+
+ if (rounds_from_last_compute_ >= (MILLI_IN_A_SEC / ROUND_LEN)) {
+ // compute loss rate per second
+ if (lost_per_sec_ > total_expected_packets_)
+ lost_per_sec_ = total_expected_packets_;
+
+ if (total_expected_packets_ == 0)
+ per_sec_loss_rate_ = 0;
+ else
+ per_sec_loss_rate_ =
+ (double)((double)(lost_per_sec_) / (double)total_expected_packets_);
+
+ loss_history_.pushBack(per_sec_loss_rate_);
+
+ if (in_sync && expected_packets_ != 0) {
+ // compute residual loss rate
+ if (packets_sent_to_app_ > expected_packets_) {
+ // this may happen if we get packet from the prev bin that get recovered
+ // on the current one
+ packets_sent_to_app_ = expected_packets_;
+ }
+
+ residual_loss_rate_ =
+ 1.0 - ((double)packets_sent_to_app_ / (double)expected_packets_);
+ if (residual_loss_rate_ < 0.0) residual_loss_rate_ = 0.0;
+ }
+
+ lost_per_sec_ = 0;
+ total_expected_packets_ = 0;
+ expected_packets_ = 0;
+ packets_sent_to_app_ = 0;
+ rounds_from_last_compute_ = 0;
+ }
+
+ rounds_from_last_compute_++;
+}
+
+void RTCState::dataToBeReceived(uint32_t seq) {
+ addToPacketCache(seq, PacketState::TO_BE_RECEIVED);
+}
+
+void RTCState::updateHighestSeqReceived(uint32_t seq) {
+ if (seq > highest_seq_received_) highest_seq_received_ = seq;
+}
+
+void RTCState::addRecvOrLost(uint32_t seq, PacketState state) {
+ auto it = pending_interests_.find(seq);
+ if (it != pending_interests_.end()) {
+ pending_interests_.erase(it);
+ if (indexer_->isFec(seq)) pending_fec_pkt_--;
+ }
+
+ addToPacketCache(seq, state);
+
+ // keep track of the last packet received/lost
+ // without holes.
+ if (highest_seq_received_in_order_ < last_seq_nacked_) {
+ highest_seq_received_in_order_ = last_seq_nacked_;
+ }
+
+ if ((highest_seq_received_in_order_ + 1) == seq) {
+ highest_seq_received_in_order_ = seq;
+ } else if (seq <= highest_seq_received_in_order_) {
+ // here we do nothing
+ } else if (seq > highest_seq_received_in_order_) {
+ // 1) there is a gap in the sequence so we do not update
+ // highest_seq_received_in_order_
+ // 2) all the packets from highest_seq_received_in_order_ to seq are
+ // received or lost or are fec packetis. In this case we increase
+ // highest_seq_received_in_order_ until we find an hole in the sequence
+
+ for (uint32_t i = highest_seq_received_in_order_ + 1; i <= seq; i++) {
+ PacketState state = getPacketState(i);
+ if ((state == PacketState::UNKNOWN || state == PacketState::LOST)) {
+ if (indexer_->isFec(i)) {
+ // this is a fec packet and we don't care to receive it
+ // however we may need to increse the number or lost packets
+ // XXX: in case we want to use rtx to recover fec packets,
+ // this may prevent to detect a packet loss and no rtx will be sent
+ if (TRANSPORT_EXPECT_TRUE(i >= first_interest_sent_seq_)) {
+ onLossDetected(i);
+ }
+ } else {
+ // this is a data packet and we need to get it
+ break;
+ }
+ }
+ // this packet is in order so we can update the
+ // highest_seq_received_in_order_
+ highest_seq_received_in_order_ = i;
+ }
+ }
+}
+
+void RTCState::setInitRttTimer(uint32_t wait) {
+ init_rtt_timer_->cancel();
+ init_rtt_timer_->expires_from_now(std::chrono::milliseconds(wait));
+
+ std::weak_ptr<RTCState> self = shared_from_this();
+ init_rtt_timer_->async_wait([self](const std::error_code &ec) {
+ if (ec) return;
+
+ if (auto ptr = self.lock()) {
+ ptr->checkInitRttTimer();
+ }
+ });
+}
+
+void RTCState::checkInitRttTimer() {
+ if (received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV ||
+ probe_handler_->getProbeLossRate() == 1.0) {
+ // we didn't received enough probes or they were not valid, restart
+ received_probes_ = 0;
+ probe_handler_->setSuffixRange(MIN_INIT_PROBE_SEQ, MAX_INIT_PROBE_SEQ);
+ probe_handler_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES);
+ probe_handler_->sendProbes();
+ setInitRttTimer(INIT_RTT_PROBE_RESTART);
+ return;
+ }
+
+ init_rtt_ = true;
+ main_path_->roundEnd();
+ loss_history_.pushBack(probe_handler_->getProbeLossRate());
+
+ probe_handler_->setSuffixRange(MIN_RTT_PROBE_SEQ, MAX_RTT_PROBE_SEQ);
+ probe_handler_->setProbes(RTT_PROBE_INTERVAL, 0);
+ probe_handler_->sendProbes();
+
+ // init last_seq_nacked_. skip packets that may come from the cache
+ double prod_rate = getProducerRate();
+ double rtt = (double)getMinRTT() / MILLI_IN_A_SEC;
+ double packet_size = getAveragePacketSize();
+ uint32_t pkt_in_rtt_ = std::floor(((prod_rate / packet_size) * rtt));
+ last_seq_nacked_ = last_production_seq_ + pkt_in_rtt_;
+
+ discovered_rtt_callback_();
+}
+
+core::ParamsRTC RTCState::getProbeParams(const core::ContentObject &probe) {
+ uint32_t seq = probe.getName().getSuffix();
+ core::ParamsRTC params;
+
+ switch (ProbeHandler::getProbeType(seq)) {
+ case ProbeType::INIT: {
+ core::ContentObjectManifest manifest(
+ const_cast<core::ContentObject &>(probe).shared_from_this());
+ manifest.decode();
+ params = manifest.getParamsRTC();
+ break;
+ }
+ case ProbeType::RTT: {
+ struct nack_packet_t *probe_pkt =
+ (struct nack_packet_t *)probe.getPayload()->data();
+ params = core::ParamsRTC{
+ .timestamp = probe_pkt->getTimestamp(),
+ .prod_rate = probe_pkt->getProductionRate(),
+ .prod_seg = probe_pkt->getProductionSegment(),
+ };
+ break;
+ }
+ default:
+ break;
+ }
+
+ return params;
+}
+
+core::ParamsRTC RTCState::getDataParams(const core::ContentObject &data) {
+ core::ParamsRTC params;
+
+ switch (data.getPayloadType()) {
+ case core::PayloadType::DATA: {
+ struct data_packet_t *data_pkt =
+ (struct data_packet_t *)data.getPayload()->data();
+ params = core::ParamsRTC{
+ .timestamp = data_pkt->getTimestamp(),
+ .prod_rate = data_pkt->getProductionRate(),
+ .prod_seg = data.getName().getSuffix(),
+ };
+ break;
+ }
+ case core::PayloadType::MANIFEST: {
+ core::ContentObjectManifest manifest(
+ const_cast<core::ContentObject &>(data).shared_from_this());
+ manifest.decode();
+ params = manifest.getParamsRTC();
+ break;
+ }
+ default:
+ break;
+ }
+
+ return params;
+}
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_state.h b/libtransport/src/protocols/rtc/rtc_state.h
new file mode 100644
index 000000000..ac3cc621f
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_state.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <core/facade.h>
+#include <hicn/transport/config.h>
+#include <hicn/transport/core/asio_wrapper.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/utils/rtc_quality_score.h>
+#include <protocols/indexer.h>
+#include <protocols/rtc/probe_handler.h>
+#include <protocols/rtc/rtc_data_path.h>
+#include <utils/max_filter.h>
+
+#include <map>
+#include <set>
+
+namespace transport {
+
+namespace protocol {
+
+namespace rtc {
+
+// packet state
+// RECEIVED: the packet was already received
+// LOST: the packet is marked as lost but can be recovered
+// DEFINITELY_LOST: the packet is lost and cannot be recovered
+// TO_BE_RECEIVED: when a packet is received is sent to the FEC decoder. the fec
+// decoder may decide to send the packet directly to the app. to avoid
+// duplicated the packet is marked with this state
+// SKIPPED: an interest that was not sent, only for FEC packets
+// UNKNOWN: unknown state
+enum class PacketState : uint8_t {
+ RECEIVED,
+ TO_BE_RECEIVED,
+ LOST,
+ DEFINITELY_LOST,
+ SKIPPED,
+ UNKNOWN
+};
+
+class RTCState : public std::enable_shared_from_this<RTCState> {
+ using PendingInterestsMap = std::map<uint32_t, uint64_t>;
+
+ private:
+ const double MAX_CACHED_PACKETS = 8192; // XXX this value may be too small
+ // for high rate apps
+
+ public:
+ using DiscoveredRttCallback = std::function<void()>;
+
+ public:
+ RTCState(Indexer *indexer, ProbeHandler::SendProbeCallback &&probe_callback,
+ DiscoveredRttCallback &&discovered_rtt_callback,
+ asio::io_service &io_service);
+
+ ~RTCState();
+
+ // initialization
+ void initParams();
+
+ // packet events
+ void onSendNewInterest(const core::Name *interest_name);
+ void onTimeout(uint32_t seq, bool lost);
+ void onLossDetected(uint32_t seq);
+ void onRetransmission(uint32_t seq);
+ void onPossibleLossWithNoRtx(uint32_t seq);
+ void onDataPacketReceived(const core::ContentObject &content_object,
+ bool compute_stats);
+ void onFecPacketReceived(const core::ContentObject &content_object);
+ void onNackPacketReceived(const core::ContentObject &nack,
+ bool compute_stats);
+ void onPacketLost(uint32_t seq);
+ void onPacketRecoveredRtx(const core::ContentObject &content_object,
+ uint64_t rtt);
+ void onFecPacketRecoveredRtx(const core::ContentObject &content_object);
+ void onPacketRecoveredFec(uint32_t seq, uint32_t size);
+ bool onProbePacketReceived(const core::ContentObject &probe);
+ void onJumpForward(uint32_t next_seq);
+
+ // protocol state
+ void onNewRound(double round_len, bool in_sync);
+
+ // main path
+ uint32_t getProducerPath() const {
+ if (mainPathIsValid()) return main_path_->getPathId();
+ return 0;
+ }
+
+ // delay metrics
+ bool isRttDiscovered() const { return init_rtt_; }
+
+ uint64_t getMinRTT() const {
+ if (mainPathIsValid()) return main_path_->getMinRtt();
+ return 0;
+ }
+
+ uint64_t getAvgRTT() const {
+ if (mainPathIsValid()) return main_path_->getAvgRtt();
+ return 0;
+ }
+
+ uint64_t getMaxRTT() const {
+ if (mainPathIsValid()) return main_path_->getMaxRtt();
+ return 0;
+ }
+
+ uint64_t getEdgeRtt() const {
+ if (edge_path_ != nullptr) return edge_path_->getMinRtt();
+ return 0;
+ }
+
+ void resetRttStats() {
+ if (mainPathIsValid()) main_path_->clearRtt();
+ }
+
+ double getQueuing() const {
+ if (mainPathIsValid()) return main_path_->getQueuingDealy();
+ return 0.0;
+ }
+ double getIAT() const {
+ if (mainPathIsValid()) return main_path_->getInterArrivalGap();
+ return 0.0;
+ }
+
+ double getJitter() const {
+ if (mainPathIsValid()) return main_path_->getJitter();
+ return 0.0;
+ }
+
+ // pending interests
+ uint64_t getInterestSentTime(uint32_t seq) {
+ auto it = pending_interests_.find(seq);
+ if (it != pending_interests_.end()) return it->second;
+
+ return 0;
+ }
+
+ bool isPending(uint32_t seq) {
+ if (pending_interests_.find(seq) != pending_interests_.end()) return true;
+ return false;
+ }
+
+ uint32_t getPendingInterestNumber() const {
+ return (uint32_t)pending_interests_.size();
+ }
+
+ PacketState getPacketState(uint32_t seq) {
+ auto it = packet_cache_.find(seq);
+ if (it != packet_cache_.end()) return it->second;
+ return PacketState::UNKNOWN;
+ }
+
+ // loss rate
+ double getPerRoundLossRate() const { return loss_rate_; }
+ double getPerSecondLossRate() const { return per_sec_loss_rate_; }
+ double getAvgLossRate() const { return avg_loss_rate_; }
+ double getMaxLossRate() const {
+ if (loss_history_.size() != 0) return loss_history_.begin();
+ return 0;
+ }
+
+ double getLastRoundLossRate() const { return last_round_loss_rate_; }
+ double getResidualLossRate() const { return residual_loss_rate_; }
+
+ uint32_t getLostData() const { return packets_lost_; };
+ uint32_t getRecoveredLosses() const { return losses_recovered_; }
+
+ uint32_t getDefinitelyLostPackets() const { return definitely_lost_pkt_; }
+
+ uint32_t getHighestSeqReceived() const { return highest_seq_received_; }
+
+ uint32_t getHighestSeqReceivedInOrder() const {
+ return highest_seq_received_in_order_;
+ }
+
+ // fec packets
+ uint32_t getReceivedFecPackets() const { return received_fec_pkt_; }
+ uint32_t getPendingFecPackets() const { return pending_fec_pkt_; }
+
+ // generic stats
+ uint32_t getReceivedBytesInRound() const { return received_bytes_; }
+ uint32_t getReceivedFecBytesInRound() const { return received_fec_bytes_; }
+ uint32_t getRecoveredFecBytesInRound() const {
+ return recovered_bytes_with_fec_;
+ }
+ uint32_t getReceivedNacksInRound() const {
+ return received_nacks_last_round_;
+ }
+ uint32_t getReceivedDataInRound() const { return received_data_last_round_; }
+ uint32_t getSentInterestInRound() const { return sent_interests_last_round_; }
+ uint32_t getSentRtxInRound() const { return sent_rtx_last_round_; }
+
+ // bandwidth/production metrics
+ double getAvailableBw() const { return 0.0; }; // TODO
+ double getProducerRate() const { return production_rate_; }
+ double getReceivedRate() const { return received_rate_; }
+ double getReceivedFecRate() const { return fec_received_rate_; }
+ double getRecoveredFecRate() const { return fec_recovered_rate_; }
+
+ double getAveragePacketSize() const { return avg_packet_size_; }
+
+ // nacks
+ uint32_t getRoundsWithoutNacks() const { return rounds_without_nacks_; }
+ uint32_t getLastSeqNacked() const { return last_seq_nacked_; }
+
+ // producer state
+ bool isProducerActive() const { return producer_is_active_; }
+
+ // packets from cache
+ // this should be called at the end of a round beacuse otherwise we may have
+ // not enough packets to get a good stat
+ double getPacketFromCacheRatio() const {
+ if (received_data_last_round_ == 0) return 0;
+ return (double)received_data_from_cache_ /
+ (double)received_data_last_round_;
+ }
+
+ PendingInterestsMap::iterator getPendingInterestsMapBegin() {
+ return pending_interests_.begin();
+ }
+ PendingInterestsMap::iterator getPendingInterestsMapEnd() {
+ return pending_interests_.end();
+ }
+
+ // quality
+ uint8_t getQualityScore() {
+ uint8_t qs = quality_score_.getQualityScore(
+ getMaxRTT(), std::round(getResidualLossRate() * 100));
+ return qs;
+ }
+
+ // We received a data pkt that will be set to RECEIVED, but first we have to
+ // go through FEC. We do not want to consider this pkt as recovered, thus we
+ // set it as TO_BE_RECEIVED.
+ void dataToBeReceived(uint32_t seq);
+
+ void updateHighestSeqReceived(uint32_t seq);
+
+ // Extract RTC parameters from probes (init or RTT probes) and data packets.
+ static core::ParamsRTC getProbeParams(const core::ContentObject &probe);
+ static core::ParamsRTC getDataParams(const core::ContentObject &data);
+
+ private:
+ void addToPacketCache(uint32_t seq, PacketState state) {
+ // this function adds or updates the current state
+ if (packet_cache_.size() >= MAX_CACHED_PACKETS) {
+ packet_cache_.erase(packet_cache_.begin());
+ }
+ packet_cache_[seq] = state;
+ }
+
+ void eraseFromPacketCache(uint32_t seq) { packet_cache_.erase(seq); }
+
+ // update stats
+ void updateState();
+ void updateReceivedBytes(const core::ContentObject &content_object,
+ bool isFec);
+ void updatePacketSize(const core::ContentObject &content_object);
+ void updatePathStats(const core::ContentObject &content_object, bool is_nack);
+ void updateLossRate(bool in_sycn);
+
+ void addRecvOrLost(uint32_t seq, PacketState state);
+
+ void setInitRttTimer(uint32_t wait);
+ void checkInitRttTimer();
+
+ bool mainPathIsValid() const {
+ if (main_path_ != nullptr)
+ return true;
+ else
+ return false;
+ }
+
+ // packets counters (total)
+ uint32_t sent_interests_;
+ uint32_t sent_rtx_;
+ uint32_t received_data_;
+ uint32_t received_nacks_;
+ uint32_t received_timeouts_;
+ uint32_t received_probes_;
+
+ // loss counters
+ int32_t packets_lost_;
+ int32_t losses_recovered_;
+ uint32_t definitely_lost_pkt_;
+ uint32_t first_seq_in_round_;
+ uint32_t highest_seq_received_;
+ uint32_t highest_seq_received_in_order_;
+ uint32_t last_seq_nacked_; // segment for which we got an oldNack
+ double loss_rate_;
+ double avg_loss_rate_;
+ double last_round_loss_rate_;
+ utils::MaxFilter<double> loss_history_;
+
+ // per second loss rate
+ uint32_t lost_per_sec_;
+ uint32_t total_expected_packets_;
+ double per_sec_loss_rate_;
+
+ // conunters for residual losses
+ // residual losses are computed every second and are used
+ // as feedback to the upper levels (e.g application)
+ uint32_t expected_packets_;
+ uint32_t packets_sent_to_app_;
+ uint32_t rounds_from_last_compute_;
+ double residual_loss_rate_;
+
+ // bw counters
+ uint32_t received_bytes_;
+ uint32_t received_fec_bytes_;
+ uint32_t recovered_bytes_with_fec_;
+ double avg_packet_size_;
+ double production_rate_; // rate communicated by the producer using nacks
+ double received_rate_; // rate recevied by the consumer (only data)
+ double fec_received_rate_; // fec rate recevied by the consumer
+ double fec_recovered_rate_; // rate recovered using fec
+
+ // nack counters
+ // the bool takes tracks only about the valid past nacks (no rtx) and it is
+ // used to switch between the states. Instead received_nacks_last_round_ logs
+ // all the nacks for statistics
+ bool past_nack_on_last_round_;
+ uint32_t received_nacks_last_round_;
+
+ // packets counters
+ uint32_t received_packets_last_round_;
+ uint32_t received_data_last_round_;
+ uint32_t received_data_from_cache_;
+ uint32_t sent_interests_last_round_;
+ uint32_t sent_rtx_last_round_;
+
+ // fec counters
+ uint32_t received_fec_pkt_;
+ uint32_t pending_fec_pkt_;
+
+ // round counters
+ uint32_t rounds_;
+ uint32_t rounds_without_nacks_;
+ uint32_t rounds_without_packets_;
+
+ // init rtt
+ uint64_t first_interest_sent_time_;
+ uint32_t first_interest_sent_seq_;
+
+ // producer state
+ bool
+ producer_is_active_; // the prodcuer is active if we receive some packets
+ uint32_t last_production_seq_; // last production seq received by the
+ // producer used to init the sync protcol
+ uint32_t last_prod_update_seq_; // seq number of the last packet used to
+ // update the update from the producer.
+ // assumption: the highest seq number carries
+ // the most up to date info. in case of
+ // probes we look at the produced seq number
+
+ // paths stats
+ std::unordered_map<uint32_t, std::shared_ptr<RTCDataPath>> path_table_;
+ std::shared_ptr<RTCDataPath> main_path_; // this is the path that connects
+ // the consumer to the producer. in
+ // case of multipath the trasnport
+ // uses the most active path
+ std::shared_ptr<RTCDataPath> edge_path_; // path to the closest cache if it
+ // exists
+
+ // packet received
+ // cache where to store info about the last MAX_CACHED_PACKETS
+ // these are packets that are received or lost or definitely lost and are not
+ // anymore in the pending intetest list
+ std::map<uint32_t, PacketState> packet_cache_;
+
+ // pending interests
+ PendingInterestsMap pending_interests_;
+
+ // indexer
+ Indexer *indexer_;
+
+ // used to keep track of the skipped interests
+ uint32_t last_interest_sent_;
+
+ // probes
+ std::shared_ptr<ProbeHandler> probe_handler_;
+ bool init_rtt_;
+ std::unique_ptr<asio::steady_timer> init_rtt_timer_;
+
+ // quality score
+ RTCQualityScore quality_score_;
+
+ // callbacks
+ DiscoveredRttCallback discovered_rtt_callback_;
+};
+
+} // namespace rtc
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_verifier.cc b/libtransport/src/protocols/rtc/rtc_verifier.cc
new file mode 100644
index 000000000..60fce92a5
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_verifier.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2017-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <core/facade.h>
+#include <protocols/rtc/rtc_packet.h>
+#include <protocols/rtc/rtc_verifier.h>
+
+namespace transport {
+namespace protocol {
+namespace rtc {
+
+RTCVerifier::RTCVerifier(std::shared_ptr<auth::Verifier> verifier,
+ uint32_t factor_relevant, uint32_t factor_alert)
+ : verifier_(verifier),
+ factor_relevant_(factor_relevant),
+ factor_alert_(factor_alert),
+ manifest_max_capacity_(std::numeric_limits<uint8_t>::max()) {}
+
+void RTCVerifier::setState(std::shared_ptr<RTCState> rtc_state) {
+ rtc_state_ = rtc_state;
+}
+
+void RTCVerifier::setVerifier(std::shared_ptr<auth::Verifier> verifier) {
+ verifier_ = verifier;
+}
+
+void RTCVerifier::setFactorRelevant(uint32_t factor_relevant) {
+ factor_relevant_ = factor_relevant;
+}
+
+void RTCVerifier::setFactorAlert(uint32_t factor_alert) {
+ factor_alert_ = factor_alert;
+}
+
+auth::VerificationPolicy RTCVerifier::verify(core::Interest &interest) {
+ return verifier_->verifyPackets(&interest);
+}
+
+auth::VerificationPolicy RTCVerifier::verify(
+ core::ContentObject &content_object, bool is_fec) {
+ auth::Suffix suffix = content_object.getName().getSuffix();
+ auth::VerificationPolicy default_policy = auth::VerificationPolicy::ABORT;
+
+ core::PayloadType payload_type = content_object.getPayloadType();
+ bool is_probe = ProbeHandler::getProbeType(suffix) != ProbeType::NOT_PROBE;
+ bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE;
+ bool is_manifest = !is_probe && !is_nack && !is_fec &&
+ payload_type == core::PayloadType::MANIFEST;
+ bool is_data = !is_probe && !is_nack && !is_fec &&
+ payload_type == core::PayloadType::DATA;
+
+ if (is_probe) return verifyProbe(content_object);
+ if (is_nack) return verifyNack(content_object);
+ if (is_fec) return verifyFec(content_object);
+ if (is_data) return verifyData(content_object);
+ if (is_manifest) return verifyManifest(content_object);
+
+ verifier_->callVerificationFailedCallback(suffix, default_policy);
+ return default_policy;
+}
+
+auth::VerificationPolicy RTCVerifier::verifyProbe(
+ core::ContentObject &content_object) {
+ auth::Suffix suffix = content_object.getName().getSuffix();
+ auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT;
+
+ switch (ProbeHandler::getProbeType(suffix)) {
+ case ProbeType::INIT:
+ policy = verifyManifest(content_object);
+ if (policy == auth::VerificationPolicy::ACCEPT) {
+ policy = processManifest(content_object);
+ }
+ break;
+ case ProbeType::RTT:
+ policy = verifyNack(content_object);
+ break;
+ default:
+ verifier_->callVerificationFailedCallback(suffix, policy);
+ break;
+ }
+
+ return policy;
+}
+
+auth::VerificationPolicy RTCVerifier::verifyNack(
+ core::ContentObject &content_object) {
+ return verifier_->verifyPackets(&content_object);
+}
+
+auth::VerificationPolicy RTCVerifier::verifyFec(
+ core::ContentObject &content_object) {
+ return verifier_->verifyPackets(&content_object);
+}
+
+auth::VerificationPolicy RTCVerifier::verifyData(
+ core::ContentObject &content_object) {
+ if (HICN_PACKET_FORMAT_IS_AH(content_object.getFormat())) {
+ return verifier_->verifyPackets(&content_object);
+ }
+
+ auth::Suffix suffix = content_object.getName().getSuffix();
+ auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT;
+
+ uint32_t threshold_relevant = factor_relevant_ * manifest_max_capacity_;
+ uint32_t threshold_alert = factor_alert_ * manifest_max_capacity_;
+
+ // Flush packets outside relevance window
+ for (auto it = packets_unverif_.set().begin();
+ it != packets_unverif_.set().end();) {
+ if (it->first > current_index_ - threshold_relevant) {
+ break;
+ }
+ packets_unverif_erased_.insert((unsigned int)it->first);
+ it = packets_unverif_.remove(it);
+ }
+
+ // Add packet to set of unverified packets
+ packets_unverif_.add({current_index_, suffix},
+ content_object.computeDigest(manifest_hash_algo_));
+ current_index_++;
+
+ // Check that the number of unverified packets is below the alert threshold
+ if (packets_unverif_.set().size() <= threshold_alert) {
+ policy = auth::VerificationPolicy::ACCEPT;
+ }
+
+ verifier_->callVerificationFailedCallback(suffix, policy);
+ return policy;
+}
+
+auth::VerificationPolicy RTCVerifier::verifyManifest(
+ core::ContentObject &content_object) {
+ return verifier_->verifyPackets(&content_object);
+}
+
+auth::VerificationPolicy RTCVerifier::processManifest(
+ core::ContentObject &content_object) {
+ auth::Suffix suffix = content_object.getName().getSuffix();
+ auth::VerificationPolicy accept_policy = auth::VerificationPolicy::ACCEPT;
+
+ // Decode manifest
+ core::ContentObjectManifest manifest(content_object.shared_from_this());
+ manifest.decode();
+
+ // Extract manifest data
+ manifest_max_capacity_ = manifest.getMaxCapacity();
+ manifest_hash_algo_ = manifest.getHashAlgorithm();
+ auth::Verifier::SuffixMap suffix_map = manifest.getSuffixMap();
+
+ // Return early if the manifest is empty
+ if (suffix_map.empty()) {
+ verifier_->callVerificationFailedCallback(suffix, accept_policy);
+ return accept_policy;
+ }
+
+ // Add hashes to map of all manifest hashes
+ manifest_digests_.insert(suffix_map.begin(), suffix_map.end());
+
+ // Remove discarded and definitely lost packets from digest map
+ for (auto it = manifest_digests_.begin(); it != manifest_digests_.end();) {
+ auto it_erased = packets_unverif_erased_.find(it->first);
+
+ if (it_erased != packets_unverif_erased_.end()) {
+ packets_unverif_erased_.erase(it_erased);
+ it = manifest_digests_.erase(it);
+ continue;
+ }
+
+ if (rtc_state_->getPacketState(it->first) == PacketState::DEFINITELY_LOST) {
+ it = manifest_digests_.erase(it);
+ continue;
+ }
+
+ ++it;
+ }
+
+ // Verify packets
+ auth::Verifier::PolicyMap policies =
+ verifier_->verifyHashes(packets_unverif_.suffixMap(), manifest_digests_);
+
+ for (const auto &p : policies) {
+ switch (p.second) {
+ case auth::VerificationPolicy::ACCEPT: {
+ packets_unverif_.remove(packets_unverif_.packet(p.first));
+ manifest_digests_.erase(p.first);
+ break;
+ }
+ case auth::VerificationPolicy::UNKNOWN:
+ break;
+ case auth::VerificationPolicy::DROP:
+ case auth::VerificationPolicy::ABORT:
+ return p.second;
+ }
+ }
+
+ verifier_->callVerificationFailedCallback(suffix, accept_policy);
+ return accept_policy;
+}
+
+void RTCVerifier::onDataRecoveredFec(uint32_t suffix) {
+ manifest_digests_.erase(suffix);
+}
+
+std::pair<RTCVerifier::PacketSet::iterator, bool> RTCVerifier::Packets::add(
+ const Packet &packet, const auth::CryptoHash &digest) {
+ auto inserted = packets_.insert(packet);
+ if (inserted.second) {
+ packets_map_[packet.second] = inserted.first;
+ suffix_map_[packet.second] = digest;
+ }
+ return inserted;
+}
+
+RTCVerifier::PacketSet::iterator RTCVerifier::Packets::remove(
+ PacketSet::iterator packet_it) {
+ packets_map_.erase(packet_it->second);
+ suffix_map_.erase(packet_it->second);
+ return packets_.erase(packet_it);
+}
+
+const std::set<RTCVerifier::Packet> &RTCVerifier::Packets::set() const {
+ return packets_;
+};
+
+RTCVerifier::PacketSet::iterator RTCVerifier::Packets::packet(
+ auth::Suffix suffix) {
+ return packets_map_.at(suffix);
+};
+
+const auth::Verifier::SuffixMap &RTCVerifier::Packets::suffixMap() const {
+ return suffix_map_;
+}
+
+} // end namespace rtc
+} // end namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_verifier.h b/libtransport/src/protocols/rtc/rtc_verifier.h
new file mode 100644
index 000000000..c83faf08a
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_verifier.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2017-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <core/facade.h>
+#include <hicn/transport/auth/verifier.h>
+#include <hicn/transport/core/content_object.h>
+#include <protocols/rtc/rtc_state.h>
+
+namespace transport {
+namespace protocol {
+namespace rtc {
+
+class RTCVerifier {
+ public:
+ explicit RTCVerifier(std::shared_ptr<auth::Verifier> verifier,
+ uint32_t factor_relevant, uint32_t factor_alert);
+
+ virtual ~RTCVerifier() = default;
+
+ void setState(std::shared_ptr<RTCState> rtc_state);
+ void setVerifier(std::shared_ptr<auth::Verifier> verifier);
+ void setFactorRelevant(uint32_t factor_relevant);
+ void setFactorAlert(uint32_t factor_alert);
+
+ auth::VerificationPolicy verify(core::Interest &interest);
+ auth::VerificationPolicy verify(core::ContentObject &content_object,
+ bool is_fec = false);
+ auth::VerificationPolicy verifyProbe(core::ContentObject &content_object);
+ auth::VerificationPolicy verifyNack(core::ContentObject &content_object);
+ auth::VerificationPolicy verifyFec(core::ContentObject &content_object);
+ auth::VerificationPolicy verifyData(core::ContentObject &content_object);
+ auth::VerificationPolicy verifyManifest(core::ContentObject &content_object);
+
+ auth::VerificationPolicy processManifest(core::ContentObject &content_object);
+
+ void onDataRecoveredFec(uint32_t suffix);
+
+ protected:
+ using Index = uint64_t;
+ using Packet = std::pair<Index, auth::Suffix>;
+ using PacketSet = std::set<Packet>;
+
+ class Packets {
+ public:
+ std::pair<PacketSet::iterator, bool> add(const Packet &packet,
+ const auth::CryptoHash &digest);
+ PacketSet::iterator remove(PacketSet::iterator packet_it);
+ const PacketSet &set() const;
+ PacketSet::iterator packet(auth::Suffix suffix);
+ const auth::Verifier::SuffixMap &suffixMap() const;
+
+ private:
+ PacketSet packets_;
+ std::unordered_map<auth::Suffix, PacketSet::iterator> packets_map_;
+ auth::Verifier::SuffixMap suffix_map_;
+ };
+
+ // The RTC state.
+ std::shared_ptr<RTCState> rtc_state_;
+ // The verifier instance.
+ std::shared_ptr<auth::Verifier> verifier_;
+ // Used to compute the relevance windows size (in packets).
+ uint32_t factor_relevant_;
+ // Used to compute the alert threshold (in packets).
+ uint32_t factor_alert_;
+ // The maximum number of entries a manifest can contain.
+ uint8_t manifest_max_capacity_;
+ // Hash algorithm used by manifests.
+ auth::CryptoHashType manifest_hash_algo_;
+ // Digests extracted from all manifests received.
+ auth::Verifier::SuffixMap manifest_digests_;
+ // The number of data packets processed.
+ Index current_index_;
+ // Unverified packets with index in relevance window.
+ Packets packets_unverif_;
+ // Unverified erased packets with index outside relevance window.
+ std::unordered_set<auth::Suffix> packets_unverif_erased_;
+};
+
+} // namespace rtc
+} // namespace protocol
+} // namespace transport
diff --git a/libtransport/src/protocols/rtc_data_path.cc b/libtransport/src/protocols/rtc_data_path.cc
deleted file mode 100644
index 30644e939..000000000
--- a/libtransport/src/protocols/rtc_data_path.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <protocols/rtc_data_path.h>
-
-#include <cfloat>
-#include <chrono>
-
-#define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec
-
-namespace transport {
-
-namespace protocol {
-
-RTCDataPath::RTCDataPath()
- : min_rtt(UINT_MAX),
- prev_min_rtt(UINT_MAX),
- min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the
- // real OWD, but the measured one, that depends on the
- // clock of sender and receiver. the only meaningful
- // value is is the queueing delay. for this reason we
- // keep both RTT (for the windowd calculation) and OWD
- // (for congestion/quality control)
- prev_min_owd(INT_MAX),
- avg_owd(0.0),
- queuing_delay(DBL_MAX),
- lastRecvSeq_(0),
- lastRecvTime_(0),
- avg_inter_arrival_(DBL_MAX),
- received_nacks_(false),
- received_packets_(false),
- rounds_without_packets_(0),
- RTThistory_(HISTORY_LEN),
- OWDhistory_(HISTORY_LEN){};
-
-void RTCDataPath::insertRttSample(uint64_t rtt) {
- // for the rtt we only keep track of the min one
- if (rtt < min_rtt) min_rtt = rtt;
-}
-
-void RTCDataPath::insertOwdSample(int64_t owd) {
- // for owd we use both min and avg
- if (owd < min_owd) min_owd = owd;
-
- if (avg_owd != DBL_MAX)
- avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC);
- else {
- avg_owd = owd;
- }
-
- // owd is computed only for valid data packets so we count only
- // this for decide if we recevie traffic or not
- received_packets_ = true;
-}
-
-void RTCDataPath::computeInterArrivalGap(uint32_t segmentNumber) {
- // got packet in sequence, compute gap
- if (lastRecvSeq_ == (segmentNumber - 1)) {
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- uint64_t delta = now - lastRecvTime_;
- lastRecvSeq_ = segmentNumber;
- lastRecvTime_ = now;
- if (avg_inter_arrival_ == DBL_MAX)
- avg_inter_arrival_ = delta;
- else
- avg_inter_arrival_ =
- (avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC);
- return;
- }
-
- // ooo packet, update the stasts if needed
- if (lastRecvSeq_ <= segmentNumber) {
- lastRecvSeq_ = segmentNumber;
- lastRecvTime_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- }
-}
-
-void RTCDataPath::receivedNack() { received_nacks_ = true; }
-
-double RTCDataPath::getInterArrivalGap() {
- if (avg_inter_arrival_ == DBL_MAX) return 0;
- return avg_inter_arrival_;
-}
-
-bool RTCDataPath::isActive() {
- if (received_nacks_ && rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS)
- return true;
- return false;
-}
-
-void RTCDataPath::roundEnd() {
- // reset min_rtt and add it to the history
- if (min_rtt != UINT_MAX) {
- prev_min_rtt = min_rtt;
- } else {
- // this may happen if we do not receive any packet
- // from this path in the last round. in this case
- // we use the measure from the previuos round
- min_rtt = prev_min_rtt;
- }
-
- if (min_rtt == 0) min_rtt = 1;
-
- RTThistory_.pushBack(min_rtt);
- min_rtt = UINT_MAX;
-
- // do the same for min owd
- if (min_owd != INT_MAX) {
- prev_min_owd = min_owd;
- } else {
- min_owd = prev_min_owd;
- }
-
- if (min_owd != INT_MAX) {
- OWDhistory_.pushBack(min_owd);
- min_owd = INT_MAX;
-
- // compute queuing delay
- queuing_delay = avg_owd - getMinOwd();
-
- } else {
- queuing_delay = 0.0;
- }
-
- if (!received_packets_)
- rounds_without_packets_++;
- else
- rounds_without_packets_ = 0;
- received_packets_ = false;
-}
-
-double RTCDataPath::getQueuingDealy() { return queuing_delay; }
-
-uint64_t RTCDataPath::getMinRtt() { return RTThistory_.begin(); }
-
-int64_t RTCDataPath::getMinOwd() { return OWDhistory_.begin(); }
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc_data_path.h b/libtransport/src/protocols/rtc_data_path.h
deleted file mode 100644
index 9076b355f..000000000
--- a/libtransport/src/protocols/rtc_data_path.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <climits>
-
-#include <utils/min_filter.h>
-
-#define ALPHA_RTC 0.125
-#define HISTORY_LEN 20 // 4 sec
-
-namespace transport {
-
-namespace protocol {
-
-class RTCDataPath {
- public:
- RTCDataPath();
-
- public:
- void insertRttSample(uint64_t rtt);
- void insertOwdSample(int64_t owd);
- void computeInterArrivalGap(uint32_t segmentNumber);
- void receivedNack();
-
- uint64_t getMinRtt();
- double getQueuingDealy();
- double getInterArrivalGap();
- bool isActive();
-
- void roundEnd();
-
- private:
- int64_t getMinOwd();
-
- uint64_t min_rtt;
- uint64_t prev_min_rtt;
-
- int64_t min_owd;
- int64_t prev_min_owd;
-
- double avg_owd;
-
- double queuing_delay;
-
- uint32_t lastRecvSeq_;
- uint64_t lastRecvTime_;
- double avg_inter_arrival_;
-
- // flags to check if a path is active
- // we considere a path active if it reaches a producer
- //(not a cache) --aka we got at least one nack on this path--
- // and if we receives packets
- bool received_nacks_;
- bool received_packets_;
- uint8_t rounds_without_packets_; // if we don't get any packet
- // for MAX_ROUNDS_WITHOUT_PKTS
- // we consider the path inactive
-
- utils::MinFilter<uint64_t> RTThistory_;
- utils::MinFilter<int64_t> OWDhistory_;
-};
-
-} // namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/test/CMakeLists.txt b/libtransport/src/protocols/test/CMakeLists.txt
deleted file mode 100644
index 6f9fdb9aa..000000000
--- a/libtransport/src/protocols/test/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-# Enable gcov output for the tests
-add_definitions(--coverage)
-set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
-
-set(TestsExpectedToPass
- test_transport_producer)
-
-foreach(test ${TestsExpectedToPass})
- AddTest(${test})
-endforeach() \ No newline at end of file
diff --git a/libtransport/src/protocols/test/test_transport_producer.cc b/libtransport/src/protocols/test/test_transport_producer.cc
deleted file mode 100644
index 204f2cbe2..000000000
--- a/libtransport/src/protocols/test/test_transport_producer.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "../socket_producer.h"
-#include "literals.h"
-
-#include <test.h>
-#include <random>
-
-namespace transport {
-
-namespace protocol {
-
-namespace {
-// The fixture for testing class Foo.
-class ProducerTest : public ::testing::Test {
- protected:
- ProducerTest() : name_("b001::123|321"), producer_(io_service_) {
- // You can do set-up work for each test here.
- }
-
- virtual ~ProducerTest() {
- // You can do clean-up work that doesn't throw exceptions here.
- }
-
- // If the constructor and destructor are not enough for setting up
- // and cleaning up each test, you can define the following methods:
-
- virtual void SetUp() {
- // Code here will be called immediately after the constructor (right
- // before each test).
- }
-
- virtual void TearDown() {
- // Code here will be called immediately after each test (right
- // before the destructor).
- }
-
- Name name_;
- asio::io_service io_service_;
- ProducerSocket producer_;
-};
-
-} // namespace
-
-// Tests that the Foo::Bar() method does Abc.
-TEST_F(ProducerTest, ProduceContent) {
- std::string content(250000, '?');
-
- producer_.registerPrefix(Prefix("b001::/64"));
- producer_.produce(name_, reinterpret_cast<const uint8_t *>(content.data()),
- content.size(), true);
- producer_.setSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME,
- 500000000_U32);
- producer_.attach();
- producer_.serveForever();
-}
-
-} // namespace protocol
-
-} // namespace transport
-
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-} \ No newline at end of file
diff --git a/libtransport/src/protocols/transport_protocol.cc b/libtransport/src/protocols/transport_protocol.cc
new file mode 100644
index 000000000..29d140454
--- /dev/null
+++ b/libtransport/src/protocols/transport_protocol.cc
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <implementation/socket_consumer.h>
+#include <protocols/transport_protocol.h>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace interface;
+
+TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket,
+ Indexer *indexer, Reassembly *reassembly)
+ : Protocol(),
+ socket_(icn_socket),
+ indexer_verifier_(indexer),
+ reassembly_(reassembly),
+ fec_decoder_(nullptr),
+ is_first_(false),
+ on_interest_retransmission_(VOID_HANDLER),
+ on_interest_output_(VOID_HANDLER),
+ on_interest_timeout_(VOID_HANDLER),
+ on_interest_satisfied_(VOID_HANDLER),
+ on_content_object_input_(VOID_HANDLER),
+ stats_summary_(VOID_HANDLER),
+ on_fwd_strategy_(VOID_HANDLER),
+ on_rec_strategy_(VOID_HANDLER),
+ on_payload_(VOID_HANDLER),
+ fec_type_(fec::FECType::UNKNOWN) {
+ socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_);
+ socket_->getSocketOption(OtherOptions::STATISTICS, &stats_);
+
+ indexer_verifier_->setReassembly(reassembly_.get());
+ reassembly->setIndexer(indexer_verifier_.get());
+}
+
+TransportProtocol::~TransportProtocol() {}
+
+int TransportProtocol::start() {
+ // If the protocol is already running, return otherwise set as running
+ if (isRunning()) {
+ return -1;
+ }
+
+ // Start protocol on its own thread
+ portal_->getThread().add([this]() {
+ // Get all callbacks references
+ socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_RETRANSMISSION,
+ &on_interest_retransmission_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT,
+ &on_interest_output_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_EXPIRED,
+ &on_interest_timeout_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_SATISFIED,
+ &on_interest_satisfied_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT,
+ &on_content_object_input_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::STATS_SUMMARY,
+ &stats_summary_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::FWD_STRATEGY_CHANGE,
+ &on_fwd_strategy_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::REC_STRATEGY_CHANGE,
+ &on_rec_strategy_);
+ socket_->getSocketOption(ConsumerCallbacksOptions::READ_CALLBACK,
+ &on_payload_);
+
+ socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_);
+ socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_);
+
+ // Set it is the first time we schedule an interest
+ is_first_ = true;
+
+ // Reset the protocol state machine
+ reset();
+
+ // Set this transport protocol as portal's consumer callback
+ portal_->registerTransportCallback(this);
+
+ // Schedule next interests
+ scheduleNextInterests();
+
+ is_first_ = false;
+
+ // Set the protocol as running
+ setRunning();
+ });
+
+ return 0;
+}
+
+void TransportProtocol::resume() {
+ if (isRunning()) return;
+
+ setRunning();
+
+ portal_->getThread().tryRunHandlerNow([this]() { scheduleNextInterests(); });
+}
+
+void TransportProtocol::reset() {
+ reassembly_->reInitialize();
+ indexer_verifier_->reset();
+ if (fec_decoder_) {
+ fec_decoder_->reset();
+ }
+}
+
+void TransportProtocol::onContentReassembled(const std::error_code &ec) {
+ stop();
+
+ if (!on_payload_) {
+ throw errors::RuntimeException(
+ "The read callback must be installed in the transport before "
+ "starting "
+ "the content retrieval.");
+ }
+
+ if (!ec) {
+ on_payload_->readSuccess(stats_->getBytesRecv());
+ } else {
+ on_payload_->readError(ec);
+ }
+}
+
+void TransportProtocol::sendInterest(
+ const Name &interest_name,
+ std::array<uint32_t, MAX_AGGREGATED_INTEREST> *additional_suffixes,
+ uint32_t len) {
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Sending interest for name " << interest_name;
+
+ Packet::Format format;
+ socket_->getSocketOption(interface::GeneralTransportOptions::PACKET_FORMAT,
+ format);
+ size_t signature_size = 0;
+
+ // If aggregated interest, add spapce for signature
+ if (len > 0) {
+ format = Packet::toAHFormat(format);
+ signature_size = signer_->getSignatureFieldSize();
+ }
+
+ auto interest = core::PacketManager<>::getInstance().getPacket<Interest>(
+ format, signature_size);
+ interest->setName(interest_name);
+
+ for (uint32_t i = 0; i < len; i++) {
+ interest->appendSuffix(additional_suffixes->at(i));
+ }
+
+ uint32_t lifetime = default_values::interest_lifetime;
+ socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
+ lifetime);
+ interest->setLifetime(uint32_t(lifetime));
+
+ if (*on_interest_output_) {
+ (*on_interest_output_)(*socket_->getInterface(), *interest);
+ }
+
+ if (TRANSPORT_EXPECT_FALSE(!isRunning() && !is_first_)) {
+ return;
+ }
+
+ bool content_sharing_mode;
+ socket_->getSocketOption(RtcTransportOptions::CONTENT_SHARING_MODE,
+ content_sharing_mode);
+ if (content_sharing_mode) lifetime = ceil((double)lifetime * 0.9);
+
+ // Compute signature
+ bool is_ah = HICN_PACKET_FORMAT_IS_AH(interest->getFormat());
+ if (is_ah) signer_->signPacket(interest.get());
+
+ portal_->sendInterest(interest, lifetime);
+}
+
+void TransportProtocol::onError(const std::error_code &ec) {
+ // error from portal: stop socket
+ stop();
+
+ // signal error to application
+ on_payload_->readError(ec);
+}
+
+void TransportProtocol::onTimeout(Interest::Ptr &i, const Name &n) {
+ if (TRANSPORT_EXPECT_FALSE(!isRunning())) {
+ return;
+ }
+
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Timeout on content " << n;
+
+ onInterestTimeout(i, n);
+}
+
+void TransportProtocol::onContentObject(Interest &i, ContentObject &c) {
+ // Check whether it makes sense to continue
+ if (TRANSPORT_EXPECT_FALSE(!isRunning())) {
+ return;
+ }
+
+ // Call transport protocol function
+ std::error_code ec;
+ onContentObjectReceived(i, c, ec);
+
+ // Call reassemble function, if packet is eligible for reassemblying
+ bool reassemble = false;
+ if (!ec) {
+ reassemble = true;
+ }
+
+ // Perform verification and update indexer. This step may be performed offline
+ // - i.e. we may not get a result here (e.g. we use manifest). Verification
+ // failures in that case will be handled in the onPacketDropped function.
+ // XXX This step should be done before calling onContentObjectReceived, but
+ // for now we do it here since currently the indexer does not need manifests
+ // to move forward.
+ indexer_verifier_->onContentObject(i, c, reassemble);
+}
+
+} // end namespace protocol
+
+} // end namespace transport
diff --git a/libtransport/src/protocols/transport_protocol.h b/libtransport/src/protocols/transport_protocol.h
new file mode 100644
index 000000000..e71992561
--- /dev/null
+++ b/libtransport/src/protocols/transport_protocol.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/interfaces/callbacks.h>
+#include <hicn/transport/interfaces/socket_consumer.h>
+#include <hicn/transport/interfaces/statistics.h>
+#include <hicn/transport/utils/object_pool.h>
+#include <protocols/data_processing_events.h>
+#include <protocols/fec_base.h>
+#include <protocols/indexer.h>
+#include <protocols/protocol.h>
+#include <protocols/reassembly.h>
+
+#include <array>
+#include <atomic>
+
+namespace transport {
+
+namespace protocol {
+
+using namespace core;
+
+class IndexVerificationManager;
+
+using ReadCallback = interface::ConsumerSocket::ReadCallback;
+
+class TransportProtocol
+ : public ContentObjectProcessingEventCallback,
+ public Protocol,
+ public std::enable_shared_from_this<TransportProtocol> {
+ static constexpr std::size_t interest_pool_size = 4096;
+
+ friend class ManifestIndexManager;
+
+ public:
+ TransportProtocol(implementation::ConsumerSocket *icn_socket,
+ Indexer *indexer, Reassembly *reassembly);
+
+ virtual ~TransportProtocol();
+
+ virtual int start();
+
+ using Protocol::stop;
+
+ virtual void resume();
+
+ /**
+ * @brief Get the size of any additional header added by the specific
+ * transport implementation.
+ *
+ * @return The header length in bytes.
+ */
+ virtual std::size_t transportHeaderLength(bool isFEC) { return 0; }
+
+ virtual void scheduleNextInterests() = 0;
+
+ // Events generated by the indexing
+ virtual void onContentReassembled(const std::error_code &ec);
+ virtual void onPacketDropped(Interest &interest,
+ ContentObject &content_object,
+ const std::error_code &ec) override = 0;
+ virtual void onReassemblyFailed(std::uint32_t missing_segment) override = 0;
+
+ protected:
+ virtual void onContentObjectReceived(Interest &i, ContentObject &c,
+ std::error_code &ec) = 0;
+ virtual void onInterestTimeout(Interest::Ptr &i, const Name &n) = 0;
+
+ virtual void sendInterest(const Name &interest_name,
+ std::array<uint32_t, MAX_AGGREGATED_INTEREST>
+ *additional_suffixes = nullptr,
+ uint32_t len = 0);
+
+ template <typename FECHandler, typename AllocatorHandler>
+ void enableFEC(FECHandler &&fec_handler,
+ AllocatorHandler &&allocator_handler) {
+ if (!fec_decoder_) {
+ // Try to get FEC from environment
+ const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE");
+ if (fec_str && (fec_type_ == fec::FECType::UNKNOWN)) {
+ LOG(INFO) << "Using FEC " << fec_str;
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str);
+ }
+
+ if (fec_type_ == fec::FECType::UNKNOWN) {
+ return;
+ }
+
+ fec_decoder_ = fec::FECUtils::getDecoder(
+ fec_type_, indexer_verifier_->getFirstSuffix());
+ fec_decoder_->setFECCallback(std::forward<FECHandler>(fec_handler));
+ fec_decoder_->setBufferCallback(
+ std::forward<AllocatorHandler>(allocator_handler));
+ indexer_verifier_->enableFec(fec_type_);
+ }
+ }
+
+ virtual void reset();
+
+ private:
+ // Consumer Callback
+ void onContentObject(Interest &i, ContentObject &c) override;
+ void onTimeout(Interest::Ptr &i, const Name &n) override;
+ void onError(const std::error_code &ec) override;
+
+ protected:
+ implementation::ConsumerSocket *socket_;
+ std::unique_ptr<Indexer> indexer_verifier_;
+ std::unique_ptr<Reassembly> reassembly_;
+ std::unique_ptr<fec::ConsumerFEC> fec_decoder_;
+ // True if it si the first time we schedule an interest
+ std::atomic<bool> is_first_;
+ interface::TransportStatistics *stats_;
+
+ // Callbacks
+ interface::ConsumerInterestCallback *on_interest_retransmission_;
+ interface::ConsumerInterestCallback *on_interest_output_;
+ interface::ConsumerInterestCallback *on_interest_timeout_;
+ interface::ConsumerInterestCallback *on_interest_satisfied_;
+ interface::ConsumerContentObjectCallback *on_content_object_input_;
+ interface::ConsumerContentObjectCallback *on_content_object_;
+ interface::ConsumerTimerCallback *stats_summary_;
+ interface::StrategyCallback *on_fwd_strategy_;
+ interface::StrategyCallback *on_rec_strategy_;
+ ReadCallback *on_payload_;
+
+ bool is_async_;
+
+ fec::FECType fec_type_;
+
+ // Signer for aggregated interests
+ std::shared_ptr<auth::Signer> signer_;
+};
+
+} // end namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/verification_manager.cc b/libtransport/src/protocols/verification_manager.cc
deleted file mode 100644
index 8eedd6106..000000000
--- a/libtransport/src/protocols/verification_manager.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hicn/transport/interfaces/socket_consumer.h>
-#include <hicn/transport/security/verifier.h>
-
-#include <implementation/socket_consumer.h>
-#include <protocols/verification_manager.h>
-
-namespace transport {
-
-namespace protocol {
-
-interface::VerificationPolicy SignatureVerificationManager::onPacketToVerify(
- const Packet& packet) {
- using namespace interface;
-
- bool verify_signature = false, key_content = false;
- VerificationPolicy ret = VerificationPolicy::DROP_PACKET;
-
- icn_socket_->getSocketOption(GeneralTransportOptions::VERIFY_SIGNATURE,
- verify_signature);
- icn_socket_->getSocketOption(GeneralTransportOptions::KEY_CONTENT,
- key_content);
-
- if (!verify_signature) {
- return VerificationPolicy::ACCEPT_PACKET;
- }
-
- if (key_content) {
- key_packets_.push(copyPacket(packet));
- return VerificationPolicy::ACCEPT_PACKET;
- } else if (!key_packets_.empty()) {
- std::queue<ContentObjectPtr>().swap(key_packets_);
- }
-
- ConsumerContentObjectVerificationFailedCallback*
- verification_failed_callback = VOID_HANDLER;
- icn_socket_->getSocketOption(ConsumerCallbacksOptions::VERIFICATION_FAILED,
- &verification_failed_callback);
-
- if (!verification_failed_callback) {
- throw errors::RuntimeException(
- "No verification failed callback provided by application. "
- "Aborting.");
- }
-
- std::shared_ptr<utils::Verifier> verifier;
- icn_socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier);
-
- if (TRANSPORT_EXPECT_FALSE(!verifier)) {
- ret = (*verification_failed_callback)(
- *icn_socket_->getInterface(),
- dynamic_cast<const ContentObject&>(packet),
- make_error_code(protocol_error::no_verifier_provided));
- return ret;
- }
-
- if (!verifier->verify(packet)) {
- ret = (*verification_failed_callback)(
- *icn_socket_->getInterface(),
- dynamic_cast<const ContentObject&>(packet),
- make_error_code(protocol_error::signature_verification_failed));
- } else {
- ret = VerificationPolicy::ACCEPT_PACKET;
- }
-
- return ret;
-}
-
-bool SignatureVerificationManager::onKeyToVerify() {
- if (TRANSPORT_EXPECT_FALSE(key_packets_.empty())) {
- throw errors::RuntimeException("No key to verify.");
- }
-
- while (!key_packets_.empty()) {
- ContentObjectPtr packet_to_verify = key_packets_.front();
- key_packets_.pop();
- if (onPacketToVerify(*packet_to_verify) !=
- VerificationPolicy::ACCEPT_PACKET)
- return false;
- }
-
- return true;
-}
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/verification_manager.h b/libtransport/src/protocols/verification_manager.h
deleted file mode 100644
index 7d8a00a65..000000000
--- a/libtransport/src/protocols/verification_manager.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <hicn/transport/interfaces/callbacks.h>
-#include <hicn/transport/interfaces/verification_policy.h>
-
-#include <hicn/transport/core/content_object.h>
-
-#include <protocols/errors.h>
-
-#include <queue>
-
-namespace transport {
-
-namespace interface {
-class ConsumerSocket;
-}
-
-namespace protocol {
-
-using Packet = core::Packet;
-using interface::VerificationPolicy;
-using ContentObjectPtr = std::shared_ptr<core::ContentObject>;
-
-class VerificationManager {
- public:
- virtual ~VerificationManager() = default;
- virtual VerificationPolicy onPacketToVerify(const Packet& packet) = 0;
- virtual bool onKeyToVerify() { return false; }
-};
-
-class SignatureVerificationManager : public VerificationManager {
- public:
- SignatureVerificationManager(implementation::ConsumerSocket* icn_socket)
- : icn_socket_(icn_socket), key_packets_() {}
-
- interface::VerificationPolicy onPacketToVerify(const Packet& packet) override;
- bool onKeyToVerify() override;
-
- private:
- implementation::ConsumerSocket* icn_socket_;
- std::queue<ContentObjectPtr> key_packets_;
-
- ContentObjectPtr copyPacket(const Packet& packet) {
- std::shared_ptr<utils::MemBuf> packet_copy =
- packet.acquireMemBufReference();
- ContentObjectPtr content_object_copy =
- std::make_shared<core::ContentObject>(std::move(packet_copy));
- std::unique_ptr<utils::MemBuf> payload_copy = packet.getPayload();
- content_object_copy->appendPayload(std::move(payload_copy));
- return content_object_copy;
- }
-};
-
-} // end namespace protocol
-
-} // end namespace transport