aboutsummaryrefslogtreecommitdiffstats
path: root/libtransport/src/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'libtransport/src/protocols')
-rw-r--r--libtransport/src/protocols/CMakeLists.txt16
-rw-r--r--libtransport/src/protocols/byte_stream_reassembly.cc30
-rw-r--r--libtransport/src/protocols/byte_stream_reassembly.h10
-rw-r--r--libtransport/src/protocols/cbr.cc2
-rw-r--r--libtransport/src/protocols/cbr.h3
-rw-r--r--libtransport/src/protocols/data_processing_events.h3
-rw-r--r--libtransport/src/protocols/datagram_reassembly.cc13
-rw-r--r--libtransport/src/protocols/datagram_reassembly.h2
-rw-r--r--libtransport/src/protocols/errors.cc11
-rw-r--r--libtransport/src/protocols/errors.h4
-rw-r--r--libtransport/src/protocols/fec/CMakeLists.txt36
-rw-r--r--libtransport/src/protocols/fec/fec.cc838
-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.cc205
-rw-r--r--libtransport/src/protocols/fec/rely.h191
-rw-r--r--libtransport/src/protocols/fec/rs.cc418
-rw-r--r--libtransport/src/protocols/fec/rs.h409
-rw-r--r--libtransport/src/protocols/fec_base.cc24
-rw-r--r--libtransport/src/protocols/fec_base.h85
-rw-r--r--libtransport/src/protocols/fec_utils.h155
-rw-r--r--libtransport/src/protocols/incremental_indexer.h138
-rw-r--r--libtransport/src/protocols/incremental_indexer_bytestream.cc (renamed from libtransport/src/protocols/incremental_indexer.cc)23
-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.h91
-rw-r--r--libtransport/src/protocols/indexer.cc60
-rw-r--r--libtransport/src/protocols/indexer.h91
-rw-r--r--libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc (renamed from libtransport/src/protocols/manifest_incremental_indexer.cc)95
-rw-r--r--libtransport/src/protocols/manifest_incremental_indexer_bytestream.h (renamed from libtransport/src/protocols/manifest_incremental_indexer.h)27
-rw-r--r--libtransport/src/protocols/packet_manager.h65
-rw-r--r--libtransport/src/protocols/prod_protocol_bytestream.cc74
-rw-r--r--libtransport/src/protocols/prod_protocol_rtc.cc230
-rw-r--r--libtransport/src/protocols/prod_protocol_rtc.h27
-rw-r--r--libtransport/src/protocols/production_protocol.cc7
-rw-r--r--libtransport/src/protocols/production_protocol.h29
-rw-r--r--libtransport/src/protocols/protocol.cc137
-rw-r--r--libtransport/src/protocols/protocol.h114
-rw-r--r--libtransport/src/protocols/raaqm.cc180
-rw-r--r--libtransport/src/protocols/raaqm.h38
-rw-r--r--libtransport/src/protocols/rate_estimation.cc6
-rw-r--r--libtransport/src/protocols/reassembly.cc2
-rw-r--r--libtransport/src/protocols/reassembly.h33
-rw-r--r--libtransport/src/protocols/rtc.cc984
-rw-r--r--libtransport/src/protocols/rtc.h228
-rw-r--r--libtransport/src/protocols/rtc/CMakeLists.txt20
-rw-r--r--libtransport/src/protocols/rtc/congestion_detection.cc101
-rw-r--r--libtransport/src/protocols/rtc/congestion_detection.h138
-rw-r--r--libtransport/src/protocols/rtc/probe_handler.cc2
-rw-r--r--libtransport/src/protocols/rtc/probe_handler.h10
-rw-r--r--libtransport/src/protocols/rtc/rtc.cc466
-rw-r--r--libtransport/src/protocols/rtc/rtc.h46
-rw-r--r--libtransport/src/protocols/rtc/rtc_consts.h35
-rw-r--r--libtransport/src/protocols/rtc/rtc_data_path.cc6
-rw-r--r--libtransport/src/protocols/rtc/rtc_indexer.h195
-rw-r--r--libtransport/src/protocols/rtc/rtc_ldr.cc212
-rw-r--r--libtransport/src/protocols/rtc/rtc_ldr.h26
-rw-r--r--libtransport/src/protocols/rtc/rtc_packet.h2
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_frame.cc79
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_frame.h46
-rw-r--r--libtransport/src/protocols/rtc/rtc_rc_queue.cc6
-rw-r--r--libtransport/src/protocols/rtc/rtc_reassembly.h46
-rw-r--r--libtransport/src/protocols/rtc/rtc_state.cc152
-rw-r--r--libtransport/src/protocols/rtc/rtc_state.h62
-rw-r--r--libtransport/src/protocols/rtc/trendline_estimator.cc334
-rw-r--r--libtransport/src/protocols/rtc/trendline_estimator.h147
-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/transport_protocol.cc102
-rw-r--r--libtransport/src/protocols/transport_protocol.h74
-rw-r--r--libtransport/src/protocols/verification_manager.cc101
-rw-r--r--libtransport/src/protocols/verification_manager.h71
72 files changed, 4460 insertions, 3707 deletions
diff --git a/libtransport/src/protocols/CMakeLists.txt b/libtransport/src/protocols/CMakeLists.txt
index eba8d1aab..b763e95e2 100644
--- a/libtransport/src/protocols/CMakeLists.txt
+++ b/libtransport/src/protocols/CMakeLists.txt
@@ -11,12 +11,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
-
list(APPEND 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
@@ -31,12 +30,14 @@ list(APPEND HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/cbr.h
${CMAKE_CURRENT_SOURCE_DIR}/errors.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
@@ -67,10 +68,11 @@ set(TRANSPORT_CONFIG
install(
FILES ${TRANSPORT_CONFIG}
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/hicn
- COMPONENT lib${LIBTRANSPORT}
+ COMPONENT ${LIBTRANSPORT_COMPONENT}
)
add_subdirectory(rtc)
+add_subdirectory(fec)
set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
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 d2bc961c4..ac36d4e61 100644
--- a/libtransport/src/protocols/byte_stream_reassembly.cc
+++ b/libtransport/src/protocols/byte_stream_reassembly.cc
@@ -33,7 +33,7 @@ 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(
@@ -54,10 +54,14 @@ void ByteStreamReassembly::reassemble(ContentObject &content_object) {
}
}
+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,11 +76,11 @@ 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_);
}
}
@@ -108,8 +112,8 @@ bool ByteStreamReassembly::copyContent(ContentObject &content_object) {
current = current->next();
} while (current != &content_object);
- download_complete_ =
- index_manager_->getFinalSuffix() == content_object.getName().getSuffix();
+ download_complete_ = indexer_verifier_->getFinalSuffix() ==
+ content_object.getName().getSuffix();
if (TRANSPORT_EXPECT_FALSE(download_complete_)) {
ret = download_complete_;
@@ -122,17 +126,19 @@ bool ByteStreamReassembly::copyContent(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 c682d58cb..278740bd3 100644
--- a/libtransport/src/protocols/byte_stream_reassembly.h
+++ b/libtransport/src/protocols/byte_stream_reassembly.h
@@ -27,11 +27,13 @@ class ByteStreamReassembly : public Reassembly {
TransportProtocol *transport_protocol);
protected:
- virtual void reassemble(core::ContentObject &content_object) override;
+ void reassemble(core::ContentObject &content_object) override;
- virtual void reassemble(
+ void reassemble(
std::unique_ptr<core::ContentObjectManifest> &&manifest) override;
+ void reassemble(utils::MemBuf &buffer, uint32_t suffix) override;
+
bool copyContent(core::ContentObject &content_object);
virtual void reInitialize() override;
@@ -40,10 +42,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..1548cc68d 100644
--- a/libtransport/src/protocols/cbr.cc
+++ b/libtransport/src/protocols/cbr.cc
@@ -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,
diff --git a/libtransport/src/protocols/cbr.h b/libtransport/src/protocols/cbr.h
index 20129f6a3..41cdbc98c 100644
--- a/libtransport/src/protocols/cbr.h
+++ b/libtransport/src/protocols/cbr.h
@@ -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/data_processing_events.h b/libtransport/src/protocols/data_processing_events.h
index 5c8c16157..28732502b 100644
--- a/libtransport/src/protocols/data_processing_events.h
+++ b/libtransport/src/protocols/data_processing_events.h
@@ -24,7 +24,8 @@ namespace protocol {
class ContentObjectProcessingEventCallback {
public:
virtual ~ContentObjectProcessingEventCallback() = default;
- virtual void onPacketDropped(core::Interest &i, core::ContentObject &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 962c1e020..069873a52 100644
--- a/libtransport/src/protocols/datagram_reassembly.cc
+++ b/libtransport/src/protocols/datagram_reassembly.cc
@@ -14,6 +14,7 @@
*/
#include <protocols/datagram_reassembly.h>
+#include <protocols/transport_protocol.h>
namespace transport {
@@ -25,7 +26,17 @@ DatagramReassembly::DatagramReassembly(
: Reassembly(icn_socket, transport_protocol) {}
void DatagramReassembly::reassemble(core::ContentObject& content_object) {
- read_buffer_ = content_object.getPayload();
+ auto read_buffer = content_object.getPayload();
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Size of payload: " << read_buffer->length() << ". Trimming "
+ << transport_protocol_->transportHeaderLength();
+ read_buffer->trimStart(transport_protocol_->transportHeaderLength());
+ 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 3462212d3..de294df06 100644
--- a/libtransport/src/protocols/datagram_reassembly.h
+++ b/libtransport/src/protocols/datagram_reassembly.h
@@ -27,11 +27,13 @@ class DatagramReassembly : public Reassembly {
TransportProtocol *transport_protocol);
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 ae7b6e634..183fcc574 100644
--- a/libtransport/src/protocols/errors.cc
+++ b/libtransport/src/protocols/errors.cc
@@ -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,6 +55,12 @@ std::string protocol_category_impl::message(int ev) const {
case protocol_error::session_aborted: {
return "The session has been aborted by the application.";
}
+ 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";
}
@@ -59,4 +68,4 @@ std::string protocol_category_impl::message(int ev) const {
}
} // 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..58dadae5a 100644
--- a/libtransport/src/protocols/errors.h
+++ b/libtransport/src/protocols/errors.h
@@ -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..6d61ae043
--- /dev/null
+++ b/libtransport/src/protocols/fec/CMakeLists.txt
@@ -0,0 +1,36 @@
+# 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.
+
+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..16a04cb98
--- /dev/null
+++ b/libtransport/src/protocols/fec/fec.cc
@@ -0,0 +1,838 @@
+/*
+ * 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 <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
+
+/*
+ * compatibility stuff
+ */
+#ifdef MSDOS /* but also for others, e.g. sun... */
+#define NEED_BCOPY
+#define bcmp(a, b, n) memcmp(a, b, n)
+#endif
+
+#ifdef ANDROID
+#define bcmp(a, b, n) memcmp(a, b, n)
+#endif
+
+#ifdef NEED_BCOPY
+#define bcopy(s, d, siz) memcpy((d), (s), (siz))
+#define bzero(d, siz) memset((d), '\0', (siz))
+#endif
+
+/*
+ * stuff used for testing purposes only
+ */
+
+#ifdef TEST
+#define DEB(x)
+#define DDB(x) x
+#define DEBUG 0 /* minimal debugging */
+#ifdef MSDOS
+#include <time.h>
+struct timeval {
+ unsigned long ticks;
+};
+#define gettimeofday(x, dummy) \
+ { (x)->ticks = clock(); }
+#define DIFF_T(a, b) (1 + 1000000 * (a.ticks - b.ticks) / CLOCKS_PER_SEC)
+typedef unsigned long u_long;
+typedef unsigned short u_short;
+#else /* typically, unix systems */
+#include <sys/time.h>
+#define DIFF_T(a, b) \
+ (1 + 1000000 * (a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec))
+#endif
+
+#define TICK(t) \
+ { \
+ struct timeval x; \
+ gettimeofday(&x, NULL); \
+ t = x.tv_usec + 1000000 * (x.tv_sec & 0xff); \
+ }
+#define TOCK(t) \
+ { \
+ u_long t1; \
+ TICK(t1); \
+ if (t1 < t) \
+ t = 256000000 + t1 - t; \
+ else \
+ t = t1 - t; \
+ if (t == 0) t = 1; \
+ }
+
+u_long ticks[10]; /* vars for timekeeping */
+#else
+#define DEB(x)
+#define DDB(x)
+#define TICK(x)
+#define TOCK(x)
+#endif /* TEST */
+
+/*
+ * You should not need to change anything beyond this point.
+ * The first part of the file implements linear algebra in GF.
+ *
+ * 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;
+ }
+ }
+}
+
+#ifdef DEBUGG
+/*
+ * returns 1 if the square matrix is identiy
+ * (only for test)
+ */
+static int is_identity(gf *m, int k) {
+ int row, col;
+ for (row = 0; row < k; row++)
+ for (col = 0; col < k; col++)
+ if ((row == col && *m != 1) || (row != col && *m != 0))
+ return 0;
+ else
+ m++;
+ return 1;
+}
+#endif /* debug */
+
+/*
+ * invert_mat() takes a matrix and produces its inverse
+ * k is the size of the matrix.
+ * (Gauss-Jordan, adapted from Numerical Recipes in C)
+ * Return non-zero if singular.
+ */
+DEB(int pivloops = 0; int pivswaps = 0; /* diagnostic */)
+static int invert_mat(gf *src, int k) {
+ 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);
+
+ bzero(id_row, k * sizeof(gf));
+ DEB(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++) {
+ DEB(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) { /* otherwhise this is a NOP */
+ /*
+ * this is done often , but optimizing is not so
+ * fruitful, at least in the obvious ways (unrolling)
+ */
+ DEB(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 (bcmp(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) {
+ 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() {
+ TICK(ticks[0]);
+ generate_gf();
+ TOCK(ticks[0]);
+ DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);)
+ TICK(ticks[0]);
+ init_mul_table();
+ TOCK(ticks[0]);
+ DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);)
+ fec_initialized = 1;
+}
+
+/*
+ * 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.
+ */
+ TICK(ticks[3]);
+ invert_vdm(tmp_m, k); /* much faster than invert_mat */
+ matmul(tmp_m + k * k, tmp_m, retval->enc_matrix + k * k, n - k, k, k);
+ /*
+ * the upper matrix is I so do not bother with a slow multiply
+ */
+ bzero(retval->enc_matrix, k * k * sizeof(gf));
+ for (p = retval->enc_matrix, col = 0; col < k; col++, p += k + 1) *p = 1;
+ free(tmp_m);
+ TOCK(ticks[3]);
+
+ DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", ticks[3]);)
+ DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");)
+ return retval;
+}
+
+/*
+ * 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;
+
+ if (index < k)
+ bcopy(src[index], fec, sz * sizeof(gf));
+ else if (index < code->n) {
+ p = &(code->enc_matrix[index * k]);
+ bzero(fec, 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) {
+ DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);)
+ return 1;
+ }
+ SWAP(index[i], index[c], int);
+ SWAP(pkt[i], pkt[c], gf *);
+ }
+ }
+ DEB(/* just test that it works... */
+ for (i = 0; i < k; i++) {
+ if (index[i] < k && index[i] != i) {
+ fprintf(stderr, "shuffle: after\n");
+ for (i = 0; i < k; i++) fprintf(stderr, "%3d ", index[i]);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ })
+ return 0;
+}
+
+/*
+ * 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, gf *pkt[], int index[]) {
+ int i, k = code->k;
+ gf *p, *matrix = NEW_GF_MATRIX(k, k);
+
+ TICK(ticks[9]);
+ for (i = 0, p = matrix; i < k; i++, p += k) {
+#if 1 /* this is simply an optimization, not very useful indeed */
+ if (index[i] < k) {
+ bzero(p, k * sizeof(gf));
+ p[i] = 1;
+ } else
+#endif
+ if (index[i] < code->n)
+ bcopy(&(code->enc_matrix[index[i] * k]), p, k * sizeof(gf));
+ else {
+ fprintf(stderr, "decode: invalid index %d (max %d)\n", index[i],
+ code->n - 1);
+ free(matrix);
+ return NULL;
+ }
+ }
+ TICK(ticks[9]);
+ if (invert_mat(matrix, k)) {
+ free(matrix);
+ matrix = NULL;
+ }
+ TOCK(ticks[9]);
+ 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;
+ int row, col, k = code->k;
+
+ if (GF_BITS > 8) sz /= 2;
+
+ if (shuffle(pkt, index, k)) /* error if true */
+ return 1;
+ m_dec = build_decode_matrix(code, pkt, index);
+
+ if (m_dec == NULL) return 1; /* error */
+ /*
+ * do the actual decoding
+ */
+ new_pkt = (gf **)my_malloc(k * sizeof(gf *), "new pkt pointers");
+ for (row = 0; row < k; row++) {
+ if (index[row] >= k) {
+ new_pkt[row] = (gf *)my_malloc(sz * sizeof(gf), "new pkt buffer");
+ bzero(new_pkt[row], sz * sizeof(gf));
+ for (col = 0; col < k; col++)
+ addmul(new_pkt[row], pkt[col], m_dec[row * k + col], sz);
+ }
+ }
+ /*
+ * move pkts to their final destination
+ */
+ for (row = 0; row < k; row++) {
+ if (index[row] >= k) {
+ bcopy(new_pkt[row], pkt[row], sz * sizeof(gf));
+ free(new_pkt[row]);
+ }
+ }
+ free(new_pkt);
+ free(m_dec);
+
+ return 0;
+}
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..7a30a62e2
--- /dev/null
+++ b/libtransport/src/protocols/fec/rely.cc
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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) {
+ // Get pointer to payload, leaving space to insert FEC header.
+ // TODO Check if this additional header is really needed.
+ auto data = content_object.writableData() + offset - sizeof(fec_header);
+ auto length = content_object.length() - offset + sizeof(fec_header);
+
+ // Check packet length does not exceed maximum length supported by the
+ // encoder (otherwise segmentation would take place).
+ assert(length < max_packet_bytes());
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Encoding packet of length " << length - sizeof(fec_header);
+
+ // Get the suffix. With rely we need to write it in the fec_header in order to
+ // be able to recognize the seq number upon recovery.
+ auto suffix = content_object.getName().getSuffix();
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Producing packet " << suffix
+ << " (index == " << current_index_ << ")";
+
+ // Consume payload. Add fec_header in front before feeding payload to encoder,
+ // and copy original content of packet
+ fec_header *h = reinterpret_cast<fec_header *>(data);
+ fec_header copy = *h;
+ h->setSeqNumberBase(suffix);
+ auto packets = consume(data, length, getCurrentTime());
+ assert(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_header);
+
+ // 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
+ assert(can_produce());
+
+ // Check new payload size and make sure it fits in packet buffer
+ auto new_payload_size = produce_bytes();
+ int difference = new_payload_size - length;
+
+ assert(difference > 0);
+ assert(content_object.ensureCapacity(difference));
+
+ // Update length
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "The packet length will be incremented by "
+ << difference + sizeof(fec_header);
+ content_object.append(difference + sizeof(fec_header));
+ content_object.updateLength();
+
+ // Make sure we got a source packet, otherwise we would put a repair symbol
+ // in a source packet
+ assert(rely::packet_is_systematic(produce_data()));
+
+ // 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
+ assert(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, 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()) {
+ assert(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) {
+ // 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());
+
+ // Drain decoder if possible
+ while (can_produce()) {
+ // Get size of decoded packet
+ auto size = produce_bytes();
+
+ // Get buffer to copy packet in
+ auto packet = core::PacketManager<>::getInstance().getMemBuf();
+
+ // Copy buffer
+ packet->append(size);
+ std::memcpy(packet->writableData(), produce_data(), size);
+
+ // Read seq number
+ fec_header *h = reinterpret_cast<fec_header *>(packet->writableData());
+ uint32_t index = h->getSeqNumberBase();
+
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "The index written in the packet is " << index;
+
+ // Remove FEC header
+ packet->trimStart(sizeof(fec_header));
+
+ // Save packet in buffer
+ packets_.emplace_back(index, 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();
+ }
+}
+
+} // namespace fec
+} // namespace protocol
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/protocols/fec/rely.h b/libtransport/src/protocols/fec/rely.h
new file mode 100644
index 000000000..bfbdb30bc
--- /dev/null
+++ b/libtransport/src/protocols/fec/rely.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <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, 3) \
+ _(Rely, 2, 3) \
+ _(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.
+ */
+ struct fec_header {
+ uint32_t seq_number;
+
+ void setSeqNumberBase(uint32_t suffix) { seq_number = htonl(suffix); }
+ uint32_t getSeqNumberBase() { return ntohl(seq_number); }
+ };
+
+ /**
+ * @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
+ auto _time = utils::SteadyClock::now().time_since_epoch();
+ auto time = std::chrono::duration_cast<utils::Milliseconds>(_time).count();
+ return time;
+#endif
+ }
+
+ protected:
+ uint32_t k_;
+ uint32_t n_;
+ std::uint32_t seq_offset_;
+ /**
+ * @brief Vector of packets to be passed to caller callbacks. For encoder it
+ * will contain the repair packets, for decoder the recovered sources.
+ */
+ std::vector<std::pair<uint32_t, buffer>> packets_;
+
+ /**
+ * @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 : private RelyBase,
+ private rely::encoder,
+ public ProducerFEC {
+ public:
+ RelyEncoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+ /**
+ * Producers will call this function when they produce a data packet.
+ */
+ void onPacketProduced(core::ContentObject &content_object,
+ uint32_t offset) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ */
+ std::size_t getFecHeaderSize() override {
+ return header_bytes() + sizeof(fec_header) + 4;
+ }
+
+ void reset() override {}
+};
+
+class RelyDecoder : private RelyBase,
+ private rely::decoder,
+ public ConsumerFEC {
+ public:
+ RelyDecoder(uint32_t k, uint32_t n, uint32_t seq_offset = 0);
+
+ /**
+ * Consumers will call this function when they receive a data packet
+ */
+ void onDataPacket(core::ContentObject &content_object,
+ uint32_t offset) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ */
+ std::size_t getFecHeaderSize() override {
+ return header_bytes() + sizeof(fec_header);
+ }
+
+ void reset() override {}
+};
+
+} // namespace fec
+
+} // namespace protocol
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/protocols/fec/rs.cc b/libtransport/src/protocols/fec/rs.cc
new file mode 100644
index 000000000..2c23d515d
--- /dev/null
+++ b/libtransport/src/protocols/fec/rs.cc
@@ -0,0 +1,418 @@
+
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <hicn/transport/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);
+}
+
+bool BlockCode::addSourceSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset) {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding source symbol of size "
+ << packet->length() << ", offset " << offset;
+ return addSymbol(packet, i, offset, packet->length() - offset);
+}
+
+bool BlockCode::addSymbol(const fec::buffer &packet, uint32_t i,
+ uint32_t offset, std::size_t size) {
+ if (size > max_buffer_size_) {
+ max_buffer_size_ = size;
+ }
+
+ operator[](current_block_size_++) = std::make_tuple(i, packet, offset);
+
+ 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 = std::get<0>(operator[](0));
+
+ // Set packet length in first 2 bytes
+ for (uint32_t i = 0; i < k_; i++) {
+ auto &packet = std::get<1>(operator[](i));
+ auto offset = std::get<2>(operator[](i));
+
+ auto 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);
+ uint16_t *length = reinterpret_cast<uint16_t *>(packet->writableData() +
+ max_buffer_size_ + offset);
+ auto buffer_length = packet->length() - offset;
+ *length = htons(buffer_length);
+
+ 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) + LEN_SIZE_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) = std::make_tuple(i, std::move(packet), uint32_t(0));
+ }
+
+ // Generate repair symbols and put them in corresponding buffers
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Calling encode with max_buffer_size_ = " << max_buffer_size_;
+ for (uint32_t i = k_; i < n_; i++) {
+ fec_encode(code_, data, data[i], i, max_buffer_size_ + LEN_SIZE_BYTES);
+ }
+
+ // Re-include header in repair packets
+ for (uint32_t i = k_; i < n_; i++) {
+ auto &packet = std::get<1>(operator[](i));
+ packet->prepend(sizeof(fec_header));
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Produced repair symbol of size = " << packet->length();
+ }
+}
+
+void BlockCode::decode() {
+ gf *data[k_];
+ uint32_t index[k_];
+
+ for (uint32_t i = 0; i < k_; i++) {
+ auto &packet = std::get<1>(operator[](i));
+ index[i] = std::get<0>(operator[](i));
+ auto offset = std::get<2>(operator[](i));
+ sorted_index_[i] = index[i];
+
+ if (index[i] < k_) {
+ 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);
+ uint16_t *length = reinterpret_cast<uint16_t *>(
+ packet->writableData() + max_buffer_size_ - LEN_SIZE_BYTES);
+
+ *length = htons(packet->length());
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "DECODE SYMBOL - index " << index[i]
+ << " - Current buffer size: " << packet->length();
+ packet->trimStart(sizeof(fec_header) + offset);
+ }
+
+ data[i] = packet->writableData();
+ }
+
+ // We decode the source block
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Calling decode with max_buffer_size_ = " << max_buffer_size_;
+ fec_decode(code_, data, reinterpret_cast<int *>(index), max_buffer_size_);
+
+ // Find the index in the block for recovered packets
+ for (uint32_t i = 0; i < k_; i++) {
+ if (index[i] != i) {
+ for (uint32_t j = 0; j < k_; j++)
+ if (sorted_index_[j] == uint32_t(index[i])) {
+ sorted_index_[j] = i;
+ }
+ }
+ }
+
+ // Reorder block by index with in-place sorting
+ for (uint32_t i = 0; i < k_; i++) {
+ for (uint32_t j = sorted_index_[i]; j != i; j = sorted_index_[i]) {
+ std::swap(sorted_index_[j], sorted_index_[i]);
+ std::swap(operator[](j), operator[](i));
+ }
+ }
+
+ // Adjust length according to the one written in the source packet
+ for (uint32_t i = 0; i < k_; i++) {
+ auto &packet = std::get<1>(operator[](i));
+ uint16_t *length = reinterpret_cast<uint16_t *>(
+ packet->writableData() + max_buffer_size_ - LEN_SIZE_BYTES);
+ packet->setLength(ntohs(*length));
+ }
+}
+
+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) {
+ if (!source_block_.addSourceSymbol(packet, index, offset)) {
+ std::vector<std::pair<uint32_t, buffer>> repair_packets;
+ for (uint32_t i = k_; i < n_; i++) {
+ repair_packets.emplace_back(std::move(std::get<0>(source_block_[i])),
+ std::move(std::get<1>(source_block_[i])));
+ }
+
+ fec_callback_(repair_packets);
+ }
+}
+
+void RSEncoder::onPacketProduced(core::ContentObject &content_object,
+ uint32_t offset) {
+ consume(content_object.shared_from_this(),
+ content_object.getName().getSuffix(), offset);
+}
+
+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;
+ std::vector<std::pair<uint32_t, buffer>> source_packets(k_);
+ for (uint32_t i = 0; i < src_block.getK(); i++) {
+ source_packets[i] = std::make_pair(src_block_it->first + i,
+ std::move(std::get<1>(src_block[i])));
+ }
+
+ 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) {
+ // Normalize index
+ assert(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);
+ if (!ret) {
+ recoverPackets(it);
+ }
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Adding to parked source packets";
+ auto ret = parked_packets_.emplace(
+ base, std::vector<std::pair<buffer, uint32_t>>());
+ ret.first->second.emplace_back(packet, i);
+
+ 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 = " << i << ". K=" << k
+ << ", N=" << 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.first,
+ packet_index.second, offset);
+ 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) {
+ 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);
+ }
+}
+
+} // 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..e159ad9f7
--- /dev/null
+++ b/libtransport/src/protocols/fec/rs.h
@@ -0,0 +1,409 @@
+
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/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, 3) \
+ _(RS, 4, 5) \
+ _(RS, 4, 6) \
+ _(RS, 4, 7) \
+ _(RS, 6, 10) \
+ _(RS, 8, 10) \
+ _(RS, 8, 11) \
+ _(RS, 8, 12) \
+ _(RS, 8, 14) \
+ _(RS, 8, 16) \
+ _(RS, 8, 32) \
+ _(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, 32, 36) \
+ _(RS, 32, 41) \
+ _(RS, 32, 46) \
+ _(RS, 32, 54) \
+ _(RS, 34, 42) \
+ _(RS, 35, 70) \
+ _(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.
+ */
+using Packets = std::array<std::tuple</* index */ uint32_t, /* buffer */ buffer,
+ uint32_t /* offset */>,
+ 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 = htonl(suffix); }
+ uint32_t getSeqNumberBase() { return ntohl(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; }
+};
+
+class rs;
+
+/**
+ * This class models the source block itself.
+ */
+class BlockCode : public Packets {
+ /**
+ * For variable length packet we need to prepend to the padded payload the
+ * real length of the packet. This is *not* sent over the network.
+ */
+ static constexpr std::size_t LEN_SIZE_BYTES = 2;
+
+ 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ void onPacketProduced(core::ContentObject &content_object,
+ uint32_t offset) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ */
+ std::size_t getFecHeaderSize() override { return 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);
+
+ /**
+ * 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) override;
+
+ /**
+ * @brief Get the fec header size, if added to source packets
+ */
+ std::size_t getFecHeaderSize() override { return 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.
+ */
+ std::unordered_map<uint32_t, std::vector<std::pair<buffer, uint32_t>>>
+ parked_packets_;
+};
+
+} // namespace fec
+
+} // namespace protocol
+
+} // namespace transport
diff --git a/libtransport/src/protocols/fec_base.cc b/libtransport/src/protocols/fec_base.cc
new file mode 100644
index 000000000..9252bc473
--- /dev/null
+++ b/libtransport/src/protocols/fec_base.cc
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/fec/rs.h>
+#include <protocols/fec_base.h>
+
+namespace transport {
+namespace protocol {
+
+namespace fec {} // namespace fec
+} // namespace protocol
+} // namespace transport \ No newline at end of file
diff --git a/libtransport/src/protocols/fec_base.h b/libtransport/src/protocols/fec_base.h
index a135c474f..a1929d85e 100644
--- a/libtransport/src/protocols/fec_base.h
+++ b/libtransport/src/protocols/fec_base.h
@@ -16,71 +16,88 @@
#pragma once
#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/errors/not_implemented_exception.h>
#include <functional>
namespace transport {
namespace protocol {
-/**
- * Interface classes to integrate FEC inside any producer transport protocol
- */
-class ProducerFECBase {
+namespace fec {
+
+using buffer = typename utils::MemBuf::Ptr;
+using BufferArray = std::vector<std::pair<uint32_t, buffer>>;
+
+class FECBase {
public:
+ virtual ~FECBase() = default;
/**
- * Callback, to be called by implementations as soon as a repair packet is
- * ready.
+ * 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 RepairPacketsReady =
- std::function<void(std::vector<core::ContentObject::Ptr> &)>;
+ using PacketsReady = std::function<void(BufferArray &)>;
/**
- * Producers will call this function upon production of a new packet.
+ * Callback to be called when a new buffer (for encoding / decoding) needs to
+ * be allocated.
*/
- virtual void onPacketProduced(const core::ContentObject &content_object) = 0;
+ using BufferRequested = std::function<buffer(std::size_t size)>;
/**
- * Set callback to signal production protocol the repair packet is ready.
+ * @brief Get size of FEC header.
*/
- void setFECCallback(const RepairPacketsReady &on_repair_packet) {
- rep_packet_ready_callback_ = on_repair_packet;
+ virtual std::size_t getFecHeaderSize() = 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;
+ }
+
+ virtual void reset() = 0;
+
protected:
- RepairPacketsReady rep_packet_ready_callback_;
+ PacketsReady fec_callback_{0};
+ BufferRequested buffer_callback_{0};
};
/**
- * Interface classes to integrate FEC inside any consumer transport protocol
+ * Interface classes to integrate FEC inside any producer transport protocol
*/
-class ConsumerFECBase {
+class ProducerFEC : public virtual FECBase {
public:
+ virtual ~ProducerFEC() = default;
/**
- * Callback, to be called by implemrntations as soon as a packet is recovered.
+ * Producers will call this function upon production of a new packet.
*/
- using OnPacketsRecovered =
- std::function<void(std::vector<core::ContentObject::Ptr> &)>;
+ virtual void onPacketProduced(core::ContentObject &content_object,
+ uint32_t offset) = 0;
+};
- /**
- * Consumers will call this function when they receive a FEC packet.
- */
- virtual void onFECPacket(const core::ContentObject &content_object) = 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(const core::ContentObject &content_object) = 0;
-
- /**
- * Set callback to signal consumer protocol the repair packet is ready.
- */
- void setFECCallback(const OnPacketsRecovered &on_repair_packet) {
- packet_recovered_callback_ = on_repair_packet;
- }
-
- protected:
- OnPacketsRecovered packet_recovered_callback_;
+ virtual void onDataPacket(core::ContentObject &content_object,
+ uint32_t offset) = 0;
};
+} // namespace fec
} // namespace protocol
} // namespace transport \ No newline at end of file
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.h b/libtransport/src/protocols/incremental_indexer.h
deleted file mode 100644
index d7760f8e6..000000000
--- a/libtransport/src/protocols/incremental_indexer.h
+++ /dev/null
@@ -1,138 +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/errors.h>
-#include <hicn/transport/interfaces/callbacks.h>
-#include <hicn/transport/auth/verifier.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, 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),
- verifier_(nullptr) {
- if (reassembly_) {
- reassembly_->setIndexer(this);
- }
- socket_->getSocketOption(implementation::GeneralTransportOptions::VERIFIER,
- verifier_);
- }
-
- 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_),
- verifier_(nullptr) {
- if (reassembly_) {
- reassembly_->setIndexer(this);
- }
- socket_->getSocketOption(implementation::GeneralTransportOptions::VERIFIER,
- verifier_);
- }
-
- 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 &interest,
- core::ContentObject &content_object) override;
-
- TRANSPORT_ALWAYS_INLINE void setReassembly(Reassembly *reassembly) {
- reassembly_ = reassembly;
-
- if (reassembly_) {
- reassembly_->setIndexer(this);
- }
- }
-
- 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::shared_ptr<auth::Verifier> verifier_;
-};
-
-} // namespace protocol
-} // namespace transport
diff --git a/libtransport/src/protocols/incremental_indexer.cc b/libtransport/src/protocols/incremental_indexer_bytestream.cc
index 95daa0a3e..cc302a98a 100644
--- a/libtransport/src/protocols/incremental_indexer.cc
+++ b/libtransport/src/protocols/incremental_indexer_bytestream.cc
@@ -15,18 +15,21 @@
#include <hicn/transport/interfaces/socket_consumer.h>
#include <protocols/errors.h>
-#include <protocols/incremental_indexer.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) {
+ core::ContentObject &content_object,
+ bool reassembly) {
using namespace interface;
- TRANSPORT_LOGD("Received content %s",
- content_object.getName().toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received content " << content_object.getName();
+
+ assert(reassembly_);
if (TRANSPORT_EXPECT_FALSE(content_object.testRst())) {
final_suffix_ = content_object.getName().getSuffix();
@@ -36,16 +39,22 @@ void IncrementalIndexer::onContentObject(core::Interest &interest,
switch (ret) {
case auth::VerificationPolicy::ACCEPT: {
- reassembly_->reassemble(content_object);
+ if (reassembly) {
+ reassembly_->reassemble(content_object);
+ }
break;
}
+
case auth::VerificationPolicy::UNKNOWN:
case auth::VerificationPolicy::DROP: {
- transport_protocol_->onPacketDropped(interest, content_object);
+ transport_->onPacketDropped(
+ interest, content_object,
+ make_error_code(protocol_error::verification_failed));
break;
}
+
case auth::VerificationPolicy::ABORT: {
- transport_protocol_->onContentReassembled(
+ transport_->onContentReassembled(
make_error_code(protocol_error::session_aborted));
break;
}
diff --git a/libtransport/src/protocols/incremental_indexer_bytestream.h b/libtransport/src/protocols/incremental_indexer_bytestream.h
new file mode 100644
index 000000000..c6a669629
--- /dev/null
+++ b/libtransport/src/protocols/incremental_indexer_bytestream.h
@@ -0,0 +1,119 @@
+/*
+ * 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/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(std::forward<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() 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() 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() override { return final_suffix_; }
+
+ void enableFec(fec::FECType fec_type) override {}
+
+ void disableFec() override {}
+
+ void setNFec(uint32_t n_fec) override {}
+ virtual uint32_t getNFec() override { return 0; }
+
+ virtual 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..c78dc634d
--- /dev/null
+++ b/libtransport/src/protocols/index_manager_bytestream.cc
@@ -0,0 +1,72 @@
+/*
+ * 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 <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..e14c8845b
--- /dev/null
+++ b/libtransport/src/protocols/index_manager_bytestream.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <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() override { return indexer_->getFirstSuffix(); }
+
+ uint32_t getNextReassemblySegment() override {
+ return indexer_->getNextReassemblySegment();
+ }
+
+ bool isFinalSuffixDiscovered() override {
+ return indexer_->isFinalSuffixDiscovered();
+ }
+
+ uint32_t getFinalSuffix() override { return indexer_->getFinalSuffix(); }
+
+ uint32_t jumpToIndex(uint32_t index) override {
+ return indexer_->jumpToIndex(index);
+ }
+
+ void setNFec(uint32_t n_fec) override { return indexer_->setNFec(n_fec); }
+ uint32_t getNFec() override { return indexer_->getNFec(); }
+
+ void enableFec(fec::FECType fec_type) override {
+ return indexer_->enableFec(fec_type);
+ }
+
+ double getFecOverhead() override { return indexer_->getFecOverhead(); }
+
+ double getMaxFecOverhead() 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 1379a609c..8d4cf04d7 100644
--- a/libtransport/src/protocols/indexer.cc
+++ b/libtransport/src/protocols/indexer.cc
@@ -13,61 +13,29 @@
* limitations under the License.
*/
-#include <hicn/transport/utils/branch_prediction.h>
-#include <protocols/incremental_indexer.h>
+#include <implementation/socket_consumer.h>
#include <protocols/indexer.h>
-#include <protocols/manifest_incremental_indexer.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) {}
+namespace protocol {
-void IndexManager::onContentObject(core::Interest &interest,
- core::ContentObject &content_object) {
- if (first_segment_received_) {
- indexer_->onContentObject(interest, 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() == core::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(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);
- }
+Indexer::Indexer(implementation::ConsumerSocket *socket,
+ TransportProtocol *transport)
+ : socket_(socket), transport_(transport) {
+ setVerifier();
+}
- first_segment_received_ = true;
- } else {
- interest_data_set_.emplace(interest.shared_from_this(),
- content_object.shared_from_this());
- }
+void Indexer::setVerifier() {
+ if (socket_) {
+ socket_->getSocketOption(GeneralTransportOptions::VERIFIER, verifier_);
}
}
-void IndexManager::reset(std::uint32_t offset) {
- indexer_ = std::make_unique<IncrementalIndexer>(icn_socket_, transport_,
- reassembly_);
- first_segment_received_ = false;
- interest_data_set_.clear();
-}
+} // end namespace protocol
-} // namespace protocol
-} // namespace transport
+} // end namespace transport
diff --git a/libtransport/src/protocols/indexer.h b/libtransport/src/protocols/indexer.h
index 49e22a4cf..7e3a52fb0 100644
--- a/libtransport/src/protocols/indexer.h
+++ b/libtransport/src/protocols/indexer.h
@@ -17,6 +17,7 @@
#include <hicn/transport/core/content_object.h>
#include <hicn/transport/core/interest.h>
+#include <protocols/fec_utils.h>
#include <set>
@@ -33,66 +34,80 @@ 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() = 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() = 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() = 0;
+ virtual void enableFec(fec::FECType fec_type) = 0;
+ virtual void disableFec() = 0;
+ virtual bool isFec(uint32_t index) { return false; }
+ virtual double getFecOverhead() { return 0.0; }
+ virtual double getMaxFecOverhead() { return 0.0; }
+ /**
+ * Final suffix helpers.
+ */
virtual bool isFinalSuffixDiscovered() = 0;
-
virtual uint32_t getFinalSuffix() = 0;
- virtual void reset(std::uint32_t offset = 0) = 0;
-
- virtual void onContentObject(core::Interest &interest,
- core::ContentObject &content_object) = 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();
+ /**
+ * Set reassembly protocol
+ */
+ virtual void setReassembly(Reassembly *reassembly) {
+ reassembly_ = reassembly;
}
- bool isFinalSuffixDiscovered() override {
- return indexer_->isFinalSuffixDiscovered();
- }
+ /**
+ * Set verifier using socket
+ */
+ virtual void setVerifier();
- uint32_t getFinalSuffix() override { return indexer_->getFinalSuffix(); }
+ /**
+ * 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 reset(std::uint32_t offset = 0) override;
+ /**
+ * Reset the indexer.
+ */
+ virtual void reset() = 0;
- void onContentObject(core::Interest &interest,
- core::ContentObject &content_object) 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_;
};
} // end namespace protocol
diff --git a/libtransport/src/protocols/manifest_incremental_indexer.cc b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc
index a6312ca90..168aa57af 100644
--- a/libtransport/src/protocols/manifest_incremental_indexer.cc
+++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.cc
@@ -15,7 +15,7 @@
#include <implementation/socket_consumer.h>
#include <protocols/errors.h>
-#include <protocols/manifest_incremental_indexer.h>
+#include <protocols/manifest_incremental_indexer_bytestream.h>
#include <protocols/transport_protocol.h>
#include <cmath>
@@ -28,26 +28,26 @@ namespace protocol {
using namespace interface;
ManifestIncrementalIndexer::ManifestIncrementalIndexer(
- implementation::ConsumerSocket *icn_socket, TransportProtocol *transport,
- Reassembly *reassembly)
- : IncrementalIndexer(icn_socket, transport, reassembly),
+ implementation::ConsumerSocket *icn_socket, TransportProtocol *transport)
+ : IncrementalIndexer(icn_socket, transport),
suffix_strategy_(utils::SuffixStrategyFactory::getSuffixStrategy(
NextSegmentCalculationStrategy::INCREMENTAL, next_download_suffix_,
0)) {}
void ManifestIncrementalIndexer::onContentObject(
- core::Interest &interest, core::ContentObject &content_object) {
+ core::Interest &interest, core::ContentObject &content_object,
+ bool reassembly) {
switch (content_object.getPayloadType()) {
case PayloadType::DATA: {
- TRANSPORT_LOGD("Received content %s",
- content_object.getName().toString().c_str());
- onUntrustedContentObject(interest, content_object);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received content " << content_object.getName();
+ onUntrustedContentObject(interest, content_object, reassembly);
break;
}
case PayloadType::MANIFEST: {
- TRANSPORT_LOGD("Received manifest %s",
- content_object.getName().toString().c_str());
- onUntrustedManifest(interest, content_object);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received manifest " << content_object.getName();
+ onUntrustedManifest(interest, content_object, reassembly);
break;
}
default: {
@@ -57,7 +57,8 @@ void ManifestIncrementalIndexer::onContentObject(
}
void ManifestIncrementalIndexer::onUntrustedManifest(
- core::Interest &interest, core::ContentObject &content_object) {
+ core::Interest &interest, core::ContentObject &content_object,
+ bool reassembly) {
auto manifest =
std::make_unique<ContentObjectManifest>(std::move(content_object));
@@ -66,16 +67,17 @@ void ManifestIncrementalIndexer::onUntrustedManifest(
manifest->decode();
if (policy != auth::VerificationPolicy::ACCEPT) {
- transport_protocol_->onContentReassembled(
+ transport_->onContentReassembled(
make_error_code(protocol_error::session_aborted));
return;
}
- processTrustedManifest(interest, std::move(manifest));
+ processTrustedManifest(interest, std::move(manifest), reassembly);
}
void ManifestIncrementalIndexer::processTrustedManifest(
- core::Interest &interest, std::unique_ptr<ContentObjectManifest> manifest) {
+ core::Interest &interest, std::unique_ptr<ContentObjectManifest> manifest,
+ bool reassembly) {
if (TRANSPORT_EXPECT_FALSE(manifest->getVersion() !=
core::ManifestVersion::VERSION_1)) {
throw errors::RuntimeException("Received manifest with unknown version.");
@@ -89,7 +91,7 @@ void ManifestIncrementalIndexer::processTrustedManifest(
std::vector<auth::PacketPtr> packets;
// Convert the received manifest to a map of packet suffixes to hashes
- std::unordered_map<auth::Suffix, auth::HashEntry> current_manifest =
+ auth::Verifier::SuffixMap current_manifest =
core::ContentObjectManifest::getSuffixMap(manifest.get());
// Update 'suffix_map_' with new hashes from the received manifest and
@@ -102,26 +104,31 @@ void ManifestIncrementalIndexer::processTrustedManifest(
continue;
}
- packets.push_back(unverified_segments_[it->first].second.get());
+ packets.push_back(std::get<1>(unverified_segments_[it->first]).get());
it++;
}
// Verify unverified segments using the received manifest
- std::vector<auth::VerificationPolicy> policies =
+ auth::Verifier::PolicyMap policies =
verifier_->verifyPackets(packets, current_manifest);
for (unsigned int i = 0; i < packets.size(); ++i) {
auth::Suffix suffix = packets[i]->getName().getSuffix();
- if (policies[i] != auth::VerificationPolicy::UNKNOWN) {
- unverified_segments_.erase(suffix);
+ auto it = unverified_segments_.find(suffix);
+
+ if (policies[suffix] != auth::VerificationPolicy::UNKNOWN) {
+ unverified_segments_.erase(it);
+ continue;
}
- applyPolicy(*unverified_segments_[suffix].first,
- *unverified_segments_[suffix].second, policies[i]);
+ applyPolicy(*std::get<0>(it->second), *std::get<1>(it->second),
+ std::get<2>(it->second), policies[suffix]);
}
- reassembly_->reassemble(std::move(manifest));
+ if (reassembly) {
+ reassembly_->reassemble(std::move(manifest));
+ }
break;
}
case core::ManifestType::FLIC_MANIFEST: {
@@ -134,15 +141,16 @@ void ManifestIncrementalIndexer::processTrustedManifest(
}
void ManifestIncrementalIndexer::onUntrustedContentObject(
- Interest &interest, ContentObject &content_object) {
+ 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_pair(
- interest.shared_from_this(), content_object.shared_from_this());
+ unverified_segments_[suffix] =
+ std::make_tuple(interest.shared_from_this(),
+ content_object.shared_from_this(), reassembly);
break;
}
default: {
@@ -151,32 +159,43 @@ void ManifestIncrementalIndexer::onUntrustedContentObject(
}
}
- applyPolicy(interest, content_object, policy);
+ applyPolicy(interest, content_object, reassembly, policy);
}
void ManifestIncrementalIndexer::applyPolicy(
core::Interest &interest, core::ContentObject &content_object,
- auth::VerificationPolicy policy) {
+ bool reassembly, auth::VerificationPolicy policy) {
+ assert(reassembly_);
switch (policy) {
case auth::VerificationPolicy::ACCEPT: {
- reassembly_->reassemble(content_object);
+ if (reassembly && !reassembly_->reassembleUnverified()) {
+ reassembly_->reassemble(content_object);
+ }
break;
}
case auth::VerificationPolicy::DROP: {
- transport_protocol_->onPacketDropped(interest, content_object);
+ transport_->onPacketDropped(
+ interest, content_object,
+ make_error_code(protocol_error::verification_failed));
break;
}
case auth::VerificationPolicy::ABORT: {
- transport_protocol_->onContentReassembled(
+ transport_->onContentReassembled(
make_error_code(protocol_error::session_aborted));
break;
}
- default: {
- break;
+ case auth::VerificationPolicy::UNKNOWN: {
+ if (reassembly && reassembly_->reassembleUnverified()) {
+ reassembly_->reassemble(content_object);
+ }
}
}
}
+uint32_t ManifestIncrementalIndexer::checkNextSuffix() {
+ return suffix_strategy_->getNextSuffix();
+}
+
uint32_t ManifestIncrementalIndexer::getNextSuffix() {
auto ret = suffix_strategy_->getNextSuffix();
@@ -186,7 +205,7 @@ uint32_t ManifestIncrementalIndexer::getNextSuffix() {
return ret;
}
- return IndexManager::invalid_index;
+ return Indexer::invalid_index;
}
uint32_t ManifestIncrementalIndexer::getFinalSuffix() {
@@ -199,7 +218,7 @@ bool ManifestIncrementalIndexer::isFinalSuffixDiscovered() {
uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() {
if (suffix_queue_.empty()) {
- return IndexManager::invalid_index;
+ return Indexer::invalid_index;
}
auto ret = suffix_queue_.front();
@@ -207,13 +226,13 @@ uint32_t ManifestIncrementalIndexer::getNextReassemblySegment() {
return ret;
}
-void ManifestIncrementalIndexer::reset(std::uint32_t offset) {
- IncrementalIndexer::reset(offset);
+void ManifestIncrementalIndexer::reset() {
+ IncrementalIndexer::reset();
suffix_map_.clear();
unverified_segments_.clear();
SuffixQueue empty;
std::swap(suffix_queue_, empty);
- suffix_strategy_->reset(offset);
+ suffix_strategy_->reset(first_suffix_);
}
} // namespace protocol
diff --git a/libtransport/src/protocols/manifest_incremental_indexer.h b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h
index 1bb76eb87..d8cf5892f 100644
--- a/libtransport/src/protocols/manifest_incremental_indexer.h
+++ b/libtransport/src/protocols/manifest_incremental_indexer_bytestream.h
@@ -17,7 +17,7 @@
#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>
@@ -31,11 +31,10 @@ class ManifestIncrementalIndexer : public IncrementalIndexer {
public:
using SuffixQueue = std::queue<uint32_t>;
using InterestContentPair =
- std::pair<core::Interest::Ptr, core::ContentObject::Ptr>;
+ 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)),
@@ -49,10 +48,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) override;
+ core::ContentObject &content_object,
+ bool reassembly) override;
+
+ uint32_t checkNextSuffix() override;
uint32_t getNextSuffix() override;
@@ -67,18 +69,21 @@ class ManifestIncrementalIndexer : public IncrementalIndexer {
SuffixQueue suffix_queue_;
// Hash verification
- std::unordered_map<auth::Suffix, auth::HashEntry> suffix_map_;
+ auth::Verifier::SuffixMap suffix_map_;
std::unordered_map<auth::Suffix, InterestContentPair> unverified_segments_;
private:
void onUntrustedManifest(core::Interest &interest,
- core::ContentObject &content_object);
+ core::ContentObject &content_object,
+ bool reassembly);
void processTrustedManifest(core::Interest &interest,
- std::unique_ptr<ContentObjectManifest> manifest);
+ std::unique_ptr<ContentObjectManifest> manifest,
+ bool reassembly);
void onUntrustedContentObject(core::Interest &interest,
- core::ContentObject &content_object);
+ core::ContentObject &content_object,
+ bool reassembly);
void applyPolicy(core::Interest &interest,
- core::ContentObject &content_object,
+ core::ContentObject &content_object, bool reassembly,
auth::VerificationPolicy policy);
};
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
index 6bd989fe4..f659cb37c 100644
--- a/libtransport/src/protocols/prod_protocol_bytestream.cc
+++ b/libtransport/src/protocols/prod_protocol_bytestream.cc
@@ -87,11 +87,6 @@ uint32_t ByteStreamProductionProtocol::produceStream(
auth::CryptoHashType hash_algo;
socket_->getSocketOption(GeneralTransportOptions::HASH_ALGORITHM, hash_algo);
- // Use manifest
- bool making_manifest;
- socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST,
- making_manifest);
-
// Suffix calculation strategy
core::NextSegmentCalculationStrategy _suffix_strategy;
socket_->getSocketOption(GeneralTransportOptions::SUFFIX_STRATEGY,
@@ -99,9 +94,6 @@ uint32_t ByteStreamProductionProtocol::produceStream(
auto suffix_strategy = utils::SuffixStrategyFactory::getSuffixStrategy(
_suffix_strategy, start_offset);
- std::shared_ptr<auth::Signer> signer;
- socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer);
-
auto buffer_size = buffer->length();
int bytes_segmented = 0;
std::size_t header_size;
@@ -115,8 +107,8 @@ uint32_t ByteStreamProductionProtocol::produceStream(
bool is_last_manifest = false;
// TODO Manifest may still be used for indexing
- if (making_manifest && !signer) {
- TRANSPORT_LOGE("Making manifests without setting producer identity.");
+ if (making_manifest_ && !signer_) {
+ LOG(FATAL) << "Making manifests without setting producer identity.";
}
core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC;
@@ -134,13 +126,13 @@ uint32_t ByteStreamProductionProtocol::produceStream(
}
format = hf_format;
- if (making_manifest) {
+ if (making_manifest_) {
manifest_header_size = core::Packet::getHeaderSizeFromFormat(
- signer ? hf_format_ah : hf_format,
- signer ? signer->getSignatureSize() : 0);
- } else if (signer) {
+ signer_ ? hf_format_ah : hf_format,
+ signer_ ? signer_->getSignatureFieldSize() : 0);
+ } else if (signer_) {
format = hf_format_ah;
- signature_length = signer->getSignatureSize();
+ signature_length = signer_->getSignatureFieldSize();
}
header_size = core::Packet::getHeaderSizeFromFormat(format, signature_length);
@@ -152,7 +144,7 @@ uint32_t ByteStreamProductionProtocol::produceStream(
}
// TODO allocate space for all the headers
- if (making_manifest) {
+ if (making_manifest_) {
uint32_t segment_in_manifest = static_cast<uint32_t>(
std::floor(double(data_packet_size - manifest_header_size -
ContentObjectManifest::getManifestHeaderSize()) /
@@ -166,7 +158,7 @@ uint32_t ByteStreamProductionProtocol::produceStream(
name.setSuffix(suffix_strategy->getNextManifestSuffix()),
core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST,
hash_algo, is_last_manifest, name, _suffix_strategy,
- signer ? signer->getSignatureSize() : 0));
+ signer_ ? signer_->getSignatureFieldSize() : 0));
manifest->setLifetime(content_object_expiry_time);
if (is_last) {
@@ -178,27 +170,26 @@ uint32_t ByteStreamProductionProtocol::produceStream(
for (unsigned int packaged_segments = 0;
packaged_segments < number_of_segments; packaged_segments++) {
- if (making_manifest) {
+ if (making_manifest_) {
if (manifest->estimateManifestSize(2) >
data_packet_size - manifest_header_size) {
manifest->encode();
// If identity set, sign manifest
- if (signer) {
- signer->signPacket(manifest.get());
+ if (signer_) {
+ signer_->signPacket(manifest.get());
}
// Send the current manifest
passContentObjectToCallbacks(manifest);
- TRANSPORT_LOGD("Send manifest %s",
- manifest->getName().toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest->getName();
// Send content objects stored in the queue
while (!content_queue_.empty()) {
passContentObjectToCallbacks(content_queue_.front());
- TRANSPORT_LOGD("Send content %s",
- content_queue_.front()->getName().toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content " << content_queue_.front()->getName();
content_queue_.pop();
}
@@ -209,7 +200,8 @@ uint32_t ByteStreamProductionProtocol::produceStream(
name.setSuffix(suffix_strategy->getNextManifestSuffix()),
core::ManifestVersion::VERSION_1,
core::ManifestType::INLINE_MANIFEST, hash_algo, is_last_manifest,
- name, _suffix_strategy, signer ? signer->getSignatureSize() : 0));
+ name, _suffix_strategy,
+ signer_ ? signer_->getSignatureFieldSize() : 0));
manifest->setLifetime(content_object_expiry_time);
manifest->setFinalBlockNumber(
@@ -221,7 +213,7 @@ uint32_t ByteStreamProductionProtocol::produceStream(
auto content_suffix = suffix_strategy->getNextContentSuffix();
auto content_object = std::make_shared<ContentObject>(
name.setSuffix(content_suffix), format,
- signer && !making_manifest ? signer->getSignatureSize() : 0);
+ signer_ && !making_manifest_ ? signer_->getSignatureFieldSize() : 0);
content_object->setLifetime(content_object_expiry_time);
auto b = buffer->cloneOne();
@@ -232,7 +224,7 @@ uint32_t ByteStreamProductionProtocol::produceStream(
b->append(buffer_size - bytes_segmented);
bytes_segmented += (int)(buffer_size - bytes_segmented);
- if (is_last && making_manifest) {
+ if (is_last && making_manifest_) {
is_last_manifest = true;
} else if (is_last) {
content_object->setRst();
@@ -245,39 +237,39 @@ uint32_t ByteStreamProductionProtocol::produceStream(
content_object->appendPayload(std::move(b));
- if (making_manifest) {
+ if (making_manifest_) {
using namespace std::chrono_literals;
auth::CryptoHash hash = content_object->computeDigest(hash_algo);
manifest->addSuffixHash(content_suffix, hash);
content_queue_.push(content_object);
} else {
- if (signer) {
- signer->signPacket(content_object.get());
+ if (signer_) {
+ signer_->signPacket(content_object.get());
}
passContentObjectToCallbacks(content_object);
- TRANSPORT_LOGD("Send content %s",
- content_object->getName().toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content " << content_object->getName();
}
}
- if (making_manifest) {
+ if (making_manifest_) {
if (is_last_manifest) {
manifest->setFinalManifest(is_last_manifest);
}
manifest->encode();
- if (signer) {
- signer->signPacket(manifest.get());
+ if (signer_) {
+ signer_->signPacket(manifest.get());
}
passContentObjectToCallbacks(manifest);
- TRANSPORT_LOGD("Send manifest %s", manifest->getName().toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send manifest " << manifest->getName();
while (!content_queue_.empty()) {
passContentObjectToCallbacks(content_queue_.front());
- TRANSPORT_LOGD("Send content %s",
- content_queue_.front()->getName().toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Send content " << content_queue_.front()->getName();
content_queue_.pop();
}
}
@@ -356,14 +348,14 @@ void ByteStreamProductionProtocol::passContentObjectToCallbacks(
}
void ByteStreamProductionProtocol::onInterest(Interest &interest) {
- TRANSPORT_LOGD("Received interest for %s",
- interest.getName().toString().c_str());
+ 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);
+ output_buffer_.find(interest.getName());
if (content_object) {
if (*on_interest_satisfied_output_buffer_) {
diff --git a/libtransport/src/protocols/prod_protocol_rtc.cc b/libtransport/src/protocols/prod_protocol_rtc.cc
index 049752876..cdc882d81 100644
--- a/libtransport/src/protocols/prod_protocol_rtc.cc
+++ b/libtransport/src/protocols/prod_protocol_rtc.cc
@@ -25,15 +25,19 @@
namespace transport {
namespace protocol {
+using Format = core::Packet::Format;
+
RTCProductionProtocol::RTCProductionProtocol(
implementation::ProducerSocket *icn_socket)
: ProductionProtocol(icn_socket),
current_seg_(1),
produced_bytes_(0),
produced_packets_(0),
+ produced_fec_packets_(0),
max_packet_production_(1),
bytes_production_rate_(0),
packets_production_rate_(0),
+ fec_packets_production_rate_(0),
last_round_(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count()),
@@ -43,11 +47,17 @@ RTCProductionProtocol::RTCProductionProtocol(
on_consumer_in_sync_(nullptr) {
srand((unsigned int)time(NULL));
prod_label_ = rand() % 256;
+ cache_label_ = (prod_label_ + 1) % 256;
interests_queue_timer_ =
std::make_unique<asio::steady_timer>(portal_->getIoService());
round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService());
setOutputBufferSize(10000);
scheduleRoundTimer();
+
+ // FEC
+ using namespace std::placeholders;
+ enableFEC(std::bind(&RTCProductionProtocol::onFecPackets, this, _1),
+ std::bind(&RTCProductionProtocol::getBuffer, this, _1));
}
RTCProductionProtocol::~RTCProductionProtocol() {}
@@ -61,10 +71,19 @@ void RTCProductionProtocol::registerNamespaceWithNetwork(
switch (family) {
case AF_INET6:
- header_size_ = (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET6_TCP);
+ data_header_size_ =
+ signer_ && !making_manifest_
+ ? (uint32_t)Packet::getHeaderSizeFromFormat(
+ HF_INET6_TCP_AH, signer_->getSignatureFieldSize())
+ : (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET6_TCP);
+ ;
break;
case AF_INET:
- header_size_ = (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET_TCP);
+ data_header_size_ =
+ signer_ && !making_manifest_
+ ? (uint32_t)Packet::getHeaderSizeFromFormat(
+ HF_INET_TCP_AH, signer_->getSignatureFieldSize())
+ : (uint32_t)Packet::getHeaderSizeFromFormat(HF_INET_TCP);
break;
default:
throw errors::RuntimeException("Unknown name format.");
@@ -90,16 +109,19 @@ void RTCProductionProtocol::updateStats() {
uint32_t prev_packets_production_rate = packets_production_rate_;
- bytes_production_rate_ = (uint32_t)ceil((double)produced_bytes_ * per_second);
- packets_production_rate_ = (uint32_t)ceil((double)produced_packets_ * per_second);
+ bytes_production_rate_ = ceil((double)produced_bytes_ * per_second);
+ packets_production_rate_ = ceil((double)produced_packets_ * per_second);
+ fec_packets_production_rate_ =
+ ceil((double)produced_fec_packets_ * per_second);
- TRANSPORT_LOGD("Updating production rate: produced_bytes_ = %u bps = %u",
- produced_bytes_, bytes_production_rate_);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Updating production rate: produced_bytes_ = " << produced_bytes_
+ << " bps = " << bytes_production_rate_;
// update the production rate as soon as it increases by 10% with respect to
// the last round
max_packet_production_ =
- produced_packets_ + (uint32_t)ceil((double)produced_packets_ * 0.1);
+ produced_packets_ + ceil((double)produced_packets_ * 0.1);
if (max_packet_production_ < rtc::WIN_MIN)
max_packet_production_ = rtc::WIN_MIN;
@@ -117,6 +139,7 @@ void RTCProductionProtocol::updateStats() {
produced_bytes_ = 0;
produced_packets_ = 0;
+ produced_fec_packets_ = 0;
last_round_ = now;
scheduleRoundTimer();
}
@@ -147,32 +170,34 @@ uint32_t RTCProductionProtocol::produceDatagram(
socket_->getSocketOption(interface::GeneralTransportOptions::DATA_PACKET_SIZE,
data_packet_size);
- if (TRANSPORT_EXPECT_FALSE((buffer_size + header_size_ +
+ if (TRANSPORT_EXPECT_FALSE((buffer_size + data_header_size_ +
rtc::DATA_HEADER_SIZE) > data_packet_size)) {
return 0;
}
auto content_object =
- core::PacketManager<>::getInstance().getPacket<ContentObject>();
+ core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP,
+ signer_ ? signer_->getSignatureFieldSize() : 0);
// 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());
- std::shared_ptr<ContentObject> co = std::move(content_object);
-
// schedule actual sending on internal thread
- portal_->getIoService().dispatch(
- [this, content_object{std::move(co)}, content_name]() mutable {
- produceInternal(std::move(content_object), content_name);
- });
+ portal_->getIoService().dispatch([this,
+ content_object{std::move(content_object)},
+ content_name]() mutable {
+ produceInternal(std::move(content_object), content_name);
+ });
return 1;
}
void RTCProductionProtocol::produceInternal(
- std::shared_ptr<ContentObject> &&content_object, const Name &content_name) {
+ std::shared_ptr<ContentObject> &&content_object, const Name &content_name,
+ bool fec) {
// set rtc header
struct rtc::data_packet_t *data_pkt =
(struct rtc::data_packet_t *)content_object->getPayload()->data();
@@ -188,10 +213,19 @@ void RTCProductionProtocol::produceInternal(
content_object->setLifetime(500); // XXX this should be set by the APP
content_object->setPathLabel(prod_label_);
+ // sign packet
+ if (signer_) {
+ signer_->signPacket(content_object.get());
+ }
+
// update stats
- produced_bytes_ += (uint32_t)(
- content_object->headerSize() + content_object->payloadSize());
- produced_packets_++;
+ if (!fec) {
+ produced_bytes_ +=
+ content_object->headerSize() + content_object->payloadSize();
+ produced_packets_++;
+ } else {
+ produced_fec_packets_++;
+ }
if (produced_packets_ >= max_packet_production_) {
// in this case all the pending interests may be used to accomodate the
@@ -201,7 +235,14 @@ void RTCProductionProtocol::produceInternal(
updateStats();
}
- TRANSPORT_LOGD("Sending content object: %s", n.toString().c_str());
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Sending content object: " << n << ", is fec: " << fec;
+
+ // pass packet to FEC encoder
+ if (fec_encoder_ && !fec) {
+ fec_encoder_->onPacketProduced(
+ *content_object, content_object->headerSize() + rtc::DATA_HEADER_SIZE);
+ }
output_buffer_.insert(content_object);
@@ -210,7 +251,10 @@ void RTCProductionProtocol::produceInternal(
*content_object);
}
- portal_->sendContentObject(*content_object);
+ auto seq_it = seqs_map_.find(current_seg_);
+ if (seq_it != seqs_map_.end()) {
+ portal_->sendContentObject(*content_object);
+ }
if (*on_content_object_output_) {
on_content_object_output_->operator()(*socket_->getInterface(),
@@ -220,58 +264,84 @@ void RTCProductionProtocol::produceInternal(
// remove interests from the interest cache if it exists
removeFromInterestQueue(current_seg_);
+ // 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::onInterest(Interest &interest) {
- uint32_t interest_seg = interest.getName().getSuffix();
- uint32_t lifetime = interest.getLifetime();
+ if (*on_interest_input_) {
+ on_interest_input_->operator()(*socket_->getInterface(), interest);
+ }
+
+ auto suffix = interest.firstSuffix();
+ // numberOfSuffixes returns only the prefixes in the payalod
+ // we add + 1 to count anche the seq in the name
+ auto n_suffixes = interest.numberOfSuffixes() + 1;
+ Name name = interest.getName();
+ bool prev_consumer_state = consumer_in_sync_;
+
+ for (uint32_t i = 0; i < n_suffixes; i++) {
+ if (i > 0) {
+ name.setSuffix(*(suffix + (i - 1)));
+ }
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received interest " << name;
+ const std::shared_ptr<ContentObject> content_object =
+ 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_);
+ portal_->sendContentObject(*content_object);
+ } else {
+ if (*on_interest_process_) {
+ on_interest_process_->operator()(*socket_->getInterface(), interest);
+ }
+ processInterest(name.getSuffix(), interest.getLifetime());
+ }
+ }
+
+ if (prev_consumer_state != consumer_in_sync_ && consumer_in_sync_)
+ on_consumer_in_sync_(*socket_->getInterface(), interest);
+}
+
+void RTCProductionProtocol::processInterest(uint32_t interest_seg,
+ uint32_t lifetime) {
if (interest_seg == 0) {
// first packet from the consumer, reset sync state
consumer_in_sync_ = false;
}
- if (*on_interest_input_) {
- on_interest_input_->operator()(*socket_->getInterface(), interest);
- }
-
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
if (interest_seg > rtc::MIN_PROBE_SEQ) {
- TRANSPORT_LOGD("received probe %u", interest_seg);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << interest_seg;
sendNack(interest_seg);
return;
}
- TRANSPORT_LOGD("received interest %u", interest_seg);
-
- const std::shared_ptr<ContentObject> content_object =
- output_buffer_.find(interest);
-
- if (content_object) {
- if (*on_interest_satisfied_output_buffer_) {
- on_interest_satisfied_output_buffer_->operator()(*socket_->getInterface(),
- interest);
- }
-
- if (*on_content_object_output_) {
- on_content_object_output_->operator()(*socket_->getInterface(),
- *content_object);
- }
-
- TRANSPORT_LOGD("Send content %u (onInterest)",
- content_object->getName().getSuffix());
- portal_->sendContentObject(*content_object);
- return;
- } else {
- if (*on_interest_process_) {
- on_interest_process_->operator()(*socket_->getInterface(), interest);
- }
- }
-
// if the production rate 0 use delayed nacks
if (allow_delayed_nacks_ && interest_seg >= current_seg_) {
uint64_t next_timer = ~0;
@@ -310,7 +380,8 @@ void RTCProductionProtocol::onInterest(Interest &interest) {
(double)((double)((double)lifetime *
rtc::INTEREST_LIFETIME_REDUCTION_FACTOR /
rtc::MILLI_IN_A_SEC) *
- (double)packets_production_rate_));
+ (double)(packets_production_rate_ +
+ fec_packets_production_rate_)));
if (interest_seg < current_seg_ || interest_seg > (max_gap + current_seg_)) {
sendNack(interest_seg);
@@ -318,14 +389,14 @@ void RTCProductionProtocol::onInterest(Interest &interest) {
if (!consumer_in_sync_ && on_consumer_in_sync_) {
// we consider the remote consumer to be in sync as soon as it covers 70%
// of the production window with interests
- uint32_t perc = (uint32_t)ceil((double)max_gap * 0.7);
+ uint32_t perc = ceil((double)max_gap * 0.7);
if (interest_seg > (perc + current_seg_)) {
consumer_in_sync_ = true;
- on_consumer_in_sync_(*socket_->getInterface(), interest);
+ // on_consumer_in_sync_(*socket_->getInterface(), interest);
}
}
- uint64_t expiration =(uint32_t)(
- now + floor((double)lifetime * rtc::INTEREST_LIFETIME_REDUCTION_FACTOR));
+ uint64_t expiration =
+ now + floor((double)lifetime * rtc::INTEREST_LIFETIME_REDUCTION_FACTOR);
addToInterestQueue(interest_seg, expiration);
}
}
@@ -377,7 +448,7 @@ void RTCProductionProtocol::sendNacksForPendingInterests() {
uint32_t packet_gap = 100000; // set it to a high value (100sec)
if (packets_production_rate_ != 0)
- packet_gap = (uint32_t)ceil(rtc::MILLI_IN_A_SEC / (double)packets_production_rate_);
+ packet_gap = ceil(rtc::MILLI_IN_A_SEC / (double)packets_production_rate_);
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
@@ -441,7 +512,9 @@ void RTCProductionProtocol::interestQueueTimer() {
}
void RTCProductionProtocol::sendNack(uint32_t sequence) {
- auto nack = core::PacketManager<>::getInstance().getPacket<ContentObject>();
+ auto nack = core::PacketManager<>::getInstance().getPacket<ContentObject>(
+ signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP,
+ signer_ ? signer_->getSignatureFieldSize() : 0);
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
@@ -460,6 +533,10 @@ void RTCProductionProtocol::sendNack(uint32_t sequence) {
nack->setLifetime(0);
nack->setPathLabel(prod_label_);
+ if (signer_) {
+ signer_->signPacket(nack.get());
+ }
+
if (!consumer_in_sync_ && on_consumer_in_sync_ &&
sequence < rtc::MIN_PROBE_SEQ && sequence > next_packet) {
consumer_in_sync_ = true;
@@ -472,10 +549,39 @@ void RTCProductionProtocol::sendNack(uint32_t sequence) {
on_content_object_output_->operator()(*socket_->getInterface(), *nack);
}
- TRANSPORT_LOGD("Send nack %u", sequence);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Send nack " << sequence;
portal_->sendContentObject(*nack);
}
+void RTCProductionProtocol::onFecPackets(
+ std::vector<std::pair<uint32_t, fec::buffer>> &packets) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Produced " << packets.size() << " FEC packets";
+ for (auto &packet : packets) {
+ auto content_object =
+ std::static_pointer_cast<ContentObject>(packet.second);
+ 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>(
+ signer_ ? Format::HF_INET6_TCP_AH : Format::HF_INET6_TCP,
+ signer_ ? signer_->getSignatureFieldSize() : 0);
+ ret->updateLength(rtc::DATA_HEADER_SIZE + size);
+ ret->append(rtc::DATA_HEADER_SIZE + size);
+ ret->trimStart(ret->headerSize() + rtc::DATA_HEADER_SIZE);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Responding with buffer of length " << ret->length();
+ assert(ret->length() >= size);
+
+ return ret;
+}
+
} // namespace protocol
} // end namespace transport
diff --git a/libtransport/src/protocols/prod_protocol_rtc.h b/libtransport/src/protocols/prod_protocol_rtc.h
index f3584f74a..96ad5673d 100644
--- a/libtransport/src/protocols/prod_protocol_rtc.h
+++ b/libtransport/src/protocols/prod_protocol_rtc.h
@@ -60,8 +60,9 @@ class RTCProductionProtocol : public ProductionProtocol {
// packet handlers
void onInterest(Interest &interest) override;
void onError(std::error_code ec) override;
+ void processInterest(uint32_t interest_seg, uint32_t lifetime);
void produceInternal(std::shared_ptr<ContentObject> &&content_object,
- const Name &content_name);
+ const Name &content_name, bool fec = false);
void sendNack(uint32_t sequence);
// stats
@@ -75,20 +76,27 @@ class RTCProductionProtocol : public ProductionProtocol {
void scheduleQueueTimer(uint64_t wait);
void interestQueueTimer();
+ // FEC functions
+ void onFecPackets(std::vector<std::pair<uint32_t, fec::buffer>> &packets);
+ fec::buffer getBuffer(std::size_t size);
+
core::Name flow_name_;
- uint32_t current_seg_; // seq id of the next packet produced
- uint32_t prod_label_; // path lable of the producer
- uint16_t header_size_; // hicn header size
+ uint32_t current_seg_; // seq id of the next packet produced
+ uint32_t prod_label_; // path lable of the producer
+ uint32_t cache_label_; // path lable for content from the producer cache
+ uint16_t data_header_size_; // hicn data header size
- uint32_t produced_bytes_; // bytes produced in the last round
- uint32_t produced_packets_; // packet produed in the last round
+ uint32_t produced_bytes_; // bytes produced in the last round
+ uint32_t produced_packets_; // packet produed in the last round
+ uint32_t produced_fec_packets_; // fec packets produced 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
+ uint32_t bytes_production_rate_; // bytes per sec
+ uint32_t packets_production_rate_; // pps
+ uint32_t fec_packets_production_rate_; // pps
std::unique_ptr<asio::steady_timer> round_timer_;
uint64_t last_round_;
@@ -120,6 +128,9 @@ class RTCProductionProtocol : public ProductionProtocol {
// impossible to know the state of the consumers so it should not be used.
bool consumer_in_sync_;
interface::ProducerInterestCallback on_consumer_in_sync_;
+
+ // Save FEC packets here before sending them
+ std::queue<ContentObject::Ptr> pending_fec_packets_;
};
} // namespace protocol
diff --git a/libtransport/src/protocols/production_protocol.cc b/libtransport/src/protocols/production_protocol.cc
index 8addf52d1..6b317d47d 100644
--- a/libtransport/src/protocols/production_protocol.cc
+++ b/libtransport/src/protocols/production_protocol.cc
@@ -26,6 +26,7 @@ ProductionProtocol::ProductionProtocol(
implementation::ProducerSocket *icn_socket)
: socket_(icn_socket),
is_running_(false),
+ fec_encoder_(nullptr),
on_interest_input_(VOID_HANDLER),
on_interest_dropped_input_buffer_(VOID_HANDLER),
on_interest_inserted_input_buffer_(VOID_HANDLER),
@@ -36,7 +37,8 @@ ProductionProtocol::ProductionProtocol(
on_content_object_in_output_buffer_(VOID_HANDLER),
on_content_object_output_(VOID_HANDLER),
on_content_object_evicted_from_output_buffer_(VOID_HANDLER),
- on_content_produced_(VOID_HANDLER) {
+ on_content_produced_(VOID_HANDLER),
+ fec_type_(fec::FECType::UNKNOWN) {
socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_);
// TODO add statistics for producer
// socket_->getSocketOption(OtherOptions::STATISTICS, &stats_);
@@ -75,6 +77,9 @@ int ProductionProtocol::start() {
&on_content_produced_);
socket_->getSocketOption(GeneralTransportOptions::ASYNC_MODE, is_async_);
+ socket_->getSocketOption(GeneralTransportOptions::SIGNER, signer_);
+ socket_->getSocketOption(GeneralTransportOptions::MAKE_MANIFEST,
+ making_manifest_);
bool first = true;
diff --git a/libtransport/src/protocols/production_protocol.h b/libtransport/src/protocols/production_protocol.h
index 780972321..7366311eb 100644
--- a/libtransport/src/protocols/production_protocol.h
+++ b/libtransport/src/protocols/production_protocol.h
@@ -20,6 +20,8 @@
#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 <utils/content_store.h>
#include <atomic>
@@ -67,6 +69,27 @@ class ProductionProtocol : public Portal::ProducerCallback {
virtual void onInterest(core::Interest &i) override = 0;
virtual void onError(std::error_code ec) override{};
+ template <typename FECHandler, typename AllocatorHandler>
+ void enableFEC(FECHandler &&fec_handler,
+ AllocatorHandler &&allocator_handler) {
+ if (!fec_encoder_) {
+ // Try to get FEC from environment
+ if (const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE")) {
+ LOG(INFO) << "Using FEC " << fec_str;
+ fec_type_ = fec::FECUtils::fecTypeFromString(fec_str);
+ }
+
+ 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_;
@@ -78,6 +101,7 @@ class ProductionProtocol : public Portal::ProducerCallback {
std::shared_ptr<Portal> portal_;
std::atomic<bool> is_running_;
interface::ProductionStatistics *stats_;
+ std::unique_ptr<fec::ProducerFEC> fec_encoder_;
// Callbacks
interface::ProducerInterestCallback *on_interest_input_;
@@ -101,7 +125,12 @@ class ProductionProtocol : public Portal::ProducerCallback {
// List ot routes served by current producer protocol
std::list<Prefix> served_namespaces_;
+ // Signature and manifest
+ std::shared_ptr<auth::Signer> signer_;
+ bool making_manifest_;
+
bool is_async_;
+ fec::FECType fec_type_;
};
} // end namespace protocol
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
deleted file mode 100644
index 73a0a2c64..000000000
--- a/libtransport/src/protocols/protocol.h
+++ /dev/null
@@ -1,114 +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/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 <atomic>
-
-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;
-
- 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;
-
- 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 {}
-
- 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_;
-};
-
-} // end namespace protocol
-} // end namespace transport
diff --git a/libtransport/src/protocols/raaqm.cc b/libtransport/src/protocols/raaqm.cc
index bc8500227..1247af400 100644
--- a/libtransport/src/protocols/raaqm.cc
+++ b/libtransport/src/protocols/raaqm.cc
@@ -17,7 +17,7 @@
#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>
@@ -31,7 +31,8 @@ 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),
@@ -47,11 +48,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::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;
+ }
+
+ // Reset rate estimator
if (rate_estimator_) {
rate_estimator_->onStart();
}
+ // If not cur_path exists, create one
if (!cur_path_) {
// RAAQM
double drop_factor;
@@ -94,37 +118,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;
- }
}
void RaaqmTransportProtocol::increaseWindow() {
@@ -194,9 +187,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;
}
@@ -322,12 +314,9 @@ void RaaqmTransportProtocol::init() {
is.close();
}
-void RaaqmTransportProtocol::onContentObject(Interest &interest,
- ContentObject &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_) {
@@ -338,18 +327,12 @@ void RaaqmTransportProtocol::onContentObject(Interest &interest,
(*on_interest_satisfied_)(*socket_->getInterface(), interest);
}
+ ec = make_error_code(protocol_error::success);
+
if (content_object.getPayloadType() == PayloadType::DATA) {
stats_->updateBytesRecv(content_object.payloadSize());
}
- onContentSegment(interest, content_object);
- scheduleNextInterests();
-}
-
-void RaaqmTransportProtocol::onContentSegment(Interest &interest,
- ContentObject &content_object) {
- uint32_t incremental_suffix = content_object.getName().getSuffix();
-
// Decrease in-flight interests
interests_in_flight_--;
@@ -358,11 +341,13 @@ void RaaqmTransportProtocol::onContentSegment(Interest &interest,
afterContentReception(interest, content_object);
}
- index_manager_->onContentObject(interest, content_object);
+ // Schedule next interests
+ scheduleNextInterests();
}
void RaaqmTransportProtocol::onPacketDropped(Interest &interest,
- ContentObject &content_object) {
+ ContentObject &content_object,
+ const std::error_code &reason) {
uint32_t max_rtx = 0;
socket_->getSocketOption(GeneralTransportOptions::MAX_INTEREST_RETX, max_rtx);
@@ -380,16 +365,15 @@ void RaaqmTransportProtocol::onPacketDropped(Interest &interest,
(*on_interest_output_)(*socket_->getInterface(), interest);
}
- if (!is_running_) {
+ if (!isRunning()) {
return;
}
interest_retransmissions_[segment & mask]++;
- interest_to_retransmit_.push(interest.shared_from_this());
+ interest_to_retransmit_.push(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));
}
@@ -399,23 +383,25 @@ 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]++;
+ 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;
}
@@ -436,32 +422,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(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();
}
@@ -470,55 +458,25 @@ 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 {
- if (TRANSPORT_EXPECT_FALSE(!is_running_ && !is_first_)) {
- TRANSPORT_LOGI("Adios");
+ if (TRANSPORT_EXPECT_FALSE(!isRunning() && !is_first_)) {
break;
}
- index = index_manager_->getNextSuffix();
+ index = indexer_verifier_->getNextSuffix();
if (index == IndexManager::invalid_index) {
break;
}
- sendInterest(index);
+ interest_retransmissions_[index & mask] = ~0;
+ sendInterest(name->setSuffix(index));
}
}
}
-void RaaqmTransportProtocol::sendInterest(std::uint64_t next_suffix) {
- auto interest = core::PacketManager<>::getInstance().getPacket<Interest>();
- 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);
- }
- // 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));
-}
-
-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) {
rate_estimator_->onDownloadFinished();
TransportProtocol::onContentReassembled(ec);
diff --git a/libtransport/src/protocols/raaqm.h b/libtransport/src/protocols/raaqm.h
index be477d39f..ffbb30d3a 100644
--- a/libtransport/src/protocols/raaqm.h
+++ b/libtransport/src/protocols/raaqm.h
@@ -32,13 +32,12 @@ 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;
@@ -62,35 +61,24 @@ class RaaqmTransportProtocol : public TransportProtocol,
private:
void init();
- void onContentObject(Interest &i, ContentObject &c) override;
-
- void onContentSegment(Interest &interest, ContentObject &content_object);
-
- void onPacketDropped(Interest &interest,
- ContentObject &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(std::uint64_t next_suffix);
-
- void sendInterest(Interest::Ptr &&interest);
+ void sendInterest(const Name &interest_name,
+ std::array<uint32_t, MAX_AGGREGATED_INTEREST>
+ *additional_suffixes = nullptr,
+ uint32_t len = 0) override;
void onContentReassembled(std::error_code ec) override;
-
void updateRtt(uint64_t segment);
-
void RAAQM();
-
void updatePathTable(const ContentObject &content_object);
-
void checkDropProbability();
-
void checkForStalePaths();
-
void printRtt();
protected:
@@ -100,7 +88,7 @@ class RaaqmTransportProtocol : public TransportProtocol,
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::queue<uint32_t> interest_to_retransmit_;
private:
/**
diff --git a/libtransport/src/protocols/rate_estimation.cc b/libtransport/src/protocols/rate_estimation.cc
index 5ca925760..2337e18be 100644
--- a/libtransport/src/protocols/rate_estimation.cc
+++ b/libtransport/src/protocols/rate_estimation.cc
@@ -13,8 +13,8 @@
* limitations under the License.
*/
+#include <glog/logging.h>
#include <hicn/transport/interfaces/socket_options_default_values.h>
-#include <hicn/transport/utils/log.h>
#include <protocols/rate_estimation.h>
#include <thread>
@@ -115,12 +115,12 @@ void InterRttEstimator::onRttUpdate(double rtt) {
if (!thread_is_running_) {
my_th_ = (pthread_t *)malloc(sizeof(pthread_t));
if (!my_th_) {
- TRANSPORT_LOGE("Error allocating thread.");
+ LOG(ERROR) << "Error allocating thread.";
my_th_ = NULL;
}
if (/*int err = */ pthread_create(my_th_, NULL, transport::protocol::Timer,
(void *)this)) {
- TRANSPORT_LOGE("Error creating the thread");
+ LOG(ERROR) << "Error creating the thread";
my_th_ = NULL;
}
thread_is_running_ = true;
diff --git a/libtransport/src/protocols/reassembly.cc b/libtransport/src/protocols/reassembly.cc
index 0e59832dc..ce24fce1b 100644
--- a/libtransport/src/protocols/reassembly.cc
+++ b/libtransport/src/protocols/reassembly.cc
@@ -31,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 385122c53..e072ad123 100644
--- a/libtransport/src/protocols/reassembly.h
+++ b/libtransport/src/protocols/reassembly.h
@@ -46,19 +46,48 @@ class Reassembly {
virtual ~Reassembly() = default;
+ /**
+ * Hanle reassembly of content object.
+ */
virtual void reassemble(core::ContentObject &content_object) = 0;
+
+ /**
+ * Hanle reassembly of content object.
+ */
+ virtual void reassemble(utils::MemBuf &buffer, uint32_t suffix) = 0;
+
+ /**
+ * Handle reassembly of manifest
+ */
virtual void reassemble(
std::unique_ptr<core::ContentObjectManifest> &&manifest) = 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
index 77f065d0e..873b345d0 100644
--- a/libtransport/src/protocols/rtc/CMakeLists.txt
+++ b/libtransport/src/protocols/rtc/CMakeLists.txt
@@ -11,27 +11,27 @@
# 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}/probe_handler.h
${CMAKE_CURRENT_SOURCE_DIR}/rtc.h
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.h
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.h
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h
${CMAKE_CURRENT_SOURCE_DIR}/rtc_consts.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.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_queue.h
- ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.h
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_packet.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_reassembly.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.h
)
list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.cc
${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc
${CMAKE_CURRENT_SOURCE_DIR}/rtc_ldr.cc
${CMAKE_CURRENT_SOURCE_DIR}/rtc_rc_queue.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc
- ${CMAKE_CURRENT_SOURCE_DIR}/probe_handler.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtc_state.cc
)
set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
diff --git a/libtransport/src/protocols/rtc/congestion_detection.cc b/libtransport/src/protocols/rtc/congestion_detection.cc
deleted file mode 100644
index e2d44ae66..000000000
--- a/libtransport/src/protocols/rtc/congestion_detection.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/utils/log.h>
-#include <protocols/rtc/congestion_detection.h>
-
-namespace transport {
-
-namespace protocol {
-
-namespace rtc {
-
-CongestionDetection::CongestionDetection()
- : cc_estimator_(), last_processed_chunk_() {}
-
-CongestionDetection::~CongestionDetection() {}
-
-void CongestionDetection::updateStats() {
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- if (chunks_number_.empty()) return;
-
- uint32_t chunk_number = chunks_number_.front();
-
- while (chunks_[chunk_number].getReceivedTime() + HICN_CC_STATS_MAX_DELAY_MS <
- now ||
- chunks_[chunk_number].isComplete()) {
- if (chunk_number == last_processed_chunk_.getFrameSeqNum() + 1) {
- chunks_[chunk_number].setPreviousSentTime(
- last_processed_chunk_.getSentTime());
-
- chunks_[chunk_number].setPreviousReceivedTime(
- last_processed_chunk_.getReceivedTime());
- cc_estimator_.Update(chunks_[chunk_number].getReceivedDelta(),
- chunks_[chunk_number].getSentDelta(),
- chunks_[chunk_number].getSentTime(),
- chunks_[chunk_number].getReceivedTime(),
- chunks_[chunk_number].getFrameSize(), true);
-
- } else {
- TRANSPORT_LOGD(
- "CongestionDetection::updateStats frame %u but not the \
- previous one, last one was %u currentFrame %u",
- chunk_number, last_processed_chunk_.getFrameSeqNum(),
- chunks_[chunk_number].getFrameSeqNum());
- }
-
- last_processed_chunk_ = chunks_[chunk_number];
-
- chunks_.erase(chunk_number);
-
- chunks_number_.pop();
- if (chunks_number_.empty()) break;
-
- chunk_number = chunks_number_.front();
- }
-}
-
-void CongestionDetection::addPacket(const core::ContentObject &content_object) {
- 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_;
- uint64_t *sentTimePtr = (uint64_t *)payload->data();
-
- // this is just for testing with hiperf, assuming a frame is 10 pkts
- // in the final version, the split should be based on the timestamp in the pkt
- uint32_t frameNum = (int)(segmentNumber / HICN_CC_STATS_CHUNK_SIZE);
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
-
- if (chunks_.find(frameNum) == chunks_.end()) {
- // new chunk of pkts or out of order
- if (last_processed_chunk_.getFrameSeqNum() > frameNum)
- return; // out of order and we already processed the chunk
-
- chunks_[frameNum] = FrameStats(frameNum, HICN_CC_STATS_CHUNK_SIZE);
- chunks_number_.push(frameNum);
- }
-
- chunks_[frameNum].addPacket(*sentTimePtr, now, payload_size);
-}
-
-} // namespace rtc
-} // namespace protocol
-} // namespace transport
diff --git a/libtransport/src/protocols/rtc/congestion_detection.h b/libtransport/src/protocols/rtc/congestion_detection.h
deleted file mode 100644
index 17f4aa54c..000000000
--- a/libtransport/src/protocols/rtc/congestion_detection.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hicn/transport/core/content_object.h>
-#include <protocols/rtc/trendline_estimator.h>
-
-#include <map>
-#include <queue>
-
-#define HICN_CC_STATS_CHUNK_SIZE 10
-#define HICN_CC_STATS_MAX_DELAY_MS 100
-
-namespace transport {
-
-namespace protocol {
-
-namespace rtc {
-
-class FrameStats {
- public:
- FrameStats()
- : frame_num_(0),
- sent_time_(0),
- received_time_(0),
- previous_sent_time_(0),
- previous_received_time_(0),
- size_(0),
- received_pkt_m(0),
- burst_size_m(HICN_CC_STATS_CHUNK_SIZE){};
-
- FrameStats(uint32_t burst_size)
- : frame_num_(0),
- sent_time_(0),
- received_time_(0),
- previous_sent_time_(0),
- previous_received_time_(0),
- size_(0),
- received_pkt_m(0),
- burst_size_m(burst_size){};
-
- FrameStats(uint32_t frame_num, uint32_t burst_size)
- : frame_num_(frame_num),
- sent_time_(0),
- received_time_(0),
- previous_sent_time_(0),
- previous_received_time_(0),
- size_(0),
- received_pkt_m(0),
- burst_size_m(burst_size){};
-
- FrameStats(uint32_t frame_num, uint64_t sent_time, uint64_t received_time,
- uint32_t size, FrameStats previousFrame, uint32_t burst_size)
- : frame_num_(frame_num),
- sent_time_(sent_time),
- received_time_(received_time),
- previous_sent_time_(previousFrame.getSentTime()),
- previous_received_time_(previousFrame.getReceivedTime()),
- size_(size),
- received_pkt_m(1),
- burst_size_m(burst_size){};
-
- void addPacket(uint64_t sent_time, uint64_t received_time, uint32_t size) {
- size_ += size;
- sent_time_ =
- (sent_time_ == 0) ? sent_time : std::min(sent_time_, sent_time);
- received_time_ = std::max(received_time, received_time_);
- received_pkt_m++;
- }
-
- bool isComplete() { return received_pkt_m == burst_size_m; }
-
- uint32_t getFrameSeqNum() const { return frame_num_; }
- uint64_t getSentTime() const { return sent_time_; }
- uint64_t getReceivedTime() const { return received_time_; }
- uint32_t getFrameSize() const { return size_; }
-
- void setPreviousReceivedTime(uint64_t time) {
- previous_received_time_ = time;
- }
- void setPreviousSentTime(uint64_t time) { previous_sent_time_ = time; }
-
- // todo manage first frame
- double getReceivedDelta() {
- return static_cast<double>(received_time_ - previous_received_time_);
- }
- double getSentDelta() {
- return static_cast<double>(sent_time_ - previous_sent_time_);
- }
-
- private:
- uint32_t frame_num_;
- uint64_t sent_time_;
- uint64_t received_time_;
-
- uint64_t previous_sent_time_;
- uint64_t previous_received_time_;
- uint32_t size_;
-
- uint32_t received_pkt_m;
- uint32_t burst_size_m;
-};
-
-class CongestionDetection {
- public:
- CongestionDetection();
- ~CongestionDetection();
-
- void addPacket(const core::ContentObject &content_object);
-
- BandwidthUsage getState() { return cc_estimator_.State(); }
-
- void updateStats();
-
- private:
- TrendlineEstimator cc_estimator_;
- std::map<uint32_t, FrameStats> chunks_;
- std::queue<uint32_t> chunks_number_;
-
- FrameStats last_processed_chunk_;
-};
-
-} // end namespace rtc
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/probe_handler.cc b/libtransport/src/protocols/rtc/probe_handler.cc
index efba362d4..abaca6ad9 100644
--- a/libtransport/src/protocols/rtc/probe_handler.cc
+++ b/libtransport/src/protocols/rtc/probe_handler.cc
@@ -43,7 +43,7 @@ uint64_t ProbeHandler::getRtt(uint32_t seq) {
std::chrono::steady_clock::now().time_since_epoch())
.count();
uint64_t rtt = now - it->second;
- if(rtt < 1) rtt = 1;
+ if (rtt < 1) rtt = 1;
pending_probes_.erase(it);
diff --git a/libtransport/src/protocols/rtc/probe_handler.h b/libtransport/src/protocols/rtc/probe_handler.h
index b8ed84445..e34b23df0 100644
--- a/libtransport/src/protocols/rtc/probe_handler.h
+++ b/libtransport/src/protocols/rtc/probe_handler.h
@@ -14,9 +14,8 @@
*/
#pragma once
#include <hicn/transport/config.h>
+#include <hicn/transport/core/asio_wrapper.h>
-#include <asio.hpp>
-#include <asio/steady_timer.hpp>
#include <functional>
#include <random>
#include <unordered_map>
@@ -32,8 +31,7 @@ class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> {
using SendProbeCallback = std::function<void(uint32_t)>;
public:
- ProbeHandler(SendProbeCallback &&send_callback,
- asio::io_service &io_service);
+ ProbeHandler(SendProbeCallback &&send_callback, asio::io_service &io_service);
~ProbeHandler();
@@ -53,8 +51,8 @@ class ProbeHandler : public std::enable_shared_from_this<ProbeHandler> {
private:
uint32_t probe_interval_; // us
- uint32_t max_probes_; // packets
- uint32_t sent_probes_; // packets
+ uint32_t max_probes_; // packets
+ uint32_t sent_probes_; // packets
std::unique_ptr<asio::steady_timer> probe_timer_;
diff --git a/libtransport/src/protocols/rtc/rtc.cc b/libtransport/src/protocols/rtc/rtc.cc
index 46659ac74..0cb4cda1d 100644
--- a/libtransport/src/protocols/rtc/rtc.cc
+++ b/libtransport/src/protocols/rtc/rtc.cc
@@ -17,8 +17,11 @@
#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_queue.h>
#include <algorithm>
@@ -33,39 +36,41 @@ using namespace interface;
RTCTransportProtocol::RTCTransportProtocol(
implementation::ConsumerSocket *icn_socket)
- : TransportProtocol(icn_socket, nullptr),
- DatagramReassembly(icn_socket, this),
+ : TransportProtocol(icn_socket, new RtcIndexer<>(icn_socket, this),
+ new DatagramReassembly(icn_socket, this)),
number_(0) {
icn_socket->getSocketOption(PORTAL, portal_);
round_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService());
scheduler_timer_ =
std::make_unique<asio::steady_timer>(portal_->getIoService());
+ pacing_timer_ = std::make_unique<asio::steady_timer>(portal_->getIoService());
}
RTCTransportProtocol::~RTCTransportProtocol() {}
void RTCTransportProtocol::resume() {
- if (is_running_) return;
-
- is_running_ = true;
-
newRound();
+ TransportProtocol::resume();
+}
- portal_->runEventsLoop();
- is_running_ = false;
+std::size_t RTCTransportProtocol::transportHeaderLength() {
+ return DATA_HEADER_SIZE +
+ (fec_decoder_ != nullptr ? fec_decoder_->getFecHeaderSize() : 0);
}
// private
void RTCTransportProtocol::initParams() {
- portal_->setConsumerCallback(this);
+ TransportProtocol::reset();
rc_ = std::make_shared<RTCRateControlQueue>();
ldr_ = std::make_shared<RTCLossDetectionAndRecovery>(
+ indexer_verifier_.get(),
std::bind(&RTCTransportProtocol::sendRtxInterest, this,
std::placeholders::_1),
portal_->getIoService());
state_ = std::make_shared<RTCState>(
+ indexer_verifier_.get(),
std::bind(&RTCTransportProtocol::sendProbeInterest, this,
std::placeholders::_1),
std::bind(&RTCTransportProtocol::discoveredRtt, this),
@@ -83,8 +88,27 @@ void RTCTransportProtocol::initParams() {
// Cancel timer
number_++;
round_timer_->cancel();
+
scheduler_timer_->cancel();
scheduler_timer_on_ = false;
+ last_interest_sent_time_ = 0;
+ last_interest_sent_seq_ = 0;
+
+#if 0
+ if(portal_->isConnectedToFwd()){
+ max_aggregated_interest_ = 1;
+ }else{
+ max_aggregated_interest_ = MAX_INTERESTS_IN_BATCH;
+ }
+#else
+ max_aggregated_interest_ = 1;
+#endif
+
+ 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();
@@ -93,16 +117,28 @@ void RTCTransportProtocol::initParams() {
current_sync_win_ = INITIAL_WIN;
max_sync_win_ = INITIAL_WIN_MAX;
- // names/packets var
- next_segment_ = 0;
-
socket_->setSocketOption(GeneralTransportOptions::INTEREST_LIFETIME,
RTC_INTEREST_LIFETIME);
+
+ // FEC
+ using namespace std::placeholders;
+ enableFEC(std::bind(&RTCTransportProtocol::onFecPackets, this, _1),
+ /* We leave the buffer allocation to the fec decoder */
+ fec::FECBase::BufferRequested(0));
+
+ if (fec_decoder_) {
+ indexer_verifier_->enableFec(fec_type_);
+ indexer_verifier_->setNFec(0);
+ ldr_->setFecParams(fec::FECUtils::getBlockSymbols(fec_type_),
+ fec::FECUtils::getSourceSymbols(fec_type_));
+ } else {
+ indexer_verifier_->disableFec();
+ }
}
// private
void RTCTransportProtocol::reset() {
- TRANSPORT_LOGD("reset called");
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "reset called";
initParams();
newRound();
}
@@ -113,11 +149,13 @@ void RTCTransportProtocol::inactiveProducer() {
current_sync_win_ = INITIAL_WIN;
max_sync_win_ = INITIAL_WIN_MAX;
- TRANSPORT_LOGD("Current window: %u, max_sync_win_: %u", current_sync_win_,
- max_sync_win_);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Current window: " << current_sync_win_
+ << ", max_sync_win_: " << max_sync_win_;
// names/packets var
- next_segment_ = 0;
+ indexer_verifier_->reset();
+ indexer_verifier_->enableFec(fec_type_);
+ indexer_verifier_->setNFec(0);
ldr_->clear();
}
@@ -137,10 +175,13 @@ void RTCTransportProtocol::newRound() {
uint32_t received_bytes = state_->getReceivedBytesInRound();
uint32_t sent_interest = state_->getSentInterestInRound();
uint32_t lost_data = state_->getLostData();
+ uint32_t definitely_lost = state_->getDefinitelyLostPackets();
uint32_t recovered_losses = state_->getRecoveredLosses();
uint32_t received_nacks = state_->getReceivedNacksInRound();
+ uint32_t received_fec = state_->getReceivedFecPackets();
bool in_sync = (current_state_ == SyncState::in_sync);
+ ldr_->onNewRound(in_sync);
state_->onNewRound((double)ROUND_LEN, in_sync);
rc_->onNewRound((double)ROUND_LEN);
@@ -161,11 +202,13 @@ void RTCTransportProtocol::newRound() {
}
}
- TRANSPORT_LOGD("Calling updateSyncWindow in newRound function");
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Calling updateSyncWindow in newRound function";
updateSyncWindow();
sendStatsToApp(sent_retx, received_bytes, sent_interest, lost_data,
- recovered_losses, received_nacks);
+ definitely_lost, recovered_losses, received_nacks,
+ received_fec);
newRound();
});
}
@@ -173,6 +216,7 @@ void RTCTransportProtocol::newRound() {
void RTCTransportProtocol::discoveredRtt() {
start_send_interest_ = true;
ldr_->turnOnRTX();
+ ldr_->onNewRound(false);
updateSyncWindow();
}
@@ -182,22 +226,23 @@ void RTCTransportProtocol::computeMaxSyncWindow() {
if (production_rate == 0.0 || packet_size == 0.0) {
// the consumer has no info about the producer,
// keep the previous maxCWin
- TRANSPORT_LOGD(
- "Returning in computeMaxSyncWindow because: prod_rate: %d || "
- "packet_size: %d",
- (int)(production_rate == 0.0), (int)(packet_size == 0.0));
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Returning in computeMaxSyncWindow because: prod_rate: "
+ << (production_rate == 0.0)
+ << " || packet_size: " << (packet_size == 0.0);
return;
}
+ 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_ = (uint32_t)ceil(
+ (production_rate * lifetime_ms * INTEREST_LIFETIME_REDUCTION_FACTOR) /
+ packet_size);
max_sync_win_ = std::min(max_sync_win_, rc_->getCongesionWindow());
}
@@ -219,12 +264,25 @@ void RTCTransportProtocol::updateSyncWindow() {
// 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);
- current_sync_win_ += (uint32_t)
- ceil(prod_rate * (PRODUCER_BUFFER_MS / MILLI_IN_A_SEC) / packet_size);
+ double fec_interest_overhead = (double)state_->getPendingFecPackets() /
+ (double)(state_->getPendingInterestNumber() -
+ state_->getPendingFecPackets());
+
+ double fec_overhead =
+ std::max(indexer_verifier_->getFecOverhead(), fec_interest_overhead);
+
+ prod_rate += (prod_rate * fec_overhead);
- if(current_state_ == SyncState::catch_up) {
- current_sync_win_ = (uint32_t) (current_sync_win_ * CATCH_UP_WIN_INCREMENT);
+ current_sync_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size);
+ uint32_t buffer = PRODUCER_BUFFER_MS;
+ if (rtt > 150)
+ buffer = buffer * 2; // if the RTT is too large we increase the
+ // the size of the buffer
+ current_sync_win_ +=
+ ceil(prod_rate * (buffer / MILLI_IN_A_SEC) / packet_size);
+
+ if (current_state_ == SyncState::catch_up) {
+ current_sync_win_ = current_sync_win_ * CATCH_UP_WIN_INCREMENT;
}
current_sync_win_ = std::min(current_sync_win_, max_sync_win_);
@@ -243,70 +301,48 @@ void RTCTransportProtocol::decreaseSyncWindow() {
scheduleNextInterests();
}
-void RTCTransportProtocol::sendInterest(Name *interest_name) {
- TRANSPORT_LOGD("Sending interest for name %s",
- interest_name->toString().c_str());
-
- auto interest = core::PacketManager<>::getInstance().getPacket<Interest>();
- interest->setName(*interest_name);
-
- 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(!is_running_ && !is_first_)) {
- return;
- }
-
- portal_->sendInterest(std::move(interest));
-}
-
void RTCTransportProtocol::sendRtxInterest(uint32_t seq) {
- if (!is_running_ && !is_first_) return;
+ if (!isRunning() && !is_first_) return;
- if(!start_send_interest_) return;
+ if (!start_send_interest_) return;
Name *interest_name = nullptr;
socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
&interest_name);
- TRANSPORT_LOGD("send rtx %u", seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "send rtx " << seq;
interest_name->setSuffix(seq);
- sendInterest(interest_name);
+ sendInterest(*interest_name);
}
void RTCTransportProtocol::sendProbeInterest(uint32_t seq) {
- if (!is_running_ && !is_first_) return;
+ if (!isRunning() && !is_first_) return;
Name *interest_name = nullptr;
socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
&interest_name);
- TRANSPORT_LOGD("send probe %u", seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "send probe " << seq;
interest_name->setSuffix(seq);
- sendInterest(interest_name);
+ sendInterest(*interest_name);
}
void RTCTransportProtocol::scheduleNextInterests() {
- TRANSPORT_LOGD("Schedule next interests");
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Schedule next interests";
- if (!is_running_ && !is_first_) return;
+ if (!isRunning() && !is_first_) return;
- if(!start_send_interest_) return; // RTT discovering phase is not finished so
- // do not start to send interests
+ if (pacing_timer_on_) return; // wait pacing timer for the next send
- if (scheduler_timer_on_) return; // wait befor send other interests
+ if (!start_send_interest_)
+ return; // RTT discovering phase is not finished so
+ // do not start to send interests
if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) {
- TRANSPORT_LOGD("Inactive producer.");
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Inactive producer.";
// here we keep seding the same interest until the producer
// does not start again
- if (next_segment_ != 0) {
+ if (indexer_verifier_->checkNextSuffix() != 0) {
// the producer just become inactive, reset the state
inactiveProducer();
}
@@ -315,125 +351,208 @@ void RTCTransportProtocol::scheduleNextInterests() {
socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
&interest_name);
- TRANSPORT_LOGD("send interest %u", next_segment_);
- interest_name->setSuffix(next_segment_);
+ 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);
+ sendInterest(*interest_name);
state_->onSendNewInterest(interest_name);
return;
}
- TRANSPORT_LOGD("Pending interest number: %d -- current_sync_win_: %d",
- state_->getPendingInterestNumber(), current_sync_win_);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Pending interest number: " << state_->getPendingInterestNumber()
+ << " -- current_sync_win_: " << current_sync_win_;
+
+ uint32_t pending = state_->getPendingInterestNumber();
+ if (pending >= current_sync_win_) return; // no space in the window
+
+ if ((current_sync_win_ - pending) < max_aggregated_interest_) {
+ if (scheduler_timer_on_) return; // timer already scheduled
+
+ uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now().time_since_epoch())
+ .count();
+
+ uint64_t time = now - last_interest_sent_time_;
+ if (time < WAIT_FOR_INTEREST_BATCH) {
+ uint64_t next = WAIT_FOR_INTEREST_BATCH - time;
+ scheduler_timer_on_ = true;
+ scheduler_timer_->expires_from_now(std::chrono::milliseconds(next));
+ scheduler_timer_->async_wait([this](std::error_code ec) {
+ if (ec) return;
+ if (!scheduler_timer_on_) return;
+
+ scheduler_timer_on_ = false;
+ scheduleNextInterests();
+ });
+ return; // whait for the timer
+ }
+ }
+
+ scheduler_timer_on_ = false;
+ scheduler_timer_->cancel();
// skip nacked pacekts
- if (next_segment_ <= state_->getLastSeqNacked()) {
- next_segment_ = state_->getLastSeqNacked() + 1;
+ if (indexer_verifier_->checkNextSuffix() <= state_->getLastSeqNacked()) {
+ indexer_verifier_->jumpToIndex(state_->getLastSeqNacked() + 1);
}
// skipe received packets
- if (next_segment_ <= state_->getHighestSeqReceivedInOrder()) {
- next_segment_ = state_->getHighestSeqReceivedInOrder() + 1;
+ if (indexer_verifier_->checkNextSuffix() <=
+ state_->getHighestSeqReceivedInOrder()) {
+ indexer_verifier_->jumpToIndex(state_->getHighestSeqReceivedInOrder() + 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() < current_sync_win_) &&
- (sent_interests < MAX_INTERESTS_IN_BATCH)) {
- TRANSPORT_LOGD("In while loop. Window size: %u", current_sync_win_);
- Name *interest_name = nullptr;
- socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME,
- &interest_name);
+ (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();
- interest_name->setSuffix(next_segment_);
+ 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 lost
// 3) is not in the rtx list
- if (portal_->interestIsPending(*interest_name) ||
- state_->isReceivedOrLost(next_segment_) != PacketState::UNKNOWN ||
- ldr_->isRtx(next_segment_)) {
- TRANSPORT_LOGD(
- "skip interest %u because: pending %u, recv %u, rtx %u",
- next_segment_, (portal_->interestIsPending(*interest_name)),
- (state_->isReceivedOrLost(next_segment_) != PacketState::UNKNOWN),
- (ldr_->isRtx(next_segment_)));
- next_segment_ = (next_segment_ + 1) % MIN_PROBE_SEQ;
+ // 4) is fec and is not in order (!= last sent + 1)
+ if (portal_->interestIsPending(*name) ||
+ state_->isReceivedOrLost(next_seg) != PacketState::UNKNOWN ||
+ ldr_->isRtx(next_seg) ||
+ (indexer_verifier_->isFec(next_seg) &&
+ next_seg != last_interest_sent_seq_ + 1)) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "skip interest " << next_seg << " because: pending "
+ << portal_->interestIsPending(*name) << ", recv "
+ << (state_->isReceivedOrLost(next_seg) != PacketState::UNKNOWN)
+ << ", rtx " << (ldr_->isRtx(next_seg)) << ", is old fec "
+ << ((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;
+ }
- sent_interests++;
- TRANSPORT_LOGD("send interest %u", next_segment_);
- sendInterest(interest_name);
- state_->onSendNewInterest(interest_name);
+ 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_ =
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now().time_since_epoch())
+ .count();
+ aggregated_counter = 0;
+ }
+ }
- next_segment_ = (next_segment_ + 1) % MIN_PROBE_SEQ;
+ // exiting the while we may have some pending interest to send
+ if (aggregated_counter != 0) {
+ sent_packets++;
+ last_interest_sent_time_ =
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now().time_since_epoch())
+ .count();
+ sendInterest(interest_name, &additional_suffixes, aggregated_counter - 1);
}
if (state_->getPendingInterestNumber() < current_sync_win_) {
- // we still have space in the window but we already sent a batch of
- // MAX_INTERESTS_IN_BATCH interest. for the following ones wait one
- // WAIT_BETWEEN_INTEREST_BATCHES to avoid local packets drop
+ // we still have space in the window but we already sent too many packets
+ // wait PACING_WAIT to avoid drops in the kernel
- scheduler_timer_on_ = true;
- scheduler_timer_->expires_from_now(
- std::chrono::microseconds(WAIT_BETWEEN_INTEREST_BATCHES));
+ pacing_timer_on_ = true;
+ pacing_timer_->expires_from_now(std::chrono::microseconds(PACING_WAIT));
scheduler_timer_->async_wait([this](std::error_code ec) {
if (ec) return;
- if (!scheduler_timer_on_) return;
+ if (!pacing_timer_on_) return;
- scheduler_timer_on_ = false;
+ pacing_timer_on_ = false;
scheduleNextInterests();
});
}
}
-void RTCTransportProtocol::onTimeout(Interest::Ptr &&interest) {
- uint32_t segment_number = interest->getName().getSuffix();
-
- TRANSPORT_LOGD("timeout for packet %u", segment_number);
+void RTCTransportProtocol::onInterestTimeout(Interest::Ptr &interest,
+ const Name &name) {
+ uint32_t segment_number = name.getSuffix();
if (segment_number >= MIN_PROBE_SEQ) {
// this is a timeout on a probe, do nothing
return;
}
+ PacketState state = state_->isReceivedOrLost(segment_number);
+ if (state != PacketState::UNKNOWN) {
+ // 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_->getHighestSeqReceivedInOrder()) {
+ 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
- TRANSPORT_LOGD("handle timeout for packet %u using rtx", segment_number);
- ldr_->onTimeout(segment_number);
- state_->onTimeout(segment_number);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "handle timeout for packet " << segment_number << " using rtx";
+ if (ldr_->isRtxOn()) {
+ ldr_->onTimeout(segment_number);
+ if (indexer_verifier_->isFec(segment_number))
+ state_->onTimeout(segment_number, true);
+ else
+ state_->onTimeout(segment_number, false);
+ } else {
+ // in this case we wil never recover the timeout
+ state_->onTimeout(segment_number, true);
+ }
scheduleNextInterests();
return;
}
- TRANSPORT_LOGD("handle timeout for packet %u using normal interests",
- segment_number);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "handle timeout for packet " << segment_number
+ << " using normal interests";
- if (segment_number < next_segment_) {
+ 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
- TRANSPORT_LOGD("on timeout next seg = %u, jump to %u",
- next_segment_, segment_number);
- next_segment_ = segment_number;
+ 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
+ current_sync_win_++;
+ indexer_verifier_->jumpToIndex(segment_number);
}
- state_->onTimeout(segment_number);
+ state_->onTimeout(segment_number, false);
scheduleNextInterests();
}
@@ -446,8 +565,8 @@ void RTCTransportProtocol::onNack(const ContentObject &content_object) {
// check if the packet got a timeout
- TRANSPORT_LOGD("Nack received %u. Production segment: %u", nack_segment,
- production_seg);
+ 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);
@@ -459,14 +578,15 @@ void RTCTransportProtocol::onNack(const ContentObject &content_object) {
state_->onNackPacketReceived(content_object, compute_stats);
ldr_->onNackPacketReceived(content_object);
- // both in case of past and future nack we set next_segment_ equal to the
+ // 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
- TRANSPORT_LOGD("on nack next seg = %u, jump to %u",
- next_segment_, production_seg);
- next_segment_ = production_seg;
+ 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
@@ -496,30 +616,33 @@ void RTCTransportProtocol::onNack(const ContentObject &content_object) {
void RTCTransportProtocol::onProbe(const ContentObject &content_object) {
bool valid = state_->onProbePacketReceived(content_object);
- if(!valid) return;
+ if (!valid) return;
struct nack_packet_t *probe =
(struct nack_packet_t *)content_object.getPayload()->data();
uint32_t production_seg = probe->getProductionSegement();
- // as for the nacks set next_segment_
- TRANSPORT_LOGD("on probe next seg = %u, jump to %u",
- next_segment_, production_seg);
- next_segment_ = production_seg;
+ // as for the nacks set next_segment
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "on probe next seg = " << indexer_verifier_->checkNextSuffix()
+ << ", jump to " << production_seg;
+ indexer_verifier_->jumpToIndex(production_seg);
ldr_->onProbePacketReceived(content_object);
updateSyncWindow();
}
-void RTCTransportProtocol::onContentObject(Interest &interest,
- ContentObject &content_object) {
- TRANSPORT_LOGD("Received content object of size: %zu",
- content_object.payloadSize());
- uint32_t payload_size = (uint32_t) content_object.payloadSize();
+void RTCTransportProtocol::onContentObjectReceived(
+ Interest &interest, ContentObject &content_object, std::error_code &ec) {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received content object of size: " << content_object.payloadSize();
+ uint32_t payload_size = content_object.payloadSize();
uint32_t segment_number = content_object.getName().getSuffix();
+ ec = make_error_code(protocol_error::not_reassemblable);
+
if (segment_number >= MIN_PROBE_SEQ) {
- TRANSPORT_LOGD("Received probe %u", segment_number);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received probe " << segment_number;
if (*on_content_object_input_) {
(*on_content_object_input_)(*socket_->getInterface(), content_object);
}
@@ -528,7 +651,7 @@ void RTCTransportProtocol::onContentObject(Interest &interest,
}
if (payload_size == NACK_HEADER_SIZE) {
- TRANSPORT_LOGD("Received nack %u", segment_number);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received nack " << segment_number;
if (*on_content_object_input_) {
(*on_content_object_input_)(*socket_->getInterface(), content_object);
}
@@ -536,9 +659,8 @@ void RTCTransportProtocol::onContentObject(Interest &interest,
return;
}
- TRANSPORT_LOGD("Received content %u", segment_number);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "Received content " << segment_number;
- rc_->onDataPacketReceived(content_object);
bool compute_stats = true;
auto tn_it = timeouts_or_nacks_.find(segment_number);
if (tn_it != timeouts_or_nacks_.end()) {
@@ -551,25 +673,49 @@ void RTCTransportProtocol::onContentObject(Interest &interest,
// check if the packet was already received
PacketState state = state_->isReceivedOrLost(segment_number);
- state_->onDataPacketReceived(content_object, compute_stats);
- ldr_->onDataPacketReceived(content_object);
- // if the stat for this seq number is received do not send the packet to app
if (state != PacketState::RECEIVED) {
- if (*on_content_object_input_) {
- (*on_content_object_input_)(*socket_->getInterface(), content_object);
+ // send packet to decoder
+ if (fec_decoder_) {
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "send packet " << segment_number << " to FEC decoder";
+ fec_decoder_->onDataPacket(
+ content_object, content_object.headerSize() + rtc::DATA_HEADER_SIZE);
+ }
+ if (!indexer_verifier_->isFec(segment_number)) {
+ // the packet may be alredy sent to the ap by the decoder, check again if
+ // it is already received
+ state = state_->isReceivedOrLost(segment_number);
+ if (state != PacketState::RECEIVED) {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received content " << segment_number;
+
+ state_->onDataPacketReceived(content_object, compute_stats);
+
+ if (*on_content_object_input_) {
+ (*on_content_object_input_)(*socket_->getInterface(), content_object);
+ }
+ ec = make_error_code(protocol_error::success);
+ }
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "Received fec " << segment_number;
+ state_->onFecPacketReceived(content_object);
}
- reassemble(content_object);
} else {
- TRANSPORT_LOGD("Received duplicated content %u, drop it", segment_number);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Received duplicated content " << segment_number << ", drop it";
+ ec = make_error_code(protocol_error::duplicated_content);
}
+ ldr_->onDataPacketReceived(content_object);
+ rc_->onDataPacketReceived(content_object);
+
updateSyncWindow();
}
void RTCTransportProtocol::sendStatsToApp(
uint32_t retx_count, uint32_t received_bytes, uint32_t sent_interests,
- uint32_t lost_data, uint32_t recovered_losses, uint32_t received_nacks) {
+ 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());
@@ -581,23 +727,35 @@ void RTCTransportProtocol::sendStatsToApp(
stats_->updateBytesRecv(received_bytes);
stats_->updateInterestTx(sent_interests);
stats_->updateReceivedNacks(received_nacks);
+ stats_->updateReceivedFEC(received_fec);
stats_->updateAverageWindowSize(current_sync_win_);
stats_->updateLossRatio(state_->getLossRate());
stats_->updateAverageRtt(state_->getRTT());
+ 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_);
}
}
-void RTCTransportProtocol::reassemble(ContentObject &content_object) {
- auto read_buffer = content_object.getPayload();
- TRANSPORT_LOGD("Size of payload: %zu", read_buffer->length());
- read_buffer->trimStart(DATA_HEADER_SIZE);
- Reassembly::read_buffer_ = std::move(read_buffer);
- Reassembly::notifyApplication();
+void RTCTransportProtocol::onFecPackets(
+ std::vector<std::pair<uint32_t, fec::buffer>> &packets) {
+ for (auto &packet : packets) {
+ PacketState state = state_->isReceivedOrLost(packet.first);
+ if (state != PacketState::RECEIVED) {
+ state_->onPacketRecoveredFec(packet.first);
+ ldr_->onPacketRecoveredFec(packet.first);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Recovered packet " << packet.first << " through FEC.";
+ reassembly_->reassemble(*packet.second, packet.first);
+ } else {
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Packet" << packet.first << "already received.";
+ }
+ }
}
} // end namespace rtc
diff --git a/libtransport/src/protocols/rtc/rtc.h b/libtransport/src/protocols/rtc/rtc.h
index 596887067..e6431264d 100644
--- a/libtransport/src/protocols/rtc/rtc.h
+++ b/libtransport/src/protocols/rtc/rtc.h
@@ -30,8 +30,7 @@ namespace protocol {
namespace rtc {
-class RTCTransportProtocol : public TransportProtocol,
- public DatagramReassembly {
+class RTCTransportProtocol : public TransportProtocol {
public:
RTCTransportProtocol(implementation::ConsumerSocket *icnet_socket);
@@ -43,6 +42,8 @@ class RTCTransportProtocol : public TransportProtocol,
void resume() override;
+ std::size_t transportHeaderLength() override;
+
private:
enum class SyncState { catch_up = 0, in_sync = 1, last };
@@ -63,24 +64,28 @@ class RTCTransportProtocol : public TransportProtocol,
void decreaseSyncWindow();
// packet functions
- void sendInterest(Name *interest_name);
void sendRtxInterest(uint32_t seq);
void sendProbeInterest(uint32_t seq);
void scheduleNextInterests() override;
- void onTimeout(Interest::Ptr &&interest) override;
+ void onInterestTimeout(Interest::Ptr &interest, const Name &name) override;
void onNack(const ContentObject &content_object);
void onProbe(const ContentObject &content_object);
- void reassemble(ContentObject &content_object) override;
- void onContentObject(Interest &interest,
- ContentObject &content_object) override;
- void onPacketDropped(Interest &interest,
- ContentObject &content_object) override {}
+ 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 {}
// interaction with app functions
void sendStatsToApp(uint32_t retx_count, uint32_t received_bytes,
uint32_t sent_interests, uint32_t lost_data,
- uint32_t recovered_losses, uint32_t received_nacks);
+ uint32_t definitely_lost, uint32_t recovered_losses,
+ uint32_t received_nacks, uint32_t received_fec);
+
+ // FEC functions
+ void onFecPackets(std::vector<std::pair<uint32_t, fec::buffer>> &packets);
+
// protocol state
bool start_send_interest_;
SyncState current_state_;
@@ -88,17 +93,30 @@ class RTCTransportProtocol : public TransportProtocol,
uint32_t current_sync_win_;
uint32_t max_sync_win_;
- // controller var
+ // 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_;
- // names/packets var
- uint32_t next_segment_;
-
std::shared_ptr<RTCState> state_;
std::shared_ptr<RTCRateControl> rc_;
std::shared_ptr<RTCLossDetectionAndRecovery> ldr_;
diff --git a/libtransport/src/protocols/rtc/rtc_consts.h b/libtransport/src/protocols/rtc/rtc_consts.h
index e172fc7a1..d04bc1b1f 100644
--- a/libtransport/src/protocols/rtc/rtc_consts.h
+++ b/libtransport/src/protocols/rtc/rtc_consts.h
@@ -37,12 +37,26 @@ const double INTEREST_LIFETIME_REDUCTION_FACTOR = 0.8;
const uint32_t PRODUCER_BUFFER_MS = 200; // 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;
+// 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 HICN_HEADER_SIZE = 40 + 20; // IPv6 + TCP bytes
-const uint32_t RTC_INTEREST_LIFETIME = 1000;
+const uint32_t RTC_INTEREST_LIFETIME = 2000;
// probes sequence range
const uint32_t MIN_PROBE_SEQ = 0xefffffff;
@@ -51,19 +65,18 @@ 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
+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
+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 = 30; // ms
+const uint32_t INIT_RTT_PROBE_WAIT = 30; // ms
// we reuires at least 5 probes to be recevied
-const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; //ms
+const uint32_t INIT_RTT_MIN_PROBES_TO_RECV = 5; // ms
const uint32_t MAX_PENDING_PROBES = 10;
-
// congestion
const double MAX_QUEUING_DELAY = 100.0; // ms
@@ -97,7 +110,7 @@ const double MAX_CACHED_PACKETS = 262144; // 2^18
// about 50 sec of traffic at 50Mbps
// with 1200 bytes packets
-const uint32_t MAX_ROUND_WHIOUT_PACKETS = (const uint32_t)
+const uint32_t MAX_ROUND_WHIOUT_PACKETS =
(20 * MILLI_IN_A_SEC) / ROUND_LEN; // 20 sec in rounds;
// used in ldr
diff --git a/libtransport/src/protocols/rtc/rtc_data_path.cc b/libtransport/src/protocols/rtc/rtc_data_path.cc
index a545225cb..c098088a3 100644
--- a/libtransport/src/protocols/rtc/rtc_data_path.cc
+++ b/libtransport/src/protocols/rtc/rtc_data_path.cc
@@ -69,7 +69,7 @@ void RTCDataPath::insertOwdSample(int64_t owd) {
if (avg_owd != DBL_MAX)
avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC);
else {
- avg_owd = (double)owd;
+ avg_owd = owd;
}
int64_t queueVal = owd - std::min(getMinOwd(), min_owd);
@@ -77,7 +77,7 @@ void RTCDataPath::insertOwdSample(int64_t owd) {
if (queuing_delay != DBL_MAX)
queuing_delay = (queuing_delay * (1 - ALPHA_RTC)) + (queueVal * ALPHA_RTC);
else {
- queuing_delay = (double)queueVal;
+ queuing_delay = queueVal;
}
// keep track of the jitter computed as for RTP (RFC 3550)
@@ -100,7 +100,7 @@ void RTCDataPath::computeInterArrivalGap(uint32_t segment_number) {
largest_recv_seq_ = segment_number;
largest_recv_seq_time_ = now;
if (avg_inter_arrival_ == DBL_MAX)
- avg_inter_arrival_ = (double)delta;
+ avg_inter_arrival_ = delta;
else
avg_inter_arrival_ =
(avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC);
diff --git a/libtransport/src/protocols/rtc/rtc_indexer.h b/libtransport/src/protocols/rtc/rtc_indexer.h
new file mode 100644
index 000000000..4aee242bb
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_indexer.h
@@ -0,0 +1,195 @@
+/*
+ * 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 <protocols/errors.h>
+#include <protocols/fec_utils.h>
+#include <protocols/indexer.h>
+#include <protocols/rtc/rtc_consts.h>
+#include <protocols/transport_protocol.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(std::forward<Indexer>(other)) {}
+
+ ~RtcIndexer() {}
+
+ void reset() override {
+ next_suffix_ = first_suffix_;
+ n_fec_ = 0;
+ }
+
+ uint32_t checkNextSuffix() 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() 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 {
+ setVerifier();
+ auto ret = verifier_->verifyPackets(&content_object);
+
+ switch (ret) {
+ case auth::VerificationPolicy::ACCEPT: {
+ if (reassembly) {
+ reassembly_->reassemble(content_object);
+ }
+ break;
+ }
+
+ case auth::VerificationPolicy::UNKNOWN:
+ case auth::VerificationPolicy::DROP: {
+ transport_->onPacketDropped(
+ interest, content_object,
+ make_error_code(protocol_error::verification_failed));
+ break;
+ }
+
+ case auth::VerificationPolicy::ABORT: {
+ transport_->onContentReassembled(
+ make_error_code(protocol_error::session_aborted));
+ break;
+ }
+ }
+ }
+
+ /**
+ * 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() 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() override { return n_fec_; }
+
+ bool isFec(uint32_t index) override {
+ return isFec(fec_type_, index, first_suffix_);
+ }
+
+ double getFecOverhead() override {
+ if (fec_type_ == fec::FECType::UNKNOWN) {
+ return 0;
+ }
+
+ double k = (double)fec::FECUtils::getSourceSymbols(fec_type_);
+ return (double)n_fec_ / k;
+ }
+
+ double getMaxFecOverhead() 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
index 0ef381fe1..f0de48871 100644
--- a/libtransport/src/protocols/rtc/rtc_ldr.cc
+++ b/libtransport/src/protocols/rtc/rtc_ldr.cc
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+#include <glog/logging.h>
#include <protocols/rtc/rtc_consts.h>
#include <protocols/rtc/rtc_ldr.h>
@@ -26,11 +27,13 @@ namespace protocol {
namespace rtc {
RTCLossDetectionAndRecovery::RTCLossDetectionAndRecovery(
- SendRtxCallback &&callback, asio::io_service &io_service)
+ Indexer *indexer, SendRtxCallback &&callback, asio::io_service &io_service)
: rtx_on_(false),
+ fec_on_(false),
next_rtx_timer_(MAX_TIMER_RTX),
last_event_(0),
sentinel_timer_interval_(MAX_TIMER_RTX),
+ indexer_(indexer),
send_rtx_callback_(std::move(callback)) {
timer_ = std::make_unique<asio::steady_timer>(io_service);
sentinel_timer_ = std::make_unique<asio::steady_timer>(io_service);
@@ -40,7 +43,7 @@ RTCLossDetectionAndRecovery::~RTCLossDetectionAndRecovery() {}
void RTCLossDetectionAndRecovery::turnOnRTX() {
rtx_on_ = true;
- scheduleSentinelTimer((uint32_t)(state_->getRTT() * CATCH_UP_RTT_INCREMENT));
+ scheduleSentinelTimer(state_->getRTT() * CATCH_UP_RTT_INCREMENT);
}
void RTCLossDetectionAndRecovery::turnOffRTX() {
@@ -48,6 +51,54 @@ void RTCLossDetectionAndRecovery::turnOffRTX() {
clear();
}
+uint32_t RTCLossDetectionAndRecovery::computeFecPacketsToAsk(bool in_sync) {
+ uint32_t current_fec = indexer_->getNFec();
+ double current_loss_rate = state_->getLossRate();
+ double last_loss_rate = state_->getLastRoundLossRate();
+
+ // when in sync ask for fec only if there are losses for 2 rounds
+ if (in_sync && current_fec == 0 &&
+ (current_loss_rate == 0 || last_loss_rate == 0))
+ return 0;
+
+ double loss_rate = state_->getMaxLossRate() * 1.5;
+
+ if (!in_sync && loss_rate == 0) loss_rate = 0.05;
+ if (loss_rate > 0.5) loss_rate = 0.5;
+
+ double exp_losses = (double)k_ * loss_rate;
+ uint32_t fec_to_ask = ceil(exp_losses / (1 - loss_rate));
+
+ if (fec_to_ask > (n_ - k_)) fec_to_ask = n_ - k_;
+
+ return fec_to_ask;
+}
+
+void RTCLossDetectionAndRecovery::onNewRound(bool in_sync) {
+ uint64_t rtt = state_->getRTT();
+ if (!fec_on_ && rtt >= 100) {
+ // turn on fec, here we may have no info so ask for all packets
+ fec_on_ = true;
+ turnOffRTX();
+ indexer_->setNFec(computeFecPacketsToAsk(in_sync));
+ return;
+ }
+
+ if (fec_on_ && rtt > 80) {
+ // keep using fec, maybe update it
+ indexer_->setNFec(computeFecPacketsToAsk(in_sync));
+ return;
+ }
+
+ if ((fec_on_ && rtt <= 80) || (!rtx_on_ && rtt <= 100)) {
+ // turn on rtx
+ fec_on_ = false;
+ indexer_->setNFec(0);
+ turnOnRTX();
+ return;
+ }
+}
+
void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq) {
// always add timeouts to the RTX list to avoid to send the same packet as if
// it was not a rtx
@@ -55,17 +106,23 @@ void RTCLossDetectionAndRecovery::onTimeout(uint32_t seq) {
last_event_ = getNow();
}
+void RTCLossDetectionAndRecovery::onPacketRecoveredFec(uint32_t seq) {
+ // if an RTX is scheduled for a packet recovered using FEC delete it
+ deleteRtx(seq);
+ recover_with_fec_.erase(seq);
+}
+
void RTCLossDetectionAndRecovery::onDataPacketReceived(
const core::ContentObject &content_object) {
last_event_ = getNow();
uint32_t seq = content_object.getName().getSuffix();
if (deleteRtx(seq)) {
- state_->onPacketRecovered(seq);
+ state_->onPacketRecoveredRtx(seq);
} else {
- if (TRANSPORT_EXPECT_FALSE(!rtx_on_)) return; // do not add if RTX is off
- TRANSPORT_LOGD("received data. add from %u to %u ",
- state_->getHighestSeqReceivedInOrder() + 1, seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "received data. add from "
+ << state_->getHighestSeqReceivedInOrder() + 1 << " to " << seq;
addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1, seq);
}
}
@@ -76,8 +133,6 @@ void RTCLossDetectionAndRecovery::onNackPacketReceived(
uint32_t seq = nack.getName().getSuffix();
- if (TRANSPORT_EXPECT_FALSE(!rtx_on_)) return; // do not add if RTX is off
-
struct nack_packet_t *nack_pkt =
(struct nack_packet_t *)nack.getPayload()->data();
uint32_t production_seq = nack_pkt->getProductionSegement();
@@ -91,8 +146,9 @@ void RTCLossDetectionAndRecovery::onNackPacketReceived(
// productionSeq = 14. 9 is lost but we can try to recover packets 12 13 and
// 14 that are not arrived yet
deleteRtx(seq);
- TRANSPORT_LOGD("received past nack. add from %u to %u ",
- state_->getHighestSeqReceivedInOrder() + 1, production_seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "received past nack. add from "
+ << state_->getHighestSeqReceivedInOrder() + 1
+ << " to " << production_seq;
addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1,
production_seq);
} else {
@@ -105,8 +161,9 @@ void RTCLossDetectionAndRecovery::onNackPacketReceived(
// with productionSeq = 18. this says that all the packets between 12 and 18
// may got lost and we should ask them
deleteRtx(seq);
- TRANSPORT_LOGD("received futrue nack. add from %u to %u ",
- state_->getHighestSeqReceivedInOrder() + 1, production_seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "received futrue nack. add from "
+ << state_->getHighestSeqReceivedInOrder() + 1
+ << " to " << production_seq;
addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1,
production_seq);
}
@@ -117,12 +174,13 @@ void RTCLossDetectionAndRecovery::onProbePacketReceived(
// we don't log the reception of a probe packet for the sentinel timer because
// probes are not taken into account into the sync window. we use them as
// future nacks to detect possible packets lost
- if (TRANSPORT_EXPECT_FALSE(!rtx_on_)) return; // do not add if RTX is off
struct nack_packet_t *probe_pkt =
(struct nack_packet_t *)probe.getPayload()->data();
uint32_t production_seq = probe_pkt->getProductionSegement();
- TRANSPORT_LOGD("received probe. add from %u to %u ",
- state_->getHighestSeqReceivedInOrder() + 1, production_seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "received probe. add from "
+ << state_->getHighestSeqReceivedInOrder() + 1 << " to " << production_seq;
+
addToRetransmissions(state_->getHighestSeqReceivedInOrder() + 1,
production_seq);
}
@@ -150,20 +208,41 @@ void RTCLossDetectionAndRecovery::addToRetransmissions(uint32_t start,
}
for (uint32_t seq = start; seq < stop; seq++) {
- if (!isRtx(seq) && // is not already an rtx
- // is not received or lost
- state_->isReceivedOrLost(seq) == PacketState::UNKNOWN) {
- // add rtx
- rtxState state;
- state.first_send_ = state_->getInterestSentTime(seq);
- if (state.first_send_ == 0) // this interest was never sent before
- state.first_send_ = getNow();
- state.next_send_ = computeNextSend(seq, true);
- state.rtx_count_ = 0;
- TRANSPORT_LOGD("add %u to retransmissions. next rtx is %lu ", seq,
- (state.next_send_ - getNow()));
- rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state));
- rtx_timers_.insert(std::pair<uint64_t, uint32_t>(state.next_send_, seq));
+ if (state_->isReceivedOrLost(seq) == PacketState::UNKNOWN) {
+ if (rtx_on_) {
+ if (!indexer_->isFec(seq)) {
+ // handle it with rtx
+ if (!isRtx(seq)) {
+ state_->onLossDetected(seq);
+ rtxState state;
+ state.first_send_ = state_->getInterestSentTime(seq);
+ if (state.first_send_ == 0) // this interest was never sent before
+ state.first_send_ = getNow();
+ state.next_send_ = computeNextSend(seq, true);
+ state.rtx_count_ = 0;
+ DLOG_IF(INFO, VLOG_IS_ON(4))
+ << "Add " << seq << " to retransmissions. next rtx is %lu "
+ << state.next_send_ - getNow();
+ rtx_state_.insert(std::pair<uint32_t, rtxState>(seq, state));
+ rtx_timers_.insert(
+ std::pair<uint64_t, uint32_t>(state.next_send_, seq));
+ }
+ } else {
+ // is fec, do not send it
+ auto it = recover_with_fec_.find(seq);
+ if (it == recover_with_fec_.end()) {
+ state_->onLossDetected(seq);
+ recover_with_fec_.insert(seq);
+ }
+ }
+ } else {
+ // keep track of losses but recover with FEC
+ auto it = recover_with_fec_.find(seq);
+ if (it == recover_with_fec_.end()) {
+ state_->onLossDetected(seq);
+ recover_with_fec_.insert(seq);
+ }
+ }
}
}
scheduleNextRtx();
@@ -182,13 +261,15 @@ uint64_t RTCLossDetectionAndRecovery::computeNextSend(uint32_t seq,
if (prod_rate != 0) {
double packet_size = state_->getAveragePacketSize();
- estimated_iat = (uint32_t)ceil(1000.0 / (prod_rate / packet_size));
- jitter = (uint32_t)ceil(state_->getJitter());
+ estimated_iat = ceil(1000.0 / (prod_rate / packet_size));
+ jitter = ceil(state_->getJitter());
}
uint32_t wait = estimated_iat + jitter;
- TRANSPORT_LOGD("first rtx for %u in %u ms, rtt = %lu ait = %u jttr = %u",
- seq, wait, state_->getRTT(), estimated_iat, jitter);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "first rtx for " << seq << " in " << wait
+ << " ms, rtt = " << state_->getRTT() << " ait = " << estimated_iat
+ << " jttr = " << jitter;
return now + wait;
} else {
@@ -202,25 +283,26 @@ uint64_t RTCLossDetectionAndRecovery::computeNextSend(uint32_t seq,
}
double packet_size = state_->getAveragePacketSize();
- uint32_t estimated_iat = (uint32_t)ceil(1000.0 / (prod_rate / packet_size));
+ uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size));
uint64_t rtt = state_->getRTT();
if (rtt == 0) rtt = SENTINEL_TIMER_INTERVAL;
- wait = (uint32_t)rtt;
+ wait = rtt;
if (estimated_iat > rtt) wait = estimated_iat;
- uint32_t jitter = (uint32_t)ceil(state_->getJitter());
+ uint32_t jitter = ceil(state_->getJitter());
wait += jitter;
// it may happen that the channel is congested and we have some additional
// queuing delay to take into account
- uint32_t queue = (uint32_t)ceil(state_->getQueuing());
+ uint32_t queue = ceil(state_->getQueuing());
wait += queue;
- TRANSPORT_LOGD(
- "next rtx for %u in %u ms, rtt = %lu ait = %u jttr = %u queue = %u",
- seq, wait, state_->getRTT(), estimated_iat, jitter, queue);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "next rtx for " << seq << " in " << wait
+ << " ms, rtt = " << state_->getRTT() << " ait = " << estimated_iat
+ << " jttr = " << jitter << " queue = " << queue;
return now + wait;
}
@@ -235,7 +317,7 @@ void RTCLossDetectionAndRecovery::retransmit() {
std::unordered_set<uint32_t> lost_pkt;
uint32_t sent_counter = 0;
while (it != rtx_timers_.end() && it->first <= now &&
- sent_counter < MAX_INTERESTS_IN_BATCH) {
+ 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
@@ -243,11 +325,11 @@ void RTCLossDetectionAndRecovery::retransmit() {
(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
- TRANSPORT_LOGD(
- "packet %u lost because 1) max rtx: %u 2) max age: %u 3) naked: %u",
- seq, (rtx_it->second.rtx_count_ >= RTC_MAX_RTX),
- ((now - rtx_it->second.first_send_) >= RTC_MAX_AGE),
- (seq < state_->getLastSeqNacked()));
+ 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 {
@@ -259,8 +341,9 @@ void RTCLossDetectionAndRecovery::retransmit() {
it = rtx_timers_.erase(it);
rtx_timers_.insert(
std::pair<uint64_t, uint32_t>(rtx_it->second.next_send_, seq));
- TRANSPORT_LOGD("send rtx for sequence %u, next send in %lu", seq,
- (rtx_it->second.next_send_ - now));
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "send rtx for sequence " << seq << ", next send in "
+ << (rtx_it->second.next_send_ - now);
send_rtx_callback_(seq);
sent_counter++;
}
@@ -358,20 +441,21 @@ void RTCLossDetectionAndRecovery::sentinelTimer() {
if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive())) {
// this happens at the beginning (or if the producer stops for some
// reason) we need to keep sending interest 0 until we get an answer
- TRANSPORT_LOGD(
- "sentinel timer: the producer is not active, send packet 0");
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "sentinel timer: the producer is not active, send packet 0";
state_->onRetransmission(0);
send_rtx_callback_(0);
} else {
- TRANSPORT_LOGD(
- "sentinel timer: the producer is active, send the 10 oldest packets");
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "sentinel timer: the producer is active, "
+ "send the 10 oldest packets";
sent = true;
uint32_t rtx = 0;
auto it = state_->getPendingInterestsMapBegin();
auto end = state_->getPendingInterestsMapEnd();
while (it != end && rtx < MAX_RTX_WITH_SENTINEL) {
uint32_t seq = it->first;
- TRANSPORT_LOGD("sentinel timer, add %u to the rtx list", seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "sentinel timer, add " << seq << " to the rtx list";
addToRetransmissions(seq, seq + 1);
rtx++;
it++;
@@ -384,36 +468,38 @@ void RTCLossDetectionAndRecovery::sentinelTimer() {
uint32_t next_timer;
double prod_rate = state_->getProducerRate();
if (TRANSPORT_EXPECT_FALSE(!state_->isProducerActive()) || prod_rate == 0) {
- TRANSPORT_LOGD("next timer in %u", SENTINEL_TIMER_INTERVAL);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "next timer in " << SENTINEL_TIMER_INTERVAL;
next_timer = SENTINEL_TIMER_INTERVAL;
} else {
double prod_rate = state_->getProducerRate();
double packet_size = state_->getAveragePacketSize();
- uint32_t estimated_iat = (uint32_t)ceil(1000.0 / (prod_rate / packet_size));
- uint32_t jitter = (uint32_t)ceil(state_->getJitter());
+ uint32_t estimated_iat = ceil(1000.0 / (prod_rate / packet_size));
+ uint32_t jitter = ceil(state_->getJitter());
// try to reduce the number of timers if the estimated IAT is too small
next_timer = std::max((estimated_iat + jitter) * 20, (uint32_t)1);
- TRANSPORT_LOGD("next sentinel in %u ms, rate: %f, iat: %u, jitter: %u",
- next_timer, ((prod_rate * 8.0) / 1000000.0), estimated_iat,
- jitter);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "next sentinel in " << next_timer
+ << " ms, rate: " << ((prod_rate * 8.0) / 1000000.0)
+ << ", iat: " << estimated_iat << ", jitter: " << jitter;
if (!expired) {
// discount the amout of time that is already passed
- uint32_t discount = (uint32_t)(now - last_event_);
+ uint32_t discount = now - last_event_;
if (next_timer > discount) {
next_timer = next_timer - discount;
} else {
// in this case we trigger the timer in 1 ms
next_timer = 1;
}
- TRANSPORT_LOGD("timer after discout: %u", next_timer);
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "timer after discout: " << next_timer;
} else if (sent) {
// wait at least one producer stats interval + owd to check if the
// production rate is reducing.
- uint32_t min_wait = PRODUCER_STATS_INTERVAL + (uint32_t)ceil(state_->getQueuing());
+ uint32_t min_wait = PRODUCER_STATS_INTERVAL + ceil(state_->getQueuing());
next_timer = std::max(next_timer, min_wait);
- TRANSPORT_LOGD("wait for updates from prod, next timer: %u", next_timer);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "wait for updates from prod, next timer: " << next_timer;
}
}
diff --git a/libtransport/src/protocols/rtc/rtc_ldr.h b/libtransport/src/protocols/rtc/rtc_ldr.h
index c0912303b..1b9f9afd6 100644
--- a/libtransport/src/protocols/rtc/rtc_ldr.h
+++ b/libtransport/src/protocols/rtc/rtc_ldr.h
@@ -15,15 +15,16 @@
#pragma once
#include <hicn/transport/config.h>
+#include <hicn/transport/core/asio_wrapper.h>
#include <hicn/transport/core/content_object.h>
#include <hicn/transport/core/name.h>
+#include <protocols/indexer.h>
#include <protocols/rtc/rtc_consts.h>
#include <protocols/rtc/rtc_state.h>
-#include <asio.hpp>
-#include <asio/steady_timer.hpp>
#include <functional>
#include <map>
+#include <unordered_map>
namespace transport {
@@ -43,16 +44,23 @@ class RTCLossDetectionAndRecovery
using SendRtxCallback = std::function<void(uint32_t)>;
public:
- RTCLossDetectionAndRecovery(SendRtxCallback &&callback,
+ RTCLossDetectionAndRecovery(Indexer *indexer, SendRtxCallback &&callback,
asio::io_service &io_service);
~RTCLossDetectionAndRecovery();
void setState(std::shared_ptr<RTCState> state) { state_ = state; }
+ void setFecParams(uint32_t n, uint32_t k) {
+ n_ = n;
+ k_ = k;
+ }
void turnOnRTX();
void turnOffRTX();
+ bool isRtxOn() { return rtx_on_; }
+ void onNewRound(bool in_sync);
void onTimeout(uint32_t seq);
+ void onPacketRecoveredFec(uint32_t seq);
void onDataPacketReceived(const core::ContentObject &content_object);
void onNackPacketReceived(const core::ContentObject &nack);
void onProbePacketReceived(const core::ContentObject &probe);
@@ -72,6 +80,7 @@ class RTCLossDetectionAndRecovery
bool deleteRtx(uint32_t seq);
void scheduleSentinelTimer(uint64_t expires_from_now);
void sentinelTimer();
+ uint32_t computeFecPacketsToAsk(bool in_sync);
uint64_t getNow() {
using namespace std::chrono;
@@ -90,14 +99,25 @@ class RTCLossDetectionAndRecovery
// should be sent, and the val is the interest seq number
std::multimap<uint64_t, uint32_t> rtx_timers_;
+ // lost packets that will be recovered with fec
+ std::unordered_set<uint32_t> recover_with_fec_;
+
bool rtx_on_;
+ bool fec_on_;
uint64_t next_rtx_timer_;
uint64_t last_event_;
uint64_t sentinel_timer_interval_;
+
+ // fec params
+ uint32_t n_;
+ uint32_t k_;
+
std::unique_ptr<asio::steady_timer> timer_;
std::unique_ptr<asio::steady_timer> sentinel_timer_;
std::shared_ptr<RTCState> state_;
+ Indexer *indexer_;
+
SendRtxCallback send_rtx_callback_;
};
diff --git a/libtransport/src/protocols/rtc/rtc_packet.h b/libtransport/src/protocols/rtc/rtc_packet.h
index 2f2b19fb9..7dc2f82c3 100644
--- a/libtransport/src/protocols/rtc/rtc_packet.h
+++ b/libtransport/src/protocols/rtc/rtc_packet.h
@@ -90,4 +90,4 @@ struct nack_packet_t {
} // end namespace protocol
-} // end namespace transport \ No newline at end of file
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_frame.cc b/libtransport/src/protocols/rtc/rtc_rc_frame.cc
deleted file mode 100644
index b577b5bea..000000000
--- a/libtransport/src/protocols/rtc/rtc_rc_frame.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2017-2021 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <protocols/rtc/rtc_consts.h>
-#include <protocols/rtc/rtc_rc_frame.h>
-
-#include <algorithm>
-
-namespace transport {
-
-namespace protocol {
-
-namespace rtc {
-
-RTCRateControlFrame::RTCRateControlFrame() : cc_detector_() {}
-
-RTCRateControlFrame::~RTCRateControlFrame() {}
-
-void RTCRateControlFrame::onNewRound(double round_len) {
- if (!rc_on_) return;
-
- CongestionState prev_congestion_state = congestion_state_;
- cc_detector_.updateStats();
- congestion_state_ = (CongestionState)cc_detector_.getState();
-
- if (congestion_state_ == CongestionState::Congested) {
- if (prev_congestion_state == CongestionState::Normal) {
- // congestion detected, notify app and init congestion win
- double prod_rate = protocol_state_->getReceivedRate();
- double rtt = (double)protocol_state_->getRTT() / MILLI_IN_A_SEC;
- double packet_size = protocol_state_->getAveragePacketSize();
-
- if (prod_rate == 0.0 || rtt == 0.0 || packet_size == 0.0) {
- // TODO do something
- return;
- }
-
- congestion_win_ = (uint32_t)ceil(prod_rate * rtt / packet_size);
- }
- uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR;
- congestion_win_ = std::max(win, WIN_MIN);
- return;
- }
-}
-
-void RTCRateControlFrame::onDataPacketReceived(
- const core::ContentObject &content_object) {
- if (!rc_on_) return;
-
- uint32_t seq = content_object.getName().getSuffix();
- if (!protocol_state_->isPending(seq)) return;
-
- cc_detector_.addPacket(content_object);
-}
-
-void RTCRateControlFrame::receivedBwProbeTrain(uint64_t firts_probe_ts,
- uint64_t last_probe_ts,
- uint32_t total_probes) {
- // TODO
- return;
-}
-
-} // end namespace rtc
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_frame.h b/libtransport/src/protocols/rtc/rtc_rc_frame.h
deleted file mode 100644
index 25d5ddbb6..000000000
--- a/libtransport/src/protocols/rtc/rtc_rc_frame.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2017-2021 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <protocols/rtc/congestion_detection.h>
-#include <protocols/rtc/rtc_rc.h>
-
-namespace transport {
-
-namespace protocol {
-
-namespace rtc {
-
-class RTCRateControlFrame : public RTCRateControl {
- public:
- RTCRateControlFrame();
-
- ~RTCRateControlFrame();
-
- void onNewRound(double round_len);
- void onDataPacketReceived(const core::ContentObject &content_object);
-
- void receivedBwProbeTrain(uint64_t firts_probe_ts, uint64_t last_probe_ts,
- uint32_t total_probes);
-
- private:
- CongestionDetection cc_detector_;
-};
-
-} // end namespace rtc
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_rc_queue.cc b/libtransport/src/protocols/rtc/rtc_rc_queue.cc
index 3c7318dae..a1c89e329 100644
--- a/libtransport/src/protocols/rtc/rtc_rc_queue.cc
+++ b/libtransport/src/protocols/rtc/rtc_rc_queue.cc
@@ -67,11 +67,11 @@ void RTCRateControlQueue::onNewRound(double round_len) {
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_ = (uint32_t)ROUNDS_BEFORE_TAKE_ACTION + 1;
+ rounds_since_last_drop_ = ROUNDS_BEFORE_TAKE_ACTION + 1;
}
if (rounds_since_last_drop_ >= ROUNDS_BEFORE_TAKE_ACTION) {
- uint32_t win = congestion_win_ * (uint32_t)WIN_DECREASE_FACTOR;
+ uint32_t win = congestion_win_ * WIN_DECREASE_FACTOR;
congestion_win_ = std::max(win, WIN_MIN);
rounds_since_last_drop_ = 0;
return;
@@ -88,7 +88,7 @@ void RTCRateControlQueue::onNewRound(double round_len) {
rounds_without_congestion_++;
if (rounds_without_congestion_ < ROUNDS_BEFORE_TAKE_ACTION) return;
- congestion_win_ = congestion_win_ * (uint32_t)WIN_INCREASE_FACTOR;
+ congestion_win_ = congestion_win_ * WIN_INCREASE_FACTOR;
congestion_win_ = std::min(congestion_win_, INITIAL_WIN_MAX);
}
}
diff --git a/libtransport/src/protocols/rtc/rtc_reassembly.h b/libtransport/src/protocols/rtc/rtc_reassembly.h
new file mode 100644
index 000000000..15722a6d5
--- /dev/null
+++ b/libtransport/src/protocols/rtc/rtc_reassembly.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <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)
+ : DatagramReassembly(icn_socket, transport_protocol) {}
+
+ void reassemble(core::ContentObject &content_object) override {
+ auto read_buffer = content_object.getPayload();
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "Size of payload: " << read_buffer->length();
+ read_buffer->trimStart(transport_protocol_->transportHeaderLength());
+ Reassembly::read_buffer_ = std::move(read_buffer);
+ Reassembly::notifyApplication();
+ }
+};
+
+} // namespace rtc
+} // namespace protocol
+} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/rtc_state.cc b/libtransport/src/protocols/rtc/rtc_state.cc
index 9c965bfed..c99205a26 100644
--- a/libtransport/src/protocols/rtc/rtc_state.cc
+++ b/libtransport/src/protocols/rtc/rtc_state.cc
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+#include <glog/logging.h>
#include <protocols/rtc/rtc_consts.h>
#include <protocols/rtc/rtc_state.h>
@@ -22,11 +23,13 @@ namespace protocol {
namespace rtc {
-RTCState::RTCState(ProbeHandler::SendProbeCallback &&rtt_probes_callback,
+RTCState::RTCState(Indexer *indexer,
+ ProbeHandler::SendProbeCallback &&rtt_probes_callback,
DiscoveredRttCallback &&discovered_rtt_callback,
asio::io_service &io_service)
- : rtt_probes_(std::make_shared<ProbeHandler>(
- std::move(rtt_probes_callback), io_service)),
+ : indexer_(indexer),
+ rtt_probes_(std::make_shared<ProbeHandler>(std::move(rtt_probes_callback),
+ io_service)),
discovered_rtt_callback_(std::move(discovered_rtt_callback)) {
init_rtt_timer_ = std::make_unique<asio::steady_timer>(io_service);
initParams();
@@ -45,14 +48,22 @@ void RTCState::initParams() {
// 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_ = 0.0;
+ max_loss_rate_ = 0.0;
+ last_round_loss_rate_ = 0.0;
residual_loss_rate_ = 0.0;
+ // fec counters
+ pending_fec_pkt_ = 0;
+ received_fec_pkt_ = 0;
+
// bw counters
received_bytes_ = 0;
avg_packet_size_ = INIT_PACKET_SIZE;
@@ -90,8 +101,14 @@ void RTCState::initParams() {
// pending interests
pending_interests_.clear();
+ // skipped interest
+ last_interest_sent_ = 0;
+ skipped_interests_.clear();
+
// init rtt
- first_interest_sent_ = ~0;
+ first_interest_sent_time_ = ~0;
+ first_interest_sent_seq_ = 0;
+
init_rtt_ = false;
rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES);
rtt_probes_->sendProbes();
@@ -106,18 +123,51 @@ void RTCState::onSendNewInterest(const core::Name *interest_name) {
uint32_t seq = interest_name->getSuffix();
pending_interests_.insert(std::pair<uint32_t, uint64_t>(seq, now));
- if(sent_interests_ == 0) first_interest_sent_ = 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?
+ // look for skipped interests
+ skipped_interests_.erase(seq); // remove seq if it is there
+ for (uint32_t i = last_interest_sent_ + 1; i < seq; i++) {
+ if (indexer_->isFec(i)) {
+ skipped_interests_.insert(i);
+ }
+ }
+
+ last_interest_sent_ = seq;
sent_interests_++;
sent_interests_last_round_++;
}
-void RTCState::onTimeout(uint32_t seq) {
+void RTCState::onTimeout(uint32_t seq, bool lost) {
auto it = pending_interests_.find(seq);
if (it != pending_interests_.end()) {
pending_interests_.erase(it);
}
received_timeouts_++;
+
+ if (lost) onPacketLost(seq);
+}
+
+void RTCState::onLossDetected(uint32_t seq) {
+ if (!indexer_->isFec(seq)) {
+ packets_lost_++;
+ } else if (skipped_interests_.find(seq) == skipped_interests_.end() &&
+ seq >= first_interest_sent_seq_) {
+ packets_lost_++;
+ }
}
void RTCState::onRetransmission(uint32_t seq) {
@@ -128,7 +178,9 @@ void RTCState::onRetransmission(uint32_t seq) {
auto it = pending_interests_.find(seq);
if (it != pending_interests_.end()) {
pending_interests_.erase(it);
+#if 0
packets_lost_++;
+#endif
}
sent_rtx_++;
sent_rtx_last_round_++;
@@ -165,6 +217,16 @@ void RTCState::onDataPacketReceived(const core::ContentObject &content_object,
received_packets_last_round_++;
}
+void RTCState::onFecPacketReceived(const core::ContentObject &content_object) {
+ uint32_t seq = content_object.getName().getSuffix();
+ updateReceivedBytes(content_object);
+ addRecvOrLost(seq, PacketState::RECEIVED);
+ received_fec_pkt_++;
+ // the producer is responding
+ // it is generating valid data packets so we consider it active
+ producer_is_active_ = true;
+}
+
void RTCState::onNackPacketReceived(const core::ContentObject &nack,
bool compute_stats) {
uint32_t seq = nack.getName().getSuffix();
@@ -197,12 +259,14 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack,
// old nack, seq is lost
// update last nacked
if (last_seq_nacked_ < seq) last_seq_nacked_ = seq;
- TRANSPORT_LOGD("lost packet %u beacuse of a past nack", seq);
+ DLOG_IF(INFO, VLOG_IS_ON(3))
+ << "lost packet " << seq << " beacuse of a past nack";
onPacketLost(seq);
} else if (seq > production_seq) {
// future nack
// remove the nack from the pending interest map
// (the packet is not received/lost yet)
+ if (indexer_->isFec(seq)) pending_fec_pkt_--;
pending_interests_.erase(seq);
} else {
// this should be a quite rear event. simply remove the
@@ -221,17 +285,28 @@ void RTCState::onNackPacketReceived(const core::ContentObject &nack,
}
void RTCState::onPacketLost(uint32_t seq) {
- TRANSPORT_LOGD("packet %u is lost", seq);
+#if 0
+ DLOG_IF(INFO, VLOG_IS_ON(3)) << "packet " << seq << " is lost";
auto it = pending_interests_.find(seq);
if (it != pending_interests_.end()) {
// this packet was never retransmitted so it does
// not appear in the loss count
packets_lost_++;
}
+#endif
+ if (!indexer_->isFec(seq)) {
+ definitely_lost_pkt_++;
+ DLOG_IF(INFO, VLOG_IS_ON(4)) << "packet " << seq << " is lost";
+ }
addRecvOrLost(seq, PacketState::LOST);
}
-void RTCState::onPacketRecovered(uint32_t seq) {
+void RTCState::onPacketRecoveredRtx(uint32_t seq) {
+ losses_recovered_++;
+ addRecvOrLost(seq, PacketState::RECEIVED);
+}
+
+void RTCState::onPacketRecoveredFec(uint32_t seq) {
losses_recovered_++;
addRecvOrLost(seq, PacketState::RECEIVED);
}
@@ -258,7 +333,6 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) {
uint32_t production_seq = probe_pkt->getProductionSegement();
uint32_t production_rate = probe_pkt->getProductionRate();
-
if (path_it == path_table_.end()) {
// found a new path
std::shared_ptr<RTCDataPath> newPath =
@@ -298,13 +372,14 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) {
// 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
+ 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) {
+ if (received_probes_ == INIT_RTT_PROBES) {
// we are done
init_rtt_timer_->cancel();
checkInitRttTimer();
@@ -314,7 +389,7 @@ bool RTCState::onProbePacketReceived(const core::ContentObject &probe) {
received_packets_last_round_++;
// ignore probes sent before the first interest
- if((now - rtt) <= first_interest_sent_) return false;
+ if ((now - rtt) <= first_interest_sent_time_) return false;
return true;
}
@@ -327,11 +402,11 @@ void RTCState::onNewRound(double round_len, bool in_sync) {
double bytes_per_sec =
((double)received_bytes_ * (MILLI_IN_A_SEC / round_len));
- if(received_rate_ == 0)
+ if (received_rate_ == 0)
received_rate_ = bytes_per_sec;
else
received_rate_ = (received_rate_ * MOVING_AVG_ALPHA) +
- ((1 - MOVING_AVG_ALPHA) * bytes_per_sec);
+ ((1 - MOVING_AVG_ALPHA) * bytes_per_sec);
// search for an active path. There should be only one active path (meaning a
// path that leads to the producer socket -no cache- and from which we are
@@ -354,7 +429,8 @@ void RTCState::onNewRound(double round_len, bool in_sync) {
}
}
- if (in_sync) updateLossRate();
+ // if (in_sync) updateLossRate();
+ updateLossRate();
// handle nacks
if (!nack_on_last_round_ && received_bytes_ > 0) {
@@ -385,6 +461,7 @@ void RTCState::onNewRound(double round_len, bool in_sync) {
// reset counters
received_bytes_ = 0;
packets_lost_ = 0;
+ definitely_lost_pkt_ = 0;
losses_recovered_ = 0;
first_seq_in_round_ = highest_seq_received_;
@@ -397,6 +474,8 @@ void RTCState::onNewRound(double round_len, bool in_sync) {
sent_interests_last_round_ = 0;
sent_rtx_last_round_ = 0;
+ received_fec_pkt_ = 0;
+
rounds_++;
}
@@ -465,6 +544,7 @@ void RTCState::updatePathStats(const core::ContentObject &content_object,
}
void RTCState::updateLossRate() {
+ last_round_loss_rate_ = loss_rate_;
loss_rate_ = 0.0;
residual_loss_rate_ = 0.0;
@@ -475,9 +555,29 @@ void RTCState::updateLossRate() {
// division by 0
if (number_theorically_received_packets_ == 0) return;
+ // XXX this may be quite inefficient if the rate is high
+ // maybe is better to iterate over the set?
+ for (uint32_t i = first_seq_in_round_; i < highest_seq_received_; i++) {
+ auto it = skipped_interests_.find(i);
+ if (it != skipped_interests_.end()) {
+ if (number_theorically_received_packets_ > 0)
+ number_theorically_received_packets_--;
+ skipped_interests_.erase(it);
+ }
+ }
+
loss_rate_ = (double)((double)(packets_lost_) /
(double)number_theorically_received_packets_);
+ if (rounds_ % 15 == 0) max_loss_rate_ = 0; // reset every 3 sec
+ if (loss_rate_ > max_loss_rate_) max_loss_rate_ = loss_rate_;
+
+ if (avg_loss_rate_ == 0)
+ avg_loss_rate_ = loss_rate_;
+ else
+ avg_loss_rate_ =
+ avg_loss_rate_ * MOVING_AVG_ALPHA + loss_rate_ * (1 - MOVING_AVG_ALPHA);
+
residual_loss_rate_ = (double)((double)(packets_lost_ - losses_recovered_) /
(double)number_theorically_received_packets_);
@@ -485,6 +585,10 @@ void RTCState::updateLossRate() {
}
void RTCState::addRecvOrLost(uint32_t seq, PacketState state) {
+ if (indexer_->isFec(seq)) {
+ pending_fec_pkt_--;
+ }
+
pending_interests_.erase(seq);
if (received_or_lost_packets_.size() >= MAX_CACHED_PACKETS) {
received_or_lost_packets_.erase(received_or_lost_packets_.begin());
@@ -507,10 +611,12 @@ void RTCState::addRecvOrLost(uint32_t seq, PacketState state) {
// 1) there is a gap in the sequence so we do not update largest_in_seq_
// 2) all the packets from largest_in_seq_ to seq are in
// received_or_lost_packets_ an we upate largest_in_seq_
+ // or are FEC packets
for (uint32_t i = highest_seq_received_in_order_ + 1; i <= seq; i++) {
if (received_or_lost_packets_.find(i) ==
- received_or_lost_packets_.end()) {
+ received_or_lost_packets_.end() &&
+ !indexer_->isFec(i)) {
break;
}
// this packet is in order so we can update the
@@ -520,17 +626,17 @@ void RTCState::addRecvOrLost(uint32_t seq, PacketState state) {
}
}
-void RTCState::setInitRttTimer(uint32_t wait){
+void RTCState::setInitRttTimer(uint32_t wait) {
init_rtt_timer_->cancel();
init_rtt_timer_->expires_from_now(std::chrono::milliseconds(wait));
init_rtt_timer_->async_wait([this](std::error_code ec) {
- if(ec) return;
+ if (ec) return;
checkInitRttTimer();
});
}
void RTCState::checkInitRttTimer() {
- if(received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV){
+ if (received_probes_ < INIT_RTT_MIN_PROBES_TO_RECV) {
// we didn't received enough probes, restart
received_probes_ = 0;
rtt_probes_->setProbes(INIT_RTT_PROBE_INTERVAL, INIT_RTT_PROBES);
@@ -547,7 +653,7 @@ void RTCState::checkInitRttTimer() {
double prod_rate = getProducerRate();
double rtt = (double)getRTT() / MILLI_IN_A_SEC;
double packet_size = getAveragePacketSize();
- uint32_t pkt_in_rtt_ = (uint32_t)std::floor(((prod_rate / packet_size) * rtt) * 0.8);
+ uint32_t pkt_in_rtt_ = std::floor(((prod_rate / packet_size) * rtt) * 0.8);
last_seq_nacked_ = last_production_seq_ + pkt_in_rtt_;
discovered_rtt_callback_();
diff --git a/libtransport/src/protocols/rtc/rtc_state.h b/libtransport/src/protocols/rtc/rtc_state.h
index e4fefaffe..729ba7a1b 100644
--- a/libtransport/src/protocols/rtc/rtc_state.h
+++ b/libtransport/src/protocols/rtc/rtc_state.h
@@ -15,13 +15,13 @@
#pragma once
#include <hicn/transport/config.h>
+#include <hicn/transport/core/asio_wrapper.h>
#include <hicn/transport/core/content_object.h>
#include <hicn/transport/core/name.h>
+#include <protocols/indexer.h>
#include <protocols/rtc/probe_handler.h>
#include <protocols/rtc/rtc_data_path.h>
-#include <asio.hpp>
-#include <asio/steady_timer.hpp>
#include <map>
#include <set>
@@ -36,8 +36,10 @@ enum class PacketState : uint8_t { RECEIVED, LOST, UNKNOWN };
class RTCState : std::enable_shared_from_this<RTCState> {
public:
using DiscoveredRttCallback = std::function<void()>;
+
public:
- RTCState(ProbeHandler::SendProbeCallback &&rtt_probes_callback,
+ RTCState(Indexer *indexer,
+ ProbeHandler::SendProbeCallback &&rtt_probes_callback,
DiscoveredRttCallback &&discovered_rtt_callback,
asio::io_service &io_service);
@@ -45,14 +47,17 @@ class RTCState : std::enable_shared_from_this<RTCState> {
// packet events
void onSendNewInterest(const core::Name *interest_name);
- void onTimeout(uint32_t seq);
+ void onTimeout(uint32_t seq, bool lost);
+ void onLossDetected(uint32_t seq);
void onRetransmission(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 onPacketRecovered(uint32_t seq);
+ void onPacketRecoveredRtx(uint32_t seq);
+ void onPacketRecoveredFec(uint32_t seq);
bool onProbePacketReceived(const core::ContentObject &probe);
// protocol state
@@ -65,9 +70,7 @@ class RTCState : std::enable_shared_from_this<RTCState> {
}
// delay metrics
- bool isRttDiscovered() const {
- return init_rtt_;
- }
+ bool isRttDiscovered() const { return init_rtt_; }
uint64_t getRTT() const {
if (mainPathIsValid()) return main_path_->getMinRtt();
@@ -97,13 +100,16 @@ class RTCState : std::enable_shared_from_this<RTCState> {
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();
+ return pending_interests_.size();
}
+
PacketState isReceivedOrLost(uint32_t seq) {
auto it = received_or_lost_packets_.find(seq);
if (it != received_or_lost_packets_.end()) return it->second;
@@ -112,12 +118,25 @@ class RTCState : std::enable_shared_from_this<RTCState> {
// loss rate
double getLossRate() const { return loss_rate_; }
+ double getAvgLossRate() const { return avg_loss_rate_; }
+ double getMaxLossRate() const { return max_loss_rate_; }
+ 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_;
}
- uint32_t getLostData() const { return packets_lost_; };
- uint32_t getRecoveredLosses() const { return losses_recovered_; }
+
+ // 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_; }
@@ -183,11 +202,15 @@ class RTCState : std::enable_shared_from_this<RTCState> {
// 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 max_loss_rate_;
+ double last_round_loss_rate_;
double residual_loss_rate_;
// bw counters
@@ -211,18 +234,24 @@ class RTCState : std::enable_shared_from_this<RTCState> {
uint32_t sent_interests_last_round_;
uint32_t sent_rtx_last_round_;
+ // fec counter
+ uint32_t received_fec_pkt_;
+ uint32_t pending_fec_pkt_;
+
// round conunters
uint32_t rounds_;
uint32_t rounds_without_nacks_;
uint32_t rounds_without_packets_;
// init rtt
- uint64_t first_interest_sent_;
+ 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
+ uint32_t
+ last_production_seq_; // last production seq received by the producer
uint64_t last_prod_update_; // timestamp of the last packets used to update
// stats from the producer
@@ -237,6 +266,13 @@ class RTCState : std::enable_shared_from_this<RTCState> {
// pending interests
std::map<uint32_t, uint64_t> pending_interests_;
+ // indexer
+ Indexer *indexer_;
+
+ // skipped interests
+ uint32_t last_interest_sent_;
+ std::unordered_set<uint32_t> skipped_interests_;
+
// probes
std::shared_ptr<ProbeHandler> rtt_probes_;
bool init_rtt_;
diff --git a/libtransport/src/protocols/rtc/trendline_estimator.cc b/libtransport/src/protocols/rtc/trendline_estimator.cc
deleted file mode 100644
index 7a0803857..000000000
--- a/libtransport/src/protocols/rtc/trendline_estimator.cc
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-// FROM
-// https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/congestion_controller/goog_cc/trendline_estimator.cc
-
-#include "trendline_estimator.h"
-
-#include <math.h>
-
-#include <algorithm>
-#include <string>
-
-namespace transport {
-
-namespace protocol {
-
-namespace rtc {
-
-// Parameters for linear least squares fit of regression line to noisy data.
-constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
-constexpr double kDefaultTrendlineThresholdGain = 4.0;
-// const char kBweWindowSizeInPacketsExperiment[] =
-// "WebRTC-BweWindowSizeInPackets";
-
-/*size_t ReadTrendlineFilterWindowSize(
- const WebRtcKeyValueConfig* key_value_config) {
- std::string experiment_string =
- key_value_config->Lookup(kBweWindowSizeInPacketsExperiment);
- size_t window_size;
- int parsed_values =
- sscanf(experiment_string.c_str(), "Enabled-%zu", &window_size);
- if (parsed_values == 1) {
- if (window_size > 1)
- return window_size;
- RTC_LOG(WARNING) << "Window size must be greater than 1.";
- }
- RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweWindowSizeInPackets"
- " experiment from field trial string. Using default.";
- return TrendlineEstimatorSettings::kDefaultTrendlineWindowSize;
-}
-*/
-
-OptionalDouble LinearFitSlope(
- const std::deque<TrendlineEstimator::PacketTiming>& packets) {
- // RTC_DCHECK(packets.size() >= 2);
- // Compute the "center of mass".
- double sum_x = 0;
- double sum_y = 0;
- for (const auto& packet : packets) {
- sum_x += packet.arrival_time_ms;
- sum_y += packet.smoothed_delay_ms;
- }
- double x_avg = sum_x / packets.size();
- double y_avg = sum_y / packets.size();
- // Compute the slope k = \sum (x_i-x_avg)(y_i-y_avg) / \sum (x_i-x_avg)^2
- double numerator = 0;
- double denominator = 0;
- for (const auto& packet : packets) {
- double x = packet.arrival_time_ms;
- double y = packet.smoothed_delay_ms;
- numerator += (x - x_avg) * (y - y_avg);
- denominator += (x - x_avg) * (x - x_avg);
- }
- if (denominator == 0) return OptionalDouble();
- return OptionalDouble(numerator / denominator);
-}
-
-OptionalDouble ComputeSlopeCap(
- const std::deque<TrendlineEstimator::PacketTiming>& packets,
- const TrendlineEstimatorSettings& settings) {
- /*RTC_DCHECK(1 <= settings.beginning_packets &&
- settings.beginning_packets < packets.size());
- RTC_DCHECK(1 <= settings.end_packets &&
- settings.end_packets < packets.size());
- RTC_DCHECK(settings.beginning_packets + settings.end_packets <=
- packets.size());*/
- TrendlineEstimator::PacketTiming early = packets[0];
- for (size_t i = 1; i < settings.beginning_packets; ++i) {
- if (packets[i].raw_delay_ms < early.raw_delay_ms) early = packets[i];
- }
- size_t late_start = packets.size() - settings.end_packets;
- TrendlineEstimator::PacketTiming late = packets[late_start];
- for (size_t i = late_start + 1; i < packets.size(); ++i) {
- if (packets[i].raw_delay_ms < late.raw_delay_ms) late = packets[i];
- }
- if (late.arrival_time_ms - early.arrival_time_ms < 1) {
- return OptionalDouble();
- }
- return OptionalDouble((late.raw_delay_ms - early.raw_delay_ms) /
- (late.arrival_time_ms - early.arrival_time_ms) +
- settings.cap_uncertainty);
-}
-
-constexpr double kMaxAdaptOffsetMs = 15.0;
-constexpr double kOverUsingTimeThreshold = 10;
-constexpr int kMinNumDeltas = 60;
-constexpr int kDeltaCounterMax = 1000;
-
-//} // namespace
-
-constexpr char TrendlineEstimatorSettings::kKey[];
-
-TrendlineEstimatorSettings::TrendlineEstimatorSettings(
- /*const WebRtcKeyValueConfig* key_value_config*/) {
- /*if (absl::StartsWith(
- key_value_config->Lookup(kBweWindowSizeInPacketsExperiment),
- "Enabled")) {
- window_size = ReadTrendlineFilterWindowSize(key_value_config);
- }
- Parser()->Parse(key_value_config->Lookup(TrendlineEstimatorSettings::kKey));*/
- window_size = kDefaultTrendlineWindowSize;
- enable_cap = false;
- beginning_packets = end_packets = 0;
- cap_uncertainty = 0.0;
-
- /*if (window_size < 10 || 200 < window_size) {
- RTC_LOG(LS_WARNING) << "Window size must be between 10 and 200 packets";
- window_size = kDefaultTrendlineWindowSize;
- }
- if (enable_cap) {
- if (beginning_packets < 1 || end_packets < 1 ||
- beginning_packets > window_size || end_packets > window_size) {
- RTC_LOG(LS_WARNING) << "Size of beginning and end must be between 1 and "
- << window_size;
- enable_cap = false;
- beginning_packets = end_packets = 0;
- cap_uncertainty = 0.0;
- }
- if (beginning_packets + end_packets > window_size) {
- RTC_LOG(LS_WARNING)
- << "Size of beginning plus end can't exceed the window size";
- enable_cap = false;
- beginning_packets = end_packets = 0;
- cap_uncertainty = 0.0;
- }
- if (cap_uncertainty < 0.0 || 0.025 < cap_uncertainty) {
- RTC_LOG(LS_WARNING) << "Cap uncertainty must be between 0 and 0.025";
- cap_uncertainty = 0.0;
- }
- }*/
-}
-
-/*std::unique_ptr<StructParametersParser> TrendlineEstimatorSettings::Parser() {
- return StructParametersParser::Create("sort", &enable_sort, //
- "cap", &enable_cap, //
- "beginning_packets",
- &beginning_packets, //
- "end_packets", &end_packets, //
- "cap_uncertainty", &cap_uncertainty, //
- "window_size", &window_size);
-}*/
-
-TrendlineEstimator::TrendlineEstimator(
- /*const WebRtcKeyValueConfig* key_value_config,
- NetworkStatePredictor* network_state_predictor*/)
- : settings_(),
- smoothing_coef_(kDefaultTrendlineSmoothingCoeff),
- threshold_gain_(kDefaultTrendlineThresholdGain),
- num_of_deltas_(0),
- first_arrival_time_ms_(-1),
- accumulated_delay_(0),
- smoothed_delay_(0),
- delay_hist_(),
- k_up_(0.0087),
- k_down_(0.039),
- overusing_time_threshold_(kOverUsingTimeThreshold),
- threshold_(12.5),
- prev_modified_trend_(NAN),
- last_update_ms_(-1),
- prev_trend_(0.0),
- time_over_using_(-1),
- overuse_counter_(0),
- hypothesis_(BandwidthUsage::kBwNormal){
- // hypothesis_predicted_(BandwidthUsage::kBwNormal){//},
- // network_state_predictor_(network_state_predictor) {
- /* RTC_LOG(LS_INFO)
- << "Using Trendline filter for delay change estimation with settings "
- << settings_.Parser()->Encode() << " and "
- // << (network_state_predictor_ ? "injected" : "no")
- << " network state predictor";*/
-}
-
-TrendlineEstimator::~TrendlineEstimator() {}
-
-void TrendlineEstimator::UpdateTrendline(double recv_delta_ms,
- double send_delta_ms,
- int64_t send_time_ms,
- int64_t arrival_time_ms,
- size_t packet_size) {
- const double delta_ms = recv_delta_ms - send_delta_ms;
- ++num_of_deltas_;
- num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax);
- if (first_arrival_time_ms_ == -1) first_arrival_time_ms_ = arrival_time_ms;
-
- // Exponential backoff filter.
- accumulated_delay_ += delta_ms;
- // BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms,
- // accumulated_delay_);
- smoothed_delay_ = smoothing_coef_ * smoothed_delay_ +
- (1 - smoothing_coef_) * accumulated_delay_;
- // BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", arrival_time_ms,
- // smoothed_delay_);
-
- // Maintain packet window
- delay_hist_.emplace_back(
- static_cast<double>(arrival_time_ms - first_arrival_time_ms_),
- smoothed_delay_, accumulated_delay_);
- if (settings_.enable_sort) {
- for (size_t i = delay_hist_.size() - 1;
- i > 0 &&
- delay_hist_[i].arrival_time_ms < delay_hist_[i - 1].arrival_time_ms;
- --i) {
- std::swap(delay_hist_[i], delay_hist_[i - 1]);
- }
- }
- if (delay_hist_.size() > settings_.window_size) delay_hist_.pop_front();
-
- // Simple linear regression.
- double trend = prev_trend_;
- if (delay_hist_.size() == settings_.window_size) {
- // Update trend_ if it is possible to fit a line to the data. The delay
- // trend can be seen as an estimate of (send_rate - capacity)/capacity.
- // 0 < trend < 1 -> the delay increases, queues are filling up
- // trend == 0 -> the delay does not change
- // trend < 0 -> the delay decreases, queues are being emptied
- OptionalDouble trendO = LinearFitSlope(delay_hist_);
- if (trendO.has_value()) trend = trendO.value();
- if (settings_.enable_cap) {
- OptionalDouble cap = ComputeSlopeCap(delay_hist_, settings_);
- // We only use the cap to filter out overuse detections, not
- // to detect additional underuses.
- if (trend >= 0 && cap.has_value() && trend > cap.value()) {
- trend = cap.value();
- }
- }
- }
- // BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trend);
-
- Detect(trend, send_delta_ms, arrival_time_ms);
-}
-
-void TrendlineEstimator::Update(double recv_delta_ms, double send_delta_ms,
- int64_t send_time_ms, int64_t arrival_time_ms,
- size_t packet_size, bool calculated_deltas) {
- if (calculated_deltas) {
- UpdateTrendline(recv_delta_ms, send_delta_ms, send_time_ms, arrival_time_ms,
- packet_size);
- }
- /*if (network_state_predictor_) {
- hypothesis_predicted_ = network_state_predictor_->Update(
- send_time_ms, arrival_time_ms, hypothesis_);
- }*/
-}
-
-BandwidthUsage TrendlineEstimator::State() const {
- return /*network_state_predictor_ ? hypothesis_predicted_ :*/ hypothesis_;
-}
-
-void TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) {
- /*if (num_of_deltas_ < 2) {
- hypothesis_ = BandwidthUsage::kBwNormal;
- return;
- }*/
-
- const double modified_trend =
- std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_;
- prev_modified_trend_ = modified_trend;
- // BWE_TEST_LOGGING_PLOT(1, "T", now_ms, modified_trend);
- // BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
- if (modified_trend > threshold_) {
- if (time_over_using_ == -1) {
- // Initialize the timer. Assume that we've been
- // over-using half of the time since the previous
- // sample.
- time_over_using_ = ts_delta / 2;
- } else {
- // Increment timer
- time_over_using_ += ts_delta;
- }
- overuse_counter_++;
- if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) {
- if (trend >= prev_trend_) {
- time_over_using_ = 0;
- overuse_counter_ = 0;
- hypothesis_ = BandwidthUsage::kBwOverusing;
- }
- }
- } else if (modified_trend < -threshold_) {
- time_over_using_ = -1;
- overuse_counter_ = 0;
- hypothesis_ = BandwidthUsage::kBwUnderusing;
- } else {
- time_over_using_ = -1;
- overuse_counter_ = 0;
- hypothesis_ = BandwidthUsage::kBwNormal;
- }
- prev_trend_ = trend;
- UpdateThreshold(modified_trend, now_ms);
-}
-
-void TrendlineEstimator::UpdateThreshold(double modified_trend,
- int64_t now_ms) {
- if (last_update_ms_ == -1) last_update_ms_ = now_ms;
-
- if (fabs(modified_trend) > threshold_ + kMaxAdaptOffsetMs) {
- // Avoid adapting the threshold to big latency spikes, caused e.g.,
- // by a sudden capacity drop.
- last_update_ms_ = now_ms;
- return;
- }
-
- const double k = fabs(modified_trend) < threshold_ ? k_down_ : k_up_;
- const int64_t kMaxTimeDeltaMs = 100;
- int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
- threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms;
- if (threshold_ < 6.f) threshold_ = 6.f;
- if (threshold_ > 600.f) threshold_ = 600.f;
- // threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
- last_update_ms_ = now_ms;
-}
-
-} // namespace rtc
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc/trendline_estimator.h b/libtransport/src/protocols/rtc/trendline_estimator.h
deleted file mode 100644
index 372acbc67..000000000
--- a/libtransport/src/protocols/rtc/trendline_estimator.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-// FROM
-// https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/congestion_controller/goog_cc/trendline_estimator.h
-
-#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
-#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <deque>
-#include <memory>
-#include <utility>
-
-namespace transport {
-
-namespace protocol {
-
-namespace rtc {
-
-class OptionalDouble {
- public:
- OptionalDouble() : val(0), has_val(false){};
- OptionalDouble(double val) : val(val), has_val(true){};
-
- double value() { return val; }
- bool has_value() { return has_val; }
-
- private:
- double val;
- bool has_val;
-};
-
-enum class BandwidthUsage {
- kBwNormal = 0,
- kBwUnderusing = 1,
- kBwOverusing = 2,
- kLast
-};
-
-struct TrendlineEstimatorSettings {
- static constexpr char kKey[] = "WebRTC-Bwe-TrendlineEstimatorSettings";
- static constexpr unsigned kDefaultTrendlineWindowSize = 20;
-
- // TrendlineEstimatorSettings() = delete;
- TrendlineEstimatorSettings(
- /*const WebRtcKeyValueConfig* key_value_config*/);
-
- // Sort the packets in the window. Should be redundant,
- // but then almost no cost.
- bool enable_sort = false;
-
- // Cap the trendline slope based on the minimum delay seen
- // in the beginning_packets and end_packets respectively.
- bool enable_cap = false;
- unsigned beginning_packets = 7;
- unsigned end_packets = 7;
- double cap_uncertainty = 0.0;
-
- // Size (in packets) of the window.
- unsigned window_size = kDefaultTrendlineWindowSize;
-
- // std::unique_ptr<StructParametersParser> Parser();
-};
-
-class TrendlineEstimator /*: public DelayIncreaseDetectorInterface */ {
- public:
- TrendlineEstimator(/*const WebRtcKeyValueConfig* key_value_config,
- NetworkStatePredictor* network_state_predictor*/);
-
- ~TrendlineEstimator();
-
- // Update the estimator with a new sample. The deltas should represent deltas
- // between timestamp groups as defined by the InterArrival class.
- void Update(double recv_delta_ms, double send_delta_ms, int64_t send_time_ms,
- int64_t arrival_time_ms, size_t packet_size,
- bool calculated_deltas);
-
- void UpdateTrendline(double recv_delta_ms, double send_delta_ms,
- int64_t send_time_ms, int64_t arrival_time_ms,
- size_t packet_size);
-
- BandwidthUsage State() const;
-
- struct PacketTiming {
- PacketTiming(double arrival_time_ms, double smoothed_delay_ms,
- double raw_delay_ms)
- : arrival_time_ms(arrival_time_ms),
- smoothed_delay_ms(smoothed_delay_ms),
- raw_delay_ms(raw_delay_ms) {}
- double arrival_time_ms;
- double smoothed_delay_ms;
- double raw_delay_ms;
- };
-
- private:
- // friend class GoogCcStatePrinter;
- void Detect(double trend, double ts_delta, int64_t now_ms);
-
- void UpdateThreshold(double modified_offset, int64_t now_ms);
-
- // Parameters.
- TrendlineEstimatorSettings settings_;
- const double smoothing_coef_;
- const double threshold_gain_;
- // Used by the existing threshold.
- int num_of_deltas_;
- // Keep the arrival times small by using the change from the first packet.
- int64_t first_arrival_time_ms_;
- // Exponential backoff filtering.
- double accumulated_delay_;
- double smoothed_delay_;
- // Linear least squares regression.
- std::deque<PacketTiming> delay_hist_;
-
- const double k_up_;
- const double k_down_;
- double overusing_time_threshold_;
- double threshold_;
- double prev_modified_trend_;
- int64_t last_update_ms_;
- double prev_trend_;
- double time_over_using_;
- int overuse_counter_;
- BandwidthUsage hypothesis_;
- // BandwidthUsage hypothesis_predicted_;
- // NetworkStatePredictor* network_state_predictor_;
-
- // RTC_DISALLOW_COPY_AND_ASSIGN(TrendlineEstimator);
-};
-
-} // namespace rtc
-
-} // end namespace protocol
-
-} // end namespace transport
-#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_
diff --git a/libtransport/src/protocols/rtc_data_path.cc b/libtransport/src/protocols/rtc_data_path.cc
deleted file mode 100644
index 30644e939..000000000
--- a/libtransport/src/protocols/rtc_data_path.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <protocols/rtc_data_path.h>
-
-#include <cfloat>
-#include <chrono>
-
-#define MAX_ROUNDS_WITHOUT_PKTS 10 // 2sec
-
-namespace transport {
-
-namespace protocol {
-
-RTCDataPath::RTCDataPath()
- : min_rtt(UINT_MAX),
- prev_min_rtt(UINT_MAX),
- min_owd(INT_MAX), // this is computed like in LEDBAT, so it is not the
- // real OWD, but the measured one, that depends on the
- // clock of sender and receiver. the only meaningful
- // value is is the queueing delay. for this reason we
- // keep both RTT (for the windowd calculation) and OWD
- // (for congestion/quality control)
- prev_min_owd(INT_MAX),
- avg_owd(0.0),
- queuing_delay(DBL_MAX),
- lastRecvSeq_(0),
- lastRecvTime_(0),
- avg_inter_arrival_(DBL_MAX),
- received_nacks_(false),
- received_packets_(false),
- rounds_without_packets_(0),
- RTThistory_(HISTORY_LEN),
- OWDhistory_(HISTORY_LEN){};
-
-void RTCDataPath::insertRttSample(uint64_t rtt) {
- // for the rtt we only keep track of the min one
- if (rtt < min_rtt) min_rtt = rtt;
-}
-
-void RTCDataPath::insertOwdSample(int64_t owd) {
- // for owd we use both min and avg
- if (owd < min_owd) min_owd = owd;
-
- if (avg_owd != DBL_MAX)
- avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC);
- else {
- avg_owd = owd;
- }
-
- // owd is computed only for valid data packets so we count only
- // this for decide if we recevie traffic or not
- received_packets_ = true;
-}
-
-void RTCDataPath::computeInterArrivalGap(uint32_t segmentNumber) {
- // got packet in sequence, compute gap
- if (lastRecvSeq_ == (segmentNumber - 1)) {
- uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- uint64_t delta = now - lastRecvTime_;
- lastRecvSeq_ = segmentNumber;
- lastRecvTime_ = now;
- if (avg_inter_arrival_ == DBL_MAX)
- avg_inter_arrival_ = delta;
- else
- avg_inter_arrival_ =
- (avg_inter_arrival_ * (1 - ALPHA_RTC)) + (delta * ALPHA_RTC);
- return;
- }
-
- // ooo packet, update the stasts if needed
- if (lastRecvSeq_ <= segmentNumber) {
- lastRecvSeq_ = segmentNumber;
- lastRecvTime_ = std::chrono::duration_cast<std::chrono::milliseconds>(
- std::chrono::steady_clock::now().time_since_epoch())
- .count();
- }
-}
-
-void RTCDataPath::receivedNack() { received_nacks_ = true; }
-
-double RTCDataPath::getInterArrivalGap() {
- if (avg_inter_arrival_ == DBL_MAX) return 0;
- return avg_inter_arrival_;
-}
-
-bool RTCDataPath::isActive() {
- if (received_nacks_ && rounds_without_packets_ < MAX_ROUNDS_WITHOUT_PKTS)
- return true;
- return false;
-}
-
-void RTCDataPath::roundEnd() {
- // reset min_rtt and add it to the history
- if (min_rtt != UINT_MAX) {
- prev_min_rtt = min_rtt;
- } else {
- // this may happen if we do not receive any packet
- // from this path in the last round. in this case
- // we use the measure from the previuos round
- min_rtt = prev_min_rtt;
- }
-
- if (min_rtt == 0) min_rtt = 1;
-
- RTThistory_.pushBack(min_rtt);
- min_rtt = UINT_MAX;
-
- // do the same for min owd
- if (min_owd != INT_MAX) {
- prev_min_owd = min_owd;
- } else {
- min_owd = prev_min_owd;
- }
-
- if (min_owd != INT_MAX) {
- OWDhistory_.pushBack(min_owd);
- min_owd = INT_MAX;
-
- // compute queuing delay
- queuing_delay = avg_owd - getMinOwd();
-
- } else {
- queuing_delay = 0.0;
- }
-
- if (!received_packets_)
- rounds_without_packets_++;
- else
- rounds_without_packets_ = 0;
- received_packets_ = false;
-}
-
-double RTCDataPath::getQueuingDealy() { return queuing_delay; }
-
-uint64_t RTCDataPath::getMinRtt() { return RTThistory_.begin(); }
-
-int64_t RTCDataPath::getMinOwd() { return OWDhistory_.begin(); }
-
-} // end namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/rtc_data_path.h b/libtransport/src/protocols/rtc_data_path.h
deleted file mode 100644
index 9076b355f..000000000
--- a/libtransport/src/protocols/rtc_data_path.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <climits>
-
-#include <utils/min_filter.h>
-
-#define ALPHA_RTC 0.125
-#define HISTORY_LEN 20 // 4 sec
-
-namespace transport {
-
-namespace protocol {
-
-class RTCDataPath {
- public:
- RTCDataPath();
-
- public:
- void insertRttSample(uint64_t rtt);
- void insertOwdSample(int64_t owd);
- void computeInterArrivalGap(uint32_t segmentNumber);
- void receivedNack();
-
- uint64_t getMinRtt();
- double getQueuingDealy();
- double getInterArrivalGap();
- bool isActive();
-
- void roundEnd();
-
- private:
- int64_t getMinOwd();
-
- uint64_t min_rtt;
- uint64_t prev_min_rtt;
-
- int64_t min_owd;
- int64_t prev_min_owd;
-
- double avg_owd;
-
- double queuing_delay;
-
- uint32_t lastRecvSeq_;
- uint64_t lastRecvTime_;
- double avg_inter_arrival_;
-
- // flags to check if a path is active
- // we considere a path active if it reaches a producer
- //(not a cache) --aka we got at least one nack on this path--
- // and if we receives packets
- bool received_nacks_;
- bool received_packets_;
- uint8_t rounds_without_packets_; // if we don't get any packet
- // for MAX_ROUNDS_WITHOUT_PKTS
- // we consider the path inactive
-
- utils::MinFilter<uint64_t> RTThistory_;
- utils::MinFilter<int64_t> OWDhistory_;
-};
-
-} // namespace protocol
-
-} // end namespace transport
diff --git a/libtransport/src/protocols/transport_protocol.cc b/libtransport/src/protocols/transport_protocol.cc
index 611c39212..d6954ac37 100644
--- a/libtransport/src/protocols/transport_protocol.cc
+++ b/libtransport/src/protocols/transport_protocol.cc
@@ -24,12 +24,11 @@ namespace protocol {
using namespace interface;
TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket,
- Reassembly *reassembly_protocol)
+ Indexer *indexer, Reassembly *reassembly)
: socket_(icn_socket),
- reassembly_protocol_(reassembly_protocol),
- index_manager_(
- std::make_unique<IndexManager>(socket_, this, reassembly_protocol)),
- is_running_(false),
+ indexer_verifier_(indexer),
+ reassembly_(reassembly),
+ fec_decoder_(nullptr),
is_first_(false),
on_interest_retransmission_(VOID_HANDLER),
on_interest_output_(VOID_HANDLER),
@@ -37,9 +36,17 @@ TransportProtocol::TransportProtocol(implementation::ConsumerSocket *icn_socket,
on_interest_satisfied_(VOID_HANDLER),
on_content_object_input_(VOID_HANDLER),
stats_summary_(VOID_HANDLER),
- on_payload_(VOID_HANDLER) {
+ on_payload_(VOID_HANDLER),
+ fec_type_(fec::FECType::UNKNOWN),
+ is_running_(false) {
socket_->getSocketOption(GeneralTransportOptions::PORTAL, portal_);
socket_->getSocketOption(OtherOptions::STATISTICS, &stats_);
+
+ // Set this transport protocol as portal's consumer callback
+ portal_->setConsumerCallback(this);
+
+ indexer_verifier_->setReassembly(reassembly_.get());
+ reassembly->setIndexer(indexer_verifier_.get());
}
int TransportProtocol::start() {
@@ -69,6 +76,7 @@ int TransportProtocol::start() {
// Reset the protocol state machine
reset();
+
// Schedule next interests
scheduleNextInterests();
@@ -99,15 +107,27 @@ void TransportProtocol::stop() {
}
void TransportProtocol::resume() {
- if (is_running_) return;
+ if (isRunning()) return;
is_running_ = true;
scheduleNextInterests();
- portal_->runEventsLoop();
+ if (!is_async_) {
+ // Start Event loop
+ portal_->runEventsLoop();
- is_running_ = false;
+ // Not running anymore
+ is_running_ = false;
+ }
+}
+
+void TransportProtocol::reset() {
+ reassembly_->reInitialize();
+ indexer_verifier_->reset();
+ if (fec_decoder_) {
+ fec_decoder_->reset();
+ }
}
void TransportProtocol::onContentReassembled(std::error_code ec) {
@@ -127,6 +147,70 @@ void TransportProtocol::onContentReassembled(std::error_code 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;
+
+ auto interest = core::PacketManager<>::getInstance().getPacket<Interest>();
+ 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;
+ }
+
+ portal_->sendInterest(std::move(interest));
+}
+
+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
index 124c57122..1008a238b 100644
--- a/libtransport/src/protocols/transport_protocol.h
+++ b/libtransport/src/protocols/transport_protocol.h
@@ -21,9 +21,11 @@
#include <hicn/transport/utils/object_pool.h>
#include <implementation/socket.h>
#include <protocols/data_processing_events.h>
+#include <protocols/fec_base.h>
#include <protocols/indexer.h>
#include <protocols/reassembly.h>
+#include <array>
#include <atomic>
namespace transport {
@@ -36,12 +38,6 @@ 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 core::Portal::ConsumerCallback,
public ContentObjectProcessingEventCallback {
static constexpr std::size_t interest_pool_size = 4096;
@@ -50,7 +46,7 @@ class TransportProtocol : public core::Portal::ConsumerCallback,
public:
TransportProtocol(implementation::ConsumerSocket *icn_socket,
- Reassembly *reassembly_protocol);
+ Indexer *indexer, Reassembly *reassembly);
virtual ~TransportProtocol() = default;
@@ -62,27 +58,70 @@ class TransportProtocol : public core::Portal::ConsumerCallback,
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() { return 0; }
+
virtual void scheduleNextInterests() = 0;
// Events generated by the indexing
virtual void onContentReassembled(std::error_code ec);
virtual void onPacketDropped(Interest &interest,
- ContentObject &content_object) override = 0;
+ 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
+ if (const char *fec_str = std::getenv("TRANSPORT_FEC_TYPE")) {
+ 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
- virtual void reset() = 0;
- virtual void onContentObject(Interest &i, ContentObject &c) override = 0;
- virtual void onTimeout(Interest::Ptr &&i) override = 0;
- virtual void onError(std::error_code ec) override {}
+ void onContentObject(Interest &i, ContentObject &c) override;
+ void onTimeout(Interest::Ptr &i, const Name &n) override;
+ void onError(std::error_code ec) override {}
protected:
implementation::ConsumerSocket *socket_;
- std::unique_ptr<Reassembly> reassembly_protocol_;
- std::unique_ptr<IndexManager> index_manager_;
+ std::unique_ptr<Indexer> indexer_verifier_;
+ std::unique_ptr<Reassembly> reassembly_;
+ std::unique_ptr<fec::ConsumerFEC> fec_decoder_;
std::shared_ptr<core::Portal> 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_;
@@ -98,6 +137,11 @@ class TransportProtocol : public core::Portal::ConsumerCallback,
ReadCallback *on_payload_;
bool is_async_;
+
+ fec::FECType fec_type_;
+
+ private:
+ std::atomic<bool> is_running_;
};
} // end namespace protocol
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