diff options
author | Luca Muscariello <lumuscar+fdio@cisco.com> | 2019-01-17 13:47:57 +0100 |
---|---|---|
committer | Luca Muscariello <lumuscar+fdio@cisco.com> | 2019-01-17 16:32:51 +0100 |
commit | bac3da61644515f05663789b122554dc77549286 (patch) | |
tree | 898210bc8e70371d77de7d446a26c5dd4fd1165a /libtransport/src | |
parent | d5165246787301d0f13b646fda5e8a8567aef5ac (diff) |
This is the first commit of the hicn projectv19.01
Change-Id: I6f2544ad9b9f8891c88cc4bcce3cf19bd3cc863f
Signed-off-by: Luca Muscariello <lumuscar+fdio@cisco.com>
Diffstat (limited to 'libtransport/src')
168 files changed, 25386 insertions, 0 deletions
diff --git a/libtransport/src/hicn/transport/CMakeLists.txt b/libtransport/src/hicn/transport/CMakeLists.txt new file mode 100755 index 000000000..f3c1cd2dc --- /dev/null +++ b/libtransport/src/hicn/transport/CMakeLists.txt @@ -0,0 +1,54 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +configure_file("config.h.in" "config.h" @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/config.h DESTINATION include/hicn/transport COMPONENT libtransport-dev) + +add_subdirectory(core) +add_subdirectory(errors) +add_subdirectory(http) +add_subdirectory(interfaces) +add_subdirectory(portability) +add_subdirectory(protocols) +add_subdirectory(utils) + +set (COMPILER_DEFINITIONS "-DASIO_STANDALONE") + +list(APPEND LIBTRANSPORT_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_BINARY_DIR}/../.. +) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") +if (ANDROID_API) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -isystem -lm") +endif() + +build_library(${LIBTRANSPORT} + STATIC SHARED + SOURCES ${SOURCE_FILES} + INSTALL_HEADERS ${HEADER_FILES} + LINK_LIBRARIES ${LIBRARIES} + DEPENDS ${DEPENDENCIES} + COMPONENT libtransport + INCLUDE_DIRS ${LIBTRANSPORT_INCLUDE_DIRS} + INSTALL_ROOT_DIR hicn/transport + DEFINITIONS ${COMPILER_DEFINITIONS} +) + +if (${COMPILE_TESTS}) + add_subdirectory(core/test) + add_subdirectory(transport/test) +endif()
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/config.h.in b/libtransport/src/hicn/transport/config.h.in new file mode 100755 index 000000000..a140f4b78 --- /dev/null +++ b/libtransport/src/hicn/transport/config.h.in @@ -0,0 +1,22 @@ +/* + * 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 + +#cmakedefine TRANSPORT_HAVE_PTHREAD 1 + +#define RAAQM_CONFIG_PATH "@raaqm_config_path@" + +#cmakedefine __vpp__ diff --git a/libtransport/src/hicn/transport/core/CMakeLists.txt b/libtransport/src/hicn/transport/core/CMakeLists.txt new file mode 100755 index 000000000..c8dea8328 --- /dev/null +++ b/libtransport/src/hicn/transport/core/CMakeLists.txt @@ -0,0 +1,87 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/content_object.h + ${CMAKE_CURRENT_SOURCE_DIR}/facade.h + ${CMAKE_CURRENT_SOURCE_DIR}/interest.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_locator.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_locator_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_inline.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.h + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format.h + ${CMAKE_CURRENT_SOURCE_DIR}/name.h + ${CMAKE_CURRENT_SOURCE_DIR}/packet.h + ${CMAKE_CURRENT_SOURCE_DIR}/payload_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/pending_interest.h + ${CMAKE_CURRENT_SOURCE_DIR}/portal.h + ${CMAKE_CURRENT_SOURCE_DIR}/prefix.h + ${CMAKE_CURRENT_SOURCE_DIR}/connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/content_object.cc + ${CMAKE_CURRENT_SOURCE_DIR}/interest.cc + ${CMAKE_CURRENT_SOURCE_DIR}/key_locator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/pending_interest.cc + ${CMAKE_CURRENT_SOURCE_DIR}/packet.cc + ${CMAKE_CURRENT_SOURCE_DIR}/name.cc + ${CMAKE_CURRENT_SOURCE_DIR}/prefix.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_forwarder_interface.cc + ${CMAKE_CURRENT_SOURCE_DIR}/manifest_format_fixed.cc + ${CMAKE_CURRENT_SOURCE_DIR}/connector.cc +) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + if (BUILD_WITH_VPP OR BUILD_VPP_PLUGIN) + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_binary_api.h + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_binary_api.h + ${CMAKE_CURRENT_SOURCE_DIR}/memif_binary_api.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_forwarder_interface.cc + ${CMAKE_CURRENT_SOURCE_DIR}/memif_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_binary_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/vpp_binary_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/memif_binary_api.c + ) + endif() + + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.h + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_connector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/raw_socket_interface.cc + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/connector.cc b/libtransport/src/hicn/transport/core/connector.cc new file mode 100755 index 000000000..ff567d78a --- /dev/null +++ b/libtransport/src/hicn/transport/core/connector.cc @@ -0,0 +1,44 @@ +/* + * 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/connector.h> + +namespace transport { + +namespace core { + +std::once_flag Connector::init_flag_; + +Connector::Connector() : packet_pool_() { init(); } + +void Connector::init() { increasePoolSize(); } + +void Connector::increasePoolSize(std::size_t size) { + // Allocate space for receiving packets + const auto capacity = packet_size * size; + uint8_t *buffer = static_cast<uint8_t *>(malloc(capacity)); + std::unique_ptr<utils::MemBuf> buffer0 = + utils::MemBuf::takeOwnership(buffer, capacity, 0, nullptr, nullptr, true); + + for (std::size_t i = 1; i < size; i++) { + auto b = buffer0->cloneOne(); + b->advance(i * packet_size); + packet_pool_.add(b.release()); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/connector.h b/libtransport/src/hicn/transport/core/connector.h new file mode 100755 index 000000000..14201879c --- /dev/null +++ b/libtransport/src/hicn/transport/core/connector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/packet.h> +#include <hicn/transport/utils/membuf.h> +#include <hicn/transport/utils/object_pool.h> +#include <hicn/transport/utils/ring_buffer.h> + +#include <deque> +#include <functional> + +namespace transport { + +namespace core { + +enum class ConnectorType : uint8_t { + SOCKET_CONNECTOR, + RAW_SOCKET_CONNECTOR, + VPP_CONNECTOR, +}; + +static constexpr std::size_t packet_size = 2000; +static constexpr std::size_t queue_size = 4096; +static constexpr std::size_t packet_pool_size = 4096; + +using PacketRing = utils::CircularFifo<Packet::MemBufPtr, queue_size>; +using PacketQueue = std::deque<Packet::MemBufPtr>; +using PacketReceivedCallback = std::function<void(Packet::MemBufPtr &&)>; +using OnReconnect = std::function<void()>; +using PacketSentCallback = std::function<void()>; + +class Connector { + public: + Connector(); + + virtual ~Connector() = default; + + virtual void send(const Packet::MemBufPtr &packet) = 0; + + virtual void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) = 0; + + virtual void close() = 0; + + virtual void enableBurst() = 0; + + virtual void state() = 0; + + protected: + void increasePoolSize(std::size_t size = packet_pool_size); + + TRANSPORT_ALWAYS_INLINE utils::ObjectPool<utils::MemBuf>::Ptr getPacket() { + auto result = packet_pool_.get(); + + while (TRANSPORT_EXPECT_FALSE(!result.first)) { + // Add packets to the pool + increasePoolSize(); + result = packet_pool_.get(); + } + + result.second->clear(); + return std::move(result.second); + } + + private: + void init(); + + protected: + static std::once_flag init_flag_; + utils::ObjectPool<utils::MemBuf> packet_pool_; + PacketQueue output_buffer_; +}; +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/content_object.cc b/libtransport/src/hicn/transport/core/content_object.cc new file mode 100755 index 000000000..dc2056582 --- /dev/null +++ b/libtransport/src/hicn/transport/core/content_object.cc @@ -0,0 +1,170 @@ +/* + * 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 <hicn/transport/errors/errors.h> +#include <hicn/transport/utils/branch_prediction.h> + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include <hicn/hicn.h> +} + +#include <cstring> +#include <memory> + +namespace transport { + +namespace core { + +ContentObject::ContentObject(const Name &name, Packet::Format format) + : Packet(format) { + if (TRANSPORT_EXPECT_FALSE( + hicn_data_set_name(format, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0)) { + throw errors::RuntimeException("Error filling the packet name."); + } + + if (TRANSPORT_EXPECT_FALSE( + hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0)) { + throw errors::MalformedPacketException(); + } +} + +ContentObject::ContentObject(hicn_format_t format) + : ContentObject(Packet::base_name, format) {} + +ContentObject::ContentObject(const Name &name, hicn_format_t format, + const uint8_t *payload, std::size_t size) + : ContentObject(name, format) { + appendPayload(payload, size); +} + +ContentObject::ContentObject(const uint8_t *buffer, std::size_t size) + : Packet(buffer, size) { + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::RuntimeException("Error getting name from content object."); + } +} + +ContentObject::ContentObject(MemBufPtr &&buffer) : Packet(std::move(buffer)) { + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::RuntimeException("Error getting name from content object."); + } +} + +ContentObject::ContentObject(ContentObject &&other) : Packet(std::move(other)) { + name_ = std::move(other.name_); + + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +ContentObject::~ContentObject() {} + +const Name &ContentObject::getName() const { + if (!name_) { + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + (hicn_name_t *)name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + return name_; +} + +ContentObject &ContentObject::setName(const Name &name) { + if (hicn_data_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException("Error setting content object name."); + } + + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + return *this; +} + +void ContentObject::setName(Name &&name) { + if (hicn_data_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException( + "Error getting the payload length from content object."); + } + + if (hicn_data_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +uint32_t ContentObject::getPathLabel() const { + uint32_t path_label; + if (hicn_data_get_path_label((hicn_header_t *)packet_start_, &path_label) < + 0) { + throw errors::RuntimeException( + "Error retrieving the path label from content object"); + } + + return path_label; +} + +ContentObject &ContentObject::setPathLabel(uint32_t path_label) { + if (hicn_data_set_path_label((hicn_header_t *)packet_start_, path_label) < + 0) { + throw errors::RuntimeException( + "Error setting the path label from content object"); + } + + return *this; +} + +void ContentObject::setLocator(const ip_address_t &ip_address) { + if (hicn_data_set_locator(format_, (hicn_header_t *)packet_start_, + &ip_address) < 0) { + throw errors::RuntimeException("Error setting content object locator"); + } + + return; +} + +ip_address_t ContentObject::getLocator() const { + ip_address_t ip; + + if (hicn_data_get_locator(format_, (hicn_header_t *)packet_start_, &ip) < 0) { + throw errors::RuntimeException("Error getting content object locator."); + } + + return ip; +} + +void ContentObject::resetForHash() { + if (hicn_data_reset_for_hash( + format_, reinterpret_cast<hicn_header_t *>(packet_start_)) < 0) { + throw errors::RuntimeException( + "Error resetting content object fields for hash computation."); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/content_object.h b/libtransport/src/hicn/transport/core/content_object.h new file mode 100755 index 000000000..c85259f20 --- /dev/null +++ b/libtransport/src/hicn/transport/core/content_object.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/name.h> +#include <hicn/transport/core/packet.h> + +namespace transport { + +namespace core { + +// This class is used just to transfer buffer pointers +// without making a copy, as std::vector<> would do + +class ContentObject : public Packet { + public: + using Ptr = utils::ObjectPool<ContentObject>::Ptr; + using HICNContentObject = hicn_header_t; + + ContentObject(Packet::Format format = HF_INET6_TCP); + + ContentObject(const Name &name, Packet::Format format = HF_INET6_TCP); + + ContentObject(const Name &name, hicn_format_t format, const uint8_t *payload, + std::size_t payload_size); + + ContentObject(const uint8_t *buffer, std::size_t size); + ContentObject(MemBufPtr &&buffer); + + ContentObject(const ContentObject &content_object) = delete; + + ContentObject(ContentObject &&content_object); + + ~ContentObject() override; + + const Name &getName() const; + + ContentObject &setName(const Name &name); + + void setName(Name &&name); + + uint32_t getPathLabel() const; + + ContentObject &setPathLabel(uint32_t path_label); + + void setLocator(const ip_address_t &ip_address) override; + + ip_address_t getLocator() const override; + + private: + void resetForHash() override; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/facade.h b/libtransport/src/hicn/transport/core/facade.h new file mode 100755 index 000000000..c28c84671 --- /dev/null +++ b/libtransport/src/hicn/transport/core/facade.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/forwarder_interface.h> +#include <hicn/transport/core/hicn_forwarder_interface.h> +#include <hicn/transport/core/manifest_format_fixed.h> +#include <hicn/transport/core/manifest_inline.h> +#include <hicn/transport/core/portal.h> + +#ifdef __linux__ +#ifndef __ANDROID__ +#include <hicn/transport/core/raw_socket_interface.h> +#ifdef __vpp__ +#include <hicn/transport/core/vpp_forwarder_interface.h> +#endif +#endif +#endif + +namespace transport { + +namespace core { + +using HicnForwarderPortal = Portal<HicnForwarderInterface>; + +#ifdef __linux__ +#ifndef __ANDROID_API__ +using RawSocketPortal = Portal<RawSocketInterface>; +#endif +#ifdef __vpp__ +using VPPForwarderPortal = Portal<VPPForwarderInterface>; +#endif +#endif + +using ContentObjectManifest = core::ManifestInline<ContentObject, Fixed>; +using InterestManifest = core::ManifestInline<Interest, Fixed>; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/forwarder_interface.h b/libtransport/src/hicn/transport/core/forwarder_interface.h new file mode 100755 index 000000000..e7b6fb1a6 --- /dev/null +++ b/libtransport/src/hicn/transport/core/forwarder_interface.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/prefix.h> +#include <hicn/transport/core/socket_connector.h> +#include <hicn/transport/portability/portability.h> + +#include <deque> + +namespace transport { + +namespace core { + +typedef struct { + uint64_t rx_packets; + uint64_t tx_packets; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t rx_errors; + uint64_t tx_errors; +} Counters; + +template <typename Implementation, typename ConnectorType> +class ForwarderInterface { + static_assert(std::is_base_of<Connector, ConnectorType>::value, + "T must inherit from connector!"); + + static constexpr uint32_t standard_cs_reserved = 5000; + + protected: + ForwarderInterface(ConnectorType &c) + : connector_(c), + inet_address_({}), + inet6_address_({}), + mtu_(1500), + output_interface_(""), + content_store_reserved_(standard_cs_reserved) { + inet_address_.family = AF_INET; + inet6_address_.family = AF_INET6; + } + + public: + static constexpr uint8_t ack_code = 102; + + virtual ~ForwarderInterface() {} + + TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { + static_cast<Implementation &>(*this).connect(is_consumer); + } + + TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { + static_cast<Implementation &>(*this).registerRoute(); + } + + TRANSPORT_ALWAYS_INLINE std::uint32_t getMtu() { + return static_cast<Implementation &>(*this).getMtu(); + } + + template < + typename R, + typename = std::enable_if_t< + std::is_base_of<Packet, typename std::remove_reference_t<R>>::value, + R>> + TRANSPORT_ALWAYS_INLINE void send(R &&packet) { + counters_.tx_packets++; + counters_.tx_bytes += packet.payloadSize() + packet.headerSize(); + + if (_is_ipv4(packet.getFormat())) { + packet.setLocator(inet_address_); + } else { + packet.setLocator(inet6_address_); + } + + packet.setChecksum(); + connector_.send(packet.data()); + } + + template <typename Handler> + TRANSPORT_ALWAYS_INLINE void send(const uint8_t *packet, std::size_t len, + Handler &&packet_sent) { + // ASIO_COMPLETION_HANDLER_CHECK(Handler, packet_sent) type_check; + counters_.tx_packets++; + counters_.tx_bytes += len; + + // Perfect forwarding + connector_.send(packet, len, std::forward<Handler>(packet_sent)); + } + + TRANSPORT_ALWAYS_INLINE void shutdown() { connector_.close(); } + + TRANSPORT_ALWAYS_INLINE Connector &getConnector() { return connector_; } + + TRANSPORT_ALWAYS_INLINE void setContentStoreSize(uint32_t cs_size) { + content_store_reserved_ = cs_size; + } + + TRANSPORT_ALWAYS_INLINE uint32_t getContentStoreSize() const { + return content_store_reserved_; + } + + TRANSPORT_ALWAYS_INLINE void setOutputInterface( + const std::string &interface) { + output_interface_ = interface; + } + + TRANSPORT_ALWAYS_INLINE std::string &getOutputInterface() { + return output_interface_; + } + + protected: + ConnectorType &connector_; + ip_address_t inet_address_; + ip_address_t inet6_address_; + uint16_t mtu_; + std::string output_interface_; + uint32_t content_store_reserved_; + Counters counters_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/hicn_binary_api.c b/libtransport/src/hicn/transport/core/hicn_binary_api.c new file mode 100755 index 000000000..c49cb5c88 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_binary_api.c @@ -0,0 +1,228 @@ +/* + * 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/config.h> + +#ifdef __vpp__ + +#include <hicn/transport/core/hicn_binary_api.h> +#include <hicn/transport/core/vpp_binary_api_internal.h> + +#include <hicn/transport/core/hicn_binary_api.h> +#include <hicn/transport/core/vpp_binary_api_internal.h> +#include <hicn/transport/utils/log.h> + +#include <fcntl.h> +#include <inttypes.h> +#include <semaphore.h> +#include <string.h> +#include <sys/stat.h> + +#include <vlib/vlib.h> +#include <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vppinfra/error.h> + +#include <vnet/ip/format.h> +#include <vnet/ip/ip4_packet.h> +#include <vnet/ip/ip6_packet.h> + +#include <vpp_plugins/hicn/error.h> +#include <vpp_plugins/hicn/hicn_api.h> + +// uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include <hicn/hicn_msg_enum.h> + +#define vl_endianfun /* define message structures */ +#define vl_print(handle, ...) +#define vl_printfun +#define vl_api_version(n, v) static u32 api_version = (v); +#define vl_msg_name_crc_list +#include <hicn/hicn_all_api_h.h> +#undef vl_msg_name_crc_list +#undef vl_api_version +#undef vl_printfun +#undef vl_endianfun + +///////////////////////////////////////////////////// +const char *HICN_ERROR_STRING[] = { +#define _(a, b, c) c, + foreach_hicn_error +#undef _ +}; +///////////////////////////////////////////////////// + +#define POINTER_MAP_SIZE 32 +static void *global_pointers_map[POINTER_MAP_SIZE]; +static uint8_t global_pointers_map_index = 0; + +#define CONTEXT_SAVE(pointer, mp) \ + do { \ + global_pointers_map[global_pointers_map_index] = pointer; \ + mp->context = global_pointers_map_index++; \ + global_pointers_map_index %= POINTER_MAP_SIZE; \ + } while (0); + +#define CONTEXT_GET(mp, pointer) \ + do { \ + pointer = global_pointers_map[mp->context]; \ + } while (0); + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_hicn_api_reply_msg \ + _(HICN_API_REGISTER_PROD_APP_REPLY, hicn_api_register_prod_app_reply) \ + _(HICN_API_REGISTER_CONS_APP_REPLY, hicn_api_register_cons_app_reply) \ + _(HICN_API_ROUTE_NHOPS_ADD_REPLY, hicn_api_route_nhops_add_reply) + +int hicn_binary_api_register_prod_app( + vpp_plugin_binary_api_t *api, hicn_producer_input_params *input_params, + hicn_producer_output_params *output_params) { + vl_api_hicn_api_register_prod_app_t *mp; + vpp_plugin_binary_api_t *hm = api; + api->vpp_api->user_param = output_params; + + /* Construct the API message */ + M(HICN_API_REGISTER_PROD_APP, mp); + + CONTEXT_SAVE(api, mp) + + mp->len = (u8)input_params->prefix.prefix_length; + mp->swif = clib_host_to_net_u32(input_params->swif); + mp->cs_reserved = clib_host_to_net_u32(input_params->cs_reserved); + + mp->prefix[0] = clib_host_to_net_u64(input_params->prefix.ip6.as_u64[0]); + mp->prefix[1] = clib_host_to_net_u64(input_params->prefix.ip6.as_u64[1]); + + TRANSPORT_LOGI("Prefix length: %u", mp->len); + TRANSPORT_LOGI("Memif ID: %u", mp->swif); + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_hicn_api_register_prod_app_reply_t_handler( + vl_api_hicn_api_register_prod_app_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api; + CONTEXT_GET(mp, binary_api); + hicn_producer_output_params *params = binary_api->vpp_api->user_param; + + binary_api->vpp_api->ret_val = clib_net_to_host_u32(mp->retval); + params->cs_reserved = mp->cs_reserved; + params->prod_addr.ip6.as_u64[0] = mp->prod_addr[0]; + params->prod_addr.ip6.as_u64[1] = mp->prod_addr[1]; + params->face_id = clib_net_to_host_u32(mp->faceid); + + TRANSPORT_LOGI("ret :%s", get_error_string(binary_api->vpp_api->ret_val)); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +int hicn_binary_api_register_cons_app( + vpp_plugin_binary_api_t *api, hicn_consumer_input_params *input_params, + hicn_consumer_output_params *output_params) { + vl_api_hicn_api_register_cons_app_t *mp; + vpp_plugin_binary_api_t *hm = api; + + hm->vpp_api->user_param = output_params; + + /* Construct the API message */ + M(HICN_API_REGISTER_CONS_APP, mp); + + mp->swif = clib_host_to_net_u32(input_params->swif); + + CONTEXT_SAVE(api, mp) + + TRANSPORT_LOGI("Message created"); + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_hicn_api_register_cons_app_reply_t_handler( + vl_api_hicn_api_register_cons_app_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api; + CONTEXT_GET(mp, binary_api); + hicn_consumer_output_params *params = binary_api->vpp_api->user_param; + + binary_api->vpp_api->ret_val = clib_net_to_host_u32(mp->retval); + + params->src4.ip4.as_u32 = clib_net_to_host_u32(mp->src_addr4); + params->src6.ip6.as_u64[0] = clib_net_to_host_u64(mp->src_addr6[0]); + params->src6.ip6.as_u64[1] = clib_net_to_host_u64(mp->src_addr6[1]); + params->face_id = clib_host_to_net_u32(mp->faceid); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +int hicn_binary_api_register_route( + vpp_plugin_binary_api_t *api, + hicn_producer_set_route_params *input_params) { + vl_api_hicn_api_route_nhops_add_t *mp; + vpp_plugin_binary_api_t *hm = api; + + /* Construct the API message */ + M(HICN_API_ROUTE_NHOPS_ADD, mp); + + CONTEXT_SAVE(api, mp) + + mp->prefix[0] = input_params->prefix.ip6.as_u64[0]; + mp->prefix[1] = input_params->prefix.ip6.as_u64[1]; + mp->len = input_params->prefix.prefix_length; + mp->face_ids[0] = input_params->face_id; + mp->n_faces = 1; + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_hicn_api_route_nhops_add_reply_t_handler( + vl_api_hicn_api_route_nhops_add_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api; + CONTEXT_GET(mp, binary_api); + + binary_api->vpp_api->ret_val = clib_net_to_host_u32(mp->retval); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +static int hicn_binary_api_setup_handlers(vpp_plugin_binary_api_t *binary_api) { + vpp_plugin_binary_api_t *sm __attribute__((unused)) = binary_api; +#define _(N, n) \ + vl_msg_api_set_handlers(VL_API_##N + sm->msg_id_base, #n, \ + vl_api_##n##_t_handler, vl_noop_handler, \ + vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_hicn_api_reply_msg; +#undef _ + return 0; +} + +char *hicn_binary_api_get_error_string(int ret_val) { + return get_error_string(ret_val); +} + +vpp_plugin_binary_api_t *hicn_binary_api_init(vpp_binary_api_t *api) { + vpp_plugin_binary_api_t *ret = malloc(sizeof(vpp_plugin_binary_api_t)); + u8 *name = format(0, "hicn_%08x%c", api_version, 0); + ret->msg_id_base = vl_client_get_first_plugin_msg_id((char *)name); + ret->vpp_api = api; + ret->my_client_index = api->my_client_index; + hicn_binary_api_setup_handlers(ret); + return ret; +} + +#endif // __vpp__ diff --git a/libtransport/src/hicn/transport/core/hicn_binary_api.h b/libtransport/src/hicn/transport/core/hicn_binary_api.h new file mode 100755 index 000000000..752844153 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_binary_api.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> + +#ifdef __vpp__ + +#include <hicn/transport/core/vpp_binary_api.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" + +typedef union { + uint8_t data[4]; + uint32_t data_u32; + /* Aliases. */ + uint8_t as_u8[4]; + uint16_t as_u16[2]; + uint32_t as_u32; +} ip4_address; + +typedef union { + uint8_t as_u8[16]; + uint16_t as_u16[8]; + uint32_t as_u32[4]; + uint64_t as_u64[2]; +} ip6_address; + +typedef enum { IP_TYPE_ANY, IP_TYPE_IP4, IP_TYPE_IP6 } ip46_type; + +typedef struct { + ip46_type type; + uint8_t prefix_length; + union { + ip4_address ip4; + ip6_address ip6; + }; +} ip46_address; + +typedef struct { + ip46_address prefix; + uint32_t swif; + uint32_t cs_reserved; +} hicn_producer_input_params; + +typedef struct { + uint32_t swif; +} hicn_consumer_input_params; + +typedef struct { + uint32_t cs_reserved; + ip46_address prod_addr; + uint32_t face_id; +} hicn_producer_output_params; + +typedef struct { + ip46_address src4; + ip46_address src6; + uint32_t face_id; +} hicn_consumer_output_params; + +typedef struct { + ip46_address prefix; + uint32_t face_id; +} hicn_producer_set_route_params; + +vpp_plugin_binary_api_t* hicn_binary_api_init(vpp_binary_api_t* api); + +int hicn_binary_api_register_prod_app( + vpp_plugin_binary_api_t* api, hicn_producer_input_params* input_params, + hicn_producer_output_params* output_params); + +int hicn_binary_api_register_cons_app( + vpp_plugin_binary_api_t* api, hicn_consumer_input_params* input_params, + hicn_consumer_output_params* output_params); + +int hicn_binary_api_register_route( + vpp_plugin_binary_api_t* api, hicn_producer_set_route_params* input_params); + +char* hicn_binary_api_get_error_string(int ret_val); + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc new file mode 100755 index 000000000..03a294957 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.cc @@ -0,0 +1,84 @@ +/* + * 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/hicn_forwarder_interface.h> + +#define ADDR_INET 1 +#define ADDR_INET6 2 +#define ADD_ROUTE 3 +#define REQUEST_LIGHT 100 + +union AddressLight { + uint32_t ipv4; + struct in6_addr ipv6; +}; + +typedef struct { + uint8_t message_type; + uint8_t command_id; + uint16_t length; + uint32_t seq_num; + char symbolic_or_connid[16]; + union AddressLight address; + uint16_t cost; + uint8_t address_type; + uint8_t len; +} RouteToSelfCommand; + +namespace transport { + +namespace core { + +HicnForwarderInterface::HicnForwarderInterface(SocketConnector &connector) + : ForwarderInterface<HicnForwarderInterface, SocketConnector>(connector) {} + +HicnForwarderInterface::~HicnForwarderInterface() {} + +void HicnForwarderInterface::connect(bool is_consumer) { connector_.connect(); } + +void HicnForwarderInterface::registerRoute(Prefix &prefix) { + auto addr = prefix.toSockaddr(); + const char *identifier = {"SELF_ROUTE"}; + + // allocate command payload + RouteToSelfCommand *route_to_self = new RouteToSelfCommand(); + + // check and set IP address + if (addr->sa_family == AF_INET) { + route_to_self->address_type = ADDR_INET; + route_to_self->address.ipv4 = ((Sockaddr4 *)addr.get())->sin_addr.s_addr; + } else if (addr->sa_family == AF_INET6) { + route_to_self->address_type = ADDR_INET6; + route_to_self->address.ipv6 = ((Sockaddr6 *)addr.get())->sin6_addr; + } + + // Fill remaining payload fields + strcpy(route_to_self->symbolic_or_connid, identifier); + route_to_self->cost = 1; + route_to_self->len = prefix.getPrefixLength(); + + // Allocate and fill the header + route_to_self->command_id = ADD_ROUTE; + route_to_self->message_type = REQUEST_LIGHT; + route_to_self->length = 1; + // route_to_self->seq_num not needed for now + + send((uint8_t *)route_to_self, sizeof(RouteToSelfCommand), + [route_to_self]() { delete route_to_self; }); +} + +} // namespace core + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/hicn_forwarder_interface.h b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.h new file mode 100755 index 000000000..e57fae105 --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_forwarder_interface.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/forwarder_interface.h> +#include <hicn/transport/core/prefix.h> +#include <hicn/transport/core/socket_connector.h> + +#include <deque> + +namespace transport { + +namespace core { + +class HicnForwarderInterface + : public ForwarderInterface<HicnForwarderInterface, SocketConnector> { + public: + union addressLight { + uint32_t ipv4; + struct in6_addr ipv6; + }; + + struct route_to_self_command { + uint8_t messageType; + uint8_t commandID; + uint16_t length; + uint32_t seqNum; + char symbolicOrConnid[16]; + union addressLight address; + uint16_t cost; + uint8_t addressType; + uint8_t len; + }; + + using route_to_self_command = struct route_to_self_command; + using ConnectorType = SocketConnector; + + HicnForwarderInterface(SocketConnector &connector); + + ~HicnForwarderInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + std::uint16_t getMtu() { return interface_mtu; } + + private: + static constexpr std::uint16_t interface_mtu = 1500; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/hicn_memif_api.c b/libtransport/src/hicn/transport/core/hicn_memif_api.c new file mode 100755 index 000000000..e69de29bb --- /dev/null +++ b/libtransport/src/hicn/transport/core/hicn_memif_api.c diff --git a/libtransport/src/hicn/transport/core/interest.cc b/libtransport/src/hicn/transport/core/interest.cc new file mode 100755 index 000000000..ff4a5bb34 --- /dev/null +++ b/libtransport/src/hicn/transport/core/interest.cc @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/interest.h> +#include <hicn/transport/errors/errors.h> +#include <hicn/transport/utils/hash.h> + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include <hicn/hicn.h> +} + +#include <cstring> +#include <memory> + +namespace transport { + +namespace core { + +Interest::Interest(const Name &interest_name, Packet::Format format) + : Packet(format) { + if (hicn_interest_set_name(format_, (hicn_header_t *)packet_start_, + interest_name.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +Interest::Interest(hicn_format_t format) : Interest(base_name, format) {} + +Interest::Interest(const uint8_t *buffer, std::size_t size) + : Packet(buffer, size) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +Interest::Interest(MemBufPtr &&buffer) : Packet(std::move(buffer)) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } +} + +Interest::Interest(Interest &&other_interest) + : Packet(std::move(other_interest)) { + name_ = std::move(other_interest.name_); +} + +Interest::~Interest() {} + +const Name &Interest::getName() const { + if (!name_) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + (hicn_name_t *)name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + return name_; +} + +Name &Interest::getWritableName() { + if (!name_) { + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + (hicn_name_t *)name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + } + + return name_; +} + +Interest &Interest::setName(const Name &name) { + if (hicn_interest_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException("Error setting interest name."); + } + + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + return *this; +} + +Interest &Interest::setName(Name &&name) { + if (hicn_interest_set_name(format_, (hicn_header_t *)packet_start_, + name.getStructReference()) < 0) { + throw errors::RuntimeException("Error setting interest name."); + } + + if (hicn_interest_get_name(format_, (hicn_header_t *)packet_start_, + name_.getStructReference()) < 0) { + throw errors::MalformedPacketException(); + } + + return *this; +} + +void Interest::setLocator(const ip_address_t &ip_address) { + if (hicn_interest_set_locator(format_, (hicn_header_t *)packet_start_, + &ip_address) < 0) { + throw errors::RuntimeException("Error setting interest locator."); + } + + return; +} + +ip_address_t Interest::getLocator() const { + ip_address_t ip; + + if (hicn_interest_get_locator(format_, (hicn_header_t *)packet_start_, &ip) < + 0) { + throw errors::RuntimeException("Error getting interest locator."); + } + + return ip; +} + +void Interest::resetForHash() { + if (hicn_interest_reset_for_hash( + format_, reinterpret_cast<hicn_header_t *>(packet_start_)) < 0) { + throw errors::RuntimeException( + "Error resetting interest fields for hash computation."); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/interest.h b/libtransport/src/hicn/transport/core/interest.h new file mode 100755 index 000000000..75fcba8eb --- /dev/null +++ b/libtransport/src/hicn/transport/core/interest.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/name.h> +#include <hicn/transport/core/packet.h> +#include <hicn/transport/utils/object_pool.h> + +namespace transport { + +namespace core { + +class Interest + : public Packet /*, public std::enable_shared_from_this<Interest>*/ { + public: + using Ptr = utils::ObjectPool<Interest>::Ptr; + + Interest(Packet::Format format = HF_INET6_TCP); + + Interest(const Name &interest_name, Packet::Format format = HF_INET6_TCP); + + Interest(const uint8_t *buffer, std::size_t size); + Interest(MemBufPtr &&buffer); + + /* + * Enforce zero-copy. + */ + Interest(const Interest &other_interest) = delete; + Interest &operator=(const Interest &other_interest) = delete; + + Interest(Interest &&other_interest); + + ~Interest() override; + + const Name &getName() const; + + Name &getWritableName(); + + Interest &setName(const Name &name); + + Interest &setName(Name &&name); + + void setLocator(const ip_address_t &ip_address) override; + + ip_address_t getLocator() const override; + + private: + void resetForHash() override; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/key_locator.cc b/libtransport/src/hicn/transport/core/key_locator.cc new file mode 100755 index 000000000..509fc35ff --- /dev/null +++ b/libtransport/src/hicn/transport/core/key_locator.cc @@ -0,0 +1,42 @@ +/* + * 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/key_locator.h> + +namespace transport { + +namespace core { + +KeyLocator::KeyLocator() : type_(KeyLocatorType::UNKNOWN) {} + +KeyLocator::KeyLocator(KeyLocatorType type, Name &name) + : type_(type), name_(name) {} + +Name &KeyLocator::getName() { return name_; } + +void KeyLocator::setName(Name &name) { name_ = name; } + +void KeyLocator::setType(KeyLocatorType type) { type_ = type; } + +KeyLocatorType KeyLocator::getType() { return type_; } + +void KeyLocator::clear() { + type_ = KeyLocatorType::UNKNOWN; + name_.clear(); +} + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/key_locator.h b/libtransport/src/hicn/transport/core/key_locator.h new file mode 100755 index 000000000..ae3a4ab08 --- /dev/null +++ b/libtransport/src/hicn/transport/core/key_locator.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/key_locator_type.h> +#include <hicn/transport/core/name.h> + +namespace transport { + +namespace core { + +class KeyLocator : public std::enable_shared_from_this<KeyLocator> { + public: + KeyLocator(); + + KeyLocator(KeyLocatorType type, Name &name); + + KeyLocatorType getType(); + + void setType(KeyLocatorType type); + + void setName(Name &name); + + Name &getName(); + + void clear(); + + private: + KeyLocatorType type_; + Name name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/key_locator_type.h b/libtransport/src/hicn/transport/core/key_locator_type.h new file mode 100755 index 000000000..0c84a43ca --- /dev/null +++ b/libtransport/src/hicn/transport/core/key_locator_type.h @@ -0,0 +1,28 @@ +/* + * 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 + +namespace transport { + +namespace core { + +enum Type { NAME = 0, KEY_DIGEST = 1, UNKNOWN = 255 }; + +typedef enum Type KeyLocatorType; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/manifest.cc b/libtransport/src/hicn/transport/core/manifest.cc new file mode 100755 index 000000000..3f890f3d0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/manifest.h> + +namespace transport { + +namespace core { + +std::string ManifestEncoding::manifest_type = std::string("manifest_type"); + +std::map<ManifestType, std::string> ManifestEncoding::manifest_types = { + {FINAL_CHUNK_NUMBER, "FinalChunkNumber"}, {NAME_LIST, "NameList"}}; + +std::string ManifestEncoding::final_chunk_number = + std::string("final_chunk_number"); +std::string ManifestEncoding::content_name = std::string("content_name"); + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest.h b/libtransport/src/hicn/transport/core/manifest.h new file mode 100755 index 000000000..767addb2e --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/core/name.h> + +#include <set> + +namespace transport { + +namespace core { + +using typename core::Name; +using typename core::Packet; +using typename core::PayloadType; + +template <typename Base, typename FormatTraits, typename ManifestImpl> +class Manifest : public Base { + static_assert(std::is_base_of<Packet, Base>::value, + "Base must inherit from packet!"); + + public: + using Encoder = typename FormatTraits::Encoder; + using Decoder = typename FormatTraits::Decoder; + + Manifest() + : packet_(new Base(HF_INET6_TCP_AH), nullptr), + encoder_(*packet_), + decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + Manifest(const core::Name& name) + : packet_(new Base(name, HF_INET6_TCP_AH), nullptr), + encoder_(*packet_), + decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + Manifest(typename Base::Ptr&& base) + : packet_(std::move(base)), encoder_(*packet_), decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + template <typename T> + Manifest(T&& base) + : packet_(new Base(std::move<T&&>(base)), nullptr), + encoder_(*packet_), + decoder_(*packet_) { + Base::setPayloadType(PayloadType::MANIFEST); + } + + virtual ~Manifest() = default; + + bool operator==(const Manifest& other) { + return this->packet_ == other.packet_; + } + + std::size_t estimateManifestSize(std::size_t additional_entries = 0) { + return static_cast<ManifestImpl&>(*this).estimateManifestSizeImpl( + additional_entries); + } + + /* + * After the call to encode, users MUST call clear before adding data + * to the manifest. + */ + Manifest& encode() { return static_cast<ManifestImpl&>(*this).encodeImpl(); } + + Manifest& decode() { + Manifest::decoder_.decode(); + + manifest_type_ = decoder_.getManifestType(); + hash_algorithm_ = decoder_.getHashAlgorithm(); + is_last_ = decoder_.getIsFinalManifest(); + + return static_cast<ManifestImpl&>(*this).decodeImpl(); + } + + static std::size_t getManifestHeaderSize() { + return Encoder::getManifestHeaderSize(); + } + + Manifest& setManifestType(ManifestType type) { + manifest_type_ = type; + encoder_.setManifestType(manifest_type_); + return *this; + } + + Manifest& setHashAlgorithm(HashAlgorithm hash_algorithm) { + hash_algorithm_ = hash_algorithm; + encoder_.setHashAlgorithm(hash_algorithm_); + return *this; + } + + HashAlgorithm getHashAlgorithm() { return hash_algorithm_; } + + ManifestType getManifestType() const { return manifest_type_; } + + bool isFinalManifest() const { return is_last_; } + + Manifest& setVersion(ManifestVersion version) { + encoder_.setVersion(version); + return *this; + } + + Manifest& setFinalBlockNumber(std::uint32_t final_block_number) { + encoder_.setFinalBlockNumber(final_block_number); + return *this; + } + + uint32_t getFinalBlockNumber() const { + return decoder_.getFinalBlockNumber(); + } + + ManifestVersion getVersion() const { return decoder_.getVersion(); } + + Manifest& setFinalManifest(bool is_final_manifest) { + encoder_.setIsFinalManifest(is_final_manifest); + is_last_ = is_final_manifest; + return *this; + } + + Manifest& clear() { + encoder_.clear(); + decoder_.clear(); + return *this; + } + + void setSignatureSize(std::size_t size_bits) { + Packet::setSignatureSize(size_bits); + encoder_.update(); + } + + typename Base::Ptr&& getPacket() { return std::move(packet_); } + + protected: + typename Base::Ptr packet_; + ManifestType manifest_type_; + HashAlgorithm hash_algorithm_; + bool is_last_; + + Encoder encoder_; + Decoder decoder_; +}; + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format.h b/libtransport/src/hicn/transport/core/manifest_format.h new file mode 100755 index 000000000..1dcf013dc --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/name.h> +#include <hicn/transport/utils/crypto_hasher.h> + +#include <cinttypes> +#include <type_traits> +#include <unordered_map> + +namespace transport { + +namespace core { + +enum class ManifestFields : uint8_t { + VERSION, + HASH_ALGORITHM, + SEGMENT_CALCULATION_STRATEGY, + FINAL_MANIFEST, + NAME_HASH_LIST, + BASE_NAME +}; + +enum class ManifestVersion : uint8_t { + VERSION_1 = 1, +}; + +enum class ManifestType : uint8_t { + INLINE_MANIFEST = 1, + FINAL_CHUNK_NUMBER = 2, + FLIC_MANIFEST = 3, +}; + +enum class HashAlgorithm : uint8_t { + SHA_256 = static_cast<uint8_t>(utils::CryptoHashType::SHA_256), + SHA_512 = static_cast<uint8_t>(utils::CryptoHashType::SHA_512), + CRC32C = static_cast<uint8_t>(utils::CryptoHashType::CRC32C), +}; + +enum class NextSegmentCalculationStrategy : uint8_t { + INCREMENTAL = 1, +}; + +template <typename T> +struct format_traits { + using Encoder = typename T::Encoder; + using Decoder = typename T::Decoder; + using HashType = typename T::HashType; + using HashList = typename T::HashList; +}; + +class Packet; + +template <typename Implementation> +class ManifestEncoder { + public: + virtual ~ManifestEncoder() = default; + + ManifestEncoder encode() { + return static_cast<Implementation&>(*this).encodeImpl(); + } + + ManifestEncoder& clear() { + return static_cast<Implementation&>(*this).clearImpl(); + } + + ManifestEncoder& setManifestType(ManifestType type) { + return static_cast<Implementation&>(*this).setManifestTypeImpl(type); + } + + ManifestEncoder& setHashAlgorithm(HashAlgorithm hash) { + return static_cast<Implementation&>(*this).setHashAlgorithmImpl(hash); + } + + ManifestEncoder& setFinalChunkNumber(uint32_t final_chunk) { + return static_cast<Implementation&>(*this).setFinalChunkImpl(final_chunk); + } + + ManifestEncoder& setNextSegmentCalculationStrategy( + NextSegmentCalculationStrategy strategy) { + return static_cast<Implementation&>(*this) + .setNextSegmentCalculationStrategyImpl(strategy); + } + + template < + typename T, + typename = std::enable_if_t<std::is_same< + std::remove_const_t<std::remove_reference_t<T>>, core::Name>::value>> + ManifestEncoder& setBaseName(T&& name) { + return static_cast<Implementation&>(*this).setBaseNameImpl(name); + } + + template <typename Hash> + ManifestEncoder& addSuffixAndHash(uint32_t suffix, Hash&& hash) { + return static_cast<Implementation&>(*this).addSuffixAndHashImpl( + suffix, std::forward<Hash&&>(hash)); + } + + ManifestEncoder& setIsFinalManifest(bool is_last) { + return static_cast<Implementation&>(*this).setIsFinalManifestImpl(is_last); + } + + ManifestEncoder& setVersion(ManifestVersion version) { + return static_cast<Implementation&>(*this).setVersionImpl(version); + } + + std::size_t estimateSerializedLength(std::size_t number_of_entries) { + return static_cast<Implementation&>(*this).estimateSerializedLengthImpl( + number_of_entries); + } + + ManifestEncoder& update() { + return static_cast<Implementation&>(*this).updateImpl(); + } + + ManifestEncoder& setFinalBlockNumber(std::uint32_t final_block_number) { + return static_cast<Implementation&>(*this).setFinalBlockNumberImpl( + final_block_number); + } + + static std::size_t getManifestHeaderSize() { + return Implementation::getManifestHeaderSizeImpl(); + } +}; + +template <typename Implementation> +class ManifestDecoder { + public: + virtual ~ManifestDecoder() = default; + + ManifestDecoder& clear() { + return static_cast<Implementation&>(*this).clearImpl(); + } + + void decode() { static_cast<Implementation&>(*this).decodeImpl(); } + + ManifestType getManifestType() const { + return static_cast<const Implementation&>(*this).getManifestTypeImpl(); + } + + HashAlgorithm getHashAlgorithm() const { + return static_cast<const Implementation&>(*this).getHashAlgorithmImpl(); + } + + uint32_t getFinalChunkNumber() const { + return static_cast<const Implementation&>(*this).getFinalChunkImpl(); + } + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() const { + return static_cast<const Implementation&>(*this) + .getNextSegmentCalculationStrategyImpl(); + } + + core::Name getBaseName() const { + return static_cast<const Implementation&>(*this).getBaseNameImpl(); + } + + auto getSuffixHashList() { + return static_cast<Implementation&>(*this).getSuffixHashListImpl(); + } + + bool getIsFinalManifest() const { + return static_cast<const Implementation&>(*this).getIsFinalManifestImpl(); + } + + ManifestVersion getVersion() const { + return static_cast<const Implementation&>(*this).getVersionImpl(); + } + + std::size_t estimateSerializedLength(std::size_t number_of_entries) const { + return static_cast<const Implementation&>(*this) + .estimateSerializedLengthImpl(number_of_entries); + } + + uint32_t getFinalBlockNumber() const { + return static_cast<const Implementation&>(*this).getFinalBlockNumberImpl(); + } +}; + +} // namespace core + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_fixed.cc b/libtransport/src/hicn/transport/core/manifest_format_fixed.cc new file mode 100755 index 000000000..f26f20adb --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_fixed.cc @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/manifest_format_fixed.h> +#include <hicn/transport/core/packet.h> +#include <hicn/transport/utils/endianess.h> +#include <hicn/transport/utils/literals.h> + +namespace transport { + +namespace core { + +// TODO use preallocated pool of membufs +FixedManifestEncoder::FixedManifestEncoder(Packet& packet) + : packet_(packet), + max_size_(Packet::default_mtu - packet_.headerSize()), + manifest_( + utils::MemBuf::create(Packet::default_mtu - packet_.headerSize())), + manifest_header_( + reinterpret_cast<ManifestHeader*>(manifest_->writableData())), + manifest_entries_(reinterpret_cast<ManifestEntry*>( + manifest_->writableData() + sizeof(ManifestHeader))), + current_entry_(0) {} + +FixedManifestEncoder::~FixedManifestEncoder() {} + +FixedManifestEncoder& FixedManifestEncoder::encodeImpl() { + packet_.appendPayload(std::move(manifest_)); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::clearImpl() { + manifest_ = utils::MemBuf::create(Packet::default_mtu - packet_.headerSize()); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setHashAlgorithmImpl( + HashAlgorithm algorithm) { + manifest_header_->hash_algorithm = static_cast<uint8_t>(algorithm); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setManifestTypeImpl( + ManifestType manifest_type) { + manifest_header_->manifest_type = static_cast<uint8_t>(manifest_type); + return *this; +} + +FixedManifestEncoder& +FixedManifestEncoder::setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy) { + manifest_header_->next_segment_strategy = static_cast<uint8_t>(strategy); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setBaseNameImpl( + const core::Name& base_name) { + base_name.copyToDestination( + reinterpret_cast<uint8_t*>(&manifest_header_->prefix[0]), false); + manifest_header_->flags.ipv6 = + base_name.getAddressFamily() == AF_INET6 ? 1_U8 : 0_U8; + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::addSuffixAndHashImpl( + uint32_t suffix, const utils::CryptoHash& hash) { + auto _hash = hash.getDigest<std::uint8_t>(); + addSuffixHashBytes(suffix, _hash.data(), _hash.length()); + return *this; +} + +void FixedManifestEncoder::addSuffixHashBytes(uint32_t suffix, + const uint8_t* hash, + std::size_t length) { + manifest_entries_[current_entry_].suffix = utils::hton(suffix); + // std::copy(hash, hash + length, + // manifest_entries_[current_entry_].hash); + std::memcpy( + reinterpret_cast<uint8_t*>(manifest_entries_[current_entry_].hash), hash, + length); + + manifest_header_->number_of_entries++; + current_entry_++; + + if (TRANSPORT_EXPECT_FALSE(estimateSerializedLengthImpl() > max_size_)) { + throw errors::RuntimeException("Manifest size exceeded the packet MTU!"); + } +} + +FixedManifestEncoder& FixedManifestEncoder::setIsFinalManifestImpl( + bool is_last) { + manifest_header_->flags.is_last = static_cast<uint8_t>(is_last); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setVersionImpl( + ManifestVersion version) { + manifest_header_->version = static_cast<uint8_t>(version); + return *this; +} + +std::size_t FixedManifestEncoder::estimateSerializedLengthImpl( + std::size_t additional_entries) { + return sizeof(ManifestHeader) + + (manifest_header_->number_of_entries + additional_entries) * + sizeof(ManifestEntry); +} + +FixedManifestEncoder& FixedManifestEncoder::updateImpl() { + max_size_ = Packet::default_mtu - packet_.headerSize(); + manifest_header_ = reinterpret_cast<ManifestHeader*>( + const_cast<uint8_t*>(packet_.getPayload().data())); + manifest_entries_ = reinterpret_cast<ManifestEntry*>( + const_cast<uint8_t*>(packet_.getPayload().data()) + + sizeof(ManifestHeader)); + return *this; +} + +FixedManifestEncoder& FixedManifestEncoder::setFinalBlockNumberImpl( + std::uint32_t final_block_number) { + manifest_header_->final_block_number = utils::hton(final_block_number); + return *this; +} + +std::size_t FixedManifestEncoder::getManifestHeaderSizeImpl() { + return sizeof(ManifestHeader); +} + +FixedManifestDecoder::FixedManifestDecoder(Packet& packet) + : packet_(packet), + manifest_header_(reinterpret_cast<ManifestHeader*>( + const_cast<uint8_t*>(packet_.getPayload().data()))), + manifest_entries_(reinterpret_cast<ManifestEntry*>( + const_cast<uint8_t*>(packet_.getPayload().data()) + + sizeof(ManifestHeader))) {} + +FixedManifestDecoder::~FixedManifestDecoder() {} + +void FixedManifestDecoder::decodeImpl() { + std::size_t packet_size = packet_.payloadSize(); + + if (packet_size < sizeof(ManifestHeader) || + packet_size < estimateSerializedLengthImpl()) { + throw errors::RuntimeException( + "The packet does not match expected manifest size."); + } +} + +FixedManifestDecoder& FixedManifestDecoder::clearImpl() { return *this; } + +ManifestType FixedManifestDecoder::getManifestTypeImpl() const { + return static_cast<ManifestType>(manifest_header_->manifest_type); +} + +HashAlgorithm FixedManifestDecoder::getHashAlgorithmImpl() const { + return static_cast<HashAlgorithm>(manifest_header_->hash_algorithm); +} + +NextSegmentCalculationStrategy +FixedManifestDecoder::getNextSegmentCalculationStrategyImpl() const { + return static_cast<NextSegmentCalculationStrategy>( + manifest_header_->next_segment_strategy); +} + +typename Fixed::SuffixList FixedManifestDecoder::getSuffixHashListImpl() { + typename Fixed::SuffixList hash_list; + + for (int i = 0; i < manifest_header_->number_of_entries; i++) { + hash_list.insert(hash_list.end(), + std::make_pair(utils::ntoh(manifest_entries_[i].suffix), + reinterpret_cast<uint8_t*>( + &manifest_entries_[i].hash[0]))); + } + + return hash_list; +} + +core::Name FixedManifestDecoder::getBaseNameImpl() const { + if (static_cast<bool>(manifest_header_->flags.ipv6)) { + return core::Name(AF_INET6, + reinterpret_cast<uint8_t*>(&manifest_header_->prefix)); + } else { + return core::Name(AF_INET, + reinterpret_cast<uint8_t*>(&manifest_header_->prefix)); + } +} + +bool FixedManifestDecoder::getIsFinalManifestImpl() const { + return static_cast<bool>(manifest_header_->flags.is_last); +} + +ManifestVersion FixedManifestDecoder::getVersionImpl() const { + return static_cast<ManifestVersion>(manifest_header_->version); +} + +std::size_t FixedManifestDecoder::estimateSerializedLengthImpl( + std::size_t additional_entries) const { + return sizeof(ManifestHeader) + + (additional_entries + manifest_header_->number_of_entries) * + sizeof(ManifestEntry); +} + +uint32_t FixedManifestDecoder::getFinalBlockNumberImpl() const { + return utils::ntoh(manifest_header_->final_block_number); +} + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_fixed.h b/libtransport/src/hicn/transport/core/manifest_format_fixed.h new file mode 100755 index 000000000..66825e2f4 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_fixed.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/core/packet.h> + +#include <string> + +namespace transport { + +namespace core { + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Version| MType |HashAlg|NextStr| Flags |NumberOfEntries| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Final Block Number | +// +---------------------------------------------------------------| +// | | +// + + +// | | +// + Prefix + +// | | +// + + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Suffix | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Hash Value | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class FixedManifestEncoder; +class FixedManifestDecoder; +class Packet; + +struct Fixed { + using Encoder = FixedManifestEncoder; + using Decoder = FixedManifestDecoder; + using HashType = utils::CryptoHash; + using SuffixList = std::list<std::pair<std::uint32_t, std::uint8_t*>>; +}; + +struct Flags { + std::uint8_t ipv6 : 1; + std::uint8_t is_last : 1; + std::uint8_t unused : 6; +}; + +struct ManifestEntry { + std::uint32_t suffix; + std::uint32_t hash[8]; +}; + +struct ManifestHeader { + std::uint8_t version : 4; + std::uint8_t manifest_type : 4; + std::uint8_t hash_algorithm : 4; + std::uint8_t next_segment_strategy : 4; + Flags flags; + std::uint8_t number_of_entries; + std::uint32_t final_block_number; + std::uint32_t prefix[4]; + ManifestEntry entries[0]; +}; + +static const constexpr std::uint8_t manifest_version = 1; + +class FixedManifestEncoder : public ManifestEncoder<FixedManifestEncoder> { + public: + FixedManifestEncoder(Packet& packet); + + ~FixedManifestEncoder(); + + FixedManifestEncoder& encodeImpl(); + + FixedManifestEncoder& clearImpl(); + + FixedManifestEncoder& setManifestTypeImpl(ManifestType manifest_type); + + FixedManifestEncoder& setHashAlgorithmImpl(HashAlgorithm algorithm); + + FixedManifestEncoder& setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy); + + FixedManifestEncoder& setBaseNameImpl(const core::Name& base_name); + + FixedManifestEncoder& addSuffixAndHashImpl(uint32_t suffix, + const utils::CryptoHash& hash); + + FixedManifestEncoder& setIsFinalManifestImpl(bool is_last); + + FixedManifestEncoder& setVersionImpl(ManifestVersion version); + + std::size_t estimateSerializedLengthImpl(std::size_t additional_entries = 0); + + FixedManifestEncoder& updateImpl(); + + FixedManifestEncoder& setFinalBlockNumberImpl( + std::uint32_t final_block_number); + + static std::size_t getManifestHeaderSizeImpl(); + + private: + void addSuffixHashBytes(uint32_t suffix, const uint8_t* hash, + std::size_t length); + + Packet& packet_; + std::size_t max_size_; + std::unique_ptr<utils::MemBuf> manifest_; + ManifestHeader* manifest_header_; + ManifestEntry* manifest_entries_; + std::size_t current_entry_; +}; + +class FixedManifestDecoder : public ManifestDecoder<FixedManifestDecoder> { + public: + FixedManifestDecoder(Packet& packet); + + ~FixedManifestDecoder(); + + void decodeImpl(); + + FixedManifestDecoder& clearImpl(); + + ManifestType getManifestTypeImpl() const; + + HashAlgorithm getHashAlgorithmImpl() const; + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; + + typename Fixed::SuffixList getSuffixHashListImpl(); + + core::Name getBaseNameImpl() const; + + bool getIsFinalManifestImpl() const; + + std::size_t estimateSerializedLengthImpl( + std::size_t additional_entries = 0) const; + + ManifestVersion getVersionImpl() const; + + uint32_t getFinalBlockNumberImpl() const; + + private: + Packet& packet_; + ManifestHeader* manifest_header_; + ManifestEntry* manifest_entries_; +}; + +} // namespace core + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc new file mode 100755 index 000000000..512cdba5b --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.cc @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/packet.h> +#include <hicn/transport/portability/transport_portability.h> + +#include <array> + +namespace transport { + +namespace core { + +namespace { + +template <typename T> +TRANSPORT_ALWAYS_INLINE void checkPointer(T* pointer) { + if (pointer == nullptr) { + throw errors::NullPointerException(); + } +} + +template <typename EnumType> +TRANSPORT_ALWAYS_INLINE void setValueToJson(Json::Value& root, EnumType value) { + root[JSONKey<EnumType>::key] = static_cast<uint8_t>(value); +} + +template <typename EnumType> +TRANSPORT_ALWAYS_INLINE EnumType getValueFromJson(const Json::Value& root) { + return static_cast<EnumType>(root[JSONKey<EnumType>::key].asUInt()); +}; + +} // namespace + +JSONManifestEncoder::JSONManifestEncoder(Packet& packet) : packet_(packet) {} + +JSONManifestEncoder::~JSONManifestEncoder() {} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::encodeImpl() { + Json::StreamWriterBuilder writer_builder; + Json::StreamWriter* fast_writer = writer_builder.newStreamWriter(); + + asio::streambuf strbuf; + strbuf.prepare(1500); + std::ostream stream(&strbuf); + fast_writer->write(root_, &stream); + + const uint8_t* buffer = asio::buffer_cast<const uint8_t*>(strbuf.data()); + + packet_.setPayload(buffer, strbuf.size()); + + delete fast_writer; + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::clearImpl() { + root_.clear(); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setHashAlgorithmImpl(HashAlgorithm algorithm) { + setValueToJson(root_, algorithm); + return *this; +} + +JSONManifestEncoder& JSONManifestEncoder::setManifestTypeImpl( + ManifestType manifest_type) { + setValueToJson(root_, manifest_type); + return *this; +} + +JSONManifestEncoder& JSONManifestEncoder::setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy) { + setValueToJson(root_, strategy); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setBaseNameImpl(const core::Name& base_name) { + root_[JSONKey<core::Name>::key] = base_name.toString().c_str(); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::addSuffixAndHashImpl(uint32_t suffix, + const utils::CryptoHash& hash) { + throw errors::NotImplementedException(); + // Json::Value value(Json::arrayValue); + // value.append(Json::Value(suffix)); + // value.append(Json::Value(Json::Value::UInt64 (hash))); + // root_[JSONKey<SuffixHashList>::key].append(value); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setIsFinalManifestImpl(bool is_last) { + root_[JSONKey<bool>::final_manifest] = is_last; + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setVersionImpl(ManifestVersion version) { + setValueToJson(root_, version); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setSuffixHashListImpl( + const typename JSON::SuffixList& name_hash_list) { + throw errors::NotImplementedException(); + // for (auto &suffix : name_hash_list) { + // addSuffixAndHashImpl(suffix.first, suffix.second); + // } + // + // return *this; +} + +TRANSPORT_ALWAYS_INLINE std::size_t +JSONManifestEncoder::estimateSerializedLengthImpl( + std::size_t number_of_entries) { + Json::StreamWriterBuilder writer_builder; + Json::StreamWriter* fast_writer = writer_builder.newStreamWriter(); + + asio::streambuf strbuf; + strbuf.prepare(1500); + std::ostream stream(&strbuf); + fast_writer->write(root_, &stream); + + return strbuf.size(); +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::updateImpl() { + throw errors::NotImplementedException(); +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setFinalBlockNumberImpl(std::uint32_t final_block_number) { + throw errors::NotImplementedException(); +} + +TRANSPORT_ALWAYS_INLINE std::size_t +JSONManifestEncoder::getManifestHeaderSizeImpl() { + return 0; +} + +JSONManifestDecoder::JSONManifestDecoder(Packet& packet) : packet_(packet) {} + +JSONManifestDecoder::~JSONManifestDecoder() {} + +TRANSPORT_ALWAYS_INLINE void JSONManifestDecoder::decodeImpl() { + auto array = packet_.getPayload(); + auto payload = array.data(); + auto payload_size = array.length(); + + Json::CharReaderBuilder reader_builder; + Json::CharReader* reader = reader_builder.newCharReader(); + std::string errors; + + if (!reader->parse((char*)payload, (char*)payload + payload_size, &root_, + &errors)) { + TRANSPORT_LOGE("Error parsing manifest!"); + TRANSPORT_LOGE("%s", errors.c_str()); + + delete reader; + + throw errors::MalformedPacketException(); + } + + delete reader; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder& JSONManifestDecoder::clearImpl() { + root_.clear(); + return *this; +} + +TRANSPORT_ALWAYS_INLINE ManifestType +JSONManifestDecoder::getManifestTypeImpl() const { + return getValueFromJson<ManifestType>(root_); +} + +TRANSPORT_ALWAYS_INLINE HashAlgorithm +JSONManifestDecoder::getHashAlgorithmImpl() const { + return getValueFromJson<HashAlgorithm>(root_); +} + +TRANSPORT_ALWAYS_INLINE NextSegmentCalculationStrategy +JSONManifestDecoder::getNextSegmentCalculationStrategyImpl() const { + return getValueFromJson<NextSegmentCalculationStrategy>(root_); +} + +TRANSPORT_ALWAYS_INLINE typename JSON::SuffixList +JSONManifestDecoder::getSuffixHashListImpl() { + throw errors::NotImplementedException(); + // SuffixHashList hash_list; + // + // Json::Value &array = root_[JSONKey<SuffixHashList>::key]; + // + // for (Json::Value::ArrayIndex i = 0; + // i != array.size(); + // i++) { + // hash_list[array[i][0].asUInt()] = array[i][1].asUInt64(); + // } + // + // return hash_list; +} + +TRANSPORT_ALWAYS_INLINE core::Name JSONManifestDecoder::getBaseNameImpl() + const { + return core::Name(root_[JSONKey<core::Name>::key].asCString()); +} + +TRANSPORT_ALWAYS_INLINE bool JSONManifestDecoder::getIsFinalManifestImpl() + const { + return root_[JSONKey<bool>::final_manifest].asBool(); +} + +TRANSPORT_ALWAYS_INLINE ManifestVersion +JSONManifestDecoder::getVersionImpl() const { + return getValueFromJson<ManifestVersion>(root_); +} + +TRANSPORT_ALWAYS_INLINE uint32_t +JSONManifestDecoder::getFinalBlockNumberImpl() const { + return 0; +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h new file mode 100755 index 000000000..39f0cf351 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_jsoncpp.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/core/name.h> + +#if defined(__APPLE__) || defined(__ANDROID__) +#include <json/json.h> +#else +#include <jsoncpp/json/json.h> +#endif /* __APPLE__ || __ANDROID__*/ + +#include <string> + +namespace transport { + +namespace core { + +class JSONManifestEncoder; +class JSONManifestDecoder; +class Packet; + +struct JSON { + using Encoder = JSONManifestEncoder; + using Decoder = JSONManifestDecoder; + using HashType = utils::CryptoHash; + using SuffixList = std::unordered_map<std::uint32_t, std::uint8_t*>; +}; + +template <typename T> +struct JSONKey; + +template <> +struct JSONKey<ManifestVersion> { + static const constexpr char* key = "manifest_version"; +}; + +template <> +struct JSONKey<HashAlgorithm> { + static const constexpr char* key = "hash_algorithm"; +}; + +template <> +struct JSONKey<ManifestType> { + static const constexpr char* key = "manifest_type"; +}; + +template <> +struct JSONKey<NextSegmentCalculationStrategy> { + static const constexpr char* key = "next_segment_strategy"; +}; + +template <> +struct JSONKey<typename JSON::SuffixList> { + static const constexpr char* key = "suffix_hash_list"; +}; + +template <> +struct JSONKey<core::Name> { + static const constexpr char* key = "base_name"; +}; + +template <> +struct JSONKey<bool> { + static const constexpr char* final_manifest = "final_manifest"; +}; + +class JSONManifestEncoder : public ManifestEncoder<JSONManifestEncoder> { + public: + JSONManifestEncoder(Packet& packet); + + ~JSONManifestEncoder() override; + + JSONManifestEncoder& encodeImpl(); + + JSONManifestEncoder& clearImpl(); + + JSONManifestEncoder& setManifestTypeImpl(ManifestType manifest_type); + + JSONManifestEncoder& setHashAlgorithmImpl(HashAlgorithm algorithm); + + JSONManifestEncoder& setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy); + + JSONManifestEncoder& setSuffixHashListImpl( + const typename JSON::SuffixList& name_hash_list); + + JSONManifestEncoder& setBaseNameImpl(const core::Name& base_name); + + JSONManifestEncoder& addSuffixAndHashImpl(uint32_t suffix, + const utils::CryptoHash& hash); + + JSONManifestEncoder& setIsFinalManifestImpl(bool is_last); + + JSONManifestEncoder& setVersionImpl(ManifestVersion version); + + std::size_t estimateSerializedLengthImpl(std::size_t number_of_entries); + + JSONManifestEncoder& updateImpl(); + + JSONManifestEncoder& setFinalBlockNumberImpl( + std::uint32_t final_block_number); + + static std::size_t getManifestHeaderSizeImpl(); + + private: + Packet& packet_; + Json::Value root_; +}; + +class JSONManifestDecoder : public ManifestDecoder<JSONManifestDecoder> { + public: + JSONManifestDecoder(Packet& packet); + + ~JSONManifestDecoder() override; + + void decodeImpl(); + + JSONManifestDecoder& clearImpl(); + + ManifestType getManifestTypeImpl() const; + + HashAlgorithm getHashAlgorithmImpl() const; + + uint32_t getFinalChunkImpl() const; + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; + + typename JSON::SuffixList getSuffixHashListImpl(); + + core::Name getBaseNameImpl() const; + + bool getIsFinalManifestImpl() const; + + std::size_t estimateSerializedLengthImpl(std::size_t number_of_entries) const; + + ManifestVersion getVersionImpl() const; + + uint32_t getFinalBlockNumberImpl() const; + + private: + Packet& packet_; + Json::Value root_; +}; + +} // namespace core + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc new file mode 100755 index 000000000..d0365d2fe --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.cc @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/manifest_format_json_libparc_deprecated.h> +#include <hicn/transport/core/packet.h> +#include <hicn/transport/errors/errors.h> +#include <hicn/transport/portability/transport_portability.h> + +extern "C" { +#include <parc/algol/parc_Memory.h> +} + +namespace transport { + +namespace core { + +namespace { + +template <typename T> +TRANSPORT_ALWAYS_INLINE void checkPointer(T* pointer) { + if (pointer == nullptr) { + throw errors::NullPointerException(); + } +} + +template <typename EnumType> +TRANSPORT_ALWAYS_INLINE void setValueToJson(PARCJSON* root, EnumType value) { + parcJSON_AddInteger(root, JSONKey<EnumType>::key, + static_cast<int64_t>(value)); +} + +template <typename EnumType> +TRANSPORT_ALWAYS_INLINE EnumType getValueFromJson(PARCJSON* root) { + checkPointer(root); + + PARCJSONValue* value = parcJSON_GetValueByName(root, JSONKey<EnumType>::key); + + EnumType ret = static_cast<EnumType>(parcJSONValue_GetInteger(value)); + // parcJSONValue_Release(&value); + + return ret; +}; + +} // namespace + +JSONManifestEncoder::JSONManifestEncoder() : root_(parcJSON_Create()) { + parcJSON_Acquire(root_); +} + +JSONManifestEncoder::~JSONManifestEncoder() { + if (root_) { + parcJSON_Release(&root_); + } +} + +TRANSPORT_ALWAYS_INLINE SONManifestEncoder& JSONManifestEncoder::encodeImpl( + Packet& packet) { + char* json_string = parcJSON_ToString(root_); + packet.setPayload(reinterpret_cast<uint8_t*>(json_string), + std::strlen(json_string)); + parcMemory_Deallocate(&json_string); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& JSONManifestEncoder::clearImpl() { + if (root_) { + parcJSON_Release(&root_); + } + + root_ = parcJSON_Create(); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setHashAlgorithmImpl(HashAlgorithm algorithm) { + setValueToJson(root_, algorithm); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setManifestTypeImpl(ManifestType manifest_type) { + setValueToJson(root_, manifest_type); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy) { + setValueToJson(root_, strategy); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setBaseNameImpl(const core::Name& base_name) { + parcJSON_AddString(root_, JSONKey<core::Name>::key, + base_name.toString().c_str()); + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::addSuffixAndHashImpl(uint32_t suffix, + utils::CryptoHash& hash) { + throw errors::NotImplementedException(); + // PARCJSONValue *value = parcJSON_GetValueByName(root_, + // JSONKey<SuffixHashList>::key); + // + // // Create the pair to store in the array. + // // It will be segment number + Hash of the segment + // PARCJSONArray * pair = parcJSONArray_Create(); + // + // PARCJSONValue *v = parcJSONValue_CreateFromInteger(suffix); + // parcJSONArray_AddValue(pair, v); + // parcJSONValue_Release(&v); + // + // v = parcJSONValue_CreateFromInteger(hash); + // parcJSONArray_AddValue(pair, v); + // parcJSONValue_Release(&v); + // + // if (value == nullptr /* || !parcJSONValue_IsArray(value) */) { + // // Create the array + // PARCJSONArray *array = parcJSONArray_Create(); + // parcJSON_AddArray(root_, + // JSONKey<SuffixHashList>::key, + // array); + // parcJSONArray_Release(&array); + // + // value = parcJSON_GetValueByName(root_, JSONKey<SuffixHashList>::key); + // } + // + // v = parcJSONValue_CreateFromJSONArray(pair); + // parcJSONArray_AddValue(parcJSONValue_GetArray(value), v); + // parcJSONValue_Release(&v); + // + // parcJSONArray_Release(&pair); + // // parcJSONValue_Release(&value); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setIsFinalManifestImpl(bool is_last) { + parcJSON_AddBoolean(root_, JSONKey<bool>::final_manifest, is_last); + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestEncoder& +JSONManifestEncoder::setSuffixHashListImpl( + const SuffixHashList& name_hash_list) { + for (auto& suffix : name_hash_list) { + addSuffixAndHashImpl(suffix.first, suffix.second); + } + + return *this; +} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder::JSONManifestDecoder() + : root_(nullptr) {} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder::~JSONManifestDecoder() { + if (root_) { + parcJSON_Release(&root_); + } +} + +TRANSPORT_ALWAYS_INLINE void JSONManifestDecoder::decodeImpl( + const uint8_t* payload, std::size_t payload_size) { + PARCBuffer* b = parcBuffer_Wrap(const_cast<uint8_t*>(payload), payload_size, + 0, payload_size); + clearImpl(); + + root_ = parcJSON_ParseBuffer(b); + parcBuffer_Release(&b); + + char* str = parcJSON_ToString(root_); +} + +TRANSPORT_ALWAYS_INLINE JSONManifestDecoder& JSONManifestDecoder::clearImpl() { + if (root_) { + parcJSON_Release(&root_); + } + + return *this; +} + +TRANSPORT_ALWAYS_INLINE ManifestType +JSONManifestDecoder::getManifestTypeImpl() const { + return getValueFromJson<ManifestType>(root_); +} + +TRANSPORT_ALWAYS_INLINE HashAlgorithm +JSONManifestDecoder::getHashAlgorithmImpl() const { + return getValueFromJson<HashAlgorithm>(root_); +} + +TRANSPORT_ALWAYS_INLINE NextSegmentCalculationStrategy +JSONManifestDecoder::getNextSegmentCalculationStrategyImpl() const { + return getValueFromJson<NextSegmentCalculationStrategy>(root_); +} + +TRANSPORT_ALWAYS_INLINE SuffixHashList +JSONManifestDecoder::getSuffixHashListImpl() { + throw errors::NotImplementedException(); + // SuffixHashList hash_list; + // + // char * str = parcJSON_ToString(root_); + // + // PARCJSONValue *value = parcJSON_GetValueByName(root_, + // JSONKey<SuffixHashList>::key); + // + // if (value == nullptr || !parcJSONValue_IsArray(value)) { + // throw errors::RuntimeException("Manifest does not contain suffix-hash + // list"); + // } + // + // PARCJSONArray *array = parcJSONValue_GetArray(value); + // std::size_t array_size = parcJSONArray_GetLength(array); + // + // for (std::size_t i = 0; i < array_size; i++) { + // PARCJSONValue *v = parcJSONArray_GetValue(array, i); + // checkPointer(v); + // PARCJSONArray *a = parcJSONValue_GetArray(v); + // PARCJSONValue *_suffix = parcJSONArray_GetValue(a, 0); + // PARCJSONValue *_hash = parcJSONArray_GetValue(a, 1); + // + // uint32_t value1 = + // static_cast<uint32_t>(parcJSONValue_GetInteger(_suffix)); uint64_t + // value2 = static_cast<uint64_t>(parcJSONValue_GetInteger(_hash)); + // + // hash_list[static_cast<uint32_t>(parcJSONValue_GetInteger(_suffix))] = + // static_cast<uint64_t>(parcJSONValue_GetInteger(_hash)); + // + //// parcJSONValue_Release(&_hash); + //// parcJSONValue_Release(&_suffix); + //// parcJSONArray_Release(&a); + //// parcJSONValue_Release(&v); + // } + // + //// parcJSONArray_Release(&array); + //// parcJSONValue_Release(&value); + // + // char * str2 = parcJSON_ToString(root_); + // + // return hash_list; +} + +TRANSPORT_ALWAYS_INLINE core::Name JSONManifestDecoder::getBaseNameImpl() + const { + checkPointer(root_); + PARCJSONValue* value = + parcJSON_GetValueByName(root_, JSONKey<core::Name>::key); + + PARCBuffer* b = parcJSONValue_GetString(value); + char* string = parcBuffer_ToString(b); + + core::Name ret(string); + + // parcJSONValue_Release(&value); + parcMemory_Deallocate(&string); + + return ret; +} + +TRANSPORT_ALWAYS_INLINE bool JSONManifestDecoder::getIsFinalManifestImpl() { + checkPointer(root_); + PARCJSONValue* value = + parcJSON_GetValueByName(root_, JSONKey<bool>::final_manifest); + + bool ret = parcJSONValue_GetBoolean(value); + + // parcJSONValue_Release(&value); + + return ret; +} + +TRANSPORT_ALWAYS_INLINE std::size_t +JSONManifestDecoder::estimateSerializedLengthImpl( + std::size_t number_of_entries) { + return 0; +} + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h new file mode 100755 index 000000000..28c6c1b40 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_format_json_libparc_deprecated.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/core/name.h> + +extern "C" { +#include <parc/algol/parc_JSON.h> +} + +#include <string> + +namespace transport { + +namespace core { + +class JSONManifestEncoder; +class JSONManifestDecoder; +class Packet; + +struct JSON { + using Encoder = JSONManifestEncoder; + using Decoder = JSONManifestDecoder; +}; + +template <typename T> +struct JSONKey; + +template <> +struct JSONKey<HashAlgorithm> { + static const constexpr char* key = "hash_algorithm"; +}; + +template <> +struct JSONKey<ManifestType> { + static const constexpr char* key = "manifest_type"; +}; + +template <> +struct JSONKey<NextSegmentCalculationStrategy> { + static const constexpr char* key = "next_segment_strategy"; +}; + +template <> +struct JSONKey<NameHashList> { + static const constexpr char* key = "name_hash_list"; +}; + +template <> +struct JSONKey<SuffixHashList> { + static const constexpr char* key = "suffix_hash_list"; +}; + +template <> +struct JSONKey<core::Name> { + static const constexpr char* key = "base_name"; +}; + +template <> +struct JSONKey<bool> { + static const constexpr char* final_manifest = "final_manifest"; +}; + +// template <> +// struct JSONKey<base_name> { +// static const std::string key = "name_hash_list"; +//}; + +// namespace JSONManifestEncoding { +// static const std::string base_name = "base_name"; +// static const std::string final_chunk_number = "final_chunk_number"; +// static const std::string hash_algorithm = "hash_algorithm"; +// static const std::string manifest_type = "manifest_type"; +// static const std::string name_hash_list = "name_hash_list"; +// static const std::string next_segment_strategy = "next_segment_strategy"; +//} + +class JSONManifestEncoder : public ManifestEncoder<JSONManifestEncoder> { + public: + JSONManifestEncoder(); + + ~JSONManifestEncoder(); + + JSONManifestEncoder& encodeImpl(Packet& packet); + + JSONManifestEncoder& clearImpl(); + + JSONManifestEncoder& setManifestTypeImpl(ManifestType manifest_type); + + JSONManifestEncoder& setHashAlgorithmImpl(HashAlgorithm algorithm); + + JSONManifestEncoder& setNextSegmentCalculationStrategyImpl( + NextSegmentCalculationStrategy strategy); + + JSONManifestEncoder& setSuffixHashListImpl( + const SuffixHashList& name_hash_list); + + JSONManifestEncoder& setBaseNameImpl(const core::Name& base_name); + + JSONManifestEncoder& addSuffixAndHashImpl(uint32_t suffix, uint64_t hash); + + JSONManifestEncoder& setIsFinalManifestImpl(bool is_last); + + private: + PARCJSON* root_; +}; + +class JSONManifestDecoder : public ManifestDecoder<JSONManifestDecoder> { + public: + JSONManifestDecoder(); + + ~JSONManifestDecoder(); + + void decodeImpl(const uint8_t* payload, std::size_t payload_size); + + JSONManifestDecoder& clearImpl(); + + ManifestType getManifestTypeImpl() const; + + HashAlgorithm getHashAlgorithmImpl() const; + + uint32_t getFinalChunkImpl() const; + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategyImpl() const; + + SuffixHashList getSuffixHashListImpl(); + + core::Name getBaseNameImpl() const; + + bool getIsFinalManifestImpl(); + + private: + PARCJSON* root_; +}; + +} // namespace core + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/manifest_inline.h b/libtransport/src/hicn/transport/core/manifest_inline.h new file mode 100755 index 000000000..fd1a9abd3 --- /dev/null +++ b/libtransport/src/hicn/transport/core/manifest_inline.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/manifest.h> +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/log.h> + +#include <set> + +namespace transport { + +namespace core { + +template <typename Base, typename FormatTraits> +class ManifestInline + : public Manifest<Base, FormatTraits, ManifestInline<Base, FormatTraits>> { + using ManifestBase = + Manifest<Base, FormatTraits, ManifestInline<Base, FormatTraits>>; + using HashType = typename FormatTraits::HashType; + using SuffixList = typename FormatTraits::SuffixList; + + public: + ManifestInline() : ManifestBase() {} + + ManifestInline(const core::Name& name) : ManifestBase(name) {} + + template <typename T> + ManifestInline(T&& base) : ManifestBase(std::forward<T&&>(base)) {} + + static TRANSPORT_ALWAYS_INLINE ManifestInline* createManifest( + const core::Name& manifest_name, ManifestVersion version, + ManifestType type, HashAlgorithm algorithm, bool is_last, + const Name& base_name, NextSegmentCalculationStrategy strategy, + std::size_t signature_size) { + auto manifest = new ManifestInline(manifest_name); + manifest->setSignatureSize(signature_size); + manifest->setVersion(version); + manifest->setManifestType(type); + manifest->setHashAlgorithm(algorithm); + manifest->setFinalManifest(is_last); + manifest->setBaseName(base_name); + manifest->setNextSegmentCalculationStrategy(strategy); + + return manifest; + } + + ManifestInline& encodeImpl() { + ManifestBase::encoder_.encode(); + return *this; + } + + ManifestInline& decodeImpl() { + base_name_ = ManifestBase::decoder_.getBaseName(); + next_segment_strategy_ = + ManifestBase::decoder_.getNextSegmentCalculationStrategy(); + suffix_hash_map_ = ManifestBase::decoder_.getSuffixHashList(); + + return *this; + } + + std::size_t estimateManifestSizeImpl(std::size_t additional_entries = 0) { + return ManifestBase::encoder_.estimateSerializedLength(additional_entries); + } + + ManifestInline& setBaseName(const Name& name) { + base_name_ = name; + ManifestBase::encoder_.setBaseName(base_name_); + return *this; + } + + const Name& getBaseName() { return base_name_; } + + ManifestInline& addSuffixHash(uint32_t suffix, const HashType& hash) { + ManifestBase::encoder_.addSuffixAndHash(suffix, hash); + return *this; + } + + // Call this function only after the decode function! + const SuffixList& getSuffixList() { return suffix_hash_map_; } + + ManifestInline& setNextSegmentCalculationStrategy( + NextSegmentCalculationStrategy strategy) { + next_segment_strategy_ = strategy; + ManifestBase::encoder_.setNextSegmentCalculationStrategy( + next_segment_strategy_); + return *this; + } + + NextSegmentCalculationStrategy getNextSegmentCalculationStrategy() { + return next_segment_strategy_; + } + + private: + core::Name base_name_; + NextSegmentCalculationStrategy next_segment_strategy_; + SuffixList suffix_hash_map_; +}; + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_binary_api.c b/libtransport/src/hicn/transport/core/memif_binary_api.c new file mode 100755 index 000000000..b443b51ce --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_binary_api.c @@ -0,0 +1,218 @@ +/* + * 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/config.h> + +#ifdef __vpp__ + +#include <hicn/transport/core/memif_binary_api.h> +#include <hicn/transport/core/vpp_binary_api_internal.h> +#include <hicn/transport/utils/log.h> + +#include <fcntl.h> +#include <inttypes.h> +#include <semaphore.h> +#include <string.h> +#include <sys/stat.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> + +// uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include <memif/memif_msg_enum.h> + +#define vl_msg_name_crc_list +#include <vpp/api/vpe_all_api_h.h> +#undef vl_msg_name_crc_list + +#define vl_typedefs +#define vl_endianfun +#define vl_print(handle, ...) +#define vl_printfun +#define vl_api_version(n, v) static u32 api_version = (v); +#define vl_msg_name_crc_list +#include <memif/memif_all_api_h.h> +#undef vl_msg_name_crc_list +#undef vl_api_version +#undef vl_printfun +#undef vl_endianfun +#undef vl_typedefs + +/* define message structures */ +#define vl_typedefs +#include <vpp/api/vpe_all_api_h.h> +#undef vl_typedefs + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_memif_api_reply_msg \ + _(MEMIF_CREATE_REPLY, memif_create_reply) \ + _(MEMIF_DELETE_REPLY, memif_delete_reply) \ + _(MEMIF_DETAILS, memif_details) + +#define POINTER_MAP_SIZE 32 +static void *global_pointers_map[POINTER_MAP_SIZE]; +static uint8_t global_pointers_map_index = 0; + +uint32_t memif_binary_api_get_next_memif_id(vpp_plugin_binary_api_t *api) { + // Dump all the memif interfaces and return the next to the largest memif id + vl_api_memif_dump_t *mp; + vpp_plugin_binary_api_t *hm = api; + + M(MEMIF_DUMP, mp); + api->vpp_api->user_param = malloc(sizeof(uint32_t)); + *(uint32_t *)(api->vpp_api->user_param) = 0; + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + vpp_binary_api_send_request(api->vpp_api, mp); + + vpp_binary_api_send_receive_ping(api->vpp_api); + + uint32_t ret = *(uint32_t *)(api->vpp_api->user_param); + free(api->vpp_api->user_param); + + return ret; +} + +static void vl_api_memif_details_t_handler(vl_api_memif_details_t *mp) { + vpp_plugin_binary_api_t *binary_api = global_pointers_map[mp->context]; + uint32_t *last_memif_id = binary_api->vpp_api->user_param; + uint32_t current_memif_id = clib_net_to_host_u32(mp->id); + if (current_memif_id >= *last_memif_id) { + *last_memif_id = current_memif_id + 1; + } +} + +int memif_binary_api_create_memif(vpp_plugin_binary_api_t *api, + memif_create_params_t *input_params, + memif_output_params_t *output_params) { + vl_api_memif_create_t *mp; + vpp_plugin_binary_api_t *hm = api; + + if (input_params->socket_id == ~0) { + // invalid socket-id + return -1; + } + + if (!is_pow2(input_params->ring_size)) { + // ring size must be power of 2 + return -1; + } + + if (input_params->rx_queues > 255 || input_params->rx_queues < 1) { + // rx queue must be between 1 - 255 + return -1; + } + + if (input_params->tx_queues > 255 || input_params->tx_queues < 1) { + // tx queue must be between 1 - 255 + return -1; + } + + api->vpp_api->user_param = output_params; + + /* Construct the API message */ + M(MEMIF_CREATE, mp); + + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + mp->role = input_params->role; + mp->mode = input_params->mode; + mp->rx_queues = input_params->rx_queues; + mp->tx_queues = input_params->tx_queues; + mp->id = clib_host_to_net_u32(input_params->id); + mp->socket_id = clib_host_to_net_u32(input_params->socket_id); + mp->ring_size = clib_host_to_net_u32(input_params->ring_size); + mp->buffer_size = clib_host_to_net_u16(input_params->buffer_size); + + int ret = vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); + if (ret < 0) { + return ret; + } + + return vpp_binary_api_set_int_state(api->vpp_api, output_params->sw_if_index, + UP); +} +int memif_binary_api_delete_memif(vpp_plugin_binary_api_t *api, + uint32_t sw_if_index) { + vl_api_memif_delete_t *mp; + vpp_plugin_binary_api_t *hm = api; + + /* Construct the API message */ + M(MEMIF_DELETE, mp); + + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + mp->sw_if_index = htonl(sw_if_index); + + return vpp_binary_api_send_request_wait_reply(api->vpp_api, mp); +} + +static void vl_api_memif_create_reply_t_handler( + vl_api_memif_create_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api = global_pointers_map[mp->context]; + memif_output_params_t *params = binary_api->vpp_api->user_param; + + binary_api->vpp_api->ret_val = ntohl(mp->retval); + params->sw_if_index = clib_net_to_host_u32(mp->sw_if_index); + + TRANSPORT_LOGI("ret :%d", binary_api->vpp_api->ret_val); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +static void vl_api_memif_delete_reply_t_handler( + vl_api_memif_delete_reply_t *mp) { + vpp_plugin_binary_api_t *binary_api = global_pointers_map[mp->context]; + + binary_api->vpp_api->ret_val = ntohl(mp->retval); + + vpp_binary_api_unlock_waiting_thread(binary_api->vpp_api); +} + +static int memif_binary_api_setup_handlers( + vpp_plugin_binary_api_t *binary_api) { + vpp_plugin_binary_api_t *sm __attribute__((unused)) = binary_api; +#define _(N, n) \ + vl_msg_api_set_handlers(VL_API_##N + sm->msg_id_base, #n, \ + vl_api_##n##_t_handler, vl_noop_handler, \ + vl_api_##n##_t_endian, vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_memif_api_reply_msg; +#undef _ + return 0; +} + +vpp_plugin_binary_api_t *memif_binary_api_init(vpp_binary_api_t *api) { + vpp_plugin_binary_api_t *ret = malloc(sizeof(vpp_plugin_binary_api_t)); + u8 *name = format(0, "memif_%08x%c", api_version, 0); + ret->msg_id_base = vl_client_get_first_plugin_msg_id((char *)name); + ret->vpp_api = api; + ret->my_client_index = api->my_client_index; + memif_binary_api_setup_handlers(ret); + return ret; +} + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_binary_api.h b/libtransport/src/hicn/transport/core/memif_binary_api.h new file mode 100755 index 000000000..582aeb1a0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_binary_api.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> +#include <hicn/transport/core/vpp_binary_api.h> + +#ifdef __vpp__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" + +typedef struct memif_create_params_s { + uint8_t role; + uint8_t mode; + uint8_t rx_queues; + uint8_t tx_queues; + uint32_t id; + uint32_t socket_id; + uint8_t secret[24]; + uint32_t ring_size; + uint16_t buffer_size; + uint8_t hw_addr[6]; +} memif_create_params_t; + +typedef struct memif_output_params_s { + uint32_t sw_if_index; +} memif_output_params_t; + +vpp_plugin_binary_api_t* memif_binary_api_init(vpp_binary_api_t* api); + +uint32_t memif_binary_api_get_next_memif_id(vpp_plugin_binary_api_t* api); + +int memif_binary_api_create_memif(vpp_plugin_binary_api_t* api, + memif_create_params_t* input_params, + memif_output_params_t* output_params); + +int memif_binary_api_delete_memif(vpp_plugin_binary_api_t* api, + uint32_t sw_if_index); + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_connector.cc b/libtransport/src/hicn/transport/core/memif_connector.cc new file mode 100755 index 000000000..7a672314e --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_connector.cc @@ -0,0 +1,493 @@ +/* + * 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/memif_connector.h> + +#ifdef __vpp__ + +#include <sys/epoll.h> +#include <cstdlib> + +#define CANCEL_TIMER 1 + +namespace transport { + +namespace core { + +std::once_flag MemifConnector::flag_; +utils::EpollEventReactor MemifConnector::main_event_reactor_; + +MemifConnector::MemifConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, + asio::io_service &io_service, + std::string app_name) + : Connector(), + memif_worker_(nullptr), + timer_set_(false), + send_timer_(std::make_unique<utils::FdDeadlineTimer>(event_reactor_)), + io_service_(io_service), + work_(std::make_unique<asio::io_service::work>(io_service_)), + packet_counter_(0), + memif_connection_({}), + tx_buf_counter_(0), + is_connecting_(true), + is_reconnection_(false), + data_available_(false), + enable_burst_(false), + app_name_(app_name), + receive_callback_(receive_callback), + on_reconnect_callback_(on_reconnect_callback), + socket_filename_("") { + std::call_once(MemifConnector::flag_, &MemifConnector::init, this); +} + +MemifConnector::~MemifConnector() { close(); } + +void MemifConnector::init() { + /* initialize memory interface */ + int err = memif_init(controlFdUpdate, const_cast<char *>(app_name_.c_str()), + nullptr, nullptr, nullptr); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_init: %s", memif_strerror(err)); + } +} + +void MemifConnector::connect(uint32_t memif_id, long memif_mode) { + TRANSPORT_LOGI("Creating memif"); + + memif_id_ = memif_id; + socket_filename_ = "/run/vpp/memif.sock"; + + createMemif(memif_id, memif_mode, nullptr); + + while (is_connecting_) { + MemifConnector::main_event_reactor_.runOneEvent(); + } + + int err; + + /* get interrupt queue id */ + int fd = -1; + err = memif_get_queue_efd(memif_connection_.conn, 0, &fd); + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_get_queue_efd: %s", memif_strerror(err)); + return; + } + + // Remove fd from main epoll + main_event_reactor_.delFileDescriptor(fd); + + // Add fd to epoll of instance + event_reactor_.addFileDescriptor( + fd, EPOLLIN, [this](const utils::Event &evt) -> int { + return onInterrupt(memif_connection_.conn, this, 0); + }); + + memif_worker_ = std::make_unique<std::thread>( + std::bind(&MemifConnector::threadMain, this)); +} + +int MemifConnector::createMemif(uint32_t index, uint8_t mode, char *s) { + memif_connection_t *c = &memif_connection_; + + /* setting memif connection arguments */ + memif_conn_args_t args; + memset(&args, 0, sizeof(args)); + + args.is_master = mode; + args.log2_ring_size = MEMIF_LOG2_RING_SIZE; + args.buffer_size = MEMIF_BUF_SIZE; + args.num_s2m_rings = 1; + args.num_m2s_rings = 1; + strncpy((char *)args.interface_name, IF_NAME, strlen(IF_NAME)); + // strncpy((char *) args.instance_name, APP_NAME, strlen(APP_NAME)); + args.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + args.socket_filename = (uint8_t *)socket_filename_.c_str(); + + TRANSPORT_LOGI("Socket filename: %s", args.socket_filename); + + args.interface_id = index; + /* last argument for memif_create (void * private_ctx) is used by user + to identify connection. this context is returned with callbacks */ + int err; + /* default interrupt */ + if (s == nullptr) { + err = memif_create(&c->conn, &args, onConnect, onDisconnect, onInterrupt, + this); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + throw errors::RuntimeException(memif_strerror(err)); + } + } + + c->index = (uint16_t)index; + c->tx_qid = 0; + /* alloc memif buffers */ + c->rx_buf_num = 0; + c->rx_bufs = static_cast<memif_buffer_t *>( + malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); + c->tx_buf_num = 0; + c->tx_bufs = static_cast<memif_buffer_t *>( + malloc(sizeof(memif_buffer_t) * MAX_MEMIF_BUFS)); + + // memif_set_rx_mode (c->conn, MEMIF_RX_MODE_POLLING, 0); + + return 0; +} + +int MemifConnector::deleteMemif() { + memif_connection_t *c = &memif_connection_; + + if (c->rx_bufs) { + free(c->rx_bufs); + } + + c->rx_bufs = nullptr; + c->rx_buf_num = 0; + + if (c->tx_bufs) { + free(c->tx_bufs); + } + + c->tx_bufs = nullptr; + c->tx_buf_num = 0; + + int err; + /* disconenct then delete memif connection */ + err = memif_delete(&c->conn); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_delete: %s", memif_strerror(err)); + } + + if (TRANSPORT_EXPECT_FALSE(c->conn != nullptr)) { + TRANSPORT_LOGI("memif delete fail"); + } + + return 0; +} + +int MemifConnector::controlFdUpdate(int fd, uint8_t events) { + /* convert memif event definitions to epoll events */ + if (events & MEMIF_FD_EVENT_DEL) { + return MemifConnector::main_event_reactor_.delFileDescriptor(fd); + } + + uint32_t evt = 0; + + if (events & MEMIF_FD_EVENT_READ) { + evt |= EPOLLIN; + } + + if (events & MEMIF_FD_EVENT_WRITE) { + evt |= EPOLLOUT; + } + + if (events & MEMIF_FD_EVENT_MOD) { + return MemifConnector::main_event_reactor_.modFileDescriptor(fd, evt); + } + + return MemifConnector::main_event_reactor_.addFileDescriptor( + fd, evt, [](const utils::Event &evt) -> int { + uint32_t event = 0; + int memif_err = 0; + + if (evt.events & EPOLLIN) { + event |= MEMIF_FD_EVENT_READ; + } + + if (evt.events & EPOLLOUT) { + event |= MEMIF_FD_EVENT_WRITE; + } + + if (evt.events & EPOLLERR) { + event |= MEMIF_FD_EVENT_ERROR; + } + + memif_err = memif_control_fd_handler(evt.data.fd, event); + + if (TRANSPORT_EXPECT_FALSE(memif_err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_control_fd_handler: %s", + memif_strerror(memif_err)); + } + + return 0; + }); +} + +int MemifConnector::bufferAlloc(long n, uint16_t qid) { + memif_connection_t *c = &memif_connection_; + int err; + uint16_t r; + /* set data pointer to shared memory and set buffer_len to shared mmeory + * buffer len */ + err = memif_buffer_alloc(c->conn, qid, c->tx_bufs, n, &r, 2000); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGD("memif_buffer_alloc: %s", memif_strerror(err)); + } + + c->tx_buf_num += r; + TRANSPORT_LOGD("allocated %d/%ld buffers, %u free buffers", r, n, + MAX_MEMIF_BUFS - c->tx_buf_num); + return r; +} + +int MemifConnector::txBurst(uint16_t qid) { + memif_connection_t *c = &memif_connection_; + int err; + uint16_t r; + /* inform peer memif interface about data in shared memory buffers */ + /* mark memif buffers as free */ + err = memif_tx_burst(c->conn, qid, c->tx_bufs, c->tx_buf_num, &r); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_tx_burst: %s", memif_strerror(err)); + } + + // err = memif_refill_queue(c->conn, qid, r, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_tx_burst: %s", memif_strerror(err)); + c->tx_buf_num -= r; + return -1; + } + + TRANSPORT_LOGD("tx: %d/%u", r, c->tx_buf_num); + c->tx_buf_num -= r; + return 0; +} + +void MemifConnector::sendCallback(const std::error_code &ec) { + if (TRANSPORT_EXPECT_TRUE(!ec && !is_connecting_)) { + doSend(); + } + + if (output_buffer_.size() > 0) { + send_timer_->expiresFromNow(std::chrono::microseconds(50)); + send_timer_->asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } else { + timer_set_ = false; + } +} + +void MemifConnector::processInputBuffer() { + Packet::MemBufPtr ptr; + + while (input_buffer_.pop(ptr)) { + receive_callback_(std::move(ptr)); + } +} + +/* informs user about connected status. private_ctx is used by user to identify + connection (multiple connections WIP) */ +int MemifConnector::onConnect(memif_conn_handle_t conn, void *private_ctx) { + TRANSPORT_LOGI("memif connected!\n"); + MemifConnector *connector = (MemifConnector *)private_ctx; + memif_refill_queue(conn, 0, -1, 0); + connector->is_connecting_ = false; + + return 0; +} + +/* informs user about disconnected status. private_ctx is used by user to + identify connection (multiple connections WIP) */ +int MemifConnector::onDisconnect(memif_conn_handle_t conn, void *private_ctx) { + TRANSPORT_LOGI("memif disconnected!"); + MemifConnector *connector = (MemifConnector *)private_ctx; + // TRANSPORT_LOGI ("Packet received: %u", connector->packet_counter_); + TRANSPORT_LOGI("Packet to process: %u", + connector->memif_connection_.tx_buf_num); + return 0; +} + +void MemifConnector::threadMain() { event_reactor_.runEventLoop(1); } + +int MemifConnector::onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid) { + MemifConnector *connector = (MemifConnector *)private_ctx; + + memif_connection_t *c = &connector->memif_connection_; + int err = MEMIF_ERR_SUCCESS, ret_val; + uint16_t rx; + + do { + err = memif_rx_burst(conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx); + ret_val = err; + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS && + err != MEMIF_ERR_NOBUF)) { + TRANSPORT_LOGI("memif_rx_burst: %s", memif_strerror(err)); + goto error; + } + + c->rx_buf_num += rx; + + if (TRANSPORT_EXPECT_TRUE(connector->io_service_.stopped())) { + TRANSPORT_LOGD("socket stopped: ignoring %u packets", rx); + goto error; + } + + for (int i = 0; i < rx; i++) { + auto packet = connector->getPacket(); + std::memcpy(packet->writableData(), + reinterpret_cast<const uint8_t *>((c->rx_bufs + i)->data), + (c->rx_bufs + i)->len); + + if (!connector->input_buffer_.push(std::move(packet))) { + TRANSPORT_LOGI("Error pushing packet. Ring buffer full."); + + // TODO Here we should consider the possibility to signal the congestion + // to the application, that would react properly (e.g. slow down + // message) + } + } + + connector->io_service_.post( + std::bind(&MemifConnector::processInputBuffer, connector)); + + /* mark memif buffers and shared memory buffers as free */ + /* free processed buffers */ + + err = memif_refill_queue(conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_buffer_free: %s", memif_strerror(err)); + } + + c->rx_buf_num -= rx; + + TRANSPORT_LOGD("freed %d buffers. %u/%u alloc/free buffers", rx, rx, + MAX_MEMIF_BUFS - rx); + + // if (connector->enable_burst_) { + // connector->doSend(); + // } + } while (ret_val == MEMIF_ERR_NOBUF); + + return 0; + +error: + err = memif_refill_queue(c->conn, qid, rx, 0); + + if (TRANSPORT_EXPECT_FALSE(err != MEMIF_ERR_SUCCESS)) { + TRANSPORT_LOGI("memif_buffer_free: %s", memif_strerror(err)); + } + c->rx_buf_num -= rx; + + TRANSPORT_LOGD("freed %d buffers. %u/%u alloc/free buffers", rx, + c->rx_buf_num, MAX_MEMIF_BUFS - c->rx_buf_num); + return 0; +} + +// void MemifConnector::runEventsLoop() { +// io_service_.run(); +//} + +void MemifConnector::close() { + event_reactor_.stop(); + io_service_.stop(); + + if (memif_worker_ && memif_worker_->joinable()) { + memif_worker_->join(); + TRANSPORT_LOGD("Memif worker joined"); + deleteMemif(); + } else { + TRANSPORT_LOGD("Memif worker not joined"); + } +} + +void MemifConnector::enableBurst() { enable_burst_ = true; } + +void MemifConnector::send(const Packet::MemBufPtr &packet) { +#ifdef CANCEL_TIMER + if (!timer_set_) { + timer_set_ = true; + send_timer_->expiresFromNow(std::chrono::microseconds(50)); + send_timer_->asyncWait( + std::bind(&MemifConnector::sendCallback, this, std::placeholders::_1)); + } +#endif + + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + output_buffer_.push_back(packet); + } +} + +int MemifConnector::doSend() { + std::size_t max = 0; + uint16_t n = 0; + std::size_t size = 0; + + { + utils::SpinLock::Acquire locked(write_msgs_lock_); + size = output_buffer_.size(); + } + + do { + max = size < MAX_MEMIF_BUFS ? size : MAX_MEMIF_BUFS; + + if (TRANSPORT_EXPECT_FALSE( + (n = bufferAlloc(max, memif_connection_.tx_qid)) < 0)) { + TRANSPORT_LOGI("Error allocating buffers."); + return -1; + } + + for (uint16_t i = 0; i < n; i++) { + utils::SpinLock::Acquire locked(write_msgs_lock_); + + auto packet = output_buffer_.front().get(); + const utils::MemBuf *current = packet; + std::size_t offset = 0; + uint8_t *shared_buffer = + reinterpret_cast<uint8_t *>(memif_connection_.tx_bufs[i].data); + do { + std::memcpy(shared_buffer + offset, current->data(), current->length()); + offset += current->length(); + current = current->next(); + } while (current != packet); + + memif_connection_.tx_bufs[i].len = uint32_t(offset); + + TRANSPORT_LOGD("Packet size : %zu", offset); + + output_buffer_.pop_front(); + } + + txBurst(memif_connection_.tx_qid); + + utils::SpinLock::Acquire locked(write_msgs_lock_); + size = output_buffer_.size(); + } while (size > 0); + + return 0; +} + +void MemifConnector::state() { + TRANSPORT_LOGD("Event reactor map: %zu", event_reactor_.mapSize()); + TRANSPORT_LOGD("Output buffer %zu", output_buffer_.size()); +} + +void MemifConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) {} + +} // end namespace core + +} // end namespace transport + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/memif_connector.h b/libtransport/src/hicn/transport/core/memif_connector.h new file mode 100755 index 000000000..24c8ac16c --- /dev/null +++ b/libtransport/src/hicn/transport/core/memif_connector.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> +#include <hicn/transport/core/connector.h> +#include <hicn/transport/core/hicn_binary_api.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/epoll_event_reactor.h> +#include <hicn/transport/utils/fd_deadline_timer.h> +#include <hicn/transport/utils/ring_buffer.h> + +#include <asio.hpp> +#include <deque> +#include <mutex> +#include <thread> + +#ifdef __vpp__ + +#define _Static_assert static_assert + +extern "C" { +#include <memif/libmemif.h> +}; + +namespace transport { + +namespace core { + +typedef struct { + uint16_t index; + /* memif conenction handle */ + memif_conn_handle_t conn; + /* transmit queue id */ + uint16_t tx_qid; + /* tx buffers */ + memif_buffer_t *tx_bufs; + /* allocated tx buffers counter */ + /* number of tx buffers pointing to shared memory */ + uint16_t tx_buf_num; + /* rx buffers */ + memif_buffer_t *rx_bufs; + /* allcoated rx buffers counter */ + /* number of rx buffers pointing to shared memory */ + uint16_t rx_buf_num; + /* interface ip address */ + uint8_t ip_addr[4]; +} memif_connection_t; + +#define APP_NAME "libtransport" +#define IF_NAME "vpp_connection" + +#define MAX_MEMIF_BUFS 1024 +#define MEMIF_BUF_SIZE 2048 +#define MEMIF_LOG2_RING_SIZE 11 + +class MemifConnector : public Connector { + public: + MemifConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~MemifConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void connect(uint32_t memif_id, long memif_mode); + + // void runEventsLoop(); + + void enableBurst() override; + + void state() override; + + TRANSPORT_ALWAYS_INLINE uint32_t getMemifId() { return memif_id_; }; + + private: + void init(); + + int doSend(); + + int createMemif(uint32_t index, uint8_t mode, char *s); + + uint32_t getMemifConfiguration(); + + int deleteMemif(); + + static int controlFdUpdate(int fd, uint8_t events); + + static int onConnect(memif_conn_handle_t conn, void *private_ctx); + + static int onDisconnect(memif_conn_handle_t conn, void *private_ctx); + + static int onInterrupt(memif_conn_handle_t conn, void *private_ctx, + uint16_t qid); + + void threadMain(); + + int txBurst(uint16_t qid); + + int bufferAlloc(long n, uint16_t qid); + + void sendCallback(const std::error_code &ec); + + void processInputBuffer(); + + private: + static utils::EpollEventReactor main_event_reactor_; + static std::unique_ptr<std::thread> main_worker_; + + int epfd; + std::unique_ptr<std::thread> memif_worker_; + utils::EpollEventReactor event_reactor_; + volatile bool timer_set_; + std::unique_ptr<utils::FdDeadlineTimer> send_timer_; + asio::io_service &io_service_; + std::unique_ptr<asio::io_service::work> work_; + uint32_t packet_counter_; + memif_connection_t memif_connection_; + uint16_t tx_buf_counter_; + + PacketRing input_buffer_; + volatile bool is_connecting_; + volatile bool is_reconnection_; + bool data_available_; + bool enable_burst_; + uint32_t memif_id_; + uint8_t memif_mode_; + std::string app_name_; + uint16_t transmission_index_; + PacketReceivedCallback receive_callback_; + OnReconnect on_reconnect_callback_; + utils::SpinLock write_msgs_lock_; + std::string socket_filename_; + + static std::once_flag flag_; +}; + +} // end namespace core + +} // end namespace transport + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/name.cc b/libtransport/src/hicn/transport/core/name.cc new file mode 100755 index 000000000..10c45eb08 --- /dev/null +++ b/libtransport/src/hicn/transport/core/name.cc @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/errors/errors.h> +#include <hicn/transport/errors/tokenizer_exception.h> +#include <hicn/transport/utils/hash.h> +#include <hicn/transport/utils/string_tokenizer.h> + +namespace transport { + +namespace core { + +Name::Name() { name_ = createEmptyName(); } + +Name::Name(int family, const uint8_t *ip_address, std::uint32_t suffix) + : name_(createEmptyName()) { + std::size_t length; + uint8_t *dst = NULL; + + if (family == AF_INET) { + dst = name_->ip4.prefix_as_u8; + length = IPV4_ADDR_LEN; + name_->type = HNT_CONTIGUOUS_V4; + } else if (family == AF_INET6) { + dst = name_->ip6.prefix_as_u8; + length = IPV6_ADDR_LEN; + name_->type = HNT_CONTIGUOUS_V6; + } else { + throw errors::RuntimeException("Specified name family does not exist."); + } + + std::memcpy(dst, ip_address, length); + *reinterpret_cast<std::uint32_t *>(dst + length) = suffix; +} + +Name::Name(const char *name, uint32_t segment) { + name_ = createEmptyName(); + + if (hicn_name_create(name, segment, name_.get()) < 0) { + throw errors::InvalidIpAddressException(); + } +} + +Name::Name(const std::string &uri, uint32_t segment) + : Name(uri.c_str(), segment) {} + +Name::Name(const std::string &uri) { + utils::StringTokenizer tokenizer(uri, "|"); + std::string ip_address; + std::string seq_number; + + ip_address = tokenizer.nextToken(); + + try { + seq_number = tokenizer.nextToken(); + } catch (errors::TokenizerException &e) { + seq_number = "0"; + } + + name_ = createEmptyName(); + + if (hicn_name_create(ip_address.c_str(), (uint32_t)atoi(seq_number.c_str()), + name_.get()) < 0) { + throw errors::InvalidIpAddressException(); + } +} + +Name::Name(const Name &name, bool hard_copy) { + name_ = createEmptyName(); + + if (hard_copy) { + if (hicn_name_copy(this->name_.get(), name.name_.get()) < 0) { + throw errors::MalformedNameException(); + } + } else { + *this->name_ = *name.name_; + } +} + +Name::Name(Name &&name) : name_(std::move(name.name_)) {} + +Name &Name::operator=(const Name &name) { + if (hicn_name_copy(this->name_.get(), name.name_.get()) < 0) { + throw errors::MalformedNameException(); + } + + return *this; +} + +bool Name::operator==(const Name &name) const { + return this->equals(name, true); +} + +bool Name::operator!=(const Name &name) const { + return !this->operator==(name); +} + +Name::operator bool() const { + return bool(hicn_name_empty((hicn_name_t *)name_.get())); +} + +bool Name::equals(const Name &name, bool consider_segment) const { + return !hicn_name_compare(name_.get(), name.name_.get(), consider_segment); +} + +std::string Name::toString() const { + char *name = new char[100]; + int ret = hicn_name_ntop(name_.get(), name, standard_name_string_length); + if (ret < 0) { + throw errors::MalformedNameException(); + } + std::string name_string(name); + delete[] name; + + return name_string; +} + +uint32_t Name::getHash32() const { + uint32_t hash; + if (hicn_name_hash((hicn_name_t *)name_.get(), &hash) < 0) { + throw errors::RuntimeException("Error computing the hash of the name!"); + } + return hash; +} + +void Name::clear() { + name_.reset(); + name_ = createEmptyName(); +}; + +Name::Type Name::getType() const { return name_->type; } + +uint32_t Name::getSuffix() const { + uint32_t ret = 0; + if (hicn_name_get_seq_number((hicn_name_t *)name_.get(), &ret) < 0) { + throw errors::RuntimeException( + "Impossible to retrieve the sequence number from the name."); + } + return ret; +} + +Name &Name::setSuffix(uint32_t seq_number) { + if (hicn_name_set_seq_number(name_.get(), seq_number) < 0) { + throw errors::RuntimeException( + "Impossible to set the sequence number to the name."); + } + + return *this; +} + +std::shared_ptr<Sockaddr> Name::getAddress() const { + Sockaddr *ret = nullptr; + + switch (name_->type) { + case HNT_CONTIGUOUS_V4: + case HNT_IOV_V4: + ret = (Sockaddr *)new Sockaddr4; + break; + case HNT_CONTIGUOUS_V6: + case HNT_IOV_V6: + ret = (Sockaddr *)new Sockaddr6; + break; + default: + throw errors::MalformedNameException(); + } + + if (hicn_name_to_sockaddr_address((hicn_name_t *)name_.get(), ret) < 0) { + throw errors::MalformedNameException(); + } + + return std::shared_ptr<Sockaddr>(ret); +} + +ip_address_t Name::toIpAddress() const { + ip_address_t ret; + std::memset(&ret, 0, sizeof(ret)); + + if (hicn_name_to_ip_address(name_.get(), &ret) < 0) { + throw errors::InvalidIpAddressException(); + } + + return ret; +} + +int Name::getAddressFamily() const { + int ret = 0; + + if (hicn_name_get_family(name_.get(), &ret) < 0) { + throw errors::InvalidIpAddressException(); + } + + return ret; +} + +void Name::copyToDestination(uint8_t *destination, bool include_suffix) const { + if (hicn_name_copy_to_destination(destination, name_.get(), include_suffix) < + 0) { + throw errors::RuntimeException( + "Impossibe to copy the name into the " + "provided destination"); + } +} + +std::ostream &operator<<(std::ostream &os, const Name &name) { + const std::string &str = name.toString(); + // os << "core:/"; + os << str; + + return os; +} + +} // end namespace core + +} // end namespace transport + +namespace std { +size_t hash<transport::core::Name>::operator()( + const transport::core::Name &name) const { + return name.getHash32(); +} + +} // end namespace std diff --git a/libtransport/src/hicn/transport/core/name.h b/libtransport/src/hicn/transport/core/name.h new file mode 100755 index 000000000..b0da15026 --- /dev/null +++ b/libtransport/src/hicn/transport/core/name.h @@ -0,0 +1,133 @@ +/* + * 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/portability/portability.h> +#include <hicn/transport/utils/branch_prediction.h> + +#include <list> +#include <memory> +#include <string> +#include <unordered_map> + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include <hicn/hicn.h> +}; + +#include <vector> + +namespace transport { + +namespace core { + +typedef struct sockaddr_in6 Sockaddr6; +typedef struct sockaddr_in Sockaddr4; +typedef struct sockaddr Sockaddr; + +enum class HashAlgorithm : uint8_t; + +class Name { + friend class Packet; + friend class ContentObject; + friend class Interest; + + static const uint32_t standard_name_string_length = 100; + + public: + using NameStruct = hicn_name_t; + using Type = hicn_name_type_t; + + Name(); + + /** + * @brief Create name + * @param name The null-terminated URI string + */ + Name(const char *name, uint32_t segment); + + Name(int family, const uint8_t *ip_address, std::uint32_t suffix = 0); + + Name(const std::string &uri, uint32_t segment); + + Name(const std::string &uri); + + Name(const Name &name, bool hard_copy = false); + + Name(Name &&name); + + Name &operator=(const Name &name); + + bool operator==(const Name &name) const; + + bool operator!=(const Name &name) const; + + operator bool() const; + + std::string toString() const; + + bool equals(const Name &name, bool consider_segment = true) const; + + uint32_t getHash32() const; + + void clear(); + + Type getType() const; + + uint32_t getSuffix() const; + + std::shared_ptr<Sockaddr> getAddress() const; + + Name &setSuffix(uint32_t seq_number); + + ip_address_t toIpAddress() const; + + void copyToDestination(uint8_t *destination, + bool include_suffix = false) const; + + int getAddressFamily() const; + + private: + TRANSPORT_ALWAYS_INLINE NameStruct *getStructReference() const { + if (TRANSPORT_EXPECT_TRUE(name_ != nullptr)) { + return name_.get(); + } + + return nullptr; + } + + static TRANSPORT_ALWAYS_INLINE std::unique_ptr<NameStruct> createEmptyName() { + NameStruct *name = new NameStruct; + name->type = HNT_UNSPEC; + return std::unique_ptr<NameStruct>(name); + }; + + std::unique_ptr<NameStruct> name_; +}; + +std::ostream &operator<<(std::ostream &os, const Name &name); + +} // end namespace core + +} // end namespace transport + +namespace std { +template <> +struct hash<transport::core::Name> { + size_t operator()(const transport::core::Name &name) const; +}; + +} // end namespace std diff --git a/libtransport/src/hicn/transport/core/packet.cc b/libtransport/src/hicn/transport/core/packet.cc new file mode 100755 index 000000000..74f407ff7 --- /dev/null +++ b/libtransport/src/hicn/transport/core/packet.cc @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/packet.h> +#include <hicn/transport/errors/malformed_packet_exception.h> +#include <hicn/transport/utils/hash.h> +#include <hicn/transport/utils/log.h> + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include <hicn/error.h> +} + +namespace transport { + +namespace core { + +const core::Name Packet::base_name("0::0|0"); + +Packet::Packet(Format format) + : packet_(utils::MemBuf::create(getHeaderSizeFromFormat(format)).release()), + packet_start_(packet_->writableData()), + header_head_(packet_.get()), + payload_head_(nullptr), + format_(format) { + if (hicn_packet_init_header(format, (hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Unexpected error initializing the packet."); + } + + packet_->append(getHeaderSizeFromFormat(format_)); +} + +Packet::Packet(MemBufPtr &&buffer) + : packet_(std::move(buffer)), + packet_start_(packet_->writableData()), + header_head_(packet_.get()), + payload_head_(nullptr), + format_(getFormatFromBuffer(packet_start_)) { + + auto header_size = getHeaderSizeFromFormat(format_); + int signature_size = 0; + + if (_is_ah(format_)) { + signature_size = getSignatureSize(); + } + + auto payload_length = packet_->length() - header_size - signature_size; + + if (!payload_length && !signature_size) { + return; + } + + packet_->trimEnd(packet_->length()); + + if (signature_size) { + auto sig = packet_->cloneOne(); + sig->advance(header_size); + sig->append(signature_size); + packet_->appendChain(std::move(sig)); + } + + if (payload_length) { + auto payload = packet_->cloneOne(); + payload_head_ = payload.get(); + payload_head_->advance(header_size + signature_size); + payload_head_->append(payload_length); + packet_->prependChain(std::move(payload)); + packet_->append(header_size); + } + + +} + +Packet::Packet(const uint8_t *buffer, std::size_t size) + : Packet(MemBufPtr(utils::MemBuf::copyBuffer(buffer, size).release())) {} + +Packet::Packet(Packet &&other) + : packet_(std::move(other.packet_)), + packet_start_(packet_->writableData()), + header_head_(other.header_head_), + payload_head_(other.payload_head_), + format_(other.format_) { + other.packet_start_ = nullptr; + other.header_head_ = nullptr; + other.payload_head_ = nullptr; + other.format_ = HF_UNSPEC; +} + +Packet::~Packet() { + if (packet_->isChained()) { + packet_->separateChain(packet_->next(), packet_->prev()); + } +} + +std::size_t Packet::getHeaderSizeFromFormat(Format format, + size_t signature_size) { + std::size_t header_length; + hicn_packet_get_header_length_from_format(format, &header_length); + int is_ah = _is_ah(format); + return is_ah * (header_length + signature_size) + (!is_ah) * header_length; +} + +std::size_t Packet::getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer) { + size_t header_length; + if (hicn_packet_get_header_length(format, (hicn_header_t *)buffer, + &header_length) < 0) { + throw errors::MalformedPacketException(); + } + return header_length; +} + +bool Packet::isInterest(const uint8_t *buffer) { + bool is_interest = false; + + if (TRANSPORT_EXPECT_FALSE(hicn_packet_test_ece((const hicn_header_t *)buffer, + &is_interest) < 0)) { + throw errors::RuntimeException( + "Impossible to retrieve ece flag from packet"); + } + + return !is_interest; +} + +Packet::Format Packet::getFormatFromBuffer(const uint8_t *buffer) { + Format format = HF_UNSPEC; + + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_get_format((const hicn_header_t *)buffer, &format) < 0)) { + throw errors::MalformedPacketException(); + } + + return format; +} + +std::size_t Packet::getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer) { + std::size_t payload_length; + if (TRANSPORT_EXPECT_FALSE( + hicn_packet_get_payload_length(format, (hicn_header_t *)buffer, + &payload_length) < 0)) { + throw errors::MalformedPacketException(); + } + + return payload_length; +} + +std::size_t Packet::payloadSize() const { + return getPayloadSizeFromBuffer(format_, packet_start_); +} + +std::size_t Packet::headerSize() const { + return getHeaderSizeFromBuffer(format_, packet_start_); +} + +const uint8_t *Packet::start() const { return packet_start_; } + +void Packet::setLifetime(uint32_t lifetime) { + if (hicn_interest_set_lifetime((hicn_header_t *)packet_start_, lifetime) < + 0) { + throw errors::MalformedPacketException(); + } +} + +uint32_t Packet::getLifetime() const { + uint32_t lifetime = 0; + + if (hicn_packet_get_lifetime((hicn_header_t *)packet_start_, &lifetime) < 0) { + throw errors::MalformedPacketException(); + } + + return lifetime; +} + +Packet &Packet::appendPayload(std::unique_ptr<utils::MemBuf> &&payload) { + if (!payload_head_) { + payload_head_ = payload.get(); + } + + header_head_->prependChain(std::move(payload)); + updateLength(); + return *this; +} + +Packet &Packet::appendPayload(const uint8_t *buffer, std::size_t length) { + return appendPayload(utils::MemBuf::copyBuffer(buffer, length)); +} + +Packet &Packet::appendHeader(std::unique_ptr<utils::MemBuf> &&header) { + if (!payload_head_) { + header_head_->prependChain(std::move(header)); + } else { + payload_head_->prependChain(std::move(header)); + } + + updateLength(); + return *this; +} + +Packet &Packet::appendHeader(const uint8_t *buffer, std::size_t length) { + return appendHeader(utils::MemBuf::copyBuffer(buffer, length)); +} + +utils::Array<uint8_t> Packet::getPayload() const { + if (TRANSPORT_EXPECT_FALSE(payload_head_ == nullptr)) { + return utils::Array<uint8_t>(); + } + + // Hopefully the payload is contiguous + if (TRANSPORT_EXPECT_FALSE(payload_head_->next() != header_head_)) { + payload_head_->gather(payloadSize()); + } + + return utils::Array<uint8_t>(payload_head_->writableData(), + payload_head_->length()); +} + +Packet &Packet::updateLength(std::size_t length) { + std::size_t total_length = length; + + for (utils::MemBuf *current = payload_head_; + current && current != header_head_; current = current->next()) { + total_length += current->length(); + } + + if (hicn_packet_set_payload_length(format_, (hicn_header_t *)packet_start_, + total_length) < 0) { + throw errors::RuntimeException("Error setting the packet payload."); + } + + return *this; +} + +PayloadType Packet::getPayloadType() const { + hicn_payload_type_t ret = HPT_UNSPEC; + + if (hicn_packet_get_payload_type((hicn_header_t *)packet_start_, &ret) < 0) { + throw errors::RuntimeException("Impossible to retrieve payload type."); + } + + return PayloadType(ret); +} + +Packet &Packet::setPayloadType(PayloadType payload_type) { + if (hicn_packet_set_payload_type((hicn_header_t *)packet_start_, + hicn_payload_type_t(payload_type)) < 0) { + throw errors::RuntimeException("Error setting payload type of the packet."); + } + + return *this; +} + +Packet::Format Packet::getFormat() const { + if (format_ == HF_UNSPEC) { + if (hicn_packet_get_format((hicn_header_t *)packet_start_, &format_) < 0) { + throw errors::MalformedPacketException(); + } + } + + return format_; +} + +const std::shared_ptr<utils::MemBuf> Packet::data() { return packet_; } + +void Packet::dump() const { + TRANSPORT_LOGI("The header length is: %zu", headerSize()); + TRANSPORT_LOGI("The payload length is: %zu", payloadSize()); + std::cerr << std::endl; + + hicn_packet_dump((uint8_t *)packet_->data(), headerSize()); + // hicn_packet_dump((uint8_t *)packet_->next()->data(), payloadSize()); +} + +void Packet::setSignatureSize(std::size_t size_bytes) { + int ret = hicn_packet_set_signature_size( + format_, (hicn_header_t *)packet_start_, size_bytes); + + if (ret < 0) { + throw errors::RuntimeException("Packet without Authentication Header."); + } +} + +std::size_t Packet::getSignatureSize() const { + size_t size_bytes; + int ret = hicn_packet_get_signature_size( + format_, (hicn_header_t *)packet_start_, &size_bytes); + + if (ret < 0) { + throw errors::RuntimeException("Packet without Authentication Header."); + } + + return size_bytes; +} + +void Packet::setSignature(std::unique_ptr<utils::MemBuf> &&signature) { + // Check if packet already contains a signature + auto header = header_head_->next(); + while (header != payload_head_) { + header->unlink(); + header = header->next(); + } + + appendHeader(std::move(signature)); +} + +void Packet::setSignatureTimestamp(const uint64_t ×tamp) { + int ret = hicn_packet_set_signature_timestamp( + format_, (hicn_header_t *)packet_start_, timestamp); + + if (ret < 0) { + throw errors::RuntimeException("Error setting the signature timestamp."); + } +} + +uint64_t Packet::getSignatureTimestamp() const { + uint64_t return_value; + int ret = hicn_packet_get_signature_timestamp( + format_, (hicn_header_t *)packet_start_, &return_value); + + if (ret < 0) { + throw errors::RuntimeException("Error getting the signature timestamp."); + } + + return return_value; +} + +void Packet::setValidationAlgorithm(const utils::CryptoSuite &validation_algorithm) { + int ret = hicn_packet_set_validation_algorithm( + format_, (hicn_header_t *)packet_start_, uint8_t(validation_algorithm)); + + if (ret < 0) { + throw errors::RuntimeException("Error setting the validation algorithm."); + } +} + +utils::CryptoSuite Packet::getValidationAlgorithm() const { + uint8_t return_value; + int ret = hicn_packet_get_validation_algorithm( + format_, (hicn_header_t *)packet_start_, &return_value); + + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + + return utils::CryptoSuite(return_value); +} + +void Packet::setKeyId(const utils::KeyId &key_id) { + int ret = hicn_packet_set_key_id( + format_, (hicn_header_t *)packet_start_, key_id.first); + + if (ret < 0) { + throw errors::RuntimeException("Error setting the key id."); + } +} + +utils::KeyId Packet::getKeyId() const { + utils::KeyId return_value; + int ret = hicn_packet_get_key_id( + format_, (hicn_header_t *)packet_start_, &return_value.first, &return_value.second); + + if (ret < 0) { + throw errors::RuntimeException("Error getting the validation algorithm."); + } + + return return_value; +} + +utils::CryptoHash Packet::computeDigest(HashAlgorithm algorithm) const { + utils::CryptoHasher hasher(static_cast<utils::CryptoHashType>(algorithm)); + hasher.init(); + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + + hicn_packet_copy_header(format_, (hicn_header_t *)packet_start_, &header_copy, + false); + + const_cast<Packet *>(this)->resetForHash(); + + std::size_t payload_len = getPayloadSizeFromBuffer(format_, packet_start_); + std::size_t header_length = getHeaderSizeFromFormat(format_); + std::size_t signature_size = _is_ah(format_) ? getSignatureSize() : 0; + + hasher.updateBytes(packet_start_, + payload_len + header_length + signature_size); + + hicn_packet_copy_header(format_, &header_copy, (hicn_header_t *)packet_start_, + false); + + return hasher.finalize(); +} + +void Packet::setChecksum() { + uint16_t partial_csum = 0; + + for (utils::MemBuf *current = header_head_->next(); + current && current != header_head_; current = current->next()) { + if (partial_csum != 0) { + partial_csum = ~partial_csum; + } + partial_csum = csum(current->data(), current->length(), partial_csum); + } + if (hicn_packet_compute_header_checksum( + format_, (hicn_header_t *)packet_start_, partial_csum) < 0) { + throw errors::MalformedPacketException(); + } +} + +bool Packet::checkIntegrity() const { + if (hicn_packet_check_integrity(format_, (hicn_header_t *)packet_start_) < + 0) { + return false; + } + + return true; +} + +Packet &Packet::setSyn() { + if (hicn_packet_set_syn((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting syn bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetSyn() { + if (hicn_packet_reset_syn((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting syn bit in the packet."); + } + + return *this; +} + +bool Packet::testSyn() const { + bool res = false; + if (hicn_packet_test_syn((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing syn bit in the packet."); + } + + return res; +} + +Packet &Packet::setAck() { + if (hicn_packet_set_ack((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting ack bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetAck() { + if (hicn_packet_reset_ack((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting ack bit in the packet."); + } + + return *this; +} + +bool Packet::testAck() const { + bool res = false; + if (hicn_packet_test_ack((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing ack bit in the packet."); + } + + return res; +} + +Packet &Packet::setRst() { + if (hicn_packet_set_rst((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting rst bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetRst() { + if (hicn_packet_reset_rst((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting rst bit in the packet."); + } + + return *this; +} + +bool Packet::testRst() const { + bool res = false; + if (hicn_packet_test_rst((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing rst bit in the packet."); + } + + return res; +} + +Packet &Packet::setFin() { + if (hicn_packet_set_fin((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error setting fin bit in the packet."); + } + + return *this; +} + +Packet &Packet::resetFin() { + if (hicn_packet_reset_fin((hicn_header_t *)packet_start_) < 0) { + throw errors::RuntimeException("Error resetting fin bit in the packet."); + } + + return *this; +} + +bool Packet::testFin() const { + bool res = false; + if (hicn_packet_test_fin((hicn_header_t *)packet_start_, &res) < 0) { + throw errors::RuntimeException("Error testing fin bit in the packet."); + } + + return res; +} + +Packet &Packet::resetFlags() { + resetSyn(); + resetAck(); + resetRst(); + resetFin(); + + return *this; +} + +std::string Packet::printFlags() const { + std::string flags = ""; + if (testSyn()) { + flags += "S"; + } + if (testAck()) { + flags += "A"; + } + if (testRst()) { + flags += "R"; + } + if (testFin()) { + flags += "F"; + } + return flags; +} + +Packet &Packet::setSrcPort(uint16_t srcPort) { + if (hicn_packet_set_src_port((hicn_header_t *)packet_start_, srcPort) < 0) { + throw errors::RuntimeException("Error setting source port in the packet."); + } + + return *this; +} + +Packet &Packet::setDstPort(uint16_t dstPort) { + if (hicn_packet_set_dst_port((hicn_header_t *)packet_start_, dstPort) < 0) { + throw errors::RuntimeException( + "Error setting destination port in the packet."); + } + + return *this; +} + +uint16_t Packet::getSrcPort() const { + uint16_t port = 0; + + if (hicn_packet_get_src_port((hicn_header_t *)packet_start_, &port) < 0) { + throw errors::RuntimeException("Error reading source port in the packet."); + } + + return port; +} + +uint16_t Packet::getDstPort() const { + uint16_t port = 0; + + if (hicn_packet_get_dst_port((hicn_header_t *)packet_start_, &port) < 0) { + throw errors::RuntimeException( + "Error reading destination port in the packet."); + } + + return port; +} + +Packet &Packet::setTTL(uint8_t hops) { + if (hicn_packet_set_hoplimit((hicn_header_t *)packet_start_, hops) < 0) { + throw errors::RuntimeException("Error setting TTL."); + } + + return *this; +} + +uint8_t Packet::getTTL() const { + uint8_t hops = 0; + if (hicn_packet_get_hoplimit((hicn_header_t *)packet_start_, &hops) < 0) { + throw errors::RuntimeException("Error reading TTL."); + } + + return hops; +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/packet.h b/libtransport/src/hicn/transport/core/packet.h new file mode 100755 index 000000000..0a5673401 --- /dev/null +++ b/libtransport/src/hicn/transport/core/packet.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 <hicn/transport/core/name.h> +#include <hicn/transport/core/payload_type.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/branch_prediction.h> +#include <hicn/transport/utils/crypto_hasher.h> +#include <hicn/transport/utils/membuf.h> +#include <hicn/transport/utils/object_pool.h> +#include <hicn/transport/utils/crypto_suite.h> +#include <hicn/transport/utils/key_id.h> + +namespace utils { +class Signer; +class Verifier; +} // namespace utils + +namespace transport { + +namespace core { + +/* + * Basic IP packet, modelled as circular chain of buffers: + * Header = H + * Payload = P + * + * H_0 --> H_1 --> H_2 --> P_0 --> P_1 --> P_2 + * \_______________________________________| + */ + +class Packet : public std::enable_shared_from_this<Packet> { + friend class utils::Signer; + friend class utils::Verifier; + + public: + using MemBufPtr = std::shared_ptr<utils::MemBuf>; + using Format = hicn_format_t; + static constexpr size_t default_mtu = 1500; + + /** + * Create new IP packet. Here we allocate just the header, + * the eventual payload will be added by prepending the payload buffer + * to the buffer chain whose the fist buffer is the header itself. + */ + Packet(Format format = HF_UNSPEC); + + /** + * Create new IP packet using raw buffer. + */ + Packet(const uint8_t *buffer, std::size_t size); + Packet(MemBufPtr &&buffer); + + /* + * Enforce zero-copy lifestyle. + */ + Packet(const Packet &other) = delete; + Packet &operator=(const Packet &other) = delete; + + /* + * Move constructor. + */ + Packet(Packet &&other); + + friend bool operator==(const Packet &l_packet, const Packet &r_packet); + + virtual ~Packet(); + + static std::size_t getHeaderSizeFromFormat(Format format, + std::size_t signature_size = 0); + + static std::size_t getHeaderSizeFromBuffer(Format format, + const uint8_t *buffer); + + static std::size_t getPayloadSizeFromBuffer(Format format, + const uint8_t *buffer); + + static bool isInterest(const uint8_t *buffer); + + static Format getFormatFromBuffer(const uint8_t *buffer); + + std::size_t payloadSize() const; + + std::size_t headerSize() const; + + const std::shared_ptr<utils::MemBuf> data(); + + const uint8_t *start() const; + + virtual void setLifetime(uint32_t lifetime); + + virtual uint32_t getLifetime() const; + + Packet &appendPayload(const uint8_t *buffer, std::size_t length); + + Packet &appendPayload(std::unique_ptr<utils::MemBuf> &&payload); + + Packet &appendHeader(std::unique_ptr<utils::MemBuf> &&header); + + Packet &appendHeader(const uint8_t *buffer, std::size_t length); + + utils::Array<uint8_t> getPayload() const; + + Packet &updateLength(std::size_t length = 0); + + PayloadType getPayloadType() const; + + Packet &setPayloadType(PayloadType payload_type); + + Format getFormat() const; + + void dump() const; + + virtual void setLocator(const ip_address_t &locator) = 0; + + virtual ip_address_t getLocator() const = 0; + + void setSignatureSize(std::size_t size_bytes); + + std::size_t getSignatureSize() const; + + void setSignatureTimestamp(const uint64_t ×tamp); + + uint64_t getSignatureTimestamp() const; + + void setValidationAlgorithm(const utils::CryptoSuite &validation_algorithm); + + utils::CryptoSuite getValidationAlgorithm() const; + + void setKeyId(const utils::KeyId &key_id); + + utils::KeyId getKeyId() const; + + void setSignature(std::unique_ptr<utils::MemBuf> &&signature); + + virtual utils::CryptoHash computeDigest(HashAlgorithm algorithm) const; + + void setChecksum(); + + bool checkIntegrity() const; + + Packet &setSyn(); + Packet &resetSyn(); + bool testSyn() const; + Packet &setAck(); + Packet &resetAck(); + bool testAck() const; + Packet &setRst(); + Packet &resetRst(); + bool testRst() const; + Packet &setFin(); + Packet &resetFin(); + bool testFin() const; + Packet &resetFlags(); + std::string printFlags() const; + + Packet &setSrcPort(uint16_t srcPort); + Packet &setDstPort(uint16_t dstPort); + uint16_t getSrcPort() const; + uint16_t getDstPort() const; + + Packet &setTTL(uint8_t hops); + uint8_t getTTL() const; + + private: + virtual void resetForHash() = 0; + + protected: + Name name_; + MemBufPtr packet_; + uint8_t *packet_start_; + utils::MemBuf *header_head_; + utils::MemBuf *payload_head_; + mutable Format format_; + + static const core::Name base_name; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/payload_type.h b/libtransport/src/hicn/transport/core/payload_type.h new file mode 100755 index 000000000..fa79db35a --- /dev/null +++ b/libtransport/src/hicn/transport/core/payload_type.h @@ -0,0 +1,29 @@ +/* + * 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 + +namespace transport { + +namespace core { + +enum class PayloadType : uint16_t { + CONTENT_OBJECT = HPT_DATA, + MANIFEST = HPT_MANIFEST, +}; + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/pending_interest.cc b/libtransport/src/hicn/transport/core/pending_interest.cc new file mode 100755 index 000000000..8f6de1839 --- /dev/null +++ b/libtransport/src/hicn/transport/core/pending_interest.cc @@ -0,0 +1,49 @@ +/* + * 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/pending_interest.h> + +namespace transport { + +namespace core { + +PendingInterest::PendingInterest() + : interest_(nullptr, nullptr), timer_(), received_(false) {} + +PendingInterest::PendingInterest(Interest::Ptr &&interest, + std::unique_ptr<asio::steady_timer> &&timer) + : interest_(std::move(interest)), + timer_(std::move(timer)), + received_(false) {} + +PendingInterest::~PendingInterest() { + // timer_.reset(); +} + +void PendingInterest::cancelTimer() { timer_->cancel(); } + +bool PendingInterest::isReceived() const { return received_; } + +void PendingInterest::setReceived() { received_ = true; } + +Interest::Ptr &&PendingInterest::getInterest() { return std::move(interest_); } + +void PendingInterest::setReceived(bool received) { + PendingInterest::received_ = received; +} + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/pending_interest.h b/libtransport/src/hicn/transport/core/pending_interest.h new file mode 100755 index 000000000..cbcafb5d9 --- /dev/null +++ b/libtransport/src/hicn/transport/core/pending_interest.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/deadline_timer.h> + +#include <asio/steady_timer.hpp> + +namespace transport { + +namespace core { + +class HicnForwarderInterface; +class VPPForwarderInterface; +class RawSocketInterface; + +template <typename ForwarderInt> +class Portal; + +typedef std::function<void(const std::error_code &)> TimerCallback; + +class PendingInterest { + friend class Portal<HicnForwarderInterface>; + friend class Portal<VPPForwarderInterface>; + friend class Portal<RawSocketInterface>; + + public: + PendingInterest(); + + PendingInterest(Interest::Ptr &&interest, + std::unique_ptr<asio::steady_timer> &&timer); + + ~PendingInterest(); + + bool isReceived() const; + + template <typename Handler> + TRANSPORT_ALWAYS_INLINE void startCountdown(Handler &&cb) { + timer_->expires_from_now( + std::chrono::milliseconds(interest_->getLifetime())); + timer_->async_wait(cb); + } + + void cancelTimer(); + + void setReceived(); + + Interest::Ptr &&getInterest(); + + void setReceived(bool received); + + bool isValid() const; + + void setValid(bool valid); + + private: + Interest::Ptr interest_; + std::unique_ptr<asio::steady_timer> timer_; + bool received_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/portal.h b/libtransport/src/hicn/transport/core/portal.h new file mode 100755 index 000000000..88020447f --- /dev/null +++ b/libtransport/src/hicn/transport/core/portal.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/forwarder_interface.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/core/pending_interest.h> +#include <hicn/transport/core/prefix.h> +#include <hicn/transport/core/socket_connector.h> +#include <hicn/transport/errors/errors.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/log.h> + +#ifdef __vpp__ +#include <hicn/transport/core/memif_connector.h> +#endif + +#include <asio.hpp> +#include <asio/steady_timer.hpp> +#include <future> +#include <memory> +#include <unordered_map> + +#define UNSET_CALLBACK 0 + +namespace transport { + +namespace core { + +typedef std::unordered_map<Name, std::unique_ptr<PendingInterest>> + PendingInterestHashTable; + +template <typename PrefixType> +class BasicBindConfig { + static_assert(std::is_same<Prefix, PrefixType>::value, + "Prefix must be a Prefix type."); + + const uint32_t standard_cs_reserved = 5000; + + public: + template <typename T> + BasicBindConfig(T &&prefix) + : prefix_(std::forward<T &&>(prefix)), + content_store_reserved_(standard_cs_reserved) {} + + template <typename T> + BasicBindConfig(T &&prefix, uint32_t cs_reserved) + : prefix_(std::forward<T &&>(prefix)), + content_store_reserved_(cs_reserved) {} + + TRANSPORT_ALWAYS_INLINE const PrefixType &prefix() const { return prefix_; } + + TRANSPORT_ALWAYS_INLINE uint32_t csReserved() const { + return content_store_reserved_; + } + + private: + PrefixType prefix_; + uint32_t content_store_reserved_; +}; + +using BindConfig = BasicBindConfig<Prefix>; + +template <typename ForwarderInt> +class Portal { + static_assert( + std::is_base_of<ForwarderInterface<ForwarderInt, + typename ForwarderInt::ConnectorType>, + ForwarderInt>::value, + "ForwarderInt must inherit from ForwarderInterface!"); + + public: + class ConsumerCallback { + public: + virtual void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) = 0; + virtual void onTimeout(Interest::Ptr &&i) = 0; + }; + + class ProducerCallback { + public: + virtual void onInterest(Interest::Ptr &&i) = 0; + }; + + Portal() : Portal(internal_io_service_) { + internal_work_ = std::make_unique<asio::io_service::work>(io_service_); + } + + Portal(asio::io_service &io_service) + : io_service_(io_service), + is_running_(false), + app_name_("libtransport_application"), + consumer_callback_(nullptr), + producer_callback_(nullptr), + connector_(std::bind(&Portal::processIncomingMessages, this, + std::placeholders::_1), + std::bind(&Portal::setLocalRoutes, this), io_service_, + app_name_), + forwarder_interface_(connector_) {} + + void setConsumerCallback(ConsumerCallback *consumer_callback) { + consumer_callback_ = consumer_callback; + } + + void setProducerCallback(ProducerCallback *producer_callback) { + producer_callback_ = producer_callback; + } + + TRANSPORT_ALWAYS_INLINE void setOutputInterface( + const std::string &output_interface) { + forwarder_interface_.setOutputInterface(output_interface); + } + + TRANSPORT_ALWAYS_INLINE void connect(bool is_consumer = true) { + forwarder_interface_.connect(is_consumer); + } + + ~Portal() { + connector_.close(); + stopEventsLoop(); + } + + TRANSPORT_ALWAYS_INLINE bool interestIsPending(const Name &name) { + auto it = pending_interest_hash_table_.find(name); + if (it != pending_interest_hash_table_.end()) + if (!it->second->isReceived()) return true; + + return false; + } + + TRANSPORT_ALWAYS_INLINE void sendInterest(Interest::Ptr &&interest) { + const Name name(interest->getName(), true); + + // Send it + forwarder_interface_.send(*interest); + + pending_interest_hash_table_[name] = std::make_unique<PendingInterest>( + std::move(interest), std::make_unique<asio::steady_timer>(io_service_)); + + pending_interest_hash_table_[name]->startCountdown( + std::bind(&Portal<ForwarderInt>::timerHandler, this, + std::placeholders::_1, name)); + } + + TRANSPORT_ALWAYS_INLINE void timerHandler(const std::error_code &ec, + const Name &name) { + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + if (TRANSPORT_EXPECT_TRUE(!ec)) { + std::unordered_map<Name, std::unique_ptr<PendingInterest>>::iterator it = + pending_interest_hash_table_.find(name); + if (it != pending_interest_hash_table_.end()) { + std::unique_ptr<PendingInterest> ptr = std::move(it->second); + pending_interest_hash_table_.erase(it); + + if (consumer_callback_) { + consumer_callback_->onTimeout(std::move(ptr->getInterest())); + } + } + } + } + + TRANSPORT_ALWAYS_INLINE void bind(const BindConfig &config) { + connector_.enableBurst(); + forwarder_interface_.setContentStoreSize(config.csReserved()); + served_namespaces_.push_back(config.prefix()); + registerRoute(served_namespaces_.back()); + } + + TRANSPORT_ALWAYS_INLINE void runEventsLoop() { + if (io_service_.stopped()) { + io_service_.reset(); // ensure that run()/poll() will do some work + } + + is_running_ = true; + this->io_service_.run(); + is_running_ = false; + } + + TRANSPORT_ALWAYS_INLINE void runOneEvent() { + if (io_service_.stopped()) { + io_service_.reset(); // ensure that run()/poll() will do some work + } + + is_running_ = true; + this->io_service_.run_one(); + is_running_ = false; + } + + TRANSPORT_ALWAYS_INLINE void sendContentObject( + ContentObject &content_object) { + forwarder_interface_.send(content_object); + } + + TRANSPORT_ALWAYS_INLINE void stopEventsLoop() { + is_running_ = false; + internal_work_.reset(); + + for (auto &pend_interest : pending_interest_hash_table_) { + pend_interest.second->cancelTimer(); + } + + clear(); + + io_service_.post([this]() { io_service_.stop(); }); + } + + TRANSPORT_ALWAYS_INLINE void killConnection() { connector_.close(); } + + TRANSPORT_ALWAYS_INLINE void clear() { pending_interest_hash_table_.clear();} + + TRANSPORT_ALWAYS_INLINE asio::io_service &getIoService() { + return io_service_; + } + + TRANSPORT_ALWAYS_INLINE std::size_t getPITSize() { + connector_.state(); + return pending_interest_hash_table_.size(); + } + + TRANSPORT_ALWAYS_INLINE void registerRoute(Prefix &prefix) { + forwarder_interface_.registerRoute(prefix); + } + + private: + TRANSPORT_ALWAYS_INLINE void processIncomingMessages( + Packet::MemBufPtr &&packet_buffer) { + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + if (packet_buffer->data()[0] == ForwarderInt::ack_code) { + // Hicn forwarder message + processControlMessage(std::move(packet_buffer)); + return; + } + + bool is_interest = Packet::isInterest(packet_buffer->data()); + Packet::Format format = Packet::getFormatFromBuffer(packet_buffer->data()); + + if (TRANSPORT_EXPECT_TRUE(_is_tcp(format))) { + if (!is_interest) { + processContentObject( + ContentObject::Ptr(new ContentObject(std::move(packet_buffer)))); + } else { + processInterest(Interest::Ptr(new Interest(std::move(packet_buffer)))); + } + } else { + TRANSPORT_LOGE("Received not supported packet. Ignoring it."); + } + } + + TRANSPORT_ALWAYS_INLINE void setLocalRoutes() { + for (auto &name : served_namespaces_) { + registerRoute(name); + } + } + + TRANSPORT_ALWAYS_INLINE void processInterest(Interest::Ptr &&interest) { + // Interest for a producer + if (TRANSPORT_EXPECT_TRUE(producer_callback_ != nullptr)) { + producer_callback_->onInterest(std::move(interest)); + } + } + + TRANSPORT_ALWAYS_INLINE void processContentObject( + ContentObject::Ptr &&content_object) { + PendingInterestHashTable::iterator it = + pending_interest_hash_table_.find(content_object->getName()); + + if (TRANSPORT_EXPECT_TRUE(it != pending_interest_hash_table_.end())) { + std::unique_ptr<PendingInterest> interest_ptr = std::move(it->second); + interest_ptr->cancelTimer(); + + if (TRANSPORT_EXPECT_TRUE(!interest_ptr->isReceived())) { + interest_ptr->setReceived(); + pending_interest_hash_table_.erase(content_object->getName()); + + if (consumer_callback_) { + consumer_callback_->onContentObject( + std::move(interest_ptr->getInterest()), + std::move(content_object)); + } + + } else { + TRANSPORT_LOGW( + "Content already received (interest already satisfied)."); + } + } else { + TRANSPORT_LOGW("No pending interests for current content (%s)", + content_object->getName().toString().c_str()); + } + } + + TRANSPORT_ALWAYS_INLINE void processControlMessage( + Packet::MemBufPtr &&packet_buffer) { + // Control message as response to the route set by a producer. + // Do nothing + } + + private: + asio::io_service &io_service_; + asio::io_service internal_io_service_; + std::unique_ptr<asio::io_service::work> internal_work_; + + volatile bool is_running_; + + std::string app_name_; + + PendingInterestHashTable pending_interest_hash_table_; + + ConsumerCallback *consumer_callback_; + ProducerCallback *producer_callback_; + + typename ForwarderInt::ConnectorType connector_; + ForwarderInt forwarder_interface_; + + std::list<Prefix> served_namespaces_; + + ip_address_t locator4_; + ip_address_t locator6_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/prefix.cc b/libtransport/src/hicn/transport/core/prefix.cc new file mode 100755 index 000000000..69c2b845a --- /dev/null +++ b/libtransport/src/hicn/transport/core/prefix.cc @@ -0,0 +1,211 @@ +/* + * 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/prefix.h> +#include <hicn/transport/errors/errors.h> +#include <hicn/transport/utils/string_tokenizer.h> + +extern "C" { +#include <arpa/inet.h> +} + +#include <cstring> +#include <memory> +#include <random> + +namespace transport { + +namespace core { + +Prefix::Prefix() { std::memset(&ip_address_, 0, sizeof(ip_address_t)); } + +Prefix::Prefix(const char *prefix) : Prefix(std::string(prefix)) {} + +Prefix::Prefix(std::string &&prefix) : Prefix(prefix) {} + +Prefix::Prefix(const std::string &prefix) { + utils::StringTokenizer st(prefix, "/"); + + std::string ip_address = st.nextToken(); + int family = get_addr_family(ip_address.c_str()); + + std::string prefix_length = family == AF_INET6 ? "128" : "32"; + + if (st.hasMoreTokens()) { + prefix_length = st.nextToken(); + } + + buildPrefix(ip_address, uint16_t(atoi(prefix_length.c_str())), family); +} + +Prefix::Prefix(std::string &prefix, uint16_t prefix_length) { + int family = get_addr_family(prefix.c_str()); + buildPrefix(prefix, prefix_length, family); +} + +Prefix::Prefix(const core::Name &content_name, uint16_t prefix_length) { + int family = content_name.getAddressFamily(); + + if (!checkPrefixLengthAndAddressFamily(prefix_length, family)) { + throw errors::InvalidIpAddressException(); + } + + ip_address_ = content_name.toIpAddress(); + ip_address_.prefix_len = prefix_length; + ip_address_.family = family; +} + +void Prefix::buildPrefix(std::string &prefix, uint16_t prefix_length, + int family) { + if (!checkPrefixLengthAndAddressFamily(prefix_length, family)) { + throw errors::InvalidIpAddressException(); + } + + int ret = inet_pton(family, prefix.c_str(), ip_address_.buffer); + + if (ret != 1) { + throw errors::InvalidIpAddressException(); + } + + ip_address_.prefix_len = prefix_length; + ip_address_.family = family; +} + +std::unique_ptr<Sockaddr> Prefix::toSockaddr() { + Sockaddr *ret = nullptr; + + switch (ip_address_.family) { + case AF_INET6: + ret = (Sockaddr *)new Sockaddr6; + break; + case AF_INET: + ret = (Sockaddr *)new Sockaddr4; + break; + default: + throw errors::InvalidIpAddressException(); + } + + if (hicn_ip_to_sockaddr_address(&ip_address_, ret) < 0) { + throw errors::InvalidIpAddressException(); + } + + return std::unique_ptr<Sockaddr>(ret); +} + +uint16_t Prefix::getPrefixLength() { return ip_address_.prefix_len; } + +Prefix &Prefix::setPrefixLength(uint16_t prefix_length) { + ip_address_.prefix_len = prefix_length; + return *this; +} + +int Prefix::getAddressFamily() { return ip_address_.family; } + +Prefix &Prefix::setAddressFamily(int address_family) { + ip_address_.family = address_family; + return *this; +} + +std::string Prefix::getNetwork() const { + if (!checkPrefixLengthAndAddressFamily(ip_address_.prefix_len, + ip_address_.family)) { + throw errors::InvalidIpAddressException(); + } + + std::size_t size = + ip_address_.family == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + + std::string network(size, 0); + + if (hicn_ip_ntop(&ip_address_, (char *)network.c_str(), size) < 0) { + throw errors::RuntimeException( + "Impossible to retrieve network from ip address."); + } + + return network; +} + +Name Prefix::getName() const { + std::string s(getNetwork()); + return Name(s); +} + +Prefix &Prefix::setNetwork(std::string &network) { + if (!inet_pton(AF_INET6, network.c_str(), ip_address_.buffer)) { + throw errors::RuntimeException("The network name is not valid."); + } + + return *this; +} + +Name Prefix::makeRandomName() const { + srand(time(nullptr)); + + if (ip_address_.family == AF_INET6) { + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution<uint32_t> idis( + 0, std::numeric_limits<uint32_t>::max()); + uint64_t random_number = idis(eng); + + uint32_t hash_size_bits = IPV6_ADDR_LEN_BITS - ip_address_.prefix_len; + uint64_t ip_address[2]; + memcpy(ip_address, ip_address_.buffer, sizeof(uint64_t)); + memcpy(ip_address + 1, ip_address_.buffer + 8, sizeof(uint64_t)); + std::string network(IPV6_ADDR_LEN * 3, 0); + + // Let's do the magic ;) + int shift_size = hash_size_bits > sizeof(random_number) * 8 + ? sizeof(random_number) * 8 + : hash_size_bits; + + ip_address[1] >>= shift_size; + ip_address[1] <<= shift_size; + + ip_address[1] |= random_number >> (sizeof(uint64_t) * 8 - shift_size); + + if (!inet_ntop(ip_address_.family, ip_address, (char *)network.c_str(), + IPV6_ADDR_LEN * 3)) { + throw errors::RuntimeException( + "Impossible to retrieve network from ip address."); + } + + return Name(network); + } + + return Name(); +} + +bool Prefix::checkPrefixLengthAndAddressFamily(uint16_t prefix_length, + int family) { + // First check the family + if (family != AF_INET6 && family != AF_INET) { + return false; + } + + int max_addr_len_bits = + family == AF_INET6 ? IPV6_ADDR_LEN_BITS : IPV4_ADDR_LEN_BITS; + + if (prefix_length > max_addr_len_bits) { + return false; + } + + return true; +} + +ip_address_t &Prefix::toIpAddressStruct() { return ip_address_; } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/prefix.h b/libtransport/src/hicn/transport/core/prefix.h new file mode 100755 index 000000000..b68c6bdf6 --- /dev/null +++ b/libtransport/src/hicn/transport/core/prefix.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/name.h> +namespace transport { + +namespace core { + +class Prefix { + public: + Prefix(); + + Prefix(const char *prefix); + + Prefix(const std::string &prefix); + + Prefix(std::string &&prefix); + + Prefix(std::string &prefix, uint16_t prefix_length); + + Prefix(const core::Name &content_name, uint16_t prefix_length); + + std::unique_ptr<Sockaddr> toSockaddr(); + + uint16_t getPrefixLength(); + + Prefix &setPrefixLength(uint16_t prefix_length); + + std::string getNetwork() const; + + Name getName() const; + + Prefix &setNetwork(std::string &network); + + int getAddressFamily(); + + Prefix &setAddressFamily(int address_family); + + Name makeRandomName() const; + + ip_address_t &toIpAddressStruct(); + + private: + static bool checkPrefixLengthAndAddressFamily(uint16_t prefix_length, + int family); + + void buildPrefix(std::string &prefix, uint16_t prefix_length, int family); + + ip_address_t ip_address_; +}; + +} // end namespace core + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/raw_socket_connector.cc b/libtransport/src/hicn/transport/core/raw_socket_connector.cc new file mode 100755 index 000000000..5cfff39fb --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_connector.cc @@ -0,0 +1,214 @@ +/* + * 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/raw_socket_connector.h> +#include <hicn/transport/utils/conversions.h> +#include <hicn/transport/utils/log.h> + +#include <net/if.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#define MY_DEST_MAC0 0x0a +#define MY_DEST_MAC1 0x7b +#define MY_DEST_MAC2 0x7c +#define MY_DEST_MAC3 0x1c +#define MY_DEST_MAC4 0x4a +#define MY_DEST_MAC5 0x14 + +namespace transport { + +namespace core { + +RawSocketConnector::RawSocketConnector( + PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, asio::io_service &io_service, + std::string app_name) + : Connector(), + io_service_(io_service), + socket_(io_service_, raw_protocol(PF_PACKET, SOCK_RAW)), + // resolver_(io_service_), + timer_(io_service_), + read_msg_(packet_pool_.makePtr(nullptr)), + data_available_(false), + receive_callback_(receive_callback), + on_reconnect_callback_(on_reconnect_callback), + app_name_(app_name) { + memset(&link_layer_address_, 0, sizeof(link_layer_address_)); +} + +RawSocketConnector::~RawSocketConnector() {} + +void RawSocketConnector::connect(const std::string &interface_name, + const std::string &mac_address_str) { + memset(ðernet_header_, 0, sizeof(ethernet_header_)); + struct ifreq ifr; + struct ifreq if_mac; + uint8_t mac_address[6]; + + utils::convertStringToMacAddress(mac_address_str, mac_address); + + // Get interface mac address + int fd = static_cast<int>(socket_.native_handle()); + + /* Get the index of the interface to send on */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, interface_name.c_str(), interface_name.size()); + + // if (ioctl(fd, SIOCGIFINDEX, &if_idx) < 0) { + // perror("SIOCGIFINDEX"); + // } + + /* Get the MAC address of the interface to send on */ + memset(&if_mac, 0, sizeof(struct ifreq)); + strncpy(if_mac.ifr_name, interface_name.c_str(), interface_name.size()); + if (ioctl(fd, SIOCGIFHWADDR, &if_mac) < 0) { + perror("SIOCGIFHWADDR"); + throw errors::RuntimeException("Interface does not exist"); + } + + /* Ethernet header */ + for (int i = 0; i < 6; i++) { + ethernet_header_.ether_shost[i] = + ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[i]; + ethernet_header_.ether_dhost[i] = mac_address[i]; + } + + /* Ethertype field */ + ethernet_header_.ether_type = htons(ETH_P_IPV6); + + strcpy(ifr.ifr_name, interface_name.c_str()); + + if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) { + memcpy(link_layer_address_.sll_addr, ifr.ifr_hwaddr.sa_data, 6); + } + + // memset(&ifr, 0, sizeof(ifr)); + // ioctl(fd, SIOCGIFFLAGS, &ifr); + // ifr.ifr_flags |= IFF_PROMISC; + // ioctl(fd, SIOCSIFFLAGS, &ifr); + + link_layer_address_.sll_family = AF_PACKET; + link_layer_address_.sll_protocol = htons(ETH_P_ALL); + link_layer_address_.sll_ifindex = if_nametoindex(interface_name.c_str()); + link_layer_address_.sll_hatype = 1; + link_layer_address_.sll_halen = 6; + + // startConnectionTimer(); + doConnect(); + doRecvPacket(); +} + +void RawSocketConnector::state() { return; } + +void RawSocketConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) { + // asio::async_write(socket_, asio::buffer(packet, len), + // [packet_sent] (std::error_code ec, + // std::size_t /*length*/) { + // packet_sent(); + // }); +} + +void RawSocketConnector::send(const Packet::MemBufPtr &packet) { + // Packet &p = const_cast<Packet &>(packet); + // p.setTcpChecksum(); + + // if (!p.checkIntegrity()) { + // TRANSPORT_LOGW("Sending message with wrong checksum!!!"); + // } + + // std::shared_ptr<const Packet> ptr; + // try { + // ptr = packet.shared_from_this(); + // } catch (std::bad_weak_ptr& exc) { + // TRANSPORT_LOGW("Sending interest which has not been created using a + // shared PTR! A copy will be made."); ptr = + // std::shared_ptr<Packet>(packet.clone()); + // } + + io_service_.post([this, packet]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(packet)); + if (!write_in_progress) { + doSendPacket(); + } else { + // Tell the handle connect it has data to write + data_available_ = true; + } + }); +} + +void RawSocketConnector::close() { + io_service_.post([this]() { socket_.close(); }); +} + +void RawSocketConnector::doSendPacket() { + auto packet = output_buffer_.front().get(); + auto array = std::vector<asio::const_buffer>(); + + const utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + socket_.async_send( + std::move(array), + [this /*, packet*/](std::error_code ec, std::size_t bytes_transferred) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doSendPacket(); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + }); +} + +void RawSocketConnector::doRecvPacket() { + read_msg_ = std::move(getPacket()); + socket_.async_receive( + asio::buffer(read_msg_->writableData(), packet_size), + [this](std::error_code ec, std::size_t bytes_transferred) mutable { + if (!ec) { + // Ignore packets that are not for us + uint8_t *dst_mac_address = const_cast<uint8_t *>(read_msg_->data()); + if (!std::memcmp(dst_mac_address, ethernet_header_.ether_shost, + ETHER_ADDR_LEN)) { + read_msg_->append(bytes_transferred); + read_msg_->trimStart(sizeof(struct ether_header)); + receive_callback_(std::move(read_msg_)); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + } + doRecvPacket(); + }); +} + +void RawSocketConnector::doConnect() { + socket_.bind(raw_endpoint(&link_layer_address_, sizeof(link_layer_address_))); +} + +void RawSocketConnector::enableBurst() { return; } + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/raw_socket_connector.h b/libtransport/src/hicn/transport/core/raw_socket_connector.h new file mode 100755 index 000000000..5e39efa0e --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_connector.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/connector.h> +#include <hicn/transport/core/name.h> + +#include <linux/if_packet.h> +#include <net/ethernet.h> +#include <sys/socket.h> +#include <asio.hpp> +#include <deque> + +namespace transport { + +namespace core { + +using asio::generic::raw_protocol; +using raw_endpoint = asio::generic::basic_endpoint<raw_protocol>; + +class RawSocketConnector : public Connector { + public: + RawSocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~RawSocketConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void enableBurst() override; + + void connect(const std::string &interface_name, + const std::string &mac_address_str); + + void state() override; + + private: + void doConnect(); + + void doRecvPacket(); + + void doSendPacket(); + + private: + asio::io_service &io_service_; + raw_protocol::socket socket_; + + struct ether_header ethernet_header_; + + struct sockaddr_ll link_layer_address_; + + asio::steady_timer timer_; + + utils::ObjectPool<utils::MemBuf>::Ptr read_msg_; + + bool data_available_; + + PacketReceivedCallback receive_callback_; + OnReconnect on_reconnect_callback_; + std::string app_name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/raw_socket_interface.cc b/libtransport/src/hicn/transport/core/raw_socket_interface.cc new file mode 100755 index 000000000..37aaff7e0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_interface.cc @@ -0,0 +1,57 @@ +/* + * 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/raw_socket_interface.h> +#include <hicn/transport/utils/linux.h> + +#include <fstream> + +namespace transport { + +namespace core { + +static std::string config_folder_path = "/etc/transport/interface.conf.d"; + +RawSocketInterface::RawSocketInterface(RawSocketConnector &connector) + : ForwarderInterface<RawSocketInterface, RawSocketConnector>(connector) {} + +RawSocketInterface::~RawSocketInterface() {} + +void RawSocketInterface::connect(bool is_consumer) { + std::string complete_filename = + config_folder_path + std::string("/") + output_interface_; + + std::ifstream is(complete_filename); + std::string interface; + + if (is) { + is >> remote_mac_address_; + } + + // Get interface ip address + struct sockaddr_in6 address; + utils::retrieveInterfaceAddress(output_interface_, &address); + inet6_address_.family = address.sin6_family; + + std::memcpy(inet6_address_.buffer, &address.sin6_addr, + sizeof(address.sin6_addr)); + connector_.connect(output_interface_, remote_mac_address_); +} + +void RawSocketInterface::registerRoute(Prefix &prefix) { return; } + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/raw_socket_interface.h b/libtransport/src/hicn/transport/core/raw_socket_interface.h new file mode 100755 index 000000000..c030af662 --- /dev/null +++ b/libtransport/src/hicn/transport/core/raw_socket_interface.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/forwarder_interface.h> +#include <hicn/transport/core/prefix.h> +#include <hicn/transport/core/raw_socket_connector.h> + +#include <atomic> +#include <deque> + +namespace transport { + +namespace core { + +class RawSocketInterface + : public ForwarderInterface<RawSocketInterface, RawSocketConnector> { + public: + typedef RawSocketConnector ConnectorType; + + RawSocketInterface(RawSocketConnector &connector); + + ~RawSocketInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + std::uint16_t getMtu() { return interface_mtu; } + + private: + static constexpr std::uint16_t interface_mtu = 1500; + std::string remote_mac_address_; +}; + +} // namespace core + +} // namespace transport diff --git a/libtransport/src/hicn/transport/core/socket_connector.cc b/libtransport/src/hicn/transport/core/socket_connector.cc new file mode 100755 index 000000000..332b87ec7 --- /dev/null +++ b/libtransport/src/hicn/transport/core/socket_connector.cc @@ -0,0 +1,237 @@ +/* + * 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/socket_connector.h> +#include <hicn/transport/errors/errors.h> +#include <hicn/transport/utils/log.h> +#include <hicn/transport/utils/object_pool.h> + +#include <vector> + +namespace transport { + +namespace core { + +namespace { +class NetworkMessage { + public: + static constexpr std::size_t fixed_header_length = 10; + + static std::size_t decodeHeader(const uint8_t *packet) { + // General checks + // CCNX Control packet format + uint8_t first_byte = packet[0]; + uint8_t ip_format = (packet[0] & 0xf0) >> 4; + + if (TRANSPORT_EXPECT_FALSE(first_byte == 102)) { + // Get packet length + return 44; + } else if (TRANSPORT_EXPECT_TRUE(ip_format == 6 || ip_format == 4)) { + Packet::Format format = Packet::getFormatFromBuffer(packet); + return Packet::getHeaderSizeFromBuffer(format, packet) + + Packet::getPayloadSizeFromBuffer(format, packet); + } + + return 0; + } +}; +} // namespace + +SocketConnector::SocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&on_reconnect_callback, + asio::io_service &io_service, + std::string app_name) + : Connector(), + io_service_(io_service), + socket_(io_service_), + resolver_(io_service_), + timer_(io_service_), + read_msg_(packet_pool_.makePtr(nullptr)), + is_connecting_(false), + is_reconnection_(false), + data_available_(false), + receive_callback_(receive_callback), + on_reconnect_callback_(on_reconnect_callback), + app_name_(app_name) {} + +SocketConnector::~SocketConnector() {} + +void SocketConnector::connect(std::string ip_address, std::string port) { + endpoint_iterator_ = resolver_.resolve( + {ip_address, port, asio::ip::resolver_query_base::numeric_service}); + + startConnectionTimer(); + doConnect(); +} + +void SocketConnector::state() { return; } + +void SocketConnector::send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent) { + asio::async_write(socket_, asio::buffer(packet, len), + [packet_sent](std::error_code ec, std::size_t /*length*/) { + packet_sent(); + }); +} + +void SocketConnector::send(const Packet::MemBufPtr &packet) { + io_service_.post([this, packet]() { + bool write_in_progress = !output_buffer_.empty(); + output_buffer_.push_back(std::move(packet)); + if (TRANSPORT_EXPECT_FALSE(!is_connecting_)) { + if (!write_in_progress) { + doWrite(); + } + } else { + // Tell the handle connect it has data to write + data_available_ = true; + } + }); +} + +void SocketConnector::close() { + io_service_.post([this]() { socket_.close(); }); +} + +void SocketConnector::doWrite() { + // TODO improve this piece of code for sending many buffers togethers + // if list contains more than one packet + auto packet = output_buffer_.front().get(); + auto array = std::vector<asio::const_buffer>(); + + const utils::MemBuf *current = packet; + do { + array.push_back(asio::const_buffer(current->data(), current->length())); + current = current->next(); + } while (current != packet); + + asio::async_write( + socket_, std::move(array), + [this /*, packet*/](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + output_buffer_.pop_front(); + if (!output_buffer_.empty()) { + doWrite(); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void SocketConnector::doReadBody(std::size_t body_length) { + asio::async_read( + socket_, asio::buffer(read_msg_->writableTail(), body_length), + asio::transfer_exactly(body_length), + [this](std::error_code ec, std::size_t length) { + read_msg_->append(length); + if (TRANSPORT_EXPECT_TRUE(!ec)) { + receive_callback_(std::move(read_msg_)); + doReadHeader(); + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void SocketConnector::doReadHeader() { + read_msg_ = getPacket(); + asio::async_read( + socket_, + asio::buffer(read_msg_->writableData(), + NetworkMessage::fixed_header_length), + asio::transfer_exactly(NetworkMessage::fixed_header_length), + [this](std::error_code ec, std::size_t length) { + if (TRANSPORT_EXPECT_TRUE(!ec)) { + read_msg_->append(NetworkMessage::fixed_header_length); + std::size_t body_length = 0; + if ((body_length = NetworkMessage::decodeHeader(read_msg_->data())) > + 0) { + doReadBody(body_length - length); + } else { + TRANSPORT_LOGE("Decoding error. Ignoring packet."); + } + } else { + TRANSPORT_LOGE("%d %s", ec.value(), ec.message().c_str()); + tryReconnect(); + } + }); +} + +void SocketConnector::tryReconnect() { + if (!is_connecting_) { + TRANSPORT_LOGE("Connection lost. Trying to reconnect...\n"); + is_connecting_ = true; + is_reconnection_ = true; + io_service_.post([this]() { + socket_.close(); + startConnectionTimer(); + doConnect(); + }); + } +} + +void SocketConnector::doConnect() { + asio::async_connect(socket_, endpoint_iterator_, + [this](std::error_code ec, tcp::resolver::iterator) { + if (!ec) { + timer_.cancel(); + is_connecting_ = false; + asio::ip::tcp::no_delay noDelayOption(true); + socket_.set_option(noDelayOption); + doReadHeader(); + + if (data_available_) { + data_available_ = false; + doWrite(); + } + + if (is_reconnection_) { + is_reconnection_ = false; + TRANSPORT_LOGI("Connection recovered!\n"); + on_reconnect_callback_(); + } + } else { + sleep(1); + doConnect(); + } + }); +} + +bool SocketConnector::checkConnected() { return !is_connecting_; } + +void SocketConnector::enableBurst() { return; } + +void SocketConnector::startConnectionTimer() { + timer_.expires_from_now(std::chrono::seconds(60)); + timer_.async_wait( + std::bind(&SocketConnector::handleDeadline, this, std::placeholders::_1)); +} + +void SocketConnector::handleDeadline(const std::error_code &ec) { + if (!ec) { + io_service_.post([this]() { + socket_.close(); + TRANSPORT_LOGE("Error connecting. Is the forwarder running?\n"); + io_service_.stop(); + }); + } +} + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/socket_connector.h b/libtransport/src/hicn/transport/core/socket_connector.h new file mode 100755 index 000000000..d7a05aab4 --- /dev/null +++ b/libtransport/src/hicn/transport/core/socket_connector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/connector.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/utils/branch_prediction.h> + +#include <asio.hpp> +#include <deque> + +namespace transport { +namespace core { + +using asio::ip::tcp; + +class SocketConnector : public Connector { + public: + SocketConnector(PacketReceivedCallback &&receive_callback, + OnReconnect &&reconnect_callback, + asio::io_service &io_service, + std::string app_name = "Libtransport"); + + ~SocketConnector() override; + + void send(const Packet::MemBufPtr &packet) override; + + void send(const uint8_t *packet, std::size_t len, + const PacketSentCallback &packet_sent = 0) override; + + void close() override; + + void enableBurst() override; + + void connect(std::string ip_address = "127.0.0.1", std::string port = "9695"); + + void state() override; + + private: + void doConnect(); + + void doReadHeader(); + + void doReadBody(std::size_t body_length); + + void doWrite(); + + bool checkConnected(); + + private: + void handleDeadline(const std::error_code &ec); + + void startConnectionTimer(); + + void tryReconnect(); + + asio::io_service &io_service_; + asio::ip::tcp::socket socket_; + asio::ip::tcp::resolver resolver_; + asio::ip::tcp::resolver::iterator endpoint_iterator_; + asio::steady_timer timer_; + + utils::ObjectPool<utils::MemBuf>::Ptr read_msg_; + + bool is_connecting_; + bool is_reconnection_; + bool data_available_; + + PacketReceivedCallback receive_callback_; + OnReconnect on_reconnect_callback_; + std::string app_name_; +}; + +} // end namespace core + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/core/test/CMakeLists.txt b/libtransport/src/hicn/transport/core/test/CMakeLists.txt new file mode 100755 index 000000000..48c50e9b0 --- /dev/null +++ b/libtransport/src/hicn/transport/core/test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_core_manifest) + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach()
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/test/test_core_manifest.cc b/libtransport/src/hicn/transport/core/test/test_core_manifest.cc new file mode 100755 index 000000000..58563d8f9 --- /dev/null +++ b/libtransport/src/hicn/transport/core/test/test_core_manifest.cc @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "../manifest_format_fixed.h" +#include "../manifest_inline.h" + +#include <test.h> +#include <random> +#include <vector> + +namespace transport { + +namespace core { + +namespace { +// The fixture for testing class Foo. +class ManifestTest : public ::testing::Test { + protected: + using ContentObjectManifest = ManifestInline<ContentObject, Fixed>; + + ManifestTest() : name_("b001::123|321"), manifest1_(name_) { + // You can do set-up work for each test here. + } + + virtual ~ManifestTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + Name name_; + ContentObjectManifest manifest1_; + + std::vector<uint8_t> manifest_payload = { + 0x11, 0x11, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad // , 0x00, 0x00, + // 0x00, 0x45, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc9, 0x00, 0x00, + // 0x00, 0x44, 0xa3, + // 0xd1, 0xf2, 0x2b, + // 0x94, 0x41, 0x22, + // 0xc8 + }; +}; + +} // namespace + +TEST_F(ManifestTest, ManifestCreate) { + ContentObjectManifest manifest2(name_); + ContentObjectManifest manifest3 = manifest2; + + EXPECT_EQ(manifest1_, manifest2); + EXPECT_EQ(manifest1_, manifest3); +} + +TEST_F(ManifestTest, ManifestCreateFromBase) { + ContentObject content_object(name_); + content_object.setPayload(manifest_payload.data(), manifest_payload.size()); + ContentObjectManifest manifest(std::move(content_object)); + + auto manifest4 = ContentObjectManifest::createManifest( + name_, core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, HashAlgorithm::SHA_256, true, + core::Name("b001::dead"), + core::NextSegmentCalculationStrategy::INCREMENTAL, 128); + + manifest4->encode(); + manifest4->dump(); + manifest.dump(); + + EXPECT_EQ(manifest1_, manifest); + // EXPECT_EQ(manifest1_, manifest3); +} + +TEST_F(ManifestTest, SetLastManifest) { + manifest1_.clear(); + + manifest1_.setFinalManifest(true); + manifest1_.encode(); + manifest1_.decode(); + bool fcn = manifest1_.isFinalManifest(); + + ASSERT_TRUE(fcn); +} + +TEST_F(ManifestTest, SetManifestType) { + manifest1_.clear(); + + ManifestType type1 = ManifestType::INLINE_MANIFEST; + ManifestType type2 = ManifestType::FLIC_MANIFEST; + + manifest1_.setManifestType(type1); + manifest1_.encode(); + manifest1_.decode(); + ManifestType type_returned1 = manifest1_.getManifestType(); + + manifest1_.clear(); + + manifest1_.setManifestType(type2); + manifest1_.encode(); + manifest1_.decode(); + ManifestType type_returned2 = manifest1_.getManifestType(); + + ASSERT_EQ(type1, type_returned1); + ASSERT_EQ(type2, type_returned2); +} + +TEST_F(ManifestTest, SetHashAlgorithm) { + manifest1_.clear(); + + HashAlgorithm hash1 = HashAlgorithm::SHA_512; + HashAlgorithm hash2 = HashAlgorithm::CRC32C; + HashAlgorithm hash3 = HashAlgorithm::SHA_256; + + manifest1_.setHashAlgorithm(hash1); + manifest1_.encode(); + manifest1_.decode(); + HashAlgorithm type_returned1 = manifest1_.getHashAlgorithm(); + + manifest1_.clear(); + + manifest1_.setHashAlgorithm(hash2); + manifest1_.encode(); + manifest1_.decode(); + HashAlgorithm type_returned2 = manifest1_.getHashAlgorithm(); + + manifest1_.clear(); + + manifest1_.setHashAlgorithm(hash3); + manifest1_.encode(); + manifest1_.decode(); + HashAlgorithm type_returned3 = manifest1_.getHashAlgorithm(); + + ASSERT_EQ(hash1, type_returned1); + ASSERT_EQ(hash2, type_returned2); + ASSERT_EQ(hash3, type_returned3); +} + +TEST_F(ManifestTest, SetNextSegmentCalculationStrategy) { + manifest1_.clear(); + + NextSegmentCalculationStrategy strategy1 = + NextSegmentCalculationStrategy::INCREMENTAL; + + manifest1_.setNextSegmentCalculationStrategy(strategy1); + manifest1_.encode(); + manifest1_.decode(); + NextSegmentCalculationStrategy type_returned1 = + manifest1_.getNextSegmentCalculationStrategy(); + + ASSERT_EQ(strategy1, type_returned1); +} + +TEST_F(ManifestTest, SetBaseName) { + manifest1_.clear(); + + core::Name base_name("b001::dead"); + manifest1_.setBaseName(base_name); + manifest1_.encode(); + manifest1_.decode(); + core::Name ret_name = manifest1_.getBaseName(); + + ASSERT_EQ(base_name, ret_name); +} + +TEST_F(ManifestTest, SetSuffixList) { + manifest1_.clear(); + + core::Name base_name("b001::dead"); + + using random_bytes_engine = + std::independent_bits_engine<std::default_random_engine, CHAR_BIT, + unsigned char>; + random_bytes_engine rbe; + + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution<uint64_t> idis( + 0, std::numeric_limits<uint32_t>::max()); + + auto entries = new std::pair<uint32_t, utils::CryptoHash>[3]; + uint32_t suffixes[3]; + std::vector<unsigned char> data[3]; + + for (int i = 0; i < 3; i++) { + data[i].resize(32); + std::generate(std::begin(data[i]), std::end(data[i]), std::ref(rbe)); + suffixes[i] = idis(eng); + entries[i] = std::make_pair( + suffixes[i], utils::CryptoHash(data[i].data(), data[i].size(), + utils::CryptoHashType::SHA_256)); + manifest1_.addSuffixHash(entries[i].first, entries[i].second); + } + + manifest1_.setBaseName(base_name); + + manifest1_.encode(); + manifest1_.decode(); + + core::Name ret_name = manifest1_.getBaseName(); + + // auto & hash_list = manifest1_.getSuffixHashList(); + + bool cond; + int i = 0; + + // for (auto & item : manifest1_.getSuffixList()) { + // auto hash = manifest1_.getHash(suffixes[i]); + // cond = utils::CryptoHash::compareBinaryDigest(hash, + // entries[i].second.getDigest<uint8_t>().data(), + // entries[i].second.getType()); + // ASSERT_TRUE(cond); + // i++; + // } + + ASSERT_EQ(base_name, ret_name); + + delete[] entries; +} + +TEST_F(ManifestTest, EstimateSize) { + manifest1_.clear(); + + HashAlgorithm hash1 = HashAlgorithm::SHA_256; + NextSegmentCalculationStrategy strategy1 = + NextSegmentCalculationStrategy::INCREMENTAL; + ManifestType type1 = ManifestType::INLINE_MANIFEST; + core::Name base_name1("b001:abcd:fede:baba:cece:d0d0:face:dead"); + + manifest1_.setFinalManifest(true); + manifest1_.setBaseName(base_name1); + manifest1_.setNextSegmentCalculationStrategy(strategy1); + manifest1_.setHashAlgorithm(hash1); + manifest1_.setManifestType(type1); + + std::default_random_engine eng((std::random_device())()); + std::uniform_int_distribution<uint64_t> idis( + 0, std::numeric_limits<uint64_t>::max()); + + using random_bytes_engine = + std::independent_bits_engine<std::default_random_engine, CHAR_BIT, + unsigned char>; + random_bytes_engine rbe; + + while (manifest1_.estimateManifestSize(1) < 1440) { + uint32_t suffix = static_cast<std::uint32_t>(idis(eng)); + std::vector<unsigned char> data(32); + std::generate(std::begin(data), std::end(data), std::ref(rbe)); + auto hash = utils::CryptoHash(data.data(), data.size(), + utils::CryptoHashType::SHA_256); + manifest1_.addSuffixHash(suffix, hash); + } + + manifest1_.encode(); + manifest1_.decode(); + + manifest1_.dump(); + + ASSERT_GT(manifest1_.estimateManifestSize(), 0); + ASSERT_LT(manifest1_.estimateManifestSize(), 1500); +} + +} // namespace core + +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_binary_api.c b/libtransport/src/hicn/transport/core/vpp_binary_api.c new file mode 100755 index 000000000..ab23d3cf5 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_binary_api.c @@ -0,0 +1,221 @@ +/* + * 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/config.h> + +#ifdef __vpp__ + +#include <hicn/transport/core/vpp_binary_api.h> +#include <hicn/transport/core/vpp_binary_api_internal.h> +#include <hicn/transport/utils/log.h> + +#include <vat/vat.h> +#include <vlibapi/api.h> +#include <vlibmemory/api.h> + +#include <vnet/ip/ip.h> +#include <vppinfra/error.h> + +#include <fcntl.h> +#include <inttypes.h> +#include <string.h> +#include <sys/stat.h> + +#include <vpp/api/vpe_msg_enum.h> + +#define vl_typedefs +#include <vpp/api/vpe_all_api_h.h> +#undef vl_typedefs + +#define vl_endianfun +#include <vpp/api/vpe_all_api_h.h> +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include <vpp/api/vpe_all_api_h.h> +#undef vl_printfun + +/* Get CRC codes of the messages */ +#define vl_msg_name_crc_list +#include <vpp/api/vpe_all_api_h.h> +#undef vl_msg_name_crc_list + +// #define vl_api_version(n,v) static u32 vpe_api_version = (v); +// #include <vpp/api/vpe.api.h> +// #undef vl_api_version + +#define POINTER_MAP_SIZE 32 +static void *global_pointers_map[POINTER_MAP_SIZE]; +static uint8_t global_pointers_map_index = 0; + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_memif_api_reply_msg \ + _(MEMIF_CREATE_REPLY, memif_create_reply) \ + _(MEMIF_DELETE_REPLY, memif_delete_reply) \ + _(MEMIF_DETAILS, memif_details) + +/** + * @brief Generic VPP request structure. + */ +typedef struct __attribute__((packed)) vl_generic_request_s { + u16 _vl_msg_id; + u32 client_index; + u32 context; +} vl_generic_request_t; + +/** + * @brief Generic VPP reply structure (response with a single message). + */ +typedef struct __attribute__((packed)) vl_generic_reply_s { + u16 _vl_msg_id; + u32 context; + i32 retval; +} vl_generic_reply_t; + +static void vl_api_control_ping_reply_t_handler( + vl_api_control_ping_reply_t *mp) { + // Just unblock main thread + vpp_binary_api_t *binary_api = global_pointers_map[mp->context]; + binary_api->ret_val = ntohl(mp->retval); + vpp_binary_api_unlock_waiting_thread(binary_api); +} + +static void vl_api_sw_interface_set_flags_reply_t_handler( + vl_api_control_ping_reply_t *mp) { + // Unblock main thread setting reply message status code + vpp_binary_api_t *binary_api = global_pointers_map[mp->context]; + binary_api->ret_val = ntohl(mp->retval); + vpp_binary_api_unlock_waiting_thread(binary_api); +} + +static int vpp_connect_to_vlib(vpp_binary_api_t *binary_api, char *name) { + clib_mem_init_thread_safe(0, 256 << 20); + if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0) { + return -1; + } + + binary_api->vl_input_queue = binary_api->api_main->shmem_hdr->vl_input_queue; + binary_api->my_client_index = binary_api->api_main->my_client_index; + + return 0; +} + +vpp_binary_api_t *vpp_binary_api_init(const char *app_name) { + vpp_binary_api_t *ret = malloc(sizeof(vpp_binary_api_t)); + ret->api_main = &api_main; + ret->vlib_main = &vlib_global_main; + + vpp_connect_to_vlib(ret, (char *)app_name); + ret->semaphore = sem_open(app_name, O_CREAT, 0, 0); + + return ret; +} + +void vpp_binary_api_destroy(vpp_binary_api_t *api) { + sem_close(api->semaphore); + free(api); + vl_client_disconnect_from_vlib(); +} + +void vpp_binary_api_unlock_waiting_thread(vpp_binary_api_t *api) { + sem_post(api->semaphore); +} + +void vpp_binary_api_send_receive_ping(vpp_binary_api_t *api) { + /* Use a control ping for synchronization */ + + /* Get the control ping ID */ +#define _(id, n, crc) \ + const char *id##_CRC __attribute__((unused)) = #n "_" #crc; + foreach_vl_msg_name_crc_vpe; +#undef _ + + int ping_reply_id = + vl_msg_api_get_msg_index((u8 *)(VL_API_CONTROL_PING_REPLY_CRC)); + vl_msg_api_set_handlers(ping_reply_id, "control_ping_reply", + vl_api_control_ping_reply_t_handler, vl_noop_handler, + vl_api_control_ping_reply_t_endian, + vl_api_control_ping_reply_t_print, + sizeof(vl_api_control_ping_reply_t), 1); + + vl_api_control_ping_t *mp_ping; + mp_ping = vl_msg_api_alloc_as_if_client(sizeof(*mp_ping)); + mp_ping->_vl_msg_id = clib_host_to_net_u16( + vl_msg_api_get_msg_index((u8 *)(VL_API_CONTROL_PING_CRC))); + mp_ping->client_index = api->my_client_index; + + global_pointers_map[global_pointers_map_index] = api; + mp_ping->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + TRANSPORT_LOGI("Sending ping id %u", mp_ping->_vl_msg_id); + + vpp_binary_api_send_request_wait_reply(api, mp_ping); +} + +int vpp_binary_api_set_int_state(vpp_binary_api_t *api, uint32_t sw_index, + link_state_t state) { +#define _(id, n, crc) \ + const char *id##_CRC __attribute__((unused)) = #n "_" #crc; + foreach_vl_msg_name_crc_vpe; +#undef _ + + int sw_interface_set_flags_reply_id = VL_API_SW_INTERFACE_SET_FLAGS_REPLY; + vl_msg_api_set_handlers( + sw_interface_set_flags_reply_id, "sw_interface_set_flags_reply", + vl_api_sw_interface_set_flags_reply_t_handler, vl_noop_handler, + vl_api_sw_interface_set_flags_reply_t_endian, + vl_api_sw_interface_set_flags_reply_t_print, + sizeof(vl_api_sw_interface_set_flags_reply_t), 1); + + vl_api_sw_interface_set_flags_t *mp; + mp = vl_msg_api_alloc_as_if_client(sizeof(*mp)); + mp->_vl_msg_id = clib_host_to_net_u16(VL_API_SW_INTERFACE_SET_FLAGS); + mp->client_index = api->my_client_index; + mp->sw_if_index = clib_host_to_net_u32(sw_index); + mp->admin_up_down = (u8)state; + + global_pointers_map[global_pointers_map_index] = api; + mp->context = global_pointers_map_index++; + global_pointers_map_index %= POINTER_MAP_SIZE; + + TRANSPORT_LOGI("Sending set int flags id %u", mp->_vl_msg_id); + + return vpp_binary_api_send_request_wait_reply(api, mp); +} + +void vpp_binary_api_send_request(vpp_binary_api_t *api, void *request) { + vl_generic_request_t *req = NULL; + + req = (vl_generic_request_t *)request; + TRANSPORT_LOGI("Sending a request to VPP (id=%d).\n", ntohs(req->_vl_msg_id)); + + S(api, req); +} + +int vpp_binary_api_send_request_wait_reply(vpp_binary_api_t *api, + void *request) { + vpp_binary_api_send_request(api, request); + + sem_wait(api->semaphore); + + return api->ret_val; +} + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_binary_api.h b/libtransport/src/hicn/transport/core/vpp_binary_api.h new file mode 100755 index 000000000..1eb10e766 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_binary_api.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> + +#ifdef __vpp__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdlib.h> + +typedef struct vpp_binary_api vpp_binary_api_t; +typedef struct vpp_plugin_binary_api vpp_plugin_binary_api_t; + +typedef enum link_state_s { UP = 1, DOWN = 0 } link_state_t; + +/** + * @brief Instantiate a new vpp_binary_api_t data structure and + * connect the application to the local VPP forwarder. + */ +vpp_binary_api_t* vpp_binary_api_init(const char* app_name); + +/** + * @brief Destroy the vpp_binary_api_t and disconnect from VPP. + */ +void vpp_binary_api_destroy(vpp_binary_api_t* api); + +void vpp_binary_api_send_receive_ping(vpp_binary_api_t* api); + +int vpp_binary_api_set_int_state(vpp_binary_api_t* api, uint32_t sw_index, + link_state_t state); + +/** + * @brief Send request to VPP and wait for reply. + */ +int vpp_binary_api_send_request_wait_reply(vpp_binary_api_t* api, + void* request); + +void vpp_binary_api_unlock_waiting_thread(vpp_binary_api_t* api); + +void vpp_binary_api_send_request(vpp_binary_api_t* api, void* request); + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_binary_api_internal.h b/libtransport/src/hicn/transport/core/vpp_binary_api_internal.h new file mode 100755 index 000000000..22b665e96 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_binary_api_internal.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> + +#ifdef __vpp__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <semaphore.h> +#include <vlibapi/api.h> +#include <vlibmemory/api.h> + +struct vpp_binary_api { + api_main_t *api_main; + u32 my_client_index; + unix_shared_memory_queue_t *vl_input_queue; + vlib_main_t *vlib_main; + sem_t *semaphore; + u32 ping_id; + int ret_val; + void *user_param; +}; + +struct vpp_plugin_binary_api { + vpp_binary_api_t *vpp_api; + u16 msg_id_base; + u32 my_client_index; +}; + +#define M(T, mp) \ + do { \ + mp = vl_msg_api_alloc_as_if_client(sizeof(*mp)); \ + memset(mp, 0, sizeof(*mp)); \ + mp->_vl_msg_id = ntohs(VL_API_##T + hm->msg_id_base); \ + mp->client_index = hm->my_client_index; \ + } while (0); + +#define S(api, mp) (vl_msg_api_send_shmem(api->vl_input_queue, (u8 *)&mp)) + +#ifdef __cplusplus +} +#endif + +#endif // __vpp__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc new file mode 100755 index 000000000..3a748c821 --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.cc @@ -0,0 +1,184 @@ +/* + * 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/config.h> + +#ifdef __vpp__ + +#include <hicn/transport/core/hicn_binary_api.h> +#include <hicn/transport/core/memif_binary_api.h> +#include <hicn/transport/core/vpp_forwarder_interface.h> + +typedef enum { MASTER = 0, SLAVE = 1 } memif_role_t; + +#define MEMIF_DEFAULT_RING_SIZE 2048 +#define MEMIF_DEFAULT_RX_QUEUES 1 +#define MEMIF_DEFAULT_TX_QUEUES 1 +#define MEMIF_DEFAULT_BUFFER_SIZE 2048 + +namespace transport { + +namespace core { + +vpp_binary_api_t *VPPForwarderInterface::api_ = nullptr; +vpp_plugin_binary_api_t *VPPForwarderInterface::memif_api_ = nullptr; +vpp_plugin_binary_api_t *VPPForwarderInterface::hicn_api_ = nullptr; +std::mutex VPPForwarderInterface::global_lock_; + +VPPForwarderInterface::VPPForwarderInterface(MemifConnector &connector) + : ForwarderInterface<VPPForwarderInterface, MemifConnector>(connector), + sw_if_index_(~0), + face_id_(~0) {} + +VPPForwarderInterface::~VPPForwarderInterface() {} + +/** + * @brief Create a memif interface in the local VPP forwarder. + */ +uint32_t VPPForwarderInterface::getMemifConfiguration() { + memif_create_params_t input_params = {0}; + + memif_id_ = + memif_binary_api_get_next_memif_id(VPPForwarderInterface::memif_api_); + + input_params.id = memif_id_; + input_params.role = memif_role_t::MASTER; + input_params.mode = memif_interface_mode_t::MEMIF_INTERFACE_MODE_IP; + input_params.rx_queues = MEMIF_DEFAULT_RX_QUEUES; + input_params.tx_queues = MEMIF_DEFAULT_TX_QUEUES; + input_params.ring_size = MEMIF_DEFAULT_RING_SIZE; + input_params.buffer_size = MEMIF_DEFAULT_BUFFER_SIZE; + + memif_output_params_t output_params = {0}; + + if (memif_binary_api_create_memif(VPPForwarderInterface::memif_api_, + &input_params, &output_params) < 0) { + throw errors::RuntimeException( + "Error creating memif interface in the local VPP forwarder."); + } + + return output_params.sw_if_index; +} + +void VPPForwarderInterface::consumerConnection() { + hicn_consumer_input_params input = {0}; + hicn_consumer_output_params output; + + std::memset(&output, 0, sizeof(hicn_consumer_output_params)); + + input.swif = sw_if_index_; + + if (int ret = hicn_binary_api_register_cons_app( + VPPForwarderInterface::hicn_api_, &input, &output) < 0) { + throw errors::RuntimeException(hicn_binary_api_get_error_string(ret)); + } + + inet_address_.family = AF_INET; + inet_address_.prefix_len = output.src4.prefix_length; + std::memcpy(inet_address_.buffer, output.src4.ip4.as_u8, IPV4_ADDR_LEN); + + inet6_address_.family = AF_INET6; + inet6_address_.prefix_len = output.src6.prefix_length; + std::memcpy(inet6_address_.buffer, output.src6.ip6.as_u8, IPV6_ADDR_LEN); +} + +void VPPForwarderInterface::producerConnection() { + // Producer connection will be set when we set the first route. +} + +void VPPForwarderInterface::connect(bool is_consumer) { + std::lock_guard<std::mutex> connection_lock(global_lock_); + + srand(time(NULL)); + int secret = rand() % (1 << 10); + std::stringstream app_name; + app_name << "Libtransport_" << secret; + + if (!VPPForwarderInterface::memif_api_) { + VPPForwarderInterface::api_ = vpp_binary_api_init(app_name.str().c_str()); + } + + VPPForwarderInterface::memif_api_ = + memif_binary_api_init(VPPForwarderInterface::api_); + + sw_if_index_ = getMemifConfiguration(); + + VPPForwarderInterface::hicn_api_ = + hicn_binary_api_init(VPPForwarderInterface::api_); + if (is_consumer) { + consumerConnection(); + } + + connector_.connect(memif_id_, 0); +} + +void VPPForwarderInterface::registerRoute(Prefix &prefix) { + auto &addr = prefix.toIpAddressStruct(); + + if (face_id_ == uint32_t(~0)) { + hicn_producer_input_params input; + std::memset(&input, 0, sizeof(input)); + + hicn_producer_output_params output; + std::memset(&output, 0, sizeof(output)); + + // Here we have to ask to the actual connector what is the + // memif_id, since this function should be called after the + // memif creation. + input.swif = sw_if_index_; + input.prefix.ip6.as_u64[0] = addr.as_u64[0]; + input.prefix.ip6.as_u64[1] = addr.as_u64[1]; + input.prefix.type = addr.family == AF_INET6 ? IP_TYPE_IP6 : IP_TYPE_IP4; + input.prefix.prefix_length = addr.prefix_len; + input.cs_reserved = content_store_reserved_; + + if (int ret = hicn_binary_api_register_prod_app( + VPPForwarderInterface::hicn_api_, &input, &output) < 0) { + throw errors::RuntimeException(hicn_binary_api_get_error_string(ret)); + } + + if (addr.family == AF_INET6) { + inet6_address_.prefix_len = output.prod_addr.prefix_length; + std::memcpy(inet6_address_.buffer, output.prod_addr.ip6.as_u8, + IPV6_ADDR_LEN); + } else { + inet_address_.prefix_len = output.prod_addr.prefix_length; + // The ipv4 is written in the last 4 bytes of the ipv6 address, so we need + // to copy from the byte 12 + std::memcpy(inet_address_.buffer, output.prod_addr.ip6.as_u8 + 12, + IPV4_ADDR_LEN); + } + + face_id_ = output.face_id; + } else { + hicn_producer_set_route_params params; + params.prefix.ip6.as_u64[0] = addr.as_u64[0]; + params.prefix.ip6.as_u64[1] = addr.as_u64[1]; + params.prefix.type = addr.family == AF_INET6 ? IP_TYPE_IP6 : IP_TYPE_IP4; + params.prefix.prefix_length = addr.prefix_len; + params.face_id = face_id_; + + if (int ret = hicn_binary_api_register_route( + VPPForwarderInterface::hicn_api_, ¶ms) < 0) { + throw errors::RuntimeException(hicn_binary_api_get_error_string(ret)); + } + } +} + +} // namespace core + +} // namespace transport + +#endif
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/core/vpp_forwarder_interface.h b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.h new file mode 100755 index 000000000..322cd1f8b --- /dev/null +++ b/libtransport/src/hicn/transport/core/vpp_forwarder_interface.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> + +#ifdef __vpp__ + +#include <hicn/transport/core/forwarder_interface.h> +#include <hicn/transport/core/memif_connector.h> +#include <hicn/transport/core/prefix.h> + +#include <deque> + +namespace transport { + +namespace core { + +class VPPForwarderInterface + : public ForwarderInterface<VPPForwarderInterface, MemifConnector> { + public: + VPPForwarderInterface(MemifConnector &connector); + + typedef MemifConnector ConnectorType; + + ~VPPForwarderInterface(); + + void connect(bool is_consumer); + + void registerRoute(Prefix &prefix); + + TRANSPORT_ALWAYS_INLINE std::uint16_t getMtu() { return interface_mtu; } + + private: + uint32_t getMemifConfiguration(); + + void consumerConnection(); + + void producerConnection(); + + static vpp_binary_api_t *api_; + static vpp_plugin_binary_api_t *memif_api_; + static vpp_plugin_binary_api_t *hicn_api_; + uint32_t memif_id_; + uint32_t sw_if_index_; + uint32_t face_id_; + static std::mutex global_lock_; + static constexpr std::uint16_t interface_mtu = 1500; +}; + +} // namespace core + +} // namespace transport + +#endif
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/CMakeLists.txt b/libtransport/src/hicn/transport/errors/CMakeLists.txt new file mode 100755 index 000000000..1c19a9070 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/CMakeLists.txt @@ -0,0 +1,29 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/not_implemented_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/invalid_ip_address_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/malformed_name_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/errors.h + ${CMAKE_CURRENT_SOURCE_DIR}/malformed_packet_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/runtime_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/tokenizer_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/null_pointer_exception.h + ${CMAKE_CURRENT_SOURCE_DIR}/malformed_ahpacket_exception.h +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/errors.h b/libtransport/src/hicn/transport/errors/errors.h new file mode 100755 index 000000000..512e35736 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/errors.h @@ -0,0 +1,24 @@ +/* + * 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/invalid_ip_address_exception.h> +#include <hicn/transport/errors/malformed_name_exception.h> +#include <hicn/transport/errors/malformed_packet_exception.h> +#include <hicn/transport/errors/not_implemented_exception.h> +#include <hicn/transport/errors/null_pointer_exception.h> +#include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/errors/tokenizer_exception.h>
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h b/libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h new file mode 100755 index 000000000..60226f576 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/invalid_ip_address_exception.h @@ -0,0 +1,31 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class InvalidIpAddressException : public std::runtime_error { + public: + InvalidIpAddressException() : std::runtime_error("") {} + + virtual char const *what() const noexcept override { + return "Malformed IP address."; + } +}; + +} // end namespace errors
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h b/libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h new file mode 100755 index 000000000..f0cfe0b82 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/malformed_ahpacket_exception.h @@ -0,0 +1,31 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class MalformedAHPacketException : public std::runtime_error { + public: + MalformedAHPacketException() : std::runtime_error("") {} + + virtual char const *what() const noexcept override { + return "Malformed AH packet."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/malformed_name_exception.h b/libtransport/src/hicn/transport/errors/malformed_name_exception.h new file mode 100755 index 000000000..4ef45d2e8 --- /dev/null +++ b/libtransport/src/hicn/transport/errors/malformed_name_exception.h @@ -0,0 +1,31 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class MalformedNameException : public std::runtime_error { + public: + MalformedNameException() : std::runtime_error("") {} + + virtual char const *what() const noexcept override { + return "Malformed IP address."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/malformed_packet_exception.h b/libtransport/src/hicn/transport/errors/malformed_packet_exception.h new file mode 100755 index 000000000..ec5c97e6e --- /dev/null +++ b/libtransport/src/hicn/transport/errors/malformed_packet_exception.h @@ -0,0 +1,29 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class MalformedPacketException : public std::runtime_error { + public: + MalformedPacketException() : std::runtime_error("") {} + + char const *what() const noexcept override { return "Malformed IP packet."; } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/not_implemented_exception.h b/libtransport/src/hicn/transport/errors/not_implemented_exception.h new file mode 100755 index 000000000..e9869163d --- /dev/null +++ b/libtransport/src/hicn/transport/errors/not_implemented_exception.h @@ -0,0 +1,30 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class NotImplementedException : public std::logic_error { + public: + NotImplementedException() : std::logic_error("") {} + virtual char const *what() const noexcept override { + return "Function not yet implemented."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/null_pointer_exception.h b/libtransport/src/hicn/transport/errors/null_pointer_exception.h new file mode 100755 index 000000000..bd06485ed --- /dev/null +++ b/libtransport/src/hicn/transport/errors/null_pointer_exception.h @@ -0,0 +1,31 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class NullPointerException : public std::runtime_error { + public: + NullPointerException() : std::runtime_error("") {} + + char const *what() const noexcept override { + return "Null pointer exception."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/errors/runtime_exception.h b/libtransport/src/hicn/transport/errors/runtime_exception.h new file mode 100755 index 000000000..ba5128a7e --- /dev/null +++ b/libtransport/src/hicn/transport/errors/runtime_exception.h @@ -0,0 +1,32 @@ +/* + * 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 <iostream> +#include <sstream> +#include <stdexcept> +#include <string> + +namespace errors { + +class RuntimeException : public std::runtime_error { + public: + RuntimeException() : std::runtime_error("") {} + + RuntimeException(std::string what) : runtime_error(what){}; +}; + +} // end namespace errors
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/errors/tokenizer_exception.h b/libtransport/src/hicn/transport/errors/tokenizer_exception.h new file mode 100755 index 000000000..76eda838e --- /dev/null +++ b/libtransport/src/hicn/transport/errors/tokenizer_exception.h @@ -0,0 +1,31 @@ +/* + * 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 <stdexcept> + +namespace errors { + +class TokenizerException : public std::logic_error { + public: + TokenizerException() : std::logic_error("") {} + + virtual char const *what() const noexcept override { + return "No more tokens available."; + } +}; + +} // end namespace errors diff --git a/libtransport/src/hicn/transport/http/CMakeLists.txt b/libtransport/src/hicn/transport/http/CMakeLists.txt new file mode 100755 index 000000000..ddcf1fdc3 --- /dev/null +++ b/libtransport/src/hicn/transport/http/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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/client_connection.cc + ${CMAKE_CURRENT_SOURCE_DIR}/request.cc + ${CMAKE_CURRENT_SOURCE_DIR}/server_publisher.cc + ${CMAKE_CURRENT_SOURCE_DIR}/server_acceptor.cc + ${CMAKE_CURRENT_SOURCE_DIR}/response.cc) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/client_connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/request.h + ${CMAKE_CURRENT_SOURCE_DIR}/server_publisher.h + ${CMAKE_CURRENT_SOURCE_DIR}/server_acceptor.h + ${CMAKE_CURRENT_SOURCE_DIR}/default_values.h + ${CMAKE_CURRENT_SOURCE_DIR}/facade.h + ${CMAKE_CURRENT_SOURCE_DIR}/response.h + ${CMAKE_CURRENT_SOURCE_DIR}/message.h + ${CMAKE_CURRENT_SOURCE_DIR}/callbacks.h +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/callbacks.h b/libtransport/src/hicn/transport/http/callbacks.h new file mode 100755 index 000000000..5ca5fcbe2 --- /dev/null +++ b/libtransport/src/hicn/transport/http/callbacks.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 <hicn/transport/http/server_publisher.h> + +#include <functional> +#include <memory> + +namespace transport { + +namespace http { + +enum class RC : uint8_t { + SUCCESS, + CONTENT_PUBLISHED, + ERR_UNDEFINED, +}; + +using OnHttpRequest = + std::function<void(std::shared_ptr<HTTPServerPublisher>&, const uint8_t*, + std::size_t, int request_id)>; +using DeadlineTimerCallback = std::function<void(const std::error_code& e)>; +using ReceiveCallback = std::function<void(const std::vector<uint8_t>&)>; +using OnPayloadCallback = std::function<RC( + const std::error_code& ec, const core::Name& name, + const std::shared_ptr<utils::SharableVector<uint8_t>>& payload)>; +using ContentSentCallback = + std::function<void(const std::error_code&, const core::Name&)>; + +} // namespace http + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/client_connection.cc b/libtransport/src/hicn/transport/http/client_connection.cc new file mode 100755 index 000000000..d4207bb81 --- /dev/null +++ b/libtransport/src/hicn/transport/http/client_connection.cc @@ -0,0 +1,194 @@ +/* + * 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/http/client_connection.h> +#include <hicn/transport/utils/hash.h> + +#define DEFAULT_BETA 0.99 +#define DEFAULT_GAMMA 0.07 + +namespace transport { + +namespace http { + +using namespace transport; + +HTTPClientConnection::HTTPClientConnection() + : consumer_(TransportProtocolAlgorithms::RAAQM, io_service_), + response_(std::make_shared<HTTPResponse>()), + timer_(nullptr) { + consumer_.setSocketOption( + ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, + (ConsumerContentObjectVerificationCallback)std::bind( + &HTTPClientConnection::verifyData, this, std::placeholders::_1, + std::placeholders::_2)); + + consumer_.setSocketOption( + ConsumerCallbacksOptions::CONTENT_RETRIEVED, + (ConsumerContentCallback)std::bind( + &HTTPClientConnection::processPayload, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + + consumer_.connect(); + std::shared_ptr<typename ConsumerSocket::Portal> portal; + consumer_.getSocketOption(GeneralTransportOptions::PORTAL, portal); + timer_ = std::make_unique<asio::steady_timer>(portal->getIoService()); +} + +HTTPClientConnection &HTTPClientConnection::get( + const std::string &url, HTTPHeaders headers, HTTPPayload payload, + std::shared_ptr<HTTPResponse> response) { + return sendRequest(url, HTTPMethod::GET, headers, payload, response); +} + +HTTPClientConnection &HTTPClientConnection::sendRequest( + const std::string &url, HTTPMethod method, HTTPHeaders headers, + HTTPPayload payload, std::shared_ptr<HTTPResponse> response) { + if (!response) { + response = response_; + } + + auto start = std::chrono::steady_clock::now(); + HTTPRequest request(method, url, headers, payload); + std::string name = sendRequestGetReply(request, response); + auto end = std::chrono::steady_clock::now(); + + TRANSPORT_LOGI( + "%s %s [%s] duration: %llu [usec] %zu [bytes]\n", + method_map[method].c_str(), url.c_str(), name.c_str(), + (unsigned long long)std::chrono::duration_cast<std::chrono::microseconds>( + end - start) + .count(), + response->size()); + + return *this; +} + +std::string HTTPClientConnection::sendRequestGetReply( + const HTTPRequest &request, std::shared_ptr<HTTPResponse> &response) { + const std::string &request_string = request.getRequestString(); + const std::string &locator = request.getLocator(); + + // Hash it + + uint32_t locator_hash = + utils::hash::fnv32_buf(locator.c_str(), locator.size()); + uint64_t request_hash = + utils::hash::fnv64_buf(request_string.c_str(), request_string.size()); + + consumer_.setSocketOption( + ConsumerCallbacksOptions::INTEREST_OUTPUT, + (ConsumerInterestCallback)std::bind( + &HTTPClientConnection::processLeavingInterest, this, + std::placeholders::_1, std::placeholders::_2, request_string)); + + // Send content to producer piggybacking it through first interest (to fix) + + response->clear(); + + // Factor hicn name using hash + + std::stringstream stream; + + stream << std::hex << http::default_values::ipv6_first_word << ":"; + + for (uint16_t *word = (uint16_t *)&locator_hash; + std::size_t(word) < (std::size_t(&locator_hash) + sizeof(locator_hash)); + word++) { + stream << ":" << std::hex << *word; + } + + for (uint16_t *word = (uint16_t *)&request_hash; + std::size_t(word) < (std::size_t(&request_hash) + sizeof(request_hash)); + word++) { + stream << ":" << std::hex << *word; + } + + stream << "|0"; + + consumer_.consume(Name(stream.str()), *response); + + consumer_.stop(); + + return stream.str(); +} + +HTTPResponse &&HTTPClientConnection::response() { + // response_->parse(); + return std::move(*response_); +} + +void HTTPClientConnection::processPayload(ConsumerSocket &c, + std::size_t bytes_transferred, + const std::error_code &ec) { + if (ec) { + TRANSPORT_LOGE("Download failed!!"); + } +} + +bool HTTPClientConnection::verifyData( + ConsumerSocket &c, const core::ContentObject &contentObject) { + if (contentObject.getPayloadType() == PayloadType::CONTENT_OBJECT) { + TRANSPORT_LOGI("VERIFY CONTENT\n"); + } else if (contentObject.getPayloadType() == PayloadType::MANIFEST) { + TRANSPORT_LOGI("VERIFY MANIFEST\n"); + } + + return true; +} + +void HTTPClientConnection::processLeavingInterest( + ConsumerSocket &c, const core::Interest &interest, std::string &payload) { + // if (interest.getName().getSuffix() == 0) { + Interest &int2 = const_cast<Interest &>(interest); + int2.appendPayload((uint8_t *)payload.data(), payload.size()); + // } +} + +ConsumerSocket &HTTPClientConnection::getConsumer() { return consumer_; } + +HTTPClientConnection &HTTPClientConnection::stop() { + // This is thread safe and can be called from another thread + consumer_.stop(); + + return *this; +} + +HTTPClientConnection &HTTPClientConnection::setTimeout( + const std::chrono::seconds &timeout) { + timer_->cancel(); + timer_->expires_from_now(timeout); + timer_->async_wait([this](std::error_code ec) { + if (!ec) { + consumer_.stop(); + } + }); + + return *this; +} + +HTTPClientConnection &HTTPClientConnection::setCertificate( + const std::string &cert_path) { + if (consumer_.setSocketOption(GeneralTransportOptions::CERTIFICATE, + cert_path) == SOCKET_OPTION_NOT_SET) { + throw errors::RuntimeException("Error setting the certificate."); + } + + return *this; +} + +} // namespace http + +} // namespace transport diff --git a/libtransport/src/hicn/transport/http/client_connection.h b/libtransport/src/hicn/transport/http/client_connection.h new file mode 100755 index 000000000..f6e1fa03e --- /dev/null +++ b/libtransport/src/hicn/transport/http/client_connection.h @@ -0,0 +1,82 @@ +/* + * 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/http/default_values.h> +#include <hicn/transport/http/request.h> +#include <hicn/transport/http/response.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_producer.h> +#include <hicn/transport/utils/uri.h> + +#include <vector> + +namespace transport { + +namespace http { + +using namespace interface; +using namespace core; + +class HTTPClientConnection { + public: + HTTPClientConnection(); + + HTTPClientConnection &get(const std::string &url, HTTPHeaders headers = {}, + HTTPPayload payload = {}, + std::shared_ptr<HTTPResponse> response = nullptr); + + HTTPClientConnection &sendRequest( + const std::string &url, HTTPMethod method, HTTPHeaders headers = {}, + HTTPPayload payload = {}, + std::shared_ptr<HTTPResponse> response = nullptr); + + HTTPResponse &&response(); + + HTTPClientConnection &stop(); + + interface::ConsumerSocket &getConsumer(); + + HTTPClientConnection &setTimeout(const std::chrono::seconds &timeout); + + HTTPClientConnection &setCertificate(const std::string &cert_path); + + private: + void processPayload(interface::ConsumerSocket &c, + std::size_t bytes_transferred, const std::error_code &ec); + + std::string sendRequestGetReply(const HTTPRequest &request, + std::shared_ptr<HTTPResponse> &response); + + bool verifyData(interface::ConsumerSocket &c, + const core::ContentObject &contentObject); + + void processLeavingInterest(interface::ConsumerSocket &c, + const core::Interest &interest, + std::string &payload); + + asio::io_service io_service_; + + ConsumerSocket consumer_; + + std::shared_ptr<HTTPResponse> response_; + + std::unique_ptr<asio::steady_timer> timer_; +}; + +} // end namespace http + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/default_values.h b/libtransport/src/hicn/transport/http/default_values.h new file mode 100755 index 000000000..2d5a6b821 --- /dev/null +++ b/libtransport/src/hicn/transport/http/default_values.h @@ -0,0 +1,32 @@ +/* + * 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 <cstdint> + +namespace transport { + +namespace http { + +namespace default_values { + +const uint16_t ipv6_first_word = 0xb001; // Network byte order + +} // namespace default_values + +} // namespace http + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/http/facade.h b/libtransport/src/hicn/transport/http/facade.h new file mode 100755 index 000000000..31c2d1b8d --- /dev/null +++ b/libtransport/src/hicn/transport/http/facade.h @@ -0,0 +1,22 @@ +/* + * 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/http/transport_http_client_connection.h> +#include <hicn/transport/http/transport_http_server_acceptor.h> +#include <hicn/transport/http/transport_http_server_publisher.h> + +namespace libl4 = transport;
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/message.h b/libtransport/src/hicn/transport/http/message.h new file mode 100755 index 000000000..7d4485c90 --- /dev/null +++ b/libtransport/src/hicn/transport/http/message.h @@ -0,0 +1,58 @@ +/* + * 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/sharable_vector.h> + +#include <map> +#include <sstream> +#include <vector> + +#define HTTP_VERSION "1.1" + +namespace transport { + +namespace http { + +typedef enum { GET, POST, PUT, PATCH, DELETE } HTTPMethod; + +static std::map<HTTPMethod, std::string> method_map = { + {GET, "GET"}, {POST, "POST"}, {PUT, "PUT"}, + {PATCH, "PATCH"}, {DELETE, "DELETE"}, +}; + +typedef std::map<std::string, std::string> HTTPHeaders; +typedef std::vector<uint8_t> HTTPPayload; + +class HTTPMessage { + public: + virtual ~HTTPMessage() = default; + + virtual const HTTPHeaders &getHeaders() = 0; + + virtual const HTTPPayload &getPayload() = 0; + + virtual const std::string &getHttpVersion() const = 0; + + protected: + HTTPHeaders headers_; + HTTPPayload payload_; + std::string http_version_; +}; + +} // end namespace http + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/request.cc b/libtransport/src/hicn/transport/http/request.cc new file mode 100755 index 000000000..7a63b4f75 --- /dev/null +++ b/libtransport/src/hicn/transport/http/request.cc @@ -0,0 +1,83 @@ +/* + * 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/http/request.h> +#include <hicn/transport/utils/uri.h> + +namespace transport { + +namespace http { + +// std::map<HTTPMethod, std::string> method_map + +HTTPRequest::HTTPRequest(HTTPMethod method, const std::string &url, + const HTTPHeaders &headers, + const HTTPPayload &payload) { + utils::Uri uri; + uri.parse(url); + + path_ = uri.getPath(); + query_string_ = uri.getQueryString(); + protocol_ = uri.getProtocol(); + locator_ = uri.getLocator(); + port_ = uri.getPort(); + http_version_ = HTTP_VERSION; + + headers_ = headers; + payload_ = payload; + + std::transform(locator_.begin(), locator_.end(), locator_.begin(), ::tolower); + + std::transform(protocol_.begin(), protocol_.end(), protocol_.begin(), + ::tolower); + + std::stringstream stream; + stream << method_map[method] << " " << uri.getPath() << " HTTP/" + << HTTP_VERSION << "\r\n"; + for (auto &item : headers) { + stream << item.first << ": " << item.second << "\r\n"; + } + stream << "\r\n"; + + if (payload.size() > 0) { + stream << payload.data(); + } + + request_string_ = stream.str(); +} + +const std::string &HTTPRequest::getPort() const { return port_; } + +const std::string &HTTPRequest::getLocator() const { return locator_; } + +const std::string &HTTPRequest::getProtocol() const { return protocol_; } + +const std::string &HTTPRequest::getPath() const { return path_; } + +const std::string &HTTPRequest::getQueryString() const { return query_string_; } + +const HTTPHeaders &HTTPRequest::getHeaders() { return headers_; } + +const HTTPPayload &HTTPRequest::getPayload() { return payload_; } + +const std::string &HTTPRequest::getRequestString() const { + return request_string_; +} + +const std::string &HTTPRequest::getHttpVersion() const { return http_version_; } + +} // namespace http + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/request.h b/libtransport/src/hicn/transport/http/request.h new file mode 100755 index 000000000..88d67d4ad --- /dev/null +++ b/libtransport/src/hicn/transport/http/request.h @@ -0,0 +1,61 @@ +/* + * 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/http/message.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <map> +#include <sstream> +#include <vector> + +namespace transport { + +namespace http { + +class HTTPRequest : public HTTPMessage { + public: + HTTPRequest(HTTPMethod method, const std::string &url, + const HTTPHeaders &headers, const HTTPPayload &payload); + + const std::string &getQueryString() const; + + const std::string &getPath() const; + + const std::string &getProtocol() const; + + const std::string &getLocator() const; + + const std::string &getPort() const; + + const std::string &getRequestString() const; + + const HTTPHeaders &getHeaders() override; + + const HTTPPayload &getPayload() override; + + const std::string &getHttpVersion() const override; + + private: + std::string query_string_, path_, protocol_, locator_, port_; + std::string request_string_; + HTTPHeaders headers_; + HTTPPayload payload_; +}; + +} // end namespace http + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/response.cc b/libtransport/src/hicn/transport/http/response.cc new file mode 100755 index 000000000..0aa9affe8 --- /dev/null +++ b/libtransport/src/hicn/transport/http/response.cc @@ -0,0 +1,134 @@ +/* + * 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/errors/errors.h> +#include <hicn/transport/http/response.h> + +#include <algorithm> + +#include <cstring> + +namespace transport { + +namespace http { + +HTTPResponse::HTTPResponse() {} + +HTTPResponse::HTTPResponse(const HTTPHeaders &headers, + const HTTPPayload &payload) { + headers_ = headers; + payload_ = payload; +} + +const HTTPHeaders &HTTPResponse::getHeaders() { + parse(); + return headers_; +} + +const HTTPPayload &HTTPResponse::getPayload() { + parse(); + return payload_; +} + +bool HTTPResponse::parseHeaders() { + const char *crlf2 = "\r\n\r\n"; + auto it = + std::search(this->begin(), this->end(), crlf2, crlf2 + strlen(crlf2)); + + if (it != end()) { + std::stringstream ss; + ss.str(std::string(begin(), it)); + + std::string line; + getline(ss, line); + std::istringstream line_s(line); + std::string _http_version; + std::string http_version; + + line_s >> _http_version; + std::size_t separator; + if ((separator = _http_version.find('/')) != std::string::npos) { + if (_http_version.substr(0, separator) != "HTTP") { + return false; + } + http_version_ = + line.substr(separator + 1, _http_version.length() - separator - 1); + } else { + return false; + } + + std::string status_code, status_string; + + line_s >> status_code_; + line_s >> status_string; + + auto _it = std::search(line.begin(), line.end(), status_string.begin(), + status_string.end()); + + status_string_ = std::string(_it, line.end() - 1); + + std::size_t param_end; + std::size_t value_start; + while (getline(ss, line)) { + if ((param_end = line.find(':')) != std::string::npos) { + value_start = param_end + 1; + if ((value_start) < line.size()) { + if (line[value_start] == ' ') { + value_start++; + } + if (value_start < line.size()) { + headers_[line.substr(0, param_end)] = + line.substr(value_start, line.size() - value_start - 1); + } + } + } else { + return false; + } + } + } + + return true; +} + +void HTTPResponse::parse() { + if (!parseHeaders()) { + throw errors::RuntimeException("Malformed HTTP response"); + } + + if (payload_.empty()) { + const char *crlf2 = "\r\n\r\n"; + auto it = + std::search(this->begin(), this->end(), crlf2, crlf2 + strlen(crlf2)); + + if (it != this->end()) { + erase(begin(), it + strlen(crlf2)); + payload_ = std::move(*dynamic_cast<std::vector<uint8_t> *>(this)); + } + } +} + +const std::string &HTTPResponse::getStatusCode() const { return status_code_; } + +const std::string &HTTPResponse::getStatusString() const { + return status_string_; +} + +const std::string &HTTPResponse::getHttpVersion() const { + return http_version_; +} + +} // namespace http + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/response.h b/libtransport/src/hicn/transport/http/response.h new file mode 100755 index 000000000..e7dec8c40 --- /dev/null +++ b/libtransport/src/hicn/transport/http/response.h @@ -0,0 +1,58 @@ +/* + * 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/http/message.h> +#include <hicn/transport/utils/array.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <map> +#include <sstream> +#include <vector> + +namespace transport { + +namespace http { + +class HTTPResponse : public HTTPMessage, public utils::SharableVector<uint8_t> { + public: + HTTPResponse(const HTTPHeaders &headers, const HTTPPayload &payload); + + HTTPResponse(); + + const HTTPHeaders &getHeaders() override; + + const HTTPPayload &getPayload() override; + + const std::string &getStatusCode() const; + + const std::string &getStatusString() const; + + const std::string &getHttpVersion() const override; + + void parse(); + + private: + bool parseHeaders(); + + private: + std::string status_code_; + std::string status_string_; +}; + +} // end namespace http + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/http/server_acceptor.cc b/libtransport/src/hicn/transport/http/server_acceptor.cc new file mode 100755 index 000000000..717dfb642 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_acceptor.cc @@ -0,0 +1,112 @@ +/* + * 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/http/server_acceptor.h> +#include <hicn/transport/utils/hash.h> +#include <hicn/transport/utils/uri.h> + +namespace transport { + +namespace http { + +HTTPServerAcceptor::HTTPServerAcceptor(std::string &&server_locator, + OnHttpRequest callback) + : HTTPServerAcceptor(server_locator, callback) {} + +HTTPServerAcceptor::HTTPServerAcceptor(std::string &server_locator, + OnHttpRequest callback) + : callback_(callback) { + utils::Uri uri; + + uri.parseProtocolAndLocator(server_locator); + std::string protocol = uri.getProtocol(); + std::string locator = uri.getLocator(); + + std::transform(locator.begin(), locator.end(), locator.begin(), ::tolower); + + std::transform(protocol.begin(), protocol.end(), protocol.begin(), ::tolower); + + if (protocol != "http") { + throw errors::RuntimeException( + "Malformed server_locator. The locator format should be in the form " + "http://locator"); + } + + uint32_t locator_hash = + utils::hash::fnv32_buf(locator.c_str(), locator.size()); + + std::stringstream stream; + stream << std::hex << http::default_values::ipv6_first_word << ":0000"; + + for (uint16_t *word = (uint16_t *)&locator_hash; + std::size_t(word) < (std::size_t(&locator_hash) + sizeof(locator_hash)); + word++) { + stream << ":" << std::hex << *word; + } + + stream << "::0"; + + std::string network = stream.str(); + + core::Prefix acceptor_namespace(network, 64); + + std::string producer_identity = "acceptor_producer"; + acceptor_producer_ = std::make_shared<ProducerSocket>( + io_service_); /*, + utils::Identity::generateIdentity(producer_identity));*/ + acceptor_producer_->registerPrefix(acceptor_namespace); +} + +void HTTPServerAcceptor::listen(bool async) { + acceptor_producer_->setSocketOption( + ProducerCallbacksOptions::INTEREST_INPUT, + (ProducerInterestCallback)bind( + &HTTPServerAcceptor::processIncomingInterest, this, + std::placeholders::_1, std::placeholders::_2)); + acceptor_producer_->connect(); + + if (!async) { + acceptor_producer_->serveForever(); + } +} + +void HTTPServerAcceptor::processIncomingInterest(ProducerSocket &p, + const Interest &interest) { + // Temporary solution. With + utils::Array<uint8_t> payload = interest.getPayload(); + + int request_id = utils::hash::fnv32_buf(payload.data(), payload.length()); + + if (publishers_.find(request_id) != publishers_.end()) { + if (publishers_[request_id]) { + publishers_[request_id]->getProducer().onInterest(interest); + return; + } + } + + publishers_[request_id] = + std::make_shared<HTTPServerPublisher>(interest.getName()); + callback_(publishers_[request_id], (uint8_t *)payload.data(), + payload.length(), request_id); +} + +std::map<int, std::shared_ptr<HTTPServerPublisher>> + &HTTPServerAcceptor::getPublishers() { + return publishers_; +} + +} // namespace http + +} // namespace transport diff --git a/libtransport/src/hicn/transport/http/server_acceptor.h b/libtransport/src/hicn/transport/http/server_acceptor.h new file mode 100755 index 000000000..549962414 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_acceptor.h @@ -0,0 +1,62 @@ +/* + * 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/http/callbacks.h> +#include <hicn/transport/http/default_values.h> +#include <hicn/transport/http/request.h> +#include <hicn/transport/http/server_publisher.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_producer.h> + +#include <functional> +#include <vector> + +namespace transport { + +namespace http { + +class HTTPServerAcceptor { + friend class HTTPServerPublisher; + + public: + HTTPServerAcceptor(std::string &&server_locator, OnHttpRequest callback); + HTTPServerAcceptor(std::string &server_locator, OnHttpRequest callback); + + void listen(bool async); + + std::map<int, std::shared_ptr<HTTPServerPublisher>> &getPublishers(); + + // void asyncSendResponse(); + + // HTTPClientConnection& get(std::string &url, HTTPHeaders headers = {}, + // HTTPPayload payload = {}); + // + // HTTPResponse&& response(); + + private: + void processIncomingInterest(ProducerSocket &p, const Interest &interest); + + OnHttpRequest callback_; + asio::io_service io_service_; + std::shared_ptr<ProducerSocket> acceptor_producer_; + + std::map<int, std::shared_ptr<HTTPServerPublisher>> publishers_; +}; + +} // end namespace http + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/http/server_publisher.cc b/libtransport/src/hicn/transport/http/server_publisher.cc new file mode 100755 index 000000000..012f36091 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_publisher.cc @@ -0,0 +1,173 @@ +/* + * 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/http/server_publisher.h> +#include <hicn/transport/utils/literals.h> + +namespace transport { + +namespace http { + +HTTPServerPublisher::HTTPServerPublisher(const core::Name &content_name) + : content_name_(content_name, true) { + std::string identity = "acceptor_producer"; + producer_ = std::make_unique<ProducerSocket>(io_service_); + // utils::Identity::generateIdentity(identity)); + core::Prefix publisher_prefix(content_name_, 128); + producer_->registerPrefix(publisher_prefix); +} + +HTTPServerPublisher::~HTTPServerPublisher() { + if (timer_) { + this->timer_->cancel(); + } +} + +HTTPServerPublisher &HTTPServerPublisher::attachPublisher() { + // Create a new publisher + producer_->setSocketOption(GeneralTransportOptions::DATA_PACKET_SIZE, + 1410_U32); + producer_->connect(); + return *this; +} + +HTTPServerPublisher &HTTPServerPublisher::setTimeout( + const std::chrono::milliseconds &timeout, bool timeout_renewal) { + std::shared_ptr<typename ProducerSocket::Portal> portal; + producer_->getSocketOption(GeneralTransportOptions::PORTAL, portal); + timer_ = + std::make_unique<asio::steady_timer>(portal->getIoService(), timeout); + + wait_callback_ = [this](const std::error_code &e) { + if (!e) { + producer_->stop(); + } + }; + + if (timeout_renewal) { + interest_enter_callback_ = [this, timeout](ProducerSocket &p, + const Interest &interest) { + this->timer_->cancel(); + this->timer_->expires_from_now(timeout); + this->timer_->async_wait(wait_callback_); + }; + + producer_->setSocketOption( + ProducerCallbacksOptions::CACHE_HIT, + (ProducerInterestCallback)interest_enter_callback_); + } + + timer_->async_wait(wait_callback_); + + return *this; +} + +void HTTPServerPublisher::publishContent( + const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, bool is_last) { + if (producer_) { + producer_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + static_cast<uint32_t>(content_lifetime.count())); + producer_->produce(content_name_, buf, buffer_size, is_last); + // producer_->setSocketOption(ProducerCallbacksOptions::CACHE_MISS, + // [this](ProducerSocket &p, const + // core::Interest &interest){ + // producer_->stop(); + // }); + } +} + +template <typename Handler> +void HTTPServerPublisher::asyncPublishContent( + const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, Handler &&handler, + bool is_last) { + if (producer_) { + producer_->setSocketOption( + GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + static_cast<uint32_t>(content_lifetime.count())); + producer_->asyncProduce(content_name_, buf, buffer_size, + std::forward<Handler>(handler), is_last); + } +} + +void HTTPServerPublisher::serveClients() { producer_->serveForever(); } + +void HTTPServerPublisher::stop() { + std::shared_ptr<typename ProducerSocket::Portal> portal_ptr; + producer_->getSocketOption(GeneralTransportOptions::PORTAL, portal_ptr); + portal_ptr->getIoService().stop(); +} + +ProducerSocket &HTTPServerPublisher::getProducer() { return *producer_; } + +void HTTPServerPublisher::setPublisherName(std::string &name, + std::string &mask) { + // Name represents the last 64 bits of the ipv6 address. + // It is an ipv6 address with the first 64 bits set to 0 + uint16_t i; + std::string s = content_name_.toString(); + std::shared_ptr<core::Sockaddr> sockaddr = content_name_.getAddress(); + in6_addr name_ipv6 = ((core::Sockaddr6 *)sockaddr.get())->sin6_addr; + + in6_addr bitmask, new_address, _name; + + if (inet_pton(AF_INET6, mask.c_str(), &bitmask) != 1) { + throw errors::RuntimeException("Error during conversion to ipv6 address."); + } + + if (inet_pton(AF_INET6, name.c_str(), &_name) != 1) { + throw errors::RuntimeException("Error during conversion to ipv6 address."); + } + + for (i = 0; i < sizeof(new_address.s6_addr); i++) { + new_address.s6_addr[i] = name_ipv6.s6_addr[i] & bitmask.s6_addr[i]; + } + + for (i = 0; i < sizeof(new_address.s6_addr); i++) { + new_address.s6_addr[i] |= _name.s6_addr[i] & ~bitmask.s6_addr[i]; + } + + // Effectively change the name + char str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &new_address, str, INET6_ADDRSTRLEN); + std::string str2(str); + + core::Name new_name(str2, 0); + + // If the new name differs from the one required by the consumer part, send a + // manifest + if (!new_name.equals(content_name_, false)) { + // Publish manifest pointing to the new name + + auto manifest = + std::make_shared<ContentObjectManifest>(content_name_.setSuffix(0)); + + content_name_ = core::Name(str2, 0); + + // manifest->setNameList(content_name_); + manifest->setLifetime(4000 * 1000); + manifest->encode(); + producer_->produce(*manifest); + + core::Prefix ns(content_name_, 128); + producer_->registerPrefix(ns); + } +} + +} // namespace http + +} // namespace transport diff --git a/libtransport/src/hicn/transport/http/server_publisher.h b/libtransport/src/hicn/transport/http/server_publisher.h new file mode 100755 index 000000000..91f7e43e9 --- /dev/null +++ b/libtransport/src/hicn/transport/http/server_publisher.h @@ -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. + */ + +#pragma once + +#include <hicn/transport/http/default_values.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_producer.h> + +#include <functional> +#include <vector> + +namespace transport { + +namespace http { + +using namespace interface; +using namespace core; + +class HTTPServerPublisher { + public: + HTTPServerPublisher(const core::Name &content_name); + + ~HTTPServerPublisher(); + + void publishContent(const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, bool is_last); + + template <typename Handler> + void asyncPublishContent(const uint8_t *buf, size_t buffer_size, + std::chrono::milliseconds content_lifetime, + Handler &&handler, bool is_last); + + void serveClients(); + + void stop(); + + ProducerSocket &getProducer(); + + HTTPServerPublisher &setTimeout(const std::chrono::milliseconds &timeout, + bool timeout_renewal); + + HTTPServerPublisher &attachPublisher(); + + void setPublisherName(std::string &name, std::string &mask); + + private: + Name content_name_; + std::unique_ptr<asio::steady_timer> timer_; + asio::io_service io_service_; + std::unique_ptr<ProducerSocket> producer_; + ProducerInterestCallback interest_enter_callback_; + utils::UserCallback wait_callback_; + + utils::SharableVector<uint8_t> receive_buffer_; +}; + +} // end namespace http + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/CMakeLists.txt b/libtransport/src/hicn/transport/interfaces/CMakeLists.txt new file mode 100755 index 000000000..cbf371bac --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/CMakeLists.txt @@ -0,0 +1,38 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/socket.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_consumer.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.h + ${CMAKE_CURRENT_SOURCE_DIR}/async_transport.h + ${CMAKE_CURRENT_SOURCE_DIR}/full_duplex_socket.h + ${CMAKE_CURRENT_SOURCE_DIR}/publication_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_options_default_values.h + ${CMAKE_CURRENT_SOURCE_DIR}/socket_options_keys.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/full_duplex_socket.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_consumer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_socket_producer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_producer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/socket_consumer.cc +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/async_transport.h b/libtransport/src/hicn/transport/interfaces/async_transport.h new file mode 100755 index 000000000..492b4ec26 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/async_transport.h @@ -0,0 +1,640 @@ + +/* + * 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/publication_options.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <sys/uio.h> +#include <memory> + +namespace transport { + +namespace interface { + +/* + * flags given by the application for write* calls + */ +enum class WriteFlags : uint32_t { + NONE = 0x00, + /* + * Whether to delay the output until a subsequent non-corked write. + * (Note: may not be supported in all subclasses or on all platforms.) + */ + CORK = 0x01, + /* + * for a socket that has ACK latency enabled, it will cause the kernel + * to fire a TCP ESTATS event when the last byte of the given write call + * will be acknowledged. + */ + EOR = 0x02, + /* + * this indicates that only the write side of socket should be shutdown + */ + WRITE_SHUTDOWN = 0x04, + /* + * use msg zerocopy if allowed + */ + WRITE_MSG_ZEROCOPY = 0x08, +}; + +/* + * union operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags operator|(WriteFlags a, WriteFlags b) { + return static_cast<WriteFlags>(static_cast<uint32_t>(a) | + static_cast<uint32_t>(b)); +} + +/* + * compound assignment union operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags &operator|=(WriteFlags &a, WriteFlags b) { + a = a | b; + return a; +} + +/* + * intersection operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags operator&(WriteFlags a, WriteFlags b) { + return static_cast<WriteFlags>(static_cast<uint32_t>(a) & + static_cast<uint32_t>(b)); +} + +/* + * compound assignment intersection operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags &operator&=(WriteFlags &a, WriteFlags b) { + a = a & b; + return a; +} + +/* + * exclusion parameter + */ +TRANSPORT_ALWAYS_INLINE WriteFlags operator~(WriteFlags a) { + return static_cast<WriteFlags>(~static_cast<uint32_t>(a)); +} + +/* + * unset operator + */ +TRANSPORT_ALWAYS_INLINE WriteFlags unSet(WriteFlags a, WriteFlags b) { + return a & ~b; +} + +/* + * inclusion operator + */ +TRANSPORT_ALWAYS_INLINE bool isSet(WriteFlags a, WriteFlags b) { + return (a & b) == b; +} + +class ConnectCallback { + public: + virtual ~ConnectCallback() = default; + + /** + * connectSuccess() will be invoked when the connection has been + * successfully established. + */ + virtual void connectSuccess() noexcept = 0; + + /** + * connectErr() will be invoked if the connection attempt fails. + * + * @param ex An exception describing the error that occurred. + */ + virtual void connectErr(const std::error_code ec) noexcept = 0; +}; + +/** + * AsyncSocket defines an asynchronous API for streaming I/O. + * + * This class provides an API to for asynchronously waiting for data + * on a streaming transport, and for asynchronously sending data. + * + * The APIs for reading and writing are intentionally asymmetric. Waiting for + * data to read is a persistent API: a callback is installed, and is notified + * whenever new data is available. It continues to be notified of new events + * until it is uninstalled. + * + * AsyncSocket does not provide read timeout functionality, because it + * typically cannot determine when the timeout should be active. Generally, a + * timeout should only be enabled when processing is blocked waiting on data + * from the remote endpoint. For server-side applications, the timeout should + * not be active if the server is currently processing one or more outstanding + * requests on this transport. For client-side applications, the timeout + * should not be active if there are no requests pending on the transport. + * Additionally, if a client has multiple pending requests, it will ususally + * want a separate timeout for each request, rather than a single read timeout. + * + * The write API is fairly intuitive: a user can request to send a block of + * data, and a callback will be informed once the entire block has been + * transferred to the kernel, or on error. AsyncSocket does provide a send + * timeout, since most callers want to give up if the remote end stops + * responding and no further progress can be made sending the data. + */ +class AsyncSocket { + public: + /** + * Close the transport. + * + * This gracefully closes the transport, waiting for all pending write + * requests to complete before actually closing the underlying transport. + * + * If a read callback is set, readEOF() will be called immediately. If there + * are outstanding write requests, the close will be delayed until all + * remaining writes have completed. No new writes may be started after + * close() has been called. + */ + virtual void close() = 0; + + /** + * Close the transport immediately. + * + * This closes the transport immediately, dropping any outstanding data + * waiting to be written. + * + * If a read callback is set, readEOF() will be called immediately. + * If there are outstanding write requests, these requests will be aborted + * and writeError() will be invoked immediately on all outstanding write + * callbacks. + */ + virtual void closeNow() = 0; + + /** + * Perform a half-shutdown of the write side of the transport. + * + * The caller should not make any more calls to write() or writev() after + * shutdownWrite() is called. Any future write attempts will fail + * immediately. + * + * Not all transport types support half-shutdown. If the underlying + * transport does not support half-shutdown, it will fully shutdown both the + * read and write sides of the transport. (Fully shutting down the socket is + * better than doing nothing at all, since the caller may rely on the + * shutdownWrite() call to notify the other end of the connection that no + * more data can be read.) + * + * If there is pending data still waiting to be written on the transport, + * the actual shutdown will be delayed until the pending data has been + * written. + * + * Note: There is no corresponding shutdownRead() equivalent. Simply + * uninstall the read callback if you wish to stop reading. (On TCP sockets + * at least, shutting down the read side of the socket is a no-op anyway.) + */ + virtual void shutdownWrite() = 0; + + /** + * Perform a half-shutdown of the write side of the transport. + * + * shutdownWriteNow() is identical to shutdownWrite(), except that it + * immediately performs the shutdown, rather than waiting for pending writes + * to complete. Any pending write requests will be immediately failed when + * shutdownWriteNow() is called. + */ + virtual void shutdownWriteNow() = 0; + + /** + * Determine if transport is open and ready to read or write. + * + * Note that this function returns false on EOF; you must also call error() + * to distinguish between an EOF and an error. + * + * @return true iff the transport is open and ready, false otherwise. + */ + virtual bool good() const = 0; + + /** + * Determine if the transport is readable or not. + * + * @return true iff the transport is readable, false otherwise. + */ + virtual bool readable() const = 0; + + /** + * Determine if the transport is writable or not. + * + * @return true iff the transport is writable, false otherwise. + */ + virtual bool writable() const { + // By default return good() - leave it to implementers to override. + return good(); + } + + /** + * Determine if the there is pending data on the transport. + * + * @return true iff the if the there is pending data, false otherwise. + */ + virtual bool isPending() const { return readable(); } + + /** + * Determine if transport is connected to the endpoint + * + * @return false iff the transport is connected, otherwise true + */ + virtual bool connected() const = 0; + + /** + * Determine if an error has occurred with this transport. + * + * @return true iff an error has occurred (not EOF). + */ + virtual bool error() const = 0; + + // /** + // * Attach the transport to a EventBase. + // * + // * This may only be called if the transport is not currently attached to a + // * EventBase (by an earlier call to detachEventBase()). + // * + // * This method must be invoked in the EventBase's thread. + // */ + // virtual void attachEventBase(EventBase* eventBase) = 0; + + // /** + // * Detach the transport from its EventBase. + // * + // * This may only be called when the transport is idle and has no reads or + // * writes pending. Once detached, the transport may not be used again + // until + // * it is re-attached to a EventBase by calling attachEventBase(). + // * + // * This method must be called from the current EventBase's thread. + // */ + // virtual void detachEventBase() = 0; + + // /** + // * Determine if the transport can be detached. + // * + // * This method must be called from the current EventBase's thread. + // */ + // virtual bool isDetachable() const = 0; + + /** + * Set the send timeout. + * + * If write requests do not make any progress for more than the specified + * number of milliseconds, fail all pending writes and close the transport. + * + * If write requests are currently pending when setSendTimeout() is called, + * the timeout interval is immediately restarted using the new value. + * + * @param milliseconds The timeout duration, in milliseconds. If 0, no + * timeout will be used. + */ + virtual void setSendTimeout(uint32_t milliseconds) = 0; + + /** + * Get the send timeout. + * + * @return Returns the current send timeout, in milliseconds. A return value + * of 0 indicates that no timeout is set. + */ + virtual uint32_t getSendTimeout() const = 0; + + virtual void connect(ConnectCallback *callback, + const core::Prefix &prefix_) = 0; + + // /** + // * Get the address of the local endpoint of this transport. + // * + // * This function may throw AsyncSocketException on error. + // * + // * @param address The local address will be stored in the specified + // * SocketAddress. + // */ + // virtual void getLocalAddress(* address) const = 0; + + virtual size_t getAppBytesWritten() const = 0; + virtual size_t getRawBytesWritten() const = 0; + virtual size_t getAppBytesReceived() const = 0; + virtual size_t getRawBytesReceived() const = 0; + + class BufferCallback { + public: + virtual ~BufferCallback() {} + virtual void onEgressBuffered() = 0; + virtual void onEgressBufferCleared() = 0; + }; + + ~AsyncSocket() = default; +}; + +class AsyncAcceptor { + public: + class AcceptCallback { + public: + virtual ~AcceptCallback() = default; + + /** + * connectionAccepted() is called whenever a new client connection is + * received. + * + * The AcceptCallback will remain installed after connectionAccepted() + * returns. + * + * @param fd The newly accepted client socket. The AcceptCallback + * assumes ownership of this socket, and is responsible + * for closing it when done. The newly accepted file + * descriptor will have already been put into + * non-blocking mode. + * @param clientAddr A reference to a SocketAddress struct containing the + * client's address. This struct is only guaranteed to + * remain valid until connectionAccepted() returns. + */ + virtual void connectionAccepted( + const core::Name &subscriber_name) noexcept = 0; + + /** + * acceptError() is called if an error occurs while accepting. + * + * The AcceptCallback will remain installed even after an accept error, + * as the errors are typically somewhat transient, such as being out of + * file descriptors. The server socket must be explicitly stopped if you + * wish to stop accepting after an error. + * + * @param ex An exception representing the error. + */ + virtual void acceptError(const std::exception &ex) noexcept = 0; + + /** + * acceptStarted() will be called in the callback's EventBase thread + * after this callback has been added to the AsyncServerSocket. + * + * acceptStarted() will be called before any calls to connectionAccepted() + * or acceptError() are made on this callback. + * + * acceptStarted() makes it easier for callbacks to perform initialization + * inside the callback thread. (The call to addAcceptCallback() must + * always be made from the AsyncServerSocket's primary EventBase thread. + * acceptStarted() provides a hook that will always be invoked in the + * callback's thread.) + * + * Note that the call to acceptStarted() is made once the callback is + * added, regardless of whether or not the AsyncServerSocket is actually + * accepting at the moment. acceptStarted() will be called even if the + * AsyncServerSocket is paused when the callback is added (including if + * the initial call to startAccepting() on the AsyncServerSocket has not + * been made yet). + */ + virtual void acceptStarted() noexcept {} + + /** + * acceptStopped() will be called when this AcceptCallback is removed from + * the AsyncServerSocket, or when the AsyncServerSocket is destroyed, + * whichever occurs first. + * + * No more calls to connectionAccepted() or acceptError() will be made + * after acceptStopped() is invoked. + */ + virtual void acceptStopped() noexcept {} + }; + + /** + * Wait for subscribers + * + */ + virtual void waitForSubscribers(AcceptCallback *cb) = 0; +}; + +class AsyncReader { + public: + class ReadCallback { + public: + virtual ~ReadCallback() = default; + + /** + * When data becomes available, getReadBuffer() will be invoked to get the + * buffer into which data should be read. + * + * This method allows the ReadCallback to delay buffer allocation until + * data becomes available. This allows applications to manage large + * numbers of idle connections, without having to maintain a separate read + * buffer for each idle connection. + * + * It is possible that in some cases, getReadBuffer() may be called + * multiple times before readDataAvailable() is invoked. In this case, the + * data will be written to the buffer returned from the most recent call to + * readDataAvailable(). If the previous calls to readDataAvailable() + * returned different buffers, the ReadCallback is responsible for ensuring + * that they are not leaked. + * + * If getReadBuffer() throws an exception, returns a nullptr buffer, or + * returns a 0 length, the ReadCallback will be uninstalled and its + * readError() method will be invoked. + * + * getReadBuffer() is not allowed to change the transport state before it + * returns. (For example, it should never uninstall the read callback, or + * set a different read callback.) + * + * @param bufReturn getReadBuffer() should update *bufReturn to contain the + * address of the read buffer. This parameter will never + * be nullptr. + * @param lenReturn getReadBuffer() should update *lenReturn to contain the + * maximum number of bytes that may be written to the read + * buffer. This parameter will never be nullptr. + * + * + * XXX TODO this does not seems to be completely true Checlk i/. + */ + virtual void getReadBuffer(void **bufReturn, size_t *lenReturn) = 0; + + /** + * readDataAvailable() will be invoked when data has been successfully read + * into the buffer returned by the last call to getReadBuffer(). + * + * The read callback remains installed after readDataAvailable() returns. + * It must be explicitly uninstalled to stop receiving read events. + * getReadBuffer() will be called at least once before each call to + * readDataAvailable(). getReadBuffer() will also be called before any + * call to readEOF(). + * + * @param len The number of bytes placed in the buffer. + */ + + virtual void readDataAvailable(size_t len) noexcept = 0; + + /** + * When data becomes available, isBufferMovable() will be invoked to figure + * out which API will be used, readBufferAvailable() or + * readDataAvailable(). If isBufferMovable() returns true, that means + * ReadCallback supports the IOBuf ownership transfer and + * readBufferAvailable() will be used. Otherwise, not. + + * By default, isBufferMovable() always return false. If + * readBufferAvailable() is implemented and to be invoked, You should + * overwrite isBufferMovable() and return true in the inherited class. + * + * This method allows the AsyncSocket/AsyncSSLSocket do buffer allocation by + * itself until data becomes available. Compared with the pre/post buffer + * allocation in getReadBuffer()/readDataAvailabe(), readBufferAvailable() + * has two advantages. First, this can avoid memcpy. E.g., in + * AsyncSSLSocket, the decrypted data was copied from the openssl internal + * buffer to the readbuf buffer. With the buffer ownership transfer, the + * internal buffer can be directly "moved" to ReadCallback. Second, the + * memory allocation can be more precise. The reason is + * AsyncSocket/AsyncSSLSocket can allocate the memory of precise size + * because they have more context about the available data than + * ReadCallback. Think about the getReadBuffer() pre-allocate 4072 bytes + * buffer, but the available data is always 16KB (max OpenSSL record size). + */ + + virtual bool isBufferMovable() noexcept { return false; } + + /** + * Suggested buffer size, allocated for read operations, + * if callback is movable and supports folly::IOBuf + */ + + virtual size_t maxBufferSize() const { + return 64 * 1024; // 64K + } + + /** + * readBufferAvailable() will be invoked when data has been successfully + * read. + * + * Note that only either readBufferAvailable() or readDataAvailable() will + * be invoked according to the return value of isBufferMovable(). The timing + * and aftereffect of readBufferAvailable() are the same as + * readDataAvailable() + * + * @param readBuf The unique pointer of read buffer. + */ + + // virtual void readBufferAvailable(uint8_t** buffer, std::size_t + // *buf_length) noexcept {} + + virtual void readBufferAvailable( + utils::SharableVector<uint8_t> &&buffer) noexcept {} + + // virtual void readBufferAvailable(utils::SharableBuffer<uint8_t>&& buffer) + // noexcept {} + + /** + * readEOF() will be invoked when the transport is closed. + * + * The read callback will be automatically uninstalled immediately before + * readEOF() is invoked. + */ + virtual void readEOF() noexcept = 0; + + /** + * readError() will be invoked if an error occurs reading from the + * transport. + * + * The read callback will be automatically uninstalled immediately before + * readError() is invoked. + * + * @param ex An exception describing the error that occurred. + */ + virtual void readErr(const std::error_code ec) noexcept = 0; + }; + + // Read methods that aren't part of AsyncTransport. + virtual void setReadCB(ReadCallback *callback) = 0; + virtual ReadCallback *getReadCallback() const = 0; + + protected: + virtual ~AsyncReader() = default; +}; + +class AsyncWriter { + public: + class WriteCallback { + public: + virtual ~WriteCallback() = default; + + /** + * writeSuccess() will be invoked when all of the data has been + * successfully written. + * + * Note that this mainly signals that the buffer containing the data to + * write is no longer needed and may be freed or re-used. It does not + * guarantee that the data has been fully transmitted to the remote + * endpoint. For example, on socket-based transports, writeSuccess() only + * indicates that the data has been given to the kernel for eventual + * transmission. + */ + virtual void writeSuccess() noexcept = 0; + + /** + * writeError() will be invoked if an error occurs writing the data. + * + * @param bytesWritten The number of bytes that were successfull + * @param ex An exception describing the error that occurred. + */ + virtual void writeErr(size_t bytesWritten) noexcept = 0; + }; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void write(WriteCallback *callback, const void *buf, size_t bytes, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) = 0; + + /** + * If you supply a non-null WriteCallback, exactly one of writeSuccess() + * or writeErr() will be invoked when the write completes. If you supply + * the same WriteCallback object for multiple write() calls, it will be + * invoked exactly once per call. The only way to cancel outstanding + * write requests is to close the socket (e.g., with closeNow() or + * shutdownWriteNow()). When closing the socket this way, writeErr() will + * still be invoked once for each outstanding write operation. + */ + virtual void write(WriteCallback *callback, + utils::SharableVector<uint8_t> &&output_buffer, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) = 0; + + // /** + // * If you supply a non-null WriteCallback, exactly one of writeSuccess() + // * or writeErr() will be invoked when the write completes. If you supply + // * the same WriteCallback object for multiple write() calls, it will be + // * invoked exactly once per call. The only way to cancel outstanding + // * write requests is to close the socket (e.g., with closeNow() or + // * shutdownWriteNow()). When closing the socket this way, writeErr() will + // * still be invoked once for each outstanding write operation. + // */ + // virtual void writeChain( + // WriteCallback* callback, + // std::unique_ptr<IOBuf>&& buf, + // WriteFlags flags = WriteFlags::NONE) = 0; + + virtual void setWriteCB(WriteCallback *callback) = 0; + virtual WriteCallback *getWriteCallback() const = 0; + + protected: + virtual ~AsyncWriter() = default; +}; + +} // namespace interface + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc new file mode 100755 index 000000000..7b6342262 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.cc @@ -0,0 +1,490 @@ +/* + * 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/full_duplex_socket.h> +#include <hicn/transport/interfaces/socket_options_default_values.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <memory> + +namespace transport { + +namespace interface { + +static const std::string producer_identity = "producer_socket"; + +AsyncFullDuplexSocket::AsyncFullDuplexSocket(const Prefix &locator) + : AsyncFullDuplexSocket(locator, internal_io_service_) {} + +AsyncFullDuplexSocket::AsyncFullDuplexSocket(const Prefix &locator, + asio::io_service &io_service) + : locator_(locator), + incremental_suffix_(0), + io_service_(io_service), + work_(io_service), + producer_(std::make_unique<ProducerSocket>(io_service_)), + consumer_(std::make_unique<ConsumerSocket>( + TransportProtocolAlgorithms::RAAQM /* , io_service_ */)), + read_callback_(nullptr), + write_callback_(nullptr), + connect_callback_(nullptr), + accept_callback_(nullptr), + internal_connect_callback_(new OnConnectCallback(*this)), + internal_signal_callback_(new OnSignalCallback(*this)), + send_timeout_milliseconds_(~0), + counters_({0}), + receive_buffer_(std::make_shared<utils::SharableVector<uint8_t>>()) { + using namespace transport; + using namespace std::placeholders; + producer_->registerPrefix(locator); + + producer_->setSocketOption( + ProducerCallbacksOptions::CACHE_MISS, + std::bind(&AsyncFullDuplexSocket::onControlInterest, this, _1, _2)); + + producer_->setSocketOption(GeneralTransportOptions::OUTPUT_BUFFER_SIZE, + uint32_t{150000}); + + producer_->setSocketOption( + ProducerCallbacksOptions::CONTENT_PRODUCED, + std::bind(&AsyncFullDuplexSocket::onContentProduced, this, _1, _2, _3)); + + producer_->connect(); + + consumer_->setSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, + (ConsumerContentObjectVerificationCallback)[]( + ConsumerSocket & s, const ContentObject &c) + ->bool { return true; }); + + consumer_->setSocketOption( + ConsumerCallbacksOptions::CONTENT_RETRIEVED, + std::bind(&AsyncFullDuplexSocket::onContentRetrieved, this, _1, _2, _3)); + + consumer_->setSocketOption(GeneralTransportOptions::MAX_INTEREST_RETX, + uint32_t{4}); + + consumer_->connect(); +} + +void AsyncFullDuplexSocket::close() { + this->consumer_->stop(); + this->producer_->stop(); +} + +void AsyncFullDuplexSocket::closeNow() { close(); } + +void AsyncFullDuplexSocket::shutdownWrite() { producer_->stop(); } + +void AsyncFullDuplexSocket::shutdownWriteNow() { shutdownWrite(); } + +bool AsyncFullDuplexSocket::good() const { return true; } + +bool AsyncFullDuplexSocket::readable() const { + // TODO return status of consumer socket + return true; +} + +bool AsyncFullDuplexSocket::writable() const { + // TODO return status of producer socket + return true; +} + +bool AsyncFullDuplexSocket::isPending() const { + // TODO save if there are production operation in the ops queue + // in producer socket + return true; +} + +bool AsyncFullDuplexSocket::connected() const { + // No real connection here (ICN world). Return good + return good(); +} + +bool AsyncFullDuplexSocket::error() const { return !good(); } + +void AsyncFullDuplexSocket::setSendTimeout(uint32_t milliseconds) { + // TODO if production takes too much to complete + // let's abort the operation. + + // Normally with hicn this should be done for content + // pull, not for production. + + send_timeout_milliseconds_ = milliseconds; +} + +uint32_t AsyncFullDuplexSocket::getSendTimeout() const { + return send_timeout_milliseconds_; +} + +size_t AsyncFullDuplexSocket::getAppBytesWritten() const { + return counters_.app_bytes_written_; +} + +size_t AsyncFullDuplexSocket::getRawBytesWritten() const { return 0; } + +size_t AsyncFullDuplexSocket::getAppBytesReceived() const { + return counters_.app_bytes_read_; +} + +size_t AsyncFullDuplexSocket::getRawBytesReceived() const { return 0; } + +void AsyncFullDuplexSocket::connect(ConnectCallback *callback, + const core::Prefix &prefix) { + connect_callback_ = callback; + + // Create an interest for a subscription + auto interest = + core::Interest::Ptr(new core::Interest(prefix.makeRandomName())); + auto _payload = utils::MemBuf::create(sizeof(ActionMessage)); + _payload->append(sizeof(ActionMessage)); + auto payload = _payload->writableData(); + ActionMessage *subscription_message = + reinterpret_cast<ActionMessage *>(payload); + subscription_message->header.msg_type = MessageType::ACTION; + subscription_message->action = Action::SUBSCRIBE; + subscription_message->header.reserved[0] = 0; + subscription_message->header.reserved[1] = 0; + + // Set the name the other part should use for notifying a content production + sync_notification_ = std::move(locator_.makeRandomName()); + sync_notification_.copyToDestination( + reinterpret_cast<uint8_t *>(subscription_message->name)); + + TRANSPORT_LOGI( + "Trying to connect. Sending interest: %s, name for notifications: %s", + prefix.getName().toString().c_str(), + sync_notification_.toString().c_str()); + + interest->setLifetime(1000); + interest->appendPayload(std::move(_payload)); + consumer_->asyncSendInterest(std::move(interest), + internal_connect_callback_.get()); +} + +void AsyncFullDuplexSocket::write(WriteCallback *callback, const void *buf, + size_t bytes, + const PublicationOptions &options, + WriteFlags flags) { + using namespace transport; + + // 1 asynchronously write the content. I assume here the + // buffer contains the whole application frame. FIXME: check + // if this is true and fix it accordingly + std::cout << "Size of the PAYLOAD: " << bytes << std::endl; + + if (bytes > core::Packet::default_mtu - sizeof(PayloadMessage)) { + TRANSPORT_LOGI("Producing content with name %s", + options.name.toString().c_str()); + producer_->asyncProduce(options.name, + reinterpret_cast<const uint8_t *>(buf), bytes); + signalProductionToSubscribers(options.name); + } else { + TRANSPORT_LOGI("Sending payload through interest"); + piggybackPayloadToSubscribers( + options.name, reinterpret_cast<const uint8_t *>(buf), bytes); + } +} + +void AsyncFullDuplexSocket::write( + WriteCallback *callback, utils::SharableVector<uint8_t> &&output_buffer, + const PublicationOptions &options, WriteFlags flags) { + using namespace transport; + + // 1 asynchronously write the content. I assume here the + // buffer contains the whole application frame. FIXME: check + // if this is true and fix it accordingly + std::cout << "Size of the PAYLOAD: " << output_buffer.size() << std::endl; + + if (output_buffer.size() > + core::Packet::default_mtu - sizeof(PayloadMessage)) { + TRANSPORT_LOGI("Producing content with name %s", + options.name.toString().c_str()); + producer_->asyncProduce(options.name, std::move(output_buffer)); + signalProductionToSubscribers(options.name); + } else { + TRANSPORT_LOGI("Sending payload through interest"); + piggybackPayloadToSubscribers(options.name, &output_buffer[0], + output_buffer.size()); + } +} + +void AsyncFullDuplexSocket::piggybackPayloadToSubscribers( + const core::Name &name, const uint8_t *buffer, std::size_t bytes) { + for (auto &sub : subscribers_) { + auto interest = core::Interest::Ptr(new core::Interest(name)); + auto _payload = utils::MemBuf::create(bytes + sizeof(PayloadMessage)); + _payload->append(bytes + sizeof(PayloadMessage)); + auto payload = _payload->writableData(); + + PayloadMessage *interest_payload = + reinterpret_cast<PayloadMessage *>(payload); + interest_payload->header.msg_type = MessageType::PAYLOAD; + interest_payload->header.reserved[0] = 0; + interest_payload->header.reserved[1] = 0; + interest_payload->reserved[0] = 0; + std::memcpy(payload + sizeof(PayloadMessage), buffer, bytes); + interest->appendPayload(std::move(_payload)); + + // Set the timeout of 0.2 second + interest->setLifetime(1000); + interest->setName(sub); + interest->getWritableName().setSuffix(incremental_suffix_++); + // TRANSPORT_LOGI("Sending signalization to %s", + // interest->getName().toString().c_str()); + + consumer_->asyncSendInterest(std::move(interest), + internal_signal_callback_.get()); + } +} + +void AsyncFullDuplexSocket::signalProductionToSubscribers( + const core::Name &name) { + // Signal the other part we are producing a content + // Create an interest for a subscription + + for (auto &sub : subscribers_) { + auto interest = core::Interest::Ptr(new core::Interest(name)); + // Todo consider using preallocated pool of membufs + auto _payload = utils::MemBuf::create(sizeof(ActionMessage)); + _payload->append(sizeof(ActionMessage)); + auto payload = const_cast<uint8_t *>(interest->getPayload().data()); + + ActionMessage *produce_notification = + reinterpret_cast<ActionMessage *>(payload); + produce_notification->header.msg_type = MessageType::ACTION; + produce_notification->action = Action::SIGNAL_PRODUCTION; + produce_notification->header.reserved[0] = 0; + produce_notification->header.reserved[1] = 0; + name.copyToDestination( + reinterpret_cast<uint8_t *>(produce_notification->name)); + interest->appendPayload(std::move(_payload)); + + // Set the timeout of 0.2 second + interest->setLifetime(1000); + interest->setName(sub); + interest->getWritableName().setSuffix(incremental_suffix_++); + // TRANSPORT_LOGI("Sending signalization to %s", + // interest->getName().toString().c_str()); + + consumer_->asyncSendInterest(std::move(interest), + internal_signal_callback_.get()); + } +} + +void AsyncFullDuplexSocket::waitForSubscribers(AcceptCallback *cb) { + accept_callback_ = cb; +} + +std::shared_ptr<core::ContentObject> +AsyncFullDuplexSocket::decodeSynchronizationMessage( + const core::Interest &interest) { + auto mesg = interest.getPayload(); + const MessageHeader *header = + reinterpret_cast<const MessageHeader *>(mesg.data()); + + switch (header->msg_type) { + case MessageType::ACTION: { + // Check what is the action to perform + const ActionMessage *message = + reinterpret_cast<const ActionMessage *>(header); + + if (message->action == Action::SUBSCRIBE) { + // Add consumer to list on consumers to be notified + auto ret = + subscribers_.emplace(AF_INET6, (const uint8_t *)message->name, 0); + TRANSPORT_LOGI("Added subscriber %s :)", ret.first->toString().c_str()); + if (ret.second) { + accept_callback_->connectionAccepted(*ret.first); + } + + TRANSPORT_LOGI("Connection success!"); + + sync_notification_ = std::move(locator_.makeRandomName()); + return createSubscriptionResponse(sync_notification_); + + } else if (message->action == Action::CANCEL_SUBSCRIPTION) { + // XXX Modify name!!! Each allocated name allocates a 128 bit array. + subscribers_.erase( + core::Name(AF_INET6, (const uint8_t *)message->name, 0)); + return createAck(); + } else if (message->action == Action::SIGNAL_PRODUCTION) { + // trigger a reverse pull for the name contained in the message + core::Name n(AF_INET6, (const uint8_t *)message->name, 0); + std::cout << "PROD NOTIFICATION: Content to retrieve: " << n + << std::endl; + std::cout << "PROD NOTIFICATION: Interest name: " << interest.getName() + << std::endl; // << " compared to " << sync_notification_ << + // std::endl; + + if (sync_notification_.equals(interest.getName(), false)) { + std::cout << "Starting reverse pull for " << n << std::endl; + consumer_->asyncConsume(n, receive_buffer_); + return createAck(); + } + } else { + TRANSPORT_LOGE("Received unknown message. Dropping it."); + } + + break; + } + case MessageType::RESPONSE: { + throw errors::RuntimeException( + "The response should be a content object!!"); + } + case MessageType::PAYLOAD: { + // The interest contains the payload directly. + // We saved one round trip :) + + auto buffer = std::make_shared<utils::SharableVector<uint8_t>>(); + const uint8_t *data = mesg.data() + sizeof(PayloadMessage); + buffer->assign(data, data + mesg.length() - sizeof(PayloadMessage)); + read_callback_->readBufferAvailable(std::move(*buffer)); + return createAck(); + } + default: { + return std::shared_ptr<core::ContentObject>(nullptr); + } + } + + return std::shared_ptr<core::ContentObject>(nullptr); +} + +void AsyncFullDuplexSocket::onControlInterest(ProducerSocket &s, + const core::Interest &i) { + auto payload = i.getPayload(); + if (payload.length()) { + // Try to decode payload and see if starting an async pull operation + auto response = decodeSynchronizationMessage(i); + if (response) { + response->setName(i.getName()); + s.produce(*response); + } + } +} + +void AsyncFullDuplexSocket::onContentProduced(ProducerSocket &producer, + const std::error_code &ec, + uint64_t bytes_written) { + if (write_callback_) { + if (!ec) { + write_callback_->writeSuccess(); + } else { + write_callback_->writeErr(bytes_written); + } + } +} + +void AsyncFullDuplexSocket::onContentRetrieved(ConsumerSocket &s, + std::size_t size, + const std::error_code &ec) { + // Sanity check + if (size != receive_buffer_->size()) { + TRANSPORT_LOGE( + "Received content size differs from size retrieved from the buffer."); + return; + } + + TRANSPORT_LOGI("Received content with size %lu", size); + if (!ec) { + read_callback_->readBufferAvailable(std::move(*receive_buffer_)); + } else { + TRANSPORT_LOGE("Error retrieving content."); + } + // consumer_->stop(); +} + +void AsyncFullDuplexSocket::OnConnectCallback::onContentObject( + core::Interest::Ptr &&, core::ContentObject::Ptr &&content_object) { + // The ack message should contain the name to be used for notifying + // the production of the content to the other part + + if (content_object->getPayload().length() == 0) { + TRANSPORT_LOGW("Connection response message empty...."); + return; + } + + SubscriptionResponseMessage *response = + reinterpret_cast<SubscriptionResponseMessage *>( + content_object->getPayload().writableData()); + + if (response->response.header.msg_type == MessageType::RESPONSE) { + if (response->response.return_code == ReturnCode::OK) { + auto ret = + socket_.subscribers_.emplace(AF_INET6, (uint8_t *)response->name, 0); + TRANSPORT_LOGI("Successfully connected!!!! Subscriber added: %s", + ret.first->toString().c_str()); + socket_.connect_callback_->connectSuccess(); + } + } +} + +void AsyncFullDuplexSocket::OnSignalCallback::onContentObject( + core::Interest::Ptr &&, core::ContentObject::Ptr &&content_object) { + return; +} + +void AsyncFullDuplexSocket::OnSignalCallback::onTimeout( + core::Interest::Ptr &&interest) { + TRANSPORT_LOGE("Retransmitting signalization interest to %s!!", + interest->getName().toString().c_str()); + socket_.consumer_->asyncSendInterest(std::move(interest), + socket_.internal_signal_callback_.get()); +} + +void AsyncFullDuplexSocket::OnConnectCallback::onTimeout( + core::Interest::Ptr &&interest) { + socket_.connect_callback_->connectErr( + std::make_error_code(std::errc::not_connected)); +} + +std::shared_ptr<core::ContentObject> AsyncFullDuplexSocket::createAck() { + // Send the response back + core::Name name("b001::abcd"); + auto response = std::make_shared<core::ContentObject>(name); + auto _payload = utils::MemBuf::create(sizeof(ActionMessage)); + _payload->append(sizeof(ResponseMessage)); + auto payload = response->getPayload().data(); + ResponseMessage *response_message = (ResponseMessage *)payload; + response_message->header.msg_type = MessageType::RESPONSE; + response_message->header.reserved[0] = 0; + response_message->header.reserved[1] = 0; + response_message->return_code = ReturnCode::OK; + response->appendPayload(std::move(_payload)); + response->setLifetime(0); + return response; +} + +std::shared_ptr<core::ContentObject> +AsyncFullDuplexSocket::createSubscriptionResponse(const core::Name &name) { + // Send the response back + core::Name tmp_name("b001::abcd"); + auto response = std::make_shared<core::ContentObject>(tmp_name); + auto _payload = utils::MemBuf::create(sizeof(SubscriptionResponseMessage)); + _payload->append(sizeof(SubscriptionResponseMessage)); + auto payload = _payload->data(); + SubscriptionResponseMessage *response_message = + (SubscriptionResponseMessage *)payload; + response_message->response.header.msg_type = MessageType::RESPONSE; + response_message->response.header.reserved[0] = 0; + response_message->response.header.reserved[1] = 0; + response_message->response.return_code = ReturnCode::OK; + name.copyToDestination(reinterpret_cast<uint8_t *>(response_message->name)); + response->appendPayload(std::move(_payload)); + response->setLifetime(0); + return response; +} + +} // namespace interface +} // namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/full_duplex_socket.h b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.h new file mode 100755 index 000000000..f881bea54 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/full_duplex_socket.h @@ -0,0 +1,254 @@ +/* + * 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. + */ + +/* + * This class is created for sending/receiving data over an ICN network. + */ + +#pragma once + +#include <hicn/transport/core/prefix.h> +#include <hicn/transport/interfaces/async_transport.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/interfaces/socket_producer.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <unordered_set> +#include <vector> + +namespace transport { + +namespace interface { + +enum class MessageType : uint8_t { ACTION, RESPONSE, PAYLOAD }; + +enum class Action : uint8_t { + SUBSCRIBE, + CANCEL_SUBSCRIPTION, + SIGNAL_PRODUCTION, +}; + +enum class ReturnCode : uint8_t { + OK, + FAILED, +}; + +struct MessageHeader { + MessageType msg_type; + uint8_t reserved[2]; +}; + +struct ActionMessage { + MessageHeader header; + Action action; + uint64_t name[2]; +}; + +struct ResponseMessage { + MessageHeader header; + ReturnCode return_code; +}; + +struct SubscriptionResponseMessage { + ResponseMessage response; + uint64_t name[2]; +}; + +struct PayloadMessage { + MessageHeader header; + uint8_t reserved[1]; +}; + +// struct NotificationMessage { +// Action action; +// uint8_t reserved[3]; +// uint64_t +// } + +using core::Prefix; + +class AsyncFullDuplexSocket : public AsyncSocket, + public AsyncReader, + public AsyncWriter, + public AsyncAcceptor { + private: + struct Counters { + uint64_t app_bytes_written_; + uint64_t app_bytes_read_; + + TRANSPORT_ALWAYS_INLINE void updateBytesWritten(uint64_t bytes) { + app_bytes_written_ += bytes; + } + + TRANSPORT_ALWAYS_INLINE void updateBytesRead(uint64_t bytes) { + app_bytes_read_ += bytes; + } + }; + + public: + using UniquePtr = std::unique_ptr<AsyncFullDuplexSocket>; + using SharedPtr = std::unique_ptr<AsyncFullDuplexSocket>; + + AsyncFullDuplexSocket(const Prefix &locator, asio::io_service &io_service); + AsyncFullDuplexSocket(const core::Prefix &locator); + + ~AsyncFullDuplexSocket() { + TRANSPORT_LOGI("Adios AsyncFullDuplexSocket!!!"); + }; + + using ReadCallback = AsyncReader::ReadCallback; + using WriteCallback = AsyncWriter::WriteCallback; + + TRANSPORT_ALWAYS_INLINE void setReadCB(ReadCallback *callback) override { + read_callback_ = callback; + } + + TRANSPORT_ALWAYS_INLINE ReadCallback *getReadCallback() const override { + return read_callback_; + } + + TRANSPORT_ALWAYS_INLINE void setWriteCB(WriteCallback *callback) override { + write_callback_ = callback; + } + + TRANSPORT_ALWAYS_INLINE WriteCallback *getWriteCallback() const override { + return write_callback_; + } + + TRANSPORT_ALWAYS_INLINE const core::Prefix &getLocator() { return locator_; } + + void connect(ConnectCallback *callback, const core::Prefix &prefix) override; + + void write(WriteCallback *callback, const void *buf, size_t bytes, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) override; + + virtual void write(WriteCallback *callback, + utils::SharableVector<uint8_t> &&output_buffer, + const PublicationOptions &options, + WriteFlags flags = WriteFlags::NONE) override; + + void waitForSubscribers(AcceptCallback *cb) override; + + // void writev( + // WriteCallback* callback, + // const iovec* vec, + // size_t count, + // Name &&content_to_publish_name, + // WriteFlags flags = WriteFlags::NONE) override; + + void close() override; + + void closeNow() override; + + void shutdownWrite() override; + + void shutdownWriteNow() override; + + bool good() const override; + + bool readable() const override; + + bool writable() const override; + + bool isPending() const override; + + bool connected() const override; + + bool error() const override; + + void setSendTimeout(uint32_t milliseconds) override; + + size_t getAppBytesWritten() const override; + size_t getRawBytesWritten() const override; + size_t getAppBytesReceived() const override; + size_t getRawBytesReceived() const override; + + uint32_t getSendTimeout() const override; + + private: + std::shared_ptr<core::ContentObject> decodeSynchronizationMessage( + const core::Interest &interest); + + class OnConnectCallback : public BasePortal::ConsumerCallback { + public: + OnConnectCallback(AsyncFullDuplexSocket &socket) : socket_(socket){}; + virtual ~OnConnectCallback() = default; + void onContentObject(core::Interest::Ptr &&, + core::ContentObject::Ptr &&content_object) override; + void onTimeout(core::Interest::Ptr &&interest) override; + + private: + AsyncFullDuplexSocket &socket_; + }; + + class OnSignalCallback : public BasePortal::ConsumerCallback { + public: + OnSignalCallback(AsyncFullDuplexSocket &socket) : socket_(socket){}; + virtual ~OnSignalCallback() = default; + void onContentObject(core::Interest::Ptr &&, + core::ContentObject::Ptr &&content_object); + void onTimeout(core::Interest::Ptr &&interest); + + private: + AsyncFullDuplexSocket &socket_; + }; + + void onControlInterest(ProducerSocket &s, const core::Interest &i); + void onContentProduced(ProducerSocket &producer, const std::error_code &ec, + uint64_t bytes_written); + void onContentRetrieved(ConsumerSocket &s, std::size_t size, + const std::error_code &ec); + + void signalProductionToSubscribers(const core::Name &name); + void piggybackPayloadToSubscribers(const core::Name &name, + const uint8_t *buffer, std::size_t bytes); + + std::shared_ptr<core::ContentObject> createAck(); + std::shared_ptr<core::ContentObject> createSubscriptionResponse( + const core::Name &name); + + core::Prefix locator_; + uint32_t incremental_suffix_; + core::Name sync_notification_; + // std::unique_ptr<BasePortal> portal_; + asio::io_service internal_io_service_; + asio::io_service &io_service_; + asio::io_service::work work_; + + // These names represent the "locator" of a certain + // peer that subscribed to this. + std::unordered_set<core::Name> subscribers_; + + // Useful for publishing / Retrieving data + std::unique_ptr<ProducerSocket> producer_; + std::unique_ptr<ConsumerSocket> consumer_; + + ReadCallback *read_callback_; + WriteCallback *write_callback_; + ConnectCallback *connect_callback_; + AcceptCallback *accept_callback_; + + std::unique_ptr<OnConnectCallback> internal_connect_callback_; + std::unique_ptr<OnSignalCallback> internal_signal_callback_; + + uint32_t send_timeout_milliseconds_; + struct Counters counters_; + std::shared_ptr<utils::SharableVector<uint8_t>> receive_buffer_; +}; + +} // namespace interface +} // namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/publication_options.h b/libtransport/src/hicn/transport/interfaces/publication_options.h new file mode 100755 index 000000000..ae5366ce7 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/publication_options.h @@ -0,0 +1,34 @@ +/* + * 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 <map> +#include <sstream> +#include <vector> + +namespace transport { + +namespace interface { + +class PublicationOptions { + public: + core::Name name; + uint32_t content_lifetime_milliseconds; + // TODO Signature +}; +} // namespace interface + +} // namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc new file mode 100755 index 000000000..de3e84417 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.cc @@ -0,0 +1,35 @@ +/* + * 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/rtc_socket_consumer.h> + +namespace transport { + +namespace interface { + +RTCConsumerSocket::RTCConsumerSocket(int protocol, asio::io_service &io_service) + : ConsumerSocket(protocol, io_service) {} + +RTCConsumerSocket::~RTCConsumerSocket() {} + +void RTCConsumerSocket::handleRTCPPacket(uint8_t *packet, size_t len) { + RTCTransportProtocol *transport = dynamic_cast<RTCTransportProtocol *>( + ConsumerSocket::transport_protocol_.get()); + if (transport) transport->onRTCPPacket(packet, len); +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h new file mode 100755 index 000000000..86ccf6e22 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_consumer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/interfaces/socket_consumer.h> + +namespace transport { + +namespace interface { + +class RTCConsumerSocket : public ConsumerSocket { + public: + explicit RTCConsumerSocket(int protocol, asio::io_service &io_service); + + ~RTCConsumerSocket(); + + void handleRTCPPacket(uint8_t *packet, size_t len); +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc new file mode 100755 index 000000000..d8a9d53b9 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.cc @@ -0,0 +1,157 @@ +/* + * 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 <stdlib.h> +#include <time.h> +#include <hicn/transport/interfaces/rtc_socket_producer.h> + +#define NACK_HEADER_SIZE 8 // bytes +#define TIMESTAMP_LEN 8 // bytes +#define TCP_HEADER_SIZE 20 +#define IP6_HEADER_SIZE 40 +#define INIT_PACKET_PRODUCTION_RATE 100 // pps random value (almost 1Mbps) +#define STATS_INTERVAL_DURATION 500 // ms +#define INTEREST_LIFETIME_REDUCTION_FACTOR 0.8 + +// NACK HEADER +// +-----------------------------------------+ +// | 4 bytes: current segment in production | +// +-----------------------------------------+ +// | 4 bytes: production rate (bytes x sec) | +// +-----------------------------------------+ +// may require additional field (Rate for multiple qualities, ...) +// + +namespace transport { + +namespace interface { + +RTCProducerSocket::RTCProducerSocket(asio::io_service &io_service) + : ProducerSocket(io_service), + currentSeg_(1), + nack_(std::make_shared<ContentObject>()), + producedBytes_(0), + producedPackets_(0), + bytesProductionRate_(0), + packetsProductionRate_(INIT_PACKET_PRODUCTION_RATE), + perSecondFactor_(1000 / STATS_INTERVAL_DURATION) { + nack_->appendPayload(utils::MemBuf::create(NACK_HEADER_SIZE)); + lastStats_ = std::chrono::steady_clock::now(); + srand(time(NULL)); + prodLabel_ = ((rand() % 255) << 24UL); +} + +RTCProducerSocket::~RTCProducerSocket() {} + +void RTCProducerSocket::registerName(Prefix &producer_namespace) { + ProducerSocket::registerPrefix(producer_namespace); + + flowName_ = producer_namespace.getName(); + + if (flowName_.getType() == HNT_CONTIGUOUS_V4 || + flowName_.getType() == HNT_IOV_V4) { + headerSize_ = sizeof(hicn_v6_hdr_t::ip); + } else if (flowName_.getType() == HNT_CONTIGUOUS_V6 || + flowName_.getType() == HNT_IOV_V6) { + headerSize_ = sizeof(hicn_v4_hdr_t::ip); + } else { + throw errors::RuntimeException("Unknown name format."); + } + + headerSize_ += TCP_HEADER_SIZE; +} + +void RTCProducerSocket::updateStats(uint32_t packet_size) { + producedBytes_ += packet_size; + producedPackets_++; + std::chrono::steady_clock::duration duration = + std::chrono::steady_clock::now() - lastStats_; + if (std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() >= + STATS_INTERVAL_DURATION) { + lastStats_ = std::chrono::steady_clock::now(); + bytesProductionRate_ = producedBytes_ * perSecondFactor_; + packetsProductionRate_ = producedPackets_ * perSecondFactor_; + producedBytes_ = 0; + producedPackets_ = 0; + } +} + +void RTCProducerSocket::produce(const uint8_t *buf, size_t buffer_size) { + if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) { + return; + } + + if (TRANSPORT_EXPECT_FALSE((buffer_size + headerSize_ + TIMESTAMP_LEN) > + data_packet_size_)) { + return; + } + + updateStats(buffer_size + headerSize_ + TIMESTAMP_LEN); + + std::shared_ptr<ContentObject> content_object = + std::make_shared<ContentObject>(flowName_.setSuffix(currentSeg_)); + auto payload = utils::MemBuf::copyBuffer(buf, buffer_size, TIMESTAMP_LEN); + + // content_object->setLifetime(content_object_expiry_time_); + content_object->setLifetime(1000); // XXX this should be set by the APP + + uint64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + payload->prepend(TIMESTAMP_LEN); + uint8_t *payloadPointer = payload->writableData(); + *(uint64_t *)payloadPointer = timestamp; + content_object->appendPayload(std::move(payload)); + + content_object->setPathLabel(prodLabel_); + portal_->sendContentObject(*content_object); + + currentSeg_++; +} + +void RTCProducerSocket::onInterest(Interest::Ptr &&interest) { + uint32_t interestSeg = interest->getName().getSuffix(); + uint32_t lifetime = interest->getLifetime(); + uint32_t max_gap; + + // XXX + // packetsProductionRate_ is modified by another thread in updateStats + // this should be safe since I just read here. but, you never know. + max_gap = + floor((double)((double)((double)lifetime * + INTEREST_LIFETIME_REDUCTION_FACTOR / 1000.0) * + (double)packetsProductionRate_)); + + if (interestSeg < currentSeg_ || interestSeg > (max_gap + currentSeg_)) { + sendNack(*interest); + } + // else drop packet +} + +void RTCProducerSocket::sendNack(const Interest &interest) { + nack_->setName(interest.getName()); + uint32_t *payload_ptr = (uint32_t *)nack_->getPayload().data(); + *payload_ptr = currentSeg_; + *(++payload_ptr) = bytesProductionRate_; + + nack_->setLifetime(0); + nack_->setPathLabel(prodLabel_); + portal_->sendContentObject(*nack_); +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h new file mode 100755 index 000000000..1a42bdc56 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/rtc_socket_producer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/interfaces/socket_producer.h> +#include <hicn/transport/utils/content_store.h> + +#include <map> +#include <mutex> + +namespace transport { + +namespace interface { + +class RTCProducerSocket : public ProducerSocket { + public: + RTCProducerSocket(asio::io_service &io_service); + ~RTCProducerSocket(); + + void registerName(Prefix &producer_namespace); + + void produce(const uint8_t *buffer, size_t buffer_size); + + void onInterest(Interest::Ptr &&interest) override; + + private: + void sendNack(const Interest &interest); + void updateStats(uint32_t packet_size); + + // std::map<uint32_t, uint64_t> pendingInterests_; + uint32_t currentSeg_; + uint32_t prodLabel_; + uint16_t headerSize_; + Name flowName_; + // bool produceInSynch_; + std::shared_ptr<ContentObject> nack_; + uint32_t producedBytes_; + uint32_t producedPackets_; + uint32_t bytesProductionRate_; + uint32_t packetsProductionRate_; + uint32_t perSecondFactor_; + std::chrono::steady_clock::time_point lastStats_; +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket.h b/libtransport/src/hicn/transport/interfaces/socket.h new file mode 100755 index 000000000..22757810a --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/facade.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/core/manifest_format_fixed.h> +#include <hicn/transport/core/manifest_inline.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/interfaces/socket_options_default_values.h> +#include <hicn/transport/interfaces/socket_options_keys.h> +#include <hicn/transport/utils/crypto_suite.h> +#include <hicn/transport/utils/identity.h> +#include <hicn/transport/utils/verifier.h> + +#define SOCKET_OPTION_GET 0 +#define SOCKET_OPTION_NOT_GET 1 +#define SOCKET_OPTION_SET 2 +#define SOCKET_OPTION_NOT_SET 3 +#define SOCKET_OPTION_DEFAULT 12345 + +#define VOID_HANDLER 0 + +namespace transport { + +namespace protocol { +class IcnObserver; +} + +namespace interface { + +template <typename PortalType> +class Socket; +class ConsumerSocket; +class ProducerSocket; + +// using Interest = core::Interest; +// using ContentObject = core::ContentObject; +// using Name = core::Name; +// using HashAlgorithm = core::HashAlgorithm; +// using CryptoSuite = utils::CryptoSuite; +// using Identity = utils::Identity; +// using Verifier = utils::Verifier; + +using HicnForwarderPortal = core::HicnForwarderPortal; + +#ifdef __linux__ +#ifndef __ANDROID__ +using RawSocketPortal = core::RawSocketPortal; +#endif +#endif + +#ifdef __vpp__ +using VPPForwarderPortal = core::VPPForwarderPortal; +using BaseSocket = Socket<VPPForwarderPortal>; +using BasePortal = VPPForwarderPortal; +#else +using BaseSocket = Socket<HicnForwarderPortal>; +using BasePortal = HicnForwarderPortal; +#endif + +using PayloadType = core::PayloadType; +using Prefix = core::Prefix; +using Array = utils::Array<uint8_t>; + +using ConsumerInterestCallback = + std::function<void(ConsumerSocket &, const core::Interest &)>; + +using ConsumerContentCallback = + std::function<void(ConsumerSocket &, std::size_t, const std::error_code &)>; + +using ConsumerTimerCallback = + std::function<void(ConsumerSocket &, std::size_t, + std::chrono::milliseconds &, float, uint32_t, uint32_t)>; + +using ProducerContentCallback = std::function<void( + ProducerSocket &, const std::error_code &, uint64_t bytes_written)>; + +using ConsumerContentObjectCallback = + std::function<void(ConsumerSocket &, const core::ContentObject &)>; + +using ConsumerContentObjectVerificationCallback = + std::function<bool(ConsumerSocket &, const core::ContentObject &)>; + +using ConsumerManifestCallback = + std::function<void(ConsumerSocket &, const core::ContentObjectManifest &)>; + +using ProducerContentObjectCallback = + std::function<void(ProducerSocket &, core::ContentObject &)>; + +using ProducerInterestCallback = + std::function<void(ProducerSocket &, const core::Interest &)>; + +using ProducerInterestCallback = + std::function<void(ProducerSocket &, const core::Interest &)>; + +using namespace protocol; + +template <typename PortalType> +class Socket { + static_assert(std::is_same<PortalType, HicnForwarderPortal>::value +#ifdef __linux__ +#ifndef __ANDROID__ + || std::is_same<PortalType, RawSocketPortal>::value +#ifdef __vpp__ + || std::is_same<PortalType, VPPForwarderPortal>::value +#endif +#endif + , +#else + , + +#endif + "This class is not allowed as Portal"); + + public: + using Portal = PortalType; + + virtual asio::io_service &getIoService() = 0; + + virtual void connect() = 0; + + virtual int setSocketOption(int socket_option_key, + uint32_t socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + double socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + bool socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + core::Name socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + std::list<Prefix> socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + core::HashAlgorithm socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const utils::Identity &socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + double &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + bool &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + core::Name &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::list<Prefix> &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::shared_ptr<Portal> &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + core::HashAlgorithm &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) = 0; + + protected: + virtual ~Socket(){}; + + protected: + std::string output_interface_; +}; + +} // namespace interface + +} // namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_consumer.cc b/libtransport/src/hicn/transport/interfaces/socket_consumer.cc new file mode 100755 index 000000000..8109d0e99 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_consumer.cc @@ -0,0 +1,735 @@ +/* + * 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> + +namespace transport { + +namespace interface { + +ConsumerSocket::ConsumerSocket(int protocol) + : ConsumerSocket(protocol, internal_io_service_) {} + +ConsumerSocket::ConsumerSocket(int protocol, asio::io_service &io_service) + : io_service_(io_service), + portal_(std::make_shared<Portal>(io_service_)), + async_downloader_(), + interest_lifetime_(default_values::interest_lifetime), + min_window_size_(default_values::min_window_size), + max_window_size_(default_values::max_window_size), + current_window_size_(-1), + max_retransmissions_( + default_values::transport_protocol_max_retransmissions), + /****** RAAQM Parameters ******/ + minimum_drop_probability_(default_values::minimum_drop_probability), + sample_number_(default_values::sample_number), + gamma_(default_values::gamma_value), + beta_(default_values::beta_value), + drop_factor_(default_values::drop_factor), + /****** END RAAQM Parameters ******/ + rate_estimation_alpha_(default_values::rate_alpha), + rate_estimation_observer_(nullptr), + rate_estimation_choice_(0), + is_async_(false), + verify_signature_(false), + content_buffer_(nullptr), + 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), + on_content_object_(VOID_HANDLER), + on_manifest_(VOID_HANDLER), + on_payload_retrieved_(VOID_HANDLER), + virtual_download_(false), + rtt_stats_(false), + timer_(portal_->getIoService()), + timer_interval_milliseconds_(0) { + switch (protocol) { + case TransportProtocolAlgorithms::VEGAS: + transport_protocol_ = std::make_shared<VegasTransportProtocol>(this); + break; + case TransportProtocolAlgorithms::CBR: + transport_protocol_ = std::make_shared<CbrTransportProtocol>(this); + break; + case TransportProtocolAlgorithms::RTC: + transport_protocol_ = std::make_shared<RTCTransportProtocol>(this); + break; + case TransportProtocolAlgorithms::RAAQM: + default: + transport_protocol_ = std::make_shared<RaaqmTransportProtocol>(this); + break; + } +} + +ConsumerSocket::~ConsumerSocket() { + stop(); + + async_downloader_.stop(); + + transport_protocol_.reset(); + portal_.reset(); +} + +void ConsumerSocket::connect() { portal_->connect(); } + +int ConsumerSocket::consume(const Name &name, + utils::SharableVector<uint8_t> &receive_buffer) { + if (transport_protocol_->isRunning()) { + return CONSUMER_BUSY; + } + + content_buffer_ = receive_buffer.shared_from_this(); + + network_name_ = name; + network_name_.setSuffix(0); + is_async_ = false; + + transport_protocol_->start(receive_buffer); + + return CONSUMER_READY; +} + +int ConsumerSocket::asyncConsume( + const Name &name, + std::shared_ptr<utils::SharableVector<uint8_t>> receive_buffer) { + // XXX Try to move the name here, instead of copying it!! + if (!async_downloader_.stopped()) { + async_downloader_.add([this, receive_buffer, name]() { + network_name_ = std::move(name); + network_name_.setSuffix(0); + is_async_ = true; + transport_protocol_->start(*receive_buffer); + }); + } + + return CONSUMER_READY; +} + +void ConsumerSocket::asyncSendInterest(Interest::Ptr &&interest, + Portal::ConsumerCallback *callback) { + if (!async_downloader_.stopped()) { + // TODO Workaround, to be fixed! + auto i = interest.release(); + async_downloader_.add([this, i, callback]() mutable { + Interest::Ptr _interest(i); + portal_->setConsumerCallback(callback); + portal_->sendInterest(std::move(_interest)); + portal_->runEventsLoop(); + }); + } +} + +void ConsumerSocket::stop() { + if (transport_protocol_->isRunning()) { + transport_protocol_->stop(); + } + + //is_running_ = false; +} + +void ConsumerSocket::resume() { + if(!transport_protocol_->isRunning()){ + transport_protocol_->resume(); + } +} + +asio::io_service &ConsumerSocket::getIoService() { + return portal_->getIoService(); +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + double socket_option_value) { + switch (socket_option_key) { + case MIN_WINDOW_SIZE: + min_window_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case MAX_WINDOW_SIZE: + max_window_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case CURRENT_WINDOW_SIZE: + current_window_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GAMMA_VALUE: + gamma_ = socket_option_value; + return SOCKET_OPTION_SET; + + case BETA_VALUE: + beta_ = socket_option_value; + return SOCKET_OPTION_SET; + + case DROP_FACTOR: + drop_factor_ = socket_option_value; + return SOCKET_OPTION_SET; + + case MINIMUM_DROP_PROBABILITY: + minimum_drop_probability_ = socket_option_value; + return SOCKET_OPTION_SET; + + case RATE_ESTIMATION_ALPHA: + if (socket_option_value >= 0 && socket_option_value < 1) { + rate_estimation_alpha_ = socket_option_value; + } else { + rate_estimation_alpha_ = ALPHA; + } + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + uint32_t socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + input_buffer_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + output_buffer_size_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::MAX_INTEREST_RETX: + max_retransmissions_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::INTEREST_LIFETIME: + interest_lifetime_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_RETRANSMISSION: + if (socket_option_value == VOID_HANDLER) { + on_interest_retransmission_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::INTEREST_EXPIRED: + if (socket_option_value == VOID_HANDLER) { + on_interest_timeout_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::INTEREST_SATISFIED: + if (socket_option_value == VOID_HANDLER) { + on_interest_satisfied_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::INTEREST_OUTPUT: + if (socket_option_value == VOID_HANDLER) { + on_interest_output_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT: + if (socket_option_value == VOID_HANDLER) { + on_content_object_input_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: + if (socket_option_value == VOID_HANDLER) { + on_content_object_verification_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ConsumerCallbacksOptions::CONTENT_RETRIEVED: + if (socket_option_value == VOID_HANDLER) { + on_payload_retrieved_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case RateEstimationOptions::RATE_ESTIMATION_BATCH_PARAMETER: + if (socket_option_value > 0) { + rate_estimation_batching_parameter_ = socket_option_value; + } else { + rate_estimation_batching_parameter_ = BATCH; + } + return SOCKET_OPTION_SET; + + case RateEstimationOptions::RATE_ESTIMATION_CHOICE: + if (socket_option_value > 0) { + rate_estimation_choice_ = socket_option_value; + } else { + rate_estimation_choice_ = RATE_CHOICE; + } + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::TIMER_INTERVAL: + timer_interval_milliseconds_ = socket_option_value; + TRANSPORT_LOGD("Ok set %d", timer_interval_milliseconds_); + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + bool socket_option_value) { + switch (socket_option_key) { + case OtherOptions::VIRTUAL_DOWNLOAD: + virtual_download_ = socket_option_value; + return SOCKET_OPTION_SET; + + case RaaqmTransportOptions::RTT_STATS: + rtt_stats_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::VERIFY_SIGNATURE: + verify_signature_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + Name socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + network_name_ = socket_option_value; + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + std::list<Prefix> socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerContentObjectCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT: + on_content_object_input_ = socket_option_value; + ; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ProducerContentObjectCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: + on_content_object_verification_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerInterestCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::INTEREST_RETRANSMISSION: + on_interest_retransmission_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_OUTPUT: + on_interest_output_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_EXPIRED: + on_interest_timeout_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ConsumerCallbacksOptions::INTEREST_SATISFIED: + on_interest_satisfied_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ProducerInterestCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerContentCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_RETRIEVED: + on_payload_retrieved_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ConsumerManifestCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::MANIFEST_INPUT: + on_manifest_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, ProducerContentCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) { + if (socket_option_key == RateEstimationOptions::RATE_ESTIMATION_OBSERVER) { + rate_estimation_observer_ = socket_option_value; + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption( + int socket_option_key, const utils::Identity &socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + const std::string &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::CERTIFICATE: + key_id_ = verifier_.addKeyFromCertificate(socket_option_value); + + if (key_id_ != nullptr) { + return SOCKET_OPTION_SET; + } + + break; + + case DataLinkOptions::OUTPUT_INTERFACE: + output_interface_ = socket_option_value; + portal_->setOutputInterface(output_interface_); + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::TIMER_EXPIRES: + on_timer_expires_ = socket_option_value; + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + double &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::MIN_WINDOW_SIZE: + socket_option_value = min_window_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::MAX_WINDOW_SIZE: + socket_option_value = max_window_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::CURRENT_WINDOW_SIZE: + socket_option_value = current_window_size_; + return SOCKET_OPTION_GET; + + // RAAQM parameters + + case RaaqmTransportOptions::GAMMA_VALUE: + socket_option_value = gamma_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::BETA_VALUE: + socket_option_value = beta_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::DROP_FACTOR: + socket_option_value = drop_factor_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::MINIMUM_DROP_PROBABILITY: + socket_option_value = minimum_drop_probability_; + return SOCKET_OPTION_GET; + + case RateEstimationOptions::RATE_ESTIMATION_ALPHA: + socket_option_value = rate_estimation_alpha_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + uint32_t &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + socket_option_value = input_buffer_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + socket_option_value = output_buffer_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::MAX_INTEREST_RETX: + socket_option_value = max_retransmissions_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::INTEREST_LIFETIME: + socket_option_value = interest_lifetime_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::SAMPLE_NUMBER: + socket_option_value = sample_number_; + return SOCKET_OPTION_GET; + + case RateEstimationOptions::RATE_ESTIMATION_BATCH_PARAMETER: + socket_option_value = rate_estimation_batching_parameter_; + return SOCKET_OPTION_GET; + + case RateEstimationOptions::RATE_ESTIMATION_CHOICE: + socket_option_value = rate_estimation_choice_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + bool &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::ASYNC_MODE: + socket_option_value = is_async_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::RUNNING: + socket_option_value = transport_protocol_->isRunning(); + return SOCKET_OPTION_GET; + + case OtherOptions::VIRTUAL_DOWNLOAD: + socket_option_value = virtual_download_; + return SOCKET_OPTION_GET; + + case RaaqmTransportOptions::RTT_STATS: + socket_option_value = rtt_stats_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::VERIFY_SIGNATURE: + socket_option_value = verify_signature_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + Name &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + socket_option_value = network_name_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + std::list<Prefix> &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerContentObjectCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_INPUT: + socket_option_value = on_content_object_input_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ProducerContentObjectCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY: + socket_option_value = on_content_object_verification_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::INTEREST_RETRANSMISSION: + socket_option_value = on_interest_retransmission_; + return SOCKET_OPTION_GET; + + case ConsumerCallbacksOptions::INTEREST_OUTPUT: + socket_option_value = on_interest_output_; + return SOCKET_OPTION_GET; + + case ConsumerCallbacksOptions::INTEREST_EXPIRED: + socket_option_value = on_interest_timeout_; + return SOCKET_OPTION_GET; + + case ConsumerCallbacksOptions::INTEREST_SATISFIED: + socket_option_value = on_interest_satisfied_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerContentCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::CONTENT_RETRIEVED: + socket_option_value = on_payload_retrieved_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::MANIFEST_INPUT: + socket_option_value = on_manifest_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, std::shared_ptr<Portal> &socket_option_value) { + switch (socket_option_key) { + case PORTAL: + socket_option_value = portal_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) { + if (socket_option_key == RATE_ESTIMATION_OBSERVER) { + *socket_option_value = (rate_estimation_observer_); + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ProducerContentCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption(int socket_option_key, + std::string &socket_option_value) { + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + socket_option_value = output_interface_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ConsumerSocket::getSocketOption( + int socket_option_key, ConsumerTimerCallback &socket_option_value) { + switch (socket_option_key) { + case ConsumerCallbacksOptions::TIMER_EXPIRES: + socket_option_value = on_timer_expires_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_consumer.h b/libtransport/src/hicn/transport/interfaces/socket_consumer.h new file mode 100755 index 000000000..9e309aae8 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_consumer.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/interfaces/socket.h> + +#include <hicn/transport/protocols/cbr.h> +#include <hicn/transport/protocols/protocol.h> +#include <hicn/transport/protocols/raaqm.h> +#include <hicn/transport/protocols/rtc.h> +#include <hicn/transport/protocols/vegas.h> + +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/sharable_vector.h> + +#define CONSUMER_READY 0 +#define CONSUMER_BUSY 1 + +namespace transport { + +namespace interface { + +class ConsumerSocket : public BaseSocket { + friend class protocol::TransportProtocol; + friend class protocol::VegasTransportProtocol; + friend class protocol::RaaqmTransportProtocol; + friend class protocol::CbrTransportProtocol; + + public: + explicit ConsumerSocket(int protocol); + explicit ConsumerSocket(int protocol, asio::io_service &io_service); + + ~ConsumerSocket(); + + void connect() override; + + int consume(const Name &name, utils::SharableVector<uint8_t> &receive_buffer); + + int asyncConsume( + const Name &name, + std::shared_ptr<utils::SharableVector<uint8_t>> receive_buffer); + + void asyncSendInterest(Interest::Ptr &&interest, + Portal::ConsumerCallback *callback); + + void stop(); + + void resume(); + + asio::io_service &getIoService() override; + + int setSocketOption(int socket_option_key, + uint32_t socket_option_value) override; + + int setSocketOption(int socket_option_key, + double socket_option_value) override; + + int setSocketOption(int socket_option_key, bool socket_option_value) override; + + int setSocketOption(int socket_option_key, Name socket_option_value) override; + + int setSocketOption(int socket_option_key, + std::list<Prefix> socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) override; + + int setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) override; + + int setSocketOption(int socket_option_key, + utils::CryptoSuite crypto_suite) override; + + int setSocketOption(int socket_option_key, + const utils::Identity &crypto_suite) override; + + int setSocketOption(int socket_option_key, + const std::string &socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) override; + + int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) override; + + int getSocketOption(int socket_option_key, + double &socket_option_value) override; + + int getSocketOption(int socket_option_key, + bool &socket_option_value) override; + + int getSocketOption(int socket_option_key, + Name &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::list<Prefix> &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerInterestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerInterestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerManifestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::shared_ptr<Portal> &socket_option_value) override; + + int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) override; + + int getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) override; + + int getSocketOption(int socket_option_key, + utils::CryptoSuite &crypto_suite) override; + + int getSocketOption(int socket_option_key, + utils::Identity &crypto_suite) override; + + int getSocketOption(int socket_option_key, + std::string &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) override; + + protected: + std::shared_ptr<TransportProtocol> transport_protocol_; + + private: + // context inner state variables + asio::io_service internal_io_service_; + asio::io_service &io_service_; + + std::shared_ptr<Portal> portal_; + + utils::EventThread async_downloader_; + + Name network_name_; + + int interest_lifetime_; + + double min_window_size_; + double max_window_size_; + double current_window_size_; + uint32_t max_retransmissions_; + size_t output_buffer_size_; + size_t input_buffer_size_; + + // RAAQM Parameters + + double minimum_drop_probability_; + unsigned int sample_number_; + double gamma_; + double beta_; + double drop_factor_; + + // Rate estimation parameters + double rate_estimation_alpha_; + IcnObserver *rate_estimation_observer_; + int rate_estimation_batching_parameter_; + int rate_estimation_choice_; + + bool is_async_; + + utils::Verifier verifier_; + PARCKeyId *key_id_; + bool verify_signature_; + + std::shared_ptr<utils::SharableVector<uint8_t>> content_buffer_; + + ConsumerInterestCallback on_interest_retransmission_; + ConsumerInterestCallback on_interest_output_; + ConsumerInterestCallback on_interest_timeout_; + ConsumerInterestCallback on_interest_satisfied_; + + ConsumerContentObjectCallback on_content_object_input_; + ConsumerContentObjectVerificationCallback on_content_object_verification_; + + ConsumerContentObjectCallback on_content_object_; + ConsumerManifestCallback on_manifest_; + + ConsumerContentCallback on_payload_retrieved_; + + ConsumerTimerCallback on_timer_expires_; + + // Virtual download for traffic generator + + bool virtual_download_; + bool rtt_stats_; + + Time t0_; + Time t1_; + asio::steady_timer timer_; + uint32_t timer_interval_milliseconds_; +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_options_default_values.h b/libtransport/src/hicn/transport/interfaces/socket_options_default_values.h new file mode 100755 index 000000000..5fae1c484 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_options_default_values.h @@ -0,0 +1,68 @@ +/* + * 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 <chrono> +#include <cstdint> + +namespace transport { + +namespace interface { + +namespace default_values { + +const uint32_t interest_lifetime = 1001; // milliseconds +const uint32_t content_object_expiry_time = + 0xffff; // milliseconds -> 50 seconds +const uint32_t content_object_packet_size = 1500; // The ethernet MTU +const uint32_t producer_socket_input_buffer_size = 150000; // Interests +const uint32_t producer_socket_output_buffer_size = 150000; // Content Object +const uint32_t log_2_default_buffer_size = 12; +const uint32_t signature_size = 260; // bytes +const uint32_t key_locator_size = 60; // bytes +const uint32_t limit_guard = 80; // bytes +const uint32_t min_window_size = 1; // Interests +const uint32_t max_window_size = 128000; // Interests +const uint32_t digest_size = 34; // bytes +const uint32_t max_out_of_order_segments = 3; // content object +const uint32_t never_expire_time = 0x0000ffff << 0x0f; + +// RAAQM +const int sample_number = 30; +const double gamma_value = 1; +const double beta_value = 0.8; +const double drop_factor = 0.2; +const double minimum_drop_probability = 0.00001; +const int path_id = 0; +const double rate_alpha = 0.8; + +// Vegas +const double alpha = 1 / 8; +const double beta = 1 / 4; +const uint16_t k = 4; +const std::chrono::milliseconds clock_granularity = + std::chrono::milliseconds(100); + +// maximum allowed values +const uint32_t transport_protocol_min_retransmissions = 0; +const uint32_t transport_protocol_max_retransmissions = 128; +const uint32_t max_content_object_size = 8096; + +} // namespace default_values + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_options_keys.h b/libtransport/src/hicn/transport/interfaces/socket_options_keys.h new file mode 100755 index 000000000..1afad2b48 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_options_keys.h @@ -0,0 +1,108 @@ +/* + * 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 + +namespace transport { + +namespace interface { + +typedef enum { + RAAQM = 0, + VEGAS = 1, + CBR = 3, + RTC = 4, +} TransportProtocolAlgorithms; + +typedef enum { + INPUT_BUFFER_SIZE = 101, + OUTPUT_BUFFER_SIZE = 102, + NETWORK_NAME = 103, + NAME_SUFFIX = 104, + MAX_INTEREST_RETX = 105, + DATA_PACKET_SIZE = 106, + INTEREST_LIFETIME = 107, + CONTENT_OBJECT_EXPIRY_TIME = 108, + KEY_LOCATOR = 110, + SIGNATURE_TYPE = 111, + MIN_WINDOW_SIZE = 112, + MAX_WINDOW_SIZE = 113, + CURRENT_WINDOW_SIZE = 114, + ASYNC_MODE = 115, + MAKE_MANIFEST = 116, + PORTAL = 117, + RUNNING = 118, + HASH_ALGORITHM = 119, + CRYPTO_SUITE = 120, + IDENTITY = 121, + CERTIFICATE = 122, + VERIFY_SIGNATURE = 123, + TIMER_INTERVAL = 124 +} GeneralTransportOptions; + +typedef enum { + SAMPLE_NUMBER = 201, + GAMMA_VALUE = 202, + BETA_VALUE = 203, + DROP_FACTOR = 204, + MINIMUM_DROP_PROBABILITY = 205, + PATH_ID = 206, + RTT_STATS = 207, +} RaaqmTransportOptions; + +typedef enum { + RATE_ESTIMATION_ALPHA = 301, + RATE_ESTIMATION_OBSERVER = 302, + RATE_ESTIMATION_BATCH_PARAMETER = 303, + RATE_ESTIMATION_CHOICE = 304, +} RateEstimationOptions; + +typedef enum { + INTEREST_OUTPUT = 401, + INTEREST_RETRANSMISSION = 402, + INTEREST_EXPIRED = 403, + INTEREST_SATISFIED = 404, + CONTENT_OBJECT_INPUT = 411, + MANIFEST_INPUT = 412, + CONTENT_OBJECT_TO_VERIFY = 413, + CONTENT_RETRIEVED = 414, + TIMER_EXPIRES = 415 +} ConsumerCallbacksOptions; + +typedef enum { + INTEREST_INPUT = 501, + INTEREST_DROP = 502, + INTEREST_PASS = 503, + CACHE_HIT = 506, + CACHE_MISS = 508, + NEW_CONTENT_OBJECT = 509, + CONTENT_OBJECT_SIGN = 513, + CONTENT_OBJECT_READY = 510, + CONTENT_OBJECT_OUTPUT = 511, + CONTENT_PRODUCED = 512 +} ProducerCallbacksOptions; + +typedef enum { OUTPUT_INTERFACE = 601 } DataLinkOptions; + +typedef enum { VIRTUAL_DOWNLOAD = 601, USE_CFG_FILE = 603 } OtherOptions; + +typedef enum { + SHA_256 = 701, + RSA_256 = 702, +} SignatureType; + +} // namespace interface + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/interfaces/socket_producer.cc b/libtransport/src/hicn/transport/interfaces/socket_producer.cc new file mode 100755 index 000000000..69adc2b3f --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_producer.cc @@ -0,0 +1,948 @@ +/* + * 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_producer.h> + +namespace transport { + +namespace interface { + +typedef std::chrono::time_point<std::chrono::steady_clock> Time; +typedef std::chrono::microseconds TimeDuration; + +ProducerSocket::ProducerSocket() : ProducerSocket(internal_io_service_) {} + +ProducerSocket::ProducerSocket(asio::io_service &io_service) + : io_service_(io_service), + portal_(std::make_shared<Portal>(io_service_)), + data_packet_size_(default_values::content_object_packet_size), + content_object_expiry_time_(default_values::content_object_expiry_time), + output_buffer_(default_values::producer_socket_output_buffer_size), + async_thread_(), + registration_status_(REGISTRATION_NOT_ATTEMPTED), + making_manifest_(false), + signature_type_(SHA_256), + hash_algorithm_(HashAlgorithm::SHA_256), + input_buffer_capacity_(default_values::producer_socket_input_buffer_size), + input_buffer_size_(0), + processing_thread_stop_(false), + listening_thread_stop_(false), + on_interest_input_(VOID_HANDLER), + on_interest_dropped_input_buffer_(VOID_HANDLER), + on_interest_inserted_input_buffer_(VOID_HANDLER), + on_interest_satisfied_output_buffer_(VOID_HANDLER), + on_interest_process_(VOID_HANDLER), + on_new_segment_(VOID_HANDLER), + on_content_object_to_sign_(VOID_HANDLER), + on_content_object_in_output_buffer_(VOID_HANDLER), + on_content_object_output_(VOID_HANDLER), + on_content_object_evicted_from_output_buffer_(VOID_HANDLER), + on_content_produced_(VOID_HANDLER) { + listening_thread_stop_ = false; +} + +ProducerSocket::~ProducerSocket() { + TRANSPORT_LOGI("Destroying the ProducerSocket"); + processing_thread_stop_ = true; + portal_->stopEventsLoop(); + + if (processing_thread_.joinable()) { + processing_thread_.join(); + } + + if (listening_thread_.joinable()) { + listening_thread_.join(); + } +} + +void ProducerSocket::connect() { + portal_->connect(false); + listening_thread_ = std::thread(std::bind(&ProducerSocket::listen, this)); +} + +void ProducerSocket::serveForever() { + if (listening_thread_.joinable()) { + listening_thread_.join(); + } +} + +void ProducerSocket::stop() { + TRANSPORT_LOGI("Calling stop for ProducerSocket"); + portal_->killConnection(); + portal_->stopEventsLoop(); +} + +void ProducerSocket::registerPrefix(const Prefix &producer_namespace) { + served_namespaces_.push_back(producer_namespace); +} + +void ProducerSocket::listen() { + registration_status_ = REGISTRATION_IN_PROGRESS; + bool first = true; + + for (core::Prefix &producer_namespace : served_namespaces_) { + if (first) { + core::BindConfig bind_config(producer_namespace, 1000); + portal_->bind(bind_config); + portal_->setProducerCallback(this); + first = !first; + } else { + portal_->registerRoute(producer_namespace); + } + } + + portal_->runEventsLoop(); +} + +void ProducerSocket::passContentObjectToCallbacks( + const std::shared_ptr<ContentObject> &content_object) { + if (content_object) { + if (on_new_segment_ != VOID_HANDLER) { + on_new_segment_(*this, *content_object); + } + + if (on_content_object_to_sign_ != VOID_HANDLER) { + on_content_object_to_sign_(*this, *content_object); + } + + if (on_content_object_in_output_buffer_ != VOID_HANDLER) { + on_content_object_in_output_buffer_(*this, *content_object); + } + + output_buffer_.insert(content_object); + + if (on_content_object_output_ != VOID_HANDLER) { + on_content_object_output_(*this, *content_object); + } + +#ifndef PUSH_API + std::unordered_map<Name, std::shared_ptr<const Interest>>::iterator it; + + { + std::lock_guard<std::mutex> lock(pending_interests_mtx_); + it = pending_interests_.find(content_object->getName()); + } + + if (it != pending_interests_.end()) { + content_object->setLocator(it->second->getLocator()); + portal_->sendContentObject(*content_object); + std::lock_guard<std::mutex> lock(pending_interests_mtx_); + pending_interests_.erase(it); + } +#else + portal_->sendContentObject(*content_object); +#endif + } +} + +void ProducerSocket::produce(ContentObject &content_object) { + if (on_content_object_in_output_buffer_ != VOID_HANDLER) { + on_content_object_in_output_buffer_(*this, content_object); + } + + output_buffer_.insert(std::static_pointer_cast<ContentObject>( + content_object.shared_from_this())); + + if (on_content_object_output_ != VOID_HANDLER) { + on_content_object_output_(*this, content_object); + } + +#ifndef PUSH_API + std::unordered_map<Name, std::shared_ptr<const Interest>>::iterator it; + + { + std::lock_guard<std::mutex> lock(pending_interests_mtx_); + it = pending_interests_.find(content_object.getName()); + } + + if (it != pending_interests_.end()) { + content_object.setLocator(it->second->getLocator()); + portal_->sendContentObject(content_object); + std::lock_guard<std::mutex> lock(pending_interests_mtx_); + pending_interests_.erase(it); + } +#else + portal_->sendContentObject(content_object); +#endif +} + +uint32_t ProducerSocket::produce(Name content_name, const uint8_t *buf, + size_t buffer_size, bool is_last, + uint32_t start_offset) { + if (TRANSPORT_EXPECT_FALSE(buffer_size == 0)) { + return 0; + } + + const std::size_t hash_size = 32; + + int bytes_segmented = 0; + std::size_t header_size; + std::size_t manifest_header_size = 0; + std::size_t signature_length = 0; + std::uint32_t final_block_number = 0; + + uint64_t free_space_for_content = 0; + + core::Packet::Format format; + + uint32_t current_segment = start_offset; + std::shared_ptr<ContentObjectManifest> manifest; + bool is_last_manifest = false; + std::unique_ptr<utils::CryptoHash> zero_hash; + + // TODO Manifest may still be used for indexing + if (making_manifest_ && !identity_) { + throw errors::RuntimeException( + "Making manifests without setting producer identity. Aborting."); + } + + core::Packet::Format hf_format = core::Packet::Format::HF_UNSPEC; + core::Packet::Format hf_format_ah = core::Packet::Format::HF_UNSPEC; + if (content_name.getType() == HNT_CONTIGUOUS_V4 || + content_name.getType() == HNT_IOV_V4) { + hf_format = core::Packet::Format::HF_INET_TCP; + hf_format_ah = core::Packet::Format::HF_INET_TCP_AH; + } else if (content_name.getType() == HNT_CONTIGUOUS_V6 || + content_name.getType() == HNT_IOV_V6) { + hf_format = core::Packet::Format::HF_INET6_TCP; + hf_format_ah = core::Packet::Format::HF_INET6_TCP_AH; + } else { + throw errors::RuntimeException("Unknown name format."); + } + + format = hf_format; + if (making_manifest_) { + format = hf_format; + manifest_header_size = core::Packet::getHeaderSizeFromFormat( + hf_format_ah, identity_->getSignatureLength()); + } else if (identity_) { + format = hf_format_ah; + signature_length = identity_->getSignatureLength(); + } + + header_size = core::Packet::getHeaderSizeFromFormat(format, signature_length); + + free_space_for_content = data_packet_size_ - header_size; + + uint32_t number_of_segments = + uint32_t(std::ceil(double(buffer_size) / double(free_space_for_content))); + + if (free_space_for_content * number_of_segments < buffer_size) { + number_of_segments++; + } + + if (making_manifest_) { + auto segment_in_manifest = static_cast<float>( + std::floor(double(data_packet_size_ - manifest_header_size - + ContentObjectManifest::getManifestHeaderSize()) / + (4.0 + 32.0)) - + 1.0); + auto number_of_manifests = static_cast<uint32_t>( + std::ceil(float(number_of_segments) / segment_in_manifest)); + final_block_number = number_of_segments + number_of_manifests - 1; + + manifest.reset(ContentObjectManifest::createManifest( + content_name.setSuffix(current_segment++), + core::ManifestVersion::VERSION_1, core::ManifestType::INLINE_MANIFEST, + hash_algorithm_, is_last_manifest, content_name, + core::NextSegmentCalculationStrategy::INCREMENTAL, + identity_->getSignatureLength())); + manifest->setLifetime(content_object_expiry_time_); + + if (is_last) { + manifest->setFinalBlockNumber(final_block_number); + } else { + manifest->setFinalBlockNumber(std::numeric_limits<uint32_t>::max()); + } + + uint8_t hash[hash_size]; + std::memset(hash, 0, hash_size); + zero_hash = std::make_unique<utils::CryptoHash>( + hash, hash_size, static_cast<utils::CryptoHashType>(hash_algorithm_)); + } + + for (unsigned int packaged_segments = 0; + packaged_segments < number_of_segments; packaged_segments++) { + if (making_manifest_) { + if (manifest->estimateManifestSize(2) > + data_packet_size_ - manifest_header_size) { + // Add next manifest + manifest->addSuffixHash(current_segment, *zero_hash); + + // Send the current manifest + manifest->encode(); + + identity_->getSigner().sign(*manifest); + + passContentObjectToCallbacks(manifest); + + // Create new manifest. The reference to the last manifest has been + // acquired in the passContentObjectToCallbacks function, so we can + // safely release this reference + manifest.reset(ContentObjectManifest::createManifest( + content_name.setSuffix(current_segment), + core::ManifestVersion::VERSION_1, + core::ManifestType::INLINE_MANIFEST, hash_algorithm_, + is_last_manifest, content_name, + core::NextSegmentCalculationStrategy::INCREMENTAL, + identity_->getSignatureLength())); + manifest->setLifetime(content_object_expiry_time_); + if (is_last) { + manifest->setFinalBlockNumber(final_block_number); + } else { + manifest->setFinalBlockNumber(std::numeric_limits<uint32_t>::max()); + } + current_segment++; + } + } + + auto content_object = std::make_shared<ContentObject>( + content_name.setSuffix(current_segment), format); + content_object->setLifetime(content_object_expiry_time_); + + if (!making_manifest_ && identity_) { + content_object->setSignatureSize(signature_length); + } + + if (packaged_segments == number_of_segments - 1) { + content_object->appendPayload(&buf[bytes_segmented], + buffer_size - bytes_segmented); + bytes_segmented += buffer_size - bytes_segmented; + + if (is_last && making_manifest_) { + is_last_manifest = true; + } else if (is_last) { + content_object->setRst(); + } + + } else { + content_object->appendPayload(&buf[bytes_segmented], + free_space_for_content); + bytes_segmented += free_space_for_content; + } + + if (making_manifest_) { + using namespace std::chrono_literals; + utils::CryptoHash hash = content_object->computeDigest(hash_algorithm_); + manifest->addSuffixHash(current_segment, hash); + } else if (identity_) { + identity_->getSigner().sign(*content_object); + } + + current_segment++; + passContentObjectToCallbacks(content_object); + } + + if (making_manifest_) { + if (is_last_manifest) { + manifest->setFinalManifest(is_last_manifest); + } + manifest->encode(); + // Time t0 = std::chrono::steady_clock::now(); + identity_->getSigner().sign(*manifest); + passContentObjectToCallbacks(manifest); + } + + if (on_content_produced_ != VOID_HANDLER) { + on_content_produced_(*this, std::make_error_code(std::errc(0)), + buffer_size); + } + + return current_segment; +} + +void ProducerSocket::asyncProduce(ContentObject &content_object) { + if (!async_thread_.stopped()) { + // async_thread_.add(std::bind(&ProducerSocket::produce, this, + // content_object)); + } +} + +// void ProducerSocket::asyncProduce(const Name &suffix, +// const uint8_t *buf, +// size_t buffer_size, +// AsyncProduceCallback && handler) { +// if (!async_thread_.stopped()) { +// async_thread_.add([this, buffer = buf, size = buffer_size, cb = +// std::move(handler)] () { +// uint64_t bytes_written = produce(suff, buffer, size, 0, false); +// auto ec = std::make_errc(0); +// cb(*this, ec, bytes_written); +// }); +// } +// } + +void ProducerSocket::asyncProduce(const Name &suffix, const uint8_t *buf, + size_t buffer_size) { + if (!async_thread_.stopped()) { + async_thread_.add( + [this, suff = suffix, buffer = buf, size = buffer_size]() { + produce(suff, buffer, size, true); + }); + } +} + +void ProducerSocket::asyncProduce( + const Name &suffix, utils::SharableVector<uint8_t> &&output_buffer) { + if (!async_thread_.stopped()) { + async_thread_.add( + [this, suff = suffix, buffer = std::move(output_buffer)]() { + TRANSPORT_LOGI("FOR REAL!!!!!! --> Producing content with name %s", + suff.toString().c_str()); + produce(suff, &buffer[0], buffer.size(), true); + }); + } +} + +void ProducerSocket::onInterest(const Interest &interest) { + if (on_interest_input_ != VOID_HANDLER) { + on_interest_input_(*this, interest); + } + + const std::shared_ptr<ContentObject> content_object = + output_buffer_.find(interest); + + if (content_object) { + if (on_interest_satisfied_output_buffer_ != VOID_HANDLER) { + on_interest_satisfied_output_buffer_(*this, interest); + } + + if (on_content_object_output_ != VOID_HANDLER) { + on_content_object_output_(*this, *content_object); + } + + portal_->sendContentObject(*content_object); + } else { +#ifndef PUSH_API + { + std::lock_guard<std::mutex> lock(pending_interests_mtx_); + pending_interests_[interest.getName()] = + std::static_pointer_cast<const Interest>(interest.shared_from_this()); + } +#endif + + if (on_interest_process_ != VOID_HANDLER) { + // external_io_service_.post([this, &interest] () { + on_interest_process_(*this, interest); + // }); + } + } +} + +asio::io_service &ProducerSocket::getIoService() { return io_service_; } + +int ProducerSocket::setSocketOption(int socket_option_key, + uint32_t socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::DATA_PACKET_SIZE: + if (socket_option_value < default_values::max_content_object_size && + socket_option_value > 0) { + data_packet_size_ = socket_option_value; + return SOCKET_OPTION_SET; + } else { + return SOCKET_OPTION_NOT_SET; + } + + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + if (socket_option_value >= 1) { + input_buffer_capacity_ = socket_option_value; + return SOCKET_OPTION_SET; + } else { + return SOCKET_OPTION_NOT_SET; + } + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + output_buffer_.setLimit(socket_option_value); + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: + content_object_expiry_time_ = socket_option_value; + return SOCKET_OPTION_SET; + + case GeneralTransportOptions::SIGNATURE_TYPE: + if (socket_option_value == SOCKET_OPTION_DEFAULT) { + signature_type_ = SHA_256; + } else { + signature_type_ = socket_option_value; + } + + if (signature_type_ == SHA_256 || signature_type_ == RSA_256) { + signature_size_ = 32; + } + + case ProducerCallbacksOptions::INTEREST_INPUT: + if (socket_option_value == VOID_HANDLER) { + on_interest_input_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::INTEREST_DROP: + if (socket_option_value == VOID_HANDLER) { + on_interest_dropped_input_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::INTEREST_PASS: + if (socket_option_value == VOID_HANDLER) { + on_interest_inserted_input_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CACHE_HIT: + if (socket_option_value == VOID_HANDLER) { + on_interest_satisfied_output_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CACHE_MISS: + if (socket_option_value == VOID_HANDLER) { + on_interest_process_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::NEW_CONTENT_OBJECT: + if (socket_option_value == VOID_HANDLER) { + on_new_segment_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CONTENT_OBJECT_SIGN: + if (socket_option_value == VOID_HANDLER) { + on_content_object_to_sign_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CONTENT_OBJECT_READY: + if (socket_option_value == VOID_HANDLER) { + on_content_object_in_output_buffer_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + case ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT: + if (socket_option_value == VOID_HANDLER) { + on_content_object_output_ = VOID_HANDLER; + return SOCKET_OPTION_SET; + } + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + double socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + bool socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::MAKE_MANIFEST: + making_manifest_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + Name socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + std::list<Prefix> socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + served_namespaces_ = socket_option_value; + default: + return SOCKET_OPTION_NOT_SET; + } + + return SOCKET_OPTION_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ProducerContentObjectCallback socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::NEW_CONTENT_OBJECT: + on_new_segment_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_SIGN: + on_content_object_to_sign_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_READY: + on_content_object_in_output_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT: + on_content_object_output_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ProducerInterestCallback socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::INTEREST_INPUT: + on_interest_input_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::INTEREST_DROP: + on_interest_dropped_input_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::INTEREST_PASS: + on_interest_inserted_input_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CACHE_HIT: + on_interest_satisfied_output_buffer_ = socket_option_value; + return SOCKET_OPTION_SET; + + case ProducerCallbacksOptions::CACHE_MISS: + on_interest_process_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ProducerContentCallback socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::CONTENT_PRODUCED: + on_content_produced_ = socket_option_value; + return SOCKET_OPTION_SET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerContentObjectCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerInterestCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerContentCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, ConsumerManifestCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::HASH_ALGORITHM: + hash_algorithm_ = socket_option_value; + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::CRYPTO_SUITE: + crypto_suite_ = socket_option_value; + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption( + int socket_option_key, const utils::Identity &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::IDENTITY: + identity_.reset(); + identity_ = std::make_unique<utils::Identity>(socket_option_value); + return SOCKET_OPTION_SET; + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::setSocketOption(int socket_option_key, + const std::string &socket_option_value) { + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + output_interface_ = socket_option_value; + portal_->setOutputInterface(output_interface_); + return SOCKET_OPTION_SET; + } + + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::setSocketOption( + int socket_option_key, + interface::ConsumerTimerCallback socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + uint32_t &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::INPUT_BUFFER_SIZE: + socket_option_value = input_buffer_capacity_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::OUTPUT_BUFFER_SIZE: + socket_option_value = output_buffer_.getLimit(); + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::DATA_PACKET_SIZE: + socket_option_value = data_packet_size_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME: + socket_option_value = content_object_expiry_time_; + return SOCKET_OPTION_GET; + + case GeneralTransportOptions::SIGNATURE_TYPE: + socket_option_value = signature_type_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + double &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + bool &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::MAKE_MANIFEST: + socket_option_value = making_manifest_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + Name &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + std::list<Prefix> &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::NETWORK_NAME: + + socket_option_value = served_namespaces_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ProducerContentObjectCallback &socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::NEW_CONTENT_OBJECT: + socket_option_value = on_new_segment_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_SIGN: + socket_option_value = on_content_object_to_sign_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_READY: + socket_option_value = on_content_object_in_output_buffer_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::CONTENT_OBJECT_OUTPUT: + socket_option_value = on_content_object_output_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ProducerContentCallback &socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::CONTENT_PRODUCED: + socket_option_value = on_content_produced_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) { + switch (socket_option_key) { + case ProducerCallbacksOptions::INTEREST_INPUT: + socket_option_value = on_interest_input_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::INTEREST_DROP: + socket_option_value = on_interest_dropped_input_buffer_; + return SOCKET_OPTION_GET; + + case ProducerCallbacksOptions::INTEREST_PASS: + socket_option_value = on_interest_inserted_input_buffer_; + return SOCKET_OPTION_GET; + + case CACHE_HIT: + socket_option_value = on_interest_satisfied_output_buffer_; + return SOCKET_OPTION_GET; + + case CACHE_MISS: + socket_option_value = on_interest_process_; + return SOCKET_OPTION_GET; + + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerContentObjectCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerContentCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, std::shared_ptr<Portal> &socket_option_value) { + switch (socket_option_key) { + case PORTAL: + socket_option_value = portal_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) { + return SOCKET_OPTION_NOT_SET; +} + +int ProducerSocket::getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::HASH_ALGORITHM: + socket_option_value = hash_algorithm_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::HASH_ALGORITHM: + socket_option_value = crypto_suite_; + return SOCKET_OPTION_GET; + default: + return SOCKET_OPTION_NOT_GET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) { + switch (socket_option_key) { + case GeneralTransportOptions::IDENTITY: + if (identity_) { + socket_option_value = *identity_; + return SOCKET_OPTION_SET; + } + default: + return SOCKET_OPTION_NOT_SET; + } +} + +int ProducerSocket::getSocketOption(int socket_option_key, + std::string &socket_option_value) { + switch (socket_option_key) { + case DataLinkOptions::OUTPUT_INTERFACE: + socket_option_value = output_interface_; + return SOCKET_OPTION_GET; + } + + return SOCKET_OPTION_NOT_GET; +} + +int ProducerSocket::getSocketOption( + int socket_option_key, + interface::ConsumerTimerCallback &socket_option_value) { + return SOCKET_OPTION_NOT_GET; +} + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/interfaces/socket_producer.h b/libtransport/src/hicn/transport/interfaces/socket_producer.h new file mode 100755 index 000000000..06c47d973 --- /dev/null +++ b/libtransport/src/hicn/transport/interfaces/socket_producer.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/interfaces/socket.h> +#include <hicn/transport/utils/content_store.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <atomic> +#include <cmath> +#include <mutex> +#include <queue> +#include <thread> + +#define PUSH_API 1 + +#define REGISTRATION_NOT_ATTEMPTED 0 +#define REGISTRATION_SUCCESS 1 +#define REGISTRATION_FAILURE 2 +#define REGISTRATION_IN_PROGRESS 3 + +namespace transport { + +namespace interface { + +using namespace core; + +class ProducerSocket : public Socket<BasePortal>, + public BasePortal::ProducerCallback { + public: + explicit ProducerSocket(); + explicit ProducerSocket(asio::io_service &io_service); + + ~ProducerSocket(); + + void connect() override; + + uint32_t produce(Name content_name, const uint8_t *buffer, size_t buffer_size, + bool is_last = true, uint32_t start_offset = 0); + + void produce(ContentObject &content_object); + + void asyncProduce(const Name &suffix, const uint8_t *buf, size_t buffer_size); + + void asyncProduce(const Name &suffix, + utils::SharableVector<uint8_t> &&output_buffer); + + void asyncProduce(ContentObject &content_object); + + void registerPrefix(const Prefix &producer_namespace); + + void serveForever(); + + void stop(); + + asio::io_service &getIoService() override; + + virtual void onInterest(const Interest &interest); + + virtual void onInterest(Interest::Ptr &&interest) override { + onInterest(*interest); + }; + + int setSocketOption(int socket_option_key, + uint32_t socket_option_value) override; + + int setSocketOption(int socket_option_key, + double socket_option_value) override; + + int setSocketOption(int socket_option_key, bool socket_option_value) override; + + int setSocketOption(int socket_option_key, Name socket_option_value) override; + + int setSocketOption(int socket_option_key, + std::list<Prefix> socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) override; + + int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, IcnObserver *obs) override; + + int setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) override; + + int setSocketOption(int socket_option_key, + utils::CryptoSuite socket_option_value) override; + + int setSocketOption(int socket_option_key, + const utils::Identity &socket_option_value) override; + + int setSocketOption(int socket_option_key, + const std::string &socket_option_value) override; + + int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) override; + + int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) override; + + int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) override; + + int getSocketOption(int socket_option_key, + double &socket_option_value) override; + + int getSocketOption(int socket_option_key, + bool &socket_option_value) override; + + int getSocketOption(int socket_option_key, + Name &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::list<Prefix> &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerInterestCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) override; + + int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerInterestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerManifestCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::shared_ptr<Portal> &socket_option_value) override; + + int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) override; + + int getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) override; + + int getSocketOption(int socket_option_key, + utils::CryptoSuite &socket_option_value) override; + + int getSocketOption(int socket_option_key, + utils::Identity &socket_option_value) override; + + int getSocketOption(int socket_option_key, + std::string &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) override; + + int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) override; + + protected: + asio::io_service internal_io_service_; + asio::io_service &io_service_; + std::shared_ptr<Portal> portal_; + std::size_t data_packet_size_; + std::list<Prefix> served_namespaces_; + uint32_t content_object_expiry_time_; + + // buffers + utils::ContentStore output_buffer_; + + private: + utils::EventThread async_thread_; + + int registration_status_; + + bool making_manifest_; + + // map for storing sequence numbers for several calls of the publish function + std::unordered_map<Name, std::unordered_map<int, uint32_t>> seq_number_map_; + + int signature_type_; + int signature_size_; + + HashAlgorithm hash_algorithm_; + utils::CryptoSuite crypto_suite_; + std::unique_ptr<utils::Identity> identity_; + // utils::Signer& signer_; + + // buffers + + std::queue<std::shared_ptr<const Interest>> input_buffer_; + std::atomic_size_t input_buffer_capacity_; + std::atomic_size_t input_buffer_size_; + +#ifndef PUSH_API + std::mutex pending_interests_mtx_; + std::unordered_map<Name, std::shared_ptr<const Interest>> pending_interests_; +#endif + + // threads + std::thread listening_thread_; + std::thread processing_thread_; + volatile bool processing_thread_stop_; + volatile bool listening_thread_stop_; + + // callbacks + protected: + ProducerInterestCallback on_interest_input_; + ProducerInterestCallback on_interest_dropped_input_buffer_; + ProducerInterestCallback on_interest_inserted_input_buffer_; + ProducerInterestCallback on_interest_satisfied_output_buffer_; + ProducerInterestCallback on_interest_process_; + + ProducerContentObjectCallback on_new_segment_; + ProducerContentObjectCallback on_content_object_to_sign_; + ProducerContentObjectCallback on_content_object_in_output_buffer_; + ProducerContentObjectCallback on_content_object_output_; + ProducerContentObjectCallback on_content_object_evicted_from_output_buffer_; + + ProducerContentCallback on_content_produced_; + + private: + void listen(); + + void passContentObjectToCallbacks( + const std::shared_ptr<ContentObject> &content_object); +}; + +} // namespace interface + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/portability/CMakeLists.txt b/libtransport/src/hicn/transport/portability/CMakeLists.txt new file mode 100755 index 000000000..eee973c2d --- /dev/null +++ b/libtransport/src/hicn/transport/portability/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/c_portability.h + ${CMAKE_CURRENT_SOURCE_DIR}/portability.h +) + +list(APPEND SOURCE_FILES + "" +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/portability/c_portability.h b/libtransport/src/hicn/transport/portability/c_portability.h new file mode 100755 index 000000000..71e976a81 --- /dev/null +++ b/libtransport/src/hicn/transport/portability/c_portability.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// noinline +#ifdef _MSC_VER +#define TRANSPORT_NOINLINE __declspec(noinline) +#elif defined(__clang__) || defined(__GNUC__) +#define TRANSPORT_NOINLINE __attribute__((__noinline__)) +#else +#define TRANSPORT_NOINLINE +#endif + +// always inline +#ifdef _MSC_VER +#define TRANSPORT_ALWAYS_INLINE __forceinline +#elif defined(__clang__) || defined(__GNUC__) +#define TRANSPORT_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#else +#define TRANSPORT_ALWAYS_INLINE inline +#endif
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/portability/portability.h b/libtransport/src/hicn/transport/portability/portability.h new file mode 100755 index 000000000..7063e1822 --- /dev/null +++ b/libtransport/src/hicn/transport/portability/portability.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/portability/c_portability.h> + +#include <string.h> +#include <cstddef> + +namespace portability { + +constexpr bool little_endian_arch = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; +constexpr bool big_endian_arch = !little_endian_arch; + +#if defined(__GNUC__) +#define _TRANSPORT_GNU_DISABLE_WARNING(warning) #warning +#define TRANSPORT_GNU_DISABLE_WARNING(warning) \ + _Pragma(_TRANSPORT_GNU_DISABLE_WARNING(GCC diagnostic ignored warning)) + +#ifdef __clang__ +#define TRANSPORT_CLANG_DISABLE_WARNING(warning) \ + TRANSPORT_GNU_DISABLE_WARNING(warning) +#define TRANSPORT_GCC_DISABLE_WARNING(warning) +#else +#define TRANSPORT_CLANG_DISABLE_WARNING(warning) +#define TRANSPORT_GCC_DISABLE_WARNING(warning) \ + TRANSPORT_GNU_DISABLE_WARNING(warning) +#endif +#endif + +} // namespace portability diff --git a/libtransport/src/hicn/transport/protocols/CMakeLists.txt b/libtransport/src/hicn/transport/protocols/CMakeLists.txt new file mode 100755 index 000000000..1c3b76c24 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/CMakeLists.txt @@ -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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.h + ${CMAKE_CURRENT_SOURCE_DIR}/download_observer.h + ${CMAKE_CURRENT_SOURCE_DIR}/vegas.h + ${CMAKE_CURRENT_SOURCE_DIR}/protocol.h + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.h + ${CMAKE_CURRENT_SOURCE_DIR}/vegas_rto_estimator.h + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.h + ${CMAKE_CURRENT_SOURCE_DIR}/cbr.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc.h + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/vegas.cc + ${CMAKE_CURRENT_SOURCE_DIR}/protocol.cc + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm.cc + ${CMAKE_CURRENT_SOURCE_DIR}/vegas_rto_estimator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rate_estimation.cc + ${CMAKE_CURRENT_SOURCE_DIR}/raaqm_data_path.cc + ${CMAKE_CURRENT_SOURCE_DIR}/cbr.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rtc_data_path.cc +) + +set(TRANSPORT_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/consumer.conf +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/cbr.cc b/libtransport/src/hicn/transport/protocols/cbr.cc new file mode 100755 index 000000000..3da4819c3 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/cbr.cc @@ -0,0 +1,47 @@ +/* + * 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/protocols/cbr.h> + +namespace transport { + +namespace protocol { + +using namespace interface; + +CbrTransportProtocol::CbrTransportProtocol(BaseSocket *icnet_socket) + : VegasTransportProtocol(icnet_socket) {} + +void CbrTransportProtocol::start( + utils::SharableVector<uint8_t> &receive_buffer) { + current_window_size_ = socket_->current_window_size_; + VegasTransportProtocol::start(receive_buffer); +} + +void CbrTransportProtocol::changeInterestLifetime(uint64_t segment) { return; } + +void CbrTransportProtocol::increaseWindow() {} + +void CbrTransportProtocol::decreaseWindow() {} + +void CbrTransportProtocol::afterDataUnsatisfied(uint64_t segment) {} + +void CbrTransportProtocol::afterContentReception( + const Interest &interest, const ContentObject &content_object) {} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/cbr.h b/libtransport/src/hicn/transport/protocols/cbr.h new file mode 100755 index 000000000..0a572292a --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/cbr.h @@ -0,0 +1,48 @@ +/* + * 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/protocols/raaqm_data_path.h> +#include <hicn/transport/protocols/rate_estimation.h> +#include <hicn/transport/protocols/vegas.h> +#include <hicn/transport/protocols/vegas_rto_estimator.h> + +namespace transport { + +namespace protocol { + +class CbrTransportProtocol : public VegasTransportProtocol { + public: + CbrTransportProtocol(interface::BaseSocket *icnet_socket); + + void start(utils::SharableVector<uint8_t> &receive_buffer) override; + + private: + void afterContentReception(const Interest &interest, + const ContentObject &content_object) override; + + void afterDataUnsatisfied(uint64_t segment) override; + + void increaseWindow() override; + + void decreaseWindow() override; + + void changeInterestLifetime(uint64_t segment) override; +}; + +} // end namespace protocol + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/consumer.conf b/libtransport/src/hicn/transport/protocols/consumer.conf new file mode 100755 index 000000000..1a366f32f --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/consumer.conf @@ -0,0 +1,21 @@ +; this file contais the parameters for RAAQM +autotune = no +lifetime = 500 +retransmissions = 128 +beta = 0.99 +drop = 0.003 +beta_wifi_ = 0.99 +drop_wifi_ = 0.6 +beta_lte_ = 0.99 +drop_lte_ = 0.003 +wifi_delay_ = 200 +lte_delay_ = 9000 + +alpha = 0.95 +batching_parameter = 200 + +;Choice of rate estimator: +;0 --> an estimation each $(batching_parameter) packets +;1 --> an estimation "a la TCP", estimation at the end of the download of the segment + +rate_estimator = 0 diff --git a/libtransport/src/hicn/transport/protocols/download_observer.h b/libtransport/src/hicn/transport/protocols/download_observer.h new file mode 100755 index 000000000..6d24fe6fd --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/download_observer.h @@ -0,0 +1,32 @@ +/* + * 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 + +namespace transport { + +namespace protocol { + +class IcnObserver { + public: + virtual ~IcnObserver(){}; + + virtual void notifyStats(double throughput) = 0; + virtual void notifyDownloadTime(double downloadTime) = 0; +}; + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/protocol.cc b/libtransport/src/hicn/transport/protocols/protocol.cc new file mode 100755 index 000000000..ea4fd6dbf --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/protocol.cc @@ -0,0 +1,45 @@ +/* + * 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/protocols/protocol.h> + +namespace transport { + +namespace protocol { + +TransportProtocol::TransportProtocol(interface::BaseSocket *icn_socket) + : socket_(dynamic_cast<interface::ConsumerSocket *>(icn_socket)), + is_running_(false), + interest_pool_() { + // Create pool of interests + increasePoolSize(); +} + +TransportProtocol::~TransportProtocol() {} + +void TransportProtocol::updatePortal() { portal_ = socket_->portal_; } + +bool TransportProtocol::isRunning() { return is_running_; } + +void TransportProtocol::increasePoolSize(std::size_t size) { + for (std::size_t i = 0; i < size; i++) { + interest_pool_.add(new Interest()); + } +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/protocol.h b/libtransport/src/hicn/transport/protocols/protocol.h new file mode 100755 index 000000000..56c57e025 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/protocol.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/interfaces/socket.h> +#include <hicn/transport/utils/object_pool.h> +#include <hicn/transport/utils/sharable_vector.h> + +namespace transport { + +namespace protocol { + +using namespace core; + +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 interface::BasePortal::ConsumerCallback { + static constexpr std::size_t interest_pool_size = 4096; + + public: + TransportProtocol(interface::BaseSocket *icn_socket); + + virtual ~TransportProtocol(); + + void updatePortal(); + + bool isRunning(); + + virtual void start(utils::SharableVector<uint8_t> &content_buffer) = 0; + + virtual void stop() = 0; + + virtual void resume() = 0; + + protected: + virtual void increasePoolSize(std::size_t size = interest_pool_size); + + TRANSPORT_ALWAYS_INLINE Interest::Ptr getInterest() { + auto result = interest_pool_.get(); + + while (TRANSPORT_EXPECT_FALSE(!result.first)) { + // Add packets to the pool + increasePoolSize(); + result = interest_pool_.get(); + } + + return std::move(result.second); + } + // Consumer Callback + virtual void onContentObject(Interest::Ptr &&i, ContentObject::Ptr &&c) = 0; + virtual void onTimeout(Interest::Ptr &&i) = 0; + + protected: + interface::ConsumerSocket *socket_; + std::shared_ptr<interface::BasePortal> portal_; + volatile bool is_running_; + utils::ObjectPool<Interest> interest_pool_; +}; + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/raaqm.cc b/libtransport/src/hicn/transport/protocols/raaqm.cc new file mode 100755 index 000000000..cd22ecfdc --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm.cc @@ -0,0 +1,416 @@ +/* + * 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/protocols/raaqm.h> + +#include <fstream> + +namespace transport { + +namespace protocol { + +using namespace interface; + +RaaqmTransportProtocol::RaaqmTransportProtocol(BaseSocket *icnet_socket) + : VegasTransportProtocol(icnet_socket), rate_estimator_(NULL) { + init(); +} + +RaaqmTransportProtocol::~RaaqmTransportProtocol() { + if (this->rate_estimator_) { + delete this->rate_estimator_; + } +} + +void RaaqmTransportProtocol::init() { + std::ifstream is(RAAQM_CONFIG_PATH); + + std::string line; + + socket_->beta_ = default_values::beta_value; + socket_->drop_factor_ = default_values::drop_factor; + socket_->interest_lifetime_ = default_values::interest_lifetime; + socket_->max_retransmissions_ = + default_values::transport_protocol_max_retransmissions; + raaqm_autotune_ = false; + default_beta_ = default_values::beta_value; + default_drop_ = default_values::drop_factor; + beta_wifi_ = default_values::beta_value; + drop_wifi_ = default_values::drop_factor; + beta_lte_ = default_values::beta_value; + drop_lte_ = default_values::drop_factor; + wifi_delay_ = 1000; + lte_delay_ = 15000; + + if (!is) { + TRANSPORT_LOGW("WARNING: RAAQM parameters not found, set default values"); + return; + } + + while (getline(is, line)) { + std::string command; + std::istringstream line_s(line); + + line_s >> command; + + if (command == ";") { + continue; + } + + if (command == "autotune") { + std::string tmp; + std::string val; + line_s >> tmp >> val; + if (val == "yes") { + raaqm_autotune_ = true; + } else { + raaqm_autotune_ = false; + } + continue; + } + + if (command == "lifetime") { + std::string tmp; + uint32_t lifetime; + line_s >> tmp >> lifetime; + socket_->interest_lifetime_ = lifetime; + continue; + } + + if (command == "retransmissions") { + std::string tmp; + uint32_t rtx; + line_s >> tmp >> rtx; + socket_->max_retransmissions_ = rtx; + continue; + } + + if (command == "beta") { + std::string tmp; + line_s >> tmp >> default_beta_; + socket_->beta_ = default_beta_; + continue; + } + + if (command == "drop") { + std::string tmp; + line_s >> tmp >> default_drop_; + socket_->drop_factor_ = default_drop_; + continue; + } + + if (command == "beta_wifi_") { + std::string tmp; + line_s >> tmp >> beta_wifi_; + continue; + } + + if (command == "drop_wifi_") { + std::string tmp; + line_s >> tmp >> drop_wifi_; + continue; + } + + if (command == "beta_lte_") { + std::string tmp; + line_s >> tmp >> beta_lte_; + continue; + } + + if (command == "drop_lte_") { + std::string tmp; + line_s >> tmp >> drop_lte_; + continue; + } + + if (command == "wifi_delay_") { + std::string tmp; + line_s >> tmp >> wifi_delay_; + continue; + } + + if (command == "lte_delay_") { + std::string tmp; + line_s >> tmp >> lte_delay_; + continue; + } + if (command == "alpha") { + std::string tmp; + double rate_alpha = 0.0; + line_s >> tmp >> rate_alpha; + socket_->rate_estimation_alpha_ = rate_alpha; + continue; + } + + if (command == "batching_parameter") { + std::string tmp; + uint32_t batching_param = 0; + line_s >> tmp >> batching_param; + socket_->rate_estimation_batching_parameter_ = batching_param; + continue; + } + + if (command == "rate_estimator") { + std::string tmp; + uint32_t choice_param = 0; + line_s >> tmp >> choice_param; + socket_->rate_estimation_choice_ = choice_param; + continue; + } + } + is.close(); +} + +void RaaqmTransportProtocol::start( + utils::SharableVector<uint8_t> &content_buffer) { + if (this->rate_estimator_) { + this->rate_estimator_->onStart(); + } + + if (!cur_path_) { + double drop_factor; + double minimum_drop_probability; + uint32_t sample_number; + uint32_t interest_lifetime; + // double beta; + + drop_factor = socket_->drop_factor_; + minimum_drop_probability = socket_->minimum_drop_probability_; + sample_number = socket_->sample_number_; + interest_lifetime = socket_->interest_lifetime_; + // beta = socket_->beta_; + + double alpha = 0.0; + uint32_t batching_param = 0; + uint32_t choice_param = 0; + alpha = socket_->rate_estimation_alpha_; + batching_param = socket_->rate_estimation_batching_parameter_; + choice_param = socket_->rate_estimation_choice_; + + if (choice_param == 1) { + this->rate_estimator_ = new ALaTcpEstimator(); + } else { + this->rate_estimator_ = new SimpleEstimator(alpha, batching_param); + } + + this->rate_estimator_->observer_ = socket_->rate_estimation_observer_; + + cur_path_ = std::make_shared<RaaqmDataPath>( + drop_factor, minimum_drop_probability, interest_lifetime * 1000, + sample_number); + path_table_[default_values::path_id] = cur_path_; + } + + VegasTransportProtocol::start(content_buffer); +} + +void RaaqmTransportProtocol::copyContent(const ContentObject &content_object) { + if (TRANSPORT_EXPECT_FALSE( + (content_object.getName().getSuffix() == final_block_number_) || + !(is_running_))) { + this->rate_estimator_->onDownloadFinished(); + } + VegasTransportProtocol::copyContent(content_object); +} + +void RaaqmTransportProtocol::updatePathTable( + const ContentObject &content_object) { + uint32_t path_id = content_object.getPathLabel(); + + if (path_table_.find(path_id) == path_table_.end()) { + if (cur_path_) { + // Create a new path with some default param + if (path_table_.empty()) { + throw errors::RuntimeException( + "No path initialized for path table, error could be in default " + "path initialization."); + } else { + // Initiate the new path default param + std::shared_ptr<RaaqmDataPath> new_path = + std::make_shared<RaaqmDataPath>( + *(path_table_.at(default_values::path_id))); + // Insert the new path into hash table + path_table_[path_id] = new_path; + } + } else { + throw errors::RuntimeException( + "UNEXPECTED ERROR: when running,current path not found."); + } + } + + cur_path_ = path_table_[path_id]; + + size_t header_size = content_object.headerSize(); + size_t data_size = content_object.payloadSize(); + + // Update measurements for path + cur_path_->updateReceivedStats(header_size + data_size, data_size); +} + +void RaaqmTransportProtocol::updateRtt(uint64_t segment) { + if (TRANSPORT_EXPECT_FALSE(!cur_path_)) { + throw std::runtime_error("ERROR: no current path found, exit"); + } else { + std::chrono::microseconds rtt; + + std::chrono::steady_clock::duration duration = + std::chrono::steady_clock::now() - + interest_timepoints_[segment & mask_]; + rtt = std::chrono::duration_cast<std::chrono::microseconds>(duration); + + if (this->rate_estimator_) { + this->rate_estimator_->onRttUpdate(rtt.count()); + } + cur_path_->insertNewRtt(rtt.count()); + cur_path_->smoothTimer(); + + if (cur_path_->newPropagationDelayAvailable()) { + check_drop_probability(); + } + } +} + +void RaaqmTransportProtocol::changeInterestLifetime(uint64_t segment) { + return; +} + +void RaaqmTransportProtocol::check_drop_probability() { + if (!raaqm_autotune_) { + return; + } + + unsigned int max_pd = 0; + std::unordered_map<uint32_t, std::shared_ptr<RaaqmDataPath>>::iterator it; + for (auto it = path_table_.begin(); it != path_table_.end(); ++it) { + if (it->second->getPropagationDelay() > max_pd && + it->second->getPropagationDelay() != UINT_MAX && + !it->second->isStale()) { + max_pd = it->second->getPropagationDelay(); + } + } + + double drop_prob = 0; + double beta = 0; + if (max_pd < wifi_delay_) { // only ethernet paths + drop_prob = default_drop_; + beta = default_beta_; + } else if (max_pd < lte_delay_) { // at least one wifi path + drop_prob = drop_wifi_; + beta = beta_wifi_; + } else { // at least one lte path + drop_prob = drop_lte_; + beta = beta_lte_; + } + + double old_drop_prob = 0; + double old_beta = 0; + old_beta = socket_->beta_; + old_drop_prob = socket_->drop_factor_; + + if (drop_prob == old_drop_prob && beta == old_beta) { + return; + } + + socket_->beta_ = beta; + socket_->drop_factor_ = drop_prob; + + for (it = path_table_.begin(); it != path_table_.end(); it++) { + it->second->setDropProb(drop_prob); + } +} + +void RaaqmTransportProtocol::check_for_stale_paths() { + if (!raaqm_autotune_) { + return; + } + + bool stale = false; + std::unordered_map<uint32_t, std::shared_ptr<RaaqmDataPath>>::iterator it; + for (it = path_table_.begin(); it != path_table_.end(); ++it) { + if (it->second->isStale()) { + stale = true; + break; + } + } + if (stale) { + check_drop_probability(); + } +} + +void RaaqmTransportProtocol::onTimeout(Interest::Ptr &&interest) { + check_for_stale_paths(); + VegasTransportProtocol::onTimeout(std::move(interest)); +} + +void RaaqmTransportProtocol::increaseWindow() { + double max_window_size = socket_->max_window_size_; + if (current_window_size_ < max_window_size) { + double gamma = socket_->gamma_; + + current_window_size_ += gamma / current_window_size_; + socket_->current_window_size_ = current_window_size_; + } + this->rate_estimator_->onWindowIncrease(current_window_size_); +} + +void RaaqmTransportProtocol::decreaseWindow() { + double min_window_size = socket_->min_window_size_; + if (current_window_size_ > min_window_size) { + double beta = socket_->beta_; + + current_window_size_ = current_window_size_ * beta; + if (current_window_size_ < min_window_size) { + current_window_size_ = min_window_size; + } + + socket_->current_window_size_ = current_window_size_; + } + this->rate_estimator_->onWindowDecrease(current_window_size_); +} + +void RaaqmTransportProtocol::RAAQM() { + if (!cur_path_) { + throw errors::RuntimeException("ERROR: no current path found, exit"); + exit(EXIT_FAILURE); + } else { + // Change drop probability according to RTT statistics + cur_path_->updateDropProb(); + + if (rand() % 10000 <= cur_path_->getDropProb() * 10000) { + decreaseWindow(); + } + } +} + +void RaaqmTransportProtocol::afterDataUnsatisfied(uint64_t segment) { + // Decrease the window because the timeout happened + decreaseWindow(); +} + +void RaaqmTransportProtocol::afterContentReception( + const Interest &interest, const ContentObject &content_object) { + updatePathTable(content_object); + increaseWindow(); + updateRtt(interest.getName().getSuffix()); + this->rate_estimator_->onDataReceived((int)content_object.payloadSize() + + content_object.headerSize()); + // Set drop probablility and window size accordingly + RAAQM(); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/raaqm.h b/libtransport/src/hicn/transport/protocols/raaqm.h new file mode 100755 index 000000000..6ca410251 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm.h @@ -0,0 +1,94 @@ +/* + * 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/protocols/raaqm_data_path.h> +#include <hicn/transport/protocols/rate_estimation.h> +#include <hicn/transport/protocols/vegas.h> +#include <hicn/transport/protocols/vegas_rto_estimator.h> + +namespace transport { + +namespace protocol { + +class RaaqmTransportProtocol : public VegasTransportProtocol { + public: + RaaqmTransportProtocol(interface::BaseSocket *icnet_socket); + + ~RaaqmTransportProtocol(); + + void start(utils::SharableVector<uint8_t> &content_buffer) override; + + protected: + void copyContent(const ContentObject &content_object) override; + + private: + void init(); + + void afterContentReception(const Interest &interest, + const ContentObject &content_object) override; + + void afterDataUnsatisfied(uint64_t segment) override; + + void increaseWindow() override; + + void updateRtt(uint64_t segment); + + void decreaseWindow() override; + + void changeInterestLifetime(uint64_t segment) override; + + void onTimeout(Interest::Ptr &&interest) override; + + void RAAQM(); + + void updatePathTable(const ContentObject &content_object); + + void check_drop_probability(); + + void check_for_stale_paths(); + + void printRtt(); + + /** + * Current download path + */ + std::shared_ptr<RaaqmDataPath> cur_path_; + + /** + * Hash table for path: each entry is a pair path ID(key) - path object + */ + std::unordered_map<uint32_t, std::shared_ptr<RaaqmDataPath>> path_table_; + + bool set_interest_filter_; + // for rate-estimation at packet level + IcnRateEstimator *rate_estimator_; + + // params for autotuning + bool raaqm_autotune_; + double default_beta_; + double default_drop_; + double beta_wifi_; + double drop_wifi_; + double beta_lte_; + double drop_lte_; + unsigned int wifi_delay_; + unsigned int lte_delay_; +}; + +} // end namespace protocol + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/raaqm_data_path.cc b/libtransport/src/hicn/transport/protocols/raaqm_data_path.cc new file mode 100755 index 000000000..f876cf4f8 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm_data_path.cc @@ -0,0 +1,158 @@ +/* + * 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/protocols/raaqm_data_path.h> + +namespace transport { + +namespace protocol { + +RaaqmDataPath::RaaqmDataPath(double drop_factor, + double minimum_drop_probability, + unsigned new_timer, unsigned int samples, + uint64_t new_rtt, uint64_t new_rtt_min, + uint64_t new_rtt_max, unsigned new_pd) + + : drop_factor_(drop_factor), + minimum_drop_probability_(minimum_drop_probability), + timer_(new_timer), + samples_(samples), + rtt_(new_rtt), + rtt_min_(new_rtt_min), + rtt_max_(new_rtt_max), + prop_delay_(new_pd), + new_prop_delay_(false), + drop_prob_(0), + packets_received_(0), + last_packets_received_(0), + m_packets_bytes_received_(0), + last_packets_bytes_received_(0), + raw_data_bytes_received_(0), + last_raw_data_bytes_received_(0), + rtt_samples_(samples_), + average_rtt_(0), + alpha_(ALPHA) { + gettimeofday(&m_last_received_pkt_, 0); +} + +RaaqmDataPath &RaaqmDataPath::insertNewRtt(uint64_t new_rtt) { + rtt_ = new_rtt; + rtt_samples_.pushBack(new_rtt); + + rtt_max_ = rtt_samples_.rBegin(); + rtt_min_ = rtt_samples_.begin(); + + if (rtt_min_ < prop_delay_) { + new_prop_delay_ = true; + prop_delay_ = rtt_min_; + } + + gettimeofday(&m_last_received_pkt_, 0); + + return *this; +} + +RaaqmDataPath &RaaqmDataPath::updateReceivedStats(std::size_t packet_size, + std::size_t data_size) { + packets_received_++; + m_packets_bytes_received_ += packet_size; + raw_data_bytes_received_ += data_size; + + return *this; +} + +double RaaqmDataPath::getDropFactor() { return drop_factor_; } + +double RaaqmDataPath::getDropProb() { return drop_prob_; } + +RaaqmDataPath &RaaqmDataPath::setDropProb(double dropProb) { + drop_prob_ = dropProb; + + return *this; +} + +double RaaqmDataPath::getMinimumDropProbability() { + return minimum_drop_probability_; +} + +double RaaqmDataPath::getTimer() { return timer_; } + +RaaqmDataPath &RaaqmDataPath::smoothTimer() { + timer_ = (1 - TIMEOUT_SMOOTHER) * timer_ + + (TIMEOUT_SMOOTHER)*rtt_ * (TIMEOUT_RATIO); + + return *this; +} + +double RaaqmDataPath::getRtt() { return rtt_; } + +double RaaqmDataPath::getAverageRtt() { return average_rtt_; } + +double RaaqmDataPath::getRttMax() { return rtt_max_; } + +double RaaqmDataPath::getRttMin() { return rtt_min_; } + +unsigned RaaqmDataPath::getSampleValue() { return samples_; } + +unsigned RaaqmDataPath::getRttQueueSize() { + return static_cast<unsigned>(rtt_samples_.size()); +} + +RaaqmDataPath &RaaqmDataPath::updateDropProb() { + drop_prob_ = 0.0; + + if (getSampleValue() == getRttQueueSize()) { + if (rtt_max_ == rtt_min_) { + drop_prob_ = minimum_drop_probability_; + } else { + drop_prob_ = minimum_drop_probability_ + + drop_factor_ * (rtt_ - rtt_min_) / (rtt_max_ - rtt_min_); + } + } + + return *this; +} + +double RaaqmDataPath::getMicroSeconds(struct timeval &time) { + return (double)(time.tv_sec) * 1000000 + (double)(time.tv_usec); +} + +void RaaqmDataPath::setAlpha(double alpha) { + if (alpha >= 0 && alpha <= 1) { + alpha_ = alpha; + } +} + +bool RaaqmDataPath::newPropagationDelayAvailable() { + bool r = new_prop_delay_; + new_prop_delay_ = false; + return r; +} + +unsigned int RaaqmDataPath::getPropagationDelay() { return prop_delay_; } + +bool RaaqmDataPath::isStale() { + struct timeval now; + gettimeofday(&now, 0); + double time = getMicroSeconds(now) - getMicroSeconds(m_last_received_pkt_); + if (time > 2000000) { + return true; + } + return false; +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/raaqm_data_path.h b/libtransport/src/hicn/transport/protocols/raaqm_data_path.h new file mode 100755 index 000000000..6f63940c9 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/raaqm_data_path.h @@ -0,0 +1,230 @@ +/* + * 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/min_filter.h> + +#include <sys/time.h> +#include <climits> +#include <iostream> + +#define TIMEOUT_SMOOTHER 0.1 +#define TIMEOUT_RATIO 10 +#define ALPHA 0.8 + +namespace transport { + +namespace protocol { + +class RaaqmDataPath { + public: + RaaqmDataPath(double drop_factor, double minimum_drop_probability, + unsigned new_timer, unsigned int samples, + uint64_t new_rtt = 1000, uint64_t new_rtt_min = 1000, + uint64_t new_rtt_max = 1000, unsigned new_pd = UINT_MAX); + + public: + /* + * @brief Add a new RTT to the RTT queue of the path, check if RTT queue is + * full, and thus need overwrite. Also it maintains the validity of min and + * max of RTT. + * @param new_rtt is the value of the new RTT + */ + RaaqmDataPath &insertNewRtt(uint64_t new_rtt); + + /** + * @brief Update the path statistics + * @param packet_size the size of the packet received, including the ICN + * header + * @param data_size the size of the data received, without the ICN header + */ + RaaqmDataPath &updateReceivedStats(std::size_t packet_size, + std::size_t data_size); + + /** + * @brief Get the value of the drop factor parameter + */ + double getDropFactor(); + + /** + * @brief Get the value of the drop probability + */ + double getDropProb(); + + /** + * @brief Set the value pf the drop probability + * @param drop_prob is the value of the drop probability + */ + RaaqmDataPath &setDropProb(double drop_prob); + + /** + * @brief Get the minimum drop probability + */ + double getMinimumDropProbability(); + + /** + * @brief Get last RTT + */ + double getRtt(); + + /** + * @brief Get average RTT + */ + double getAverageRtt(); + + /** + * @brief Get the current m_timer value + */ + double getTimer(); + + /** + * @brief Smooth he value of the m_timer accordingly with the last RTT + * measured + */ + RaaqmDataPath &smoothTimer(); + + /** + * @brief Get the maximum RTT among the last samples + */ + double getRttMax(); + + /** + * @brief Get the minimum RTT among the last samples + */ + double getRttMin(); + + /** + * @brief Get the number of saved samples + */ + unsigned getSampleValue(); + + /** + * @brief Get the size og the RTT queue + */ + unsigned getRttQueueSize(); + + /* + * @brief Change drop probability according to RTT statistics + * Invoked in RAAQM(), before control window size update. + */ + RaaqmDataPath &updateDropProb(); + + /** + * @brief This function convert the time from struct timeval to its value in + * microseconds + */ + static double getMicroSeconds(struct timeval &time); + + void setAlpha(double alpha); + + /** + * @brief Returns the smallest RTT registered so far for this path + */ + + unsigned int getPropagationDelay(); + + bool newPropagationDelayAvailable(); + + bool isStale(); + + private: + /** + * The value of the drop factor + */ + double drop_factor_; + + /** + * The minumum drop probability + */ + double minimum_drop_probability_; + + /** + * The timer, expressed in milliseconds + */ + double timer_; + + /** + * The number of samples to store for computing the protocol measurements + */ + const unsigned int samples_; + + /** + * The last, the minimum and the maximum value of the RTT (among the last + * m_samples samples) + */ + uint64_t rtt_, rtt_min_, rtt_max_, prop_delay_; + + bool new_prop_delay_; + + /** + * The current drop probability + */ + double drop_prob_; + + /** + * The number of packets received in this path + */ + intmax_t packets_received_; + + /** + * The first packet received after the statistics print + */ + intmax_t last_packets_received_; + + /** + * Total number of bytes received including the ICN header + */ + intmax_t m_packets_bytes_received_; + + /** + * The amount of packet bytes received at the last path summary computation + */ + intmax_t last_packets_bytes_received_; + + /** + * Total number of bytes received without including the ICN header + */ + intmax_t raw_data_bytes_received_; + + /** + * The amount of raw dat bytes received at the last path summary computation + */ + intmax_t last_raw_data_bytes_received_; + + class byArrival; + + class byOrder; + + /** + * Double ended queue for the RTTs + */ + + typedef utils::MinFilter<uint64_t> RTTQueue; + + RTTQueue rtt_samples_; + + /** + * Time of the last call to the path reporter method + */ + struct timeval m_last_received_pkt_; + + double average_rtt_; + double alpha_; +}; + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rate_estimation.cc b/libtransport/src/hicn/transport/protocols/rate_estimation.cc new file mode 100755 index 000000000..e313bf9f6 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rate_estimation.cc @@ -0,0 +1,353 @@ +/* + * 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/protocols/rate_estimation.h> +#include <hicn/transport/utils/log.h> + +namespace transport { + +namespace protocol { + +void *Timer(void *data) { + InterRttEstimator *estimator = (InterRttEstimator *)data; + + double dat_rtt, my_avg_win, my_avg_rtt; + int my_win_change, number_of_packets, max_packet_size; + + pthread_mutex_lock(&(estimator->mutex_)); + dat_rtt = estimator->rtt_; + pthread_mutex_unlock(&(estimator->mutex_)); + + while (estimator->is_running_) { + usleep(KV * dat_rtt); + + pthread_mutex_lock(&(estimator->mutex_)); + + dat_rtt = estimator->rtt_; + my_avg_win = estimator->avg_win_; + my_avg_rtt = estimator->avg_rtt_; + my_win_change = estimator->win_change_; + number_of_packets = estimator->number_of_packets_; + max_packet_size = estimator->max_packet_size_; + estimator->avg_rtt_ = estimator->rtt_; + estimator->avg_win_ = 0; + estimator->win_change_ = 0; + estimator->number_of_packets_ = 1; + + pthread_mutex_unlock(&(estimator->mutex_)); + + if (number_of_packets == 0 || my_win_change == 0) { + continue; + } + if (estimator->estimation_ == 0) { + estimator->estimation_ = (my_avg_win * 8.0 * max_packet_size * 1000000.0 / + (1.0 * my_win_change)) / + (my_avg_rtt / (1.0 * number_of_packets)); + } + + estimator->estimation_ = + estimator->alpha_ * estimator->estimation_ + + (1 - estimator->alpha_) * ((my_avg_win * 8.0 * max_packet_size * + 1000000.0 / (1.0 * my_win_change)) / + (my_avg_rtt / (1.0 * number_of_packets))); + + if (estimator->observer_) { + estimator->observer_->notifyStats(estimator->estimation_); + } + } + + return nullptr; +} + +InterRttEstimator::InterRttEstimator(double alpha_arg) { + this->estimated_ = false; + this->observer_ = NULL; + this->alpha_ = alpha_arg; + this->thread_is_running_ = false; + this->my_th_ = NULL; + this->is_running_ = true; + this->avg_rtt_ = 0.0; + this->estimation_ = 0.0; + this->avg_win_ = 0.0; + this->rtt_ = 0.0; + this->win_change_ = 0; + this->number_of_packets_ = 0; + this->max_packet_size_ = 0; + this->win_current_ = 1.0; + + pthread_mutex_init(&(this->mutex_), NULL); + gettimeofday(&(this->start_time_), 0); + gettimeofday(&(this->begin_batch_), 0); +} + +InterRttEstimator::~InterRttEstimator() { + this->is_running_ = false; + if (this->my_th_) { + pthread_join(*(this->my_th_), NULL); + } + this->my_th_ = NULL; + pthread_mutex_destroy(&(this->mutex_)); +} + +void InterRttEstimator::onRttUpdate(double rtt) { + pthread_mutex_lock(&(this->mutex_)); + this->rtt_ = rtt; + this->number_of_packets_++; + this->avg_rtt_ += rtt; + pthread_mutex_unlock(&(this->mutex_)); + + if (!thread_is_running_) { + my_th_ = (pthread_t *)malloc(sizeof(pthread_t)); + if (!my_th_) { + TRANSPORT_LOGE("Error allocating thread."); + my_th_ = NULL; + } + if (/*int err = */ pthread_create(my_th_, NULL, transport::protocol::Timer, + (void *)this)) { + TRANSPORT_LOGE("Error creating the thread"); + my_th_ = NULL; + } + thread_is_running_ = true; + } +} + +void InterRttEstimator::onWindowIncrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + + pthread_mutex_lock(&(this->mutex_)); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + pthread_mutex_unlock(&(this->mutex_)); + + gettimeofday(&(this->begin_batch_), 0); +} + +void InterRttEstimator::onWindowDecrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + + pthread_mutex_lock(&(this->mutex_)); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + pthread_mutex_unlock(&(this->mutex_)); + + gettimeofday(&(this->begin_batch_), 0); +} + +ALaTcpEstimator::ALaTcpEstimator() { + this->estimation_ = 0.0; + this->observer_ = NULL; + gettimeofday(&(this->start_time_), 0); + this->totalSize_ = 0.0; +} + +void ALaTcpEstimator::onStart() { + this->totalSize_ = 0.0; + gettimeofday(&(this->start_time_), 0); +} + +void ALaTcpEstimator::onDownloadFinished() { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->start_time_); + this->estimation_ = this->totalSize_ * 8 * 1000000 / delay; + if (observer_) { + observer_->notifyStats(this->estimation_); + } +} + +void ALaTcpEstimator::onDataReceived(int packet_size) { + this->totalSize_ += packet_size; +} + +SimpleEstimator::SimpleEstimator(double alphaArg, int batching_param) { + this->estimation_ = 0.0; + this->estimated_ = false; + this->observer_ = NULL; + this->batching_param_ = batching_param; + this->total_size_ = 0.0; + this->number_of_packets_ = 0; + this->base_alpha_ = alphaArg; + this->alpha_ = alphaArg; + gettimeofday(&(this->start_time_), 0); + gettimeofday(&(this->begin_batch_), 0); +} + +void SimpleEstimator::onStart() { + this->estimated_ = false; + this->number_of_packets_ = 0; + this->total_size_ = 0.0; + gettimeofday(&(this->begin_batch_), 0); + gettimeofday(&(this->start_time_), 0); +} + +void SimpleEstimator::onDownloadFinished() { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->start_time_); + if (observer_) { + observer_->notifyDownloadTime(delay); + } + if (!this->estimated_) { + // Assuming all packets carry max_packet_size_ bytes of data + // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds + if (this->estimation_) { + this->estimation_ = + alpha_ * this->estimation_ + + (1 - alpha_) * (total_size_ * 8 * 1000000.0 / (delay)); + } else { + this->estimation_ = total_size_ * 8 * 1000000.0 / (delay); + } + if (observer_) { + observer_->notifyStats(this->estimation_); + } + this->alpha_ = this->base_alpha_ * (((double)this->number_of_packets_) / + ((double)this->batching_param_)); + } else { + if (this->number_of_packets_ >= + (int)(75.0 * (double)this->batching_param_ / 100.0)) { + delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + // Assuming all packets carry max_packet_size_ bytes of data + // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds + if (this->estimation_) { + this->estimation_ = + alpha_ * this->estimation_ + + (1 - alpha_) * (total_size_ * 8 * 1000000.0 / (delay)); + } else { + this->estimation_ = total_size_ * 8 * 1000000.0 / (delay); + } + if (observer_) { + observer_->notifyStats(this->estimation_); + } + this->alpha_ = this->base_alpha_ * (((double)this->number_of_packets_) / + ((double)this->batching_param_)); + } + } + this->number_of_packets_ = 0; + this->total_size_ = 0.0; + gettimeofday(&(this->begin_batch_), 0); + gettimeofday(&(this->start_time_), 0); +} + +void SimpleEstimator::onDataReceived(int packet_size) { + this->total_size_ += packet_size; +} + +void SimpleEstimator::onRttUpdate(double rtt) { + this->number_of_packets_++; + + if (number_of_packets_ == this->batching_param_) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + // Assuming all packets carry max_packet_size_ bytes of data + // (8*max_packet_size_ bits); 1000000 factor to convert us to seconds + if (this->estimation_) { + this->estimation_ = + alpha_ * this->estimation_ + + (1 - alpha_) * (total_size_ * 8 * 1000000.0 / (delay)); + } else { + this->estimation_ = total_size_ * 8 * 1000000.0 / (delay); + } + if (observer_) { + observer_->notifyStats(this->estimation_); + } + this->alpha_ = this->base_alpha_; + this->number_of_packets_ = 0; + this->total_size_ = 0.0; + gettimeofday(&(this->begin_batch_), 0); + } +} + +BatchingPacketsEstimator::BatchingPacketsEstimator(double alpha_arg, + int param) { + this->estimated_ = false; + this->observer_ = NULL; + this->alpha_ = alpha_arg; + this->batching_param_ = param; + this->number_of_packets_ = 0; + this->avg_win_ = 0.0; + this->avg_rtt_ = 0.0; + this->win_change_ = 0.0; + this->max_packet_size_ = 0; + this->estimation_ = 0.0; + this->win_current_ = 1.0; + gettimeofday(&(this->begin_batch_), 0); + gettimeofday(&(this->start_time_), 0); +} + +void BatchingPacketsEstimator::onRttUpdate(double rtt) { + this->number_of_packets_++; + this->avg_rtt_ += rtt; + + if (number_of_packets_ == this->batching_param_) { + if (estimation_ == 0) { + estimation_ = (avg_win_ * 8.0 * max_packet_size_ * 1000000.0 / + (1.0 * win_change_)) / + (avg_rtt_ / (1.0 * number_of_packets_)); + } else { + estimation_ = alpha_ * estimation_ + + (1 - alpha_) * ((avg_win_ * 8.0 * max_packet_size_ * + 1000000.0 / (1.0 * win_change_)) / + (avg_rtt_ / (1.0 * number_of_packets_))); + } + + if (observer_) { + observer_->notifyStats(estimation_); + } + + this->number_of_packets_ = 0; + this->avg_win_ = 0.0; + this->avg_rtt_ = 0.0; + this->win_change_ = 0.0; + } +} + +void BatchingPacketsEstimator::onWindowIncrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + gettimeofday(&(this->begin_batch_), 0); +} + +void BatchingPacketsEstimator::onWindowDecrease(double win_current) { + timeval end; + gettimeofday(&end, 0); + double delay = RaaqmDataPath::getMicroSeconds(end) - + RaaqmDataPath::getMicroSeconds(this->begin_batch_); + this->avg_win_ += this->win_current_ * delay; + this->win_current_ = win_current; + this->win_change_ += delay; + gettimeofday(&(this->begin_batch_), 0); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rate_estimation.h b/libtransport/src/hicn/transport/protocols/rate_estimation.h new file mode 100755 index 000000000..b889efe12 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rate_estimation.h @@ -0,0 +1,175 @@ +/* + * 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 <unistd.h> + +#include <hicn/transport/protocols/download_observer.h> +#include <hicn/transport/protocols/raaqm_data_path.h> + +#define BATCH 50 +#define KV 20 +#define ALPHA 0.8 +#define RATE_CHOICE 0 + +namespace transport { + +namespace protocol { + +class IcnRateEstimator { + public: + IcnRateEstimator(){}; + + virtual ~IcnRateEstimator(){}; + + virtual void onRttUpdate(double rtt){}; + + virtual void onDataReceived(int packetSize){}; + + virtual void onWindowIncrease(double winCurrent){}; + + virtual void onWindowDecrease(double winCurrent){}; + + virtual void onStart(){}; + + virtual void onDownloadFinished(){}; + + virtual void setObserver(IcnObserver *observer) { + this->observer_ = observer; + }; + IcnObserver *observer_; + struct timeval start_time_; + struct timeval begin_batch_; + double base_alpha_; + double alpha_; + double estimation_; + int number_of_packets_; + // this boolean is to make sure at least one estimation of the BW is done + bool estimated_; +}; + +// A rate estimator RTT-based. Computes EWMA(WinSize)/EWMA(RTT) + +class InterRttEstimator : public IcnRateEstimator { + public: + InterRttEstimator(double alpha_arg); + + ~InterRttEstimator(); + + void onRttUpdate(double rtt); + + void onDataReceived(int packet_size) { + if (packet_size > this->max_packet_size_) { + this->max_packet_size_ = packet_size; + } + }; + + void onWindowIncrease(double win_current); + + void onWindowDecrease(double win_current); + + void onStart(){}; + + void onDownloadFinished(){}; + + // private: should be done by using getters + pthread_t *my_th_; + bool thread_is_running_; + double rtt_; + bool is_running_; + pthread_mutex_t mutex_; + double avg_rtt_; + double avg_win_; + int max_packet_size_; + double win_change_; + double win_current_; +}; + +// A rate estimator, Batching Packets based. Computes EWMA(WinSize)/EWMA(RTT) + +class BatchingPacketsEstimator : public IcnRateEstimator { + public: + BatchingPacketsEstimator(double alpha_arg, int batchingParam); + + void onRttUpdate(double rtt); + + void onDataReceived(int packet_size) { + if (packet_size > this->max_packet_size_) { + this->max_packet_size_ = packet_size; + } + }; + + void onWindowIncrease(double win_current); + + void onWindowDecrease(double win_current); + + void onStart(){}; + + void onDownloadFinished(){}; + + private: + int batching_param_; + double avg_rtt_; + double avg_win_; + double win_change_; + int max_packet_size_; + double win_current_; +}; + +// Segment Estimator + +class ALaTcpEstimator : public IcnRateEstimator { + public: + ALaTcpEstimator(); + + void onDataReceived(int packet_size); + void onStart(); + void onDownloadFinished(); + + private: + double totalSize_; +}; + +// A Rate estimator, this one is the simplest: counting batching_param_ packets +// and then divide the sum of the size of these packets by the time taken to DL +// them. Should be the one used + +class SimpleEstimator : public IcnRateEstimator { + public: + SimpleEstimator(double alpha, int batching_param); + + void onRttUpdate(double rtt); + + void onDataReceived(int packet_size); + + void onWindowIncrease(double win_current){}; + + void onWindowDecrease(double win_current){}; + + void onStart(); + + void onDownloadFinished(); + + private: + int batching_param_; + double total_size_; +}; + +void *Timer(void *data); + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc.cc b/libtransport/src/hicn/transport/protocols/rtc.cc new file mode 100755 index 000000000..1f42cf230 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc.cc @@ -0,0 +1,813 @@ +/* + * 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 <math.h> + +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/protocols/rtc.h> + +/* + * TODO + * 2) start/constructor/rest variable implementation + * 3) interest retransmission: now I always recover, we should recover only if + * we have enough time 4) returnContentToUser: rememeber to remove the first + * 32bits from the payload + */ + +namespace transport { + +namespace protocol { + +using namespace interface; + +RTCTransportProtocol::RTCTransportProtocol(BaseSocket *icnet_socket) + : TransportProtocol(icnet_socket), + inflightInterests_(1 << default_values::log_2_default_buffer_size), + modMask_((1 << default_values::log_2_default_buffer_size) - 1) { + icnet_socket->getSocketOption(PORTAL, portal_); + reset(); +} + +RTCTransportProtocol::~RTCTransportProtocol() { + if (is_running_) { + stop(); + } +} + +void RTCTransportProtocol::start( + utils::SharableVector<uint8_t> &content_buffer) { + + if(is_running_) + return; + + is_running_ = true; + content_buffer_ = content_buffer.shared_from_this(); + + reset(); + scheduleNextInterest(); + + portal_->runEventsLoop(); + is_running_ = false; +} + +void RTCTransportProtocol::stop() { + if(!is_running_) + return; + + is_running_ = false; + portal_->stopEventsLoop(); +} + +void RTCTransportProtocol::resume(){ + if(is_running_) + return; + + is_running_ = true; + + lastRoundBegin_ = std::chrono::steady_clock::now(); + inflightInterestsCount_ = 0; + if(content_buffer_) + content_buffer_->clear(); + + scheduleNextInterest(); + + portal_->runEventsLoop(); + + is_running_ = false; +} + +void RTCTransportProtocol::onRTCPPacket(uint8_t *packet, size_t len) { + //#define MASK_RTCP_VERSION 192 + //#define MASK_TYPE_CODE 31 + size_t read = 0; + uint8_t *offset = packet; + while (read < len) { + if ((((*offset) & MASK_RTCP_VERSION) >> 6) != RTCP_VERSION) { + TRANSPORT_LOGE("error while parsing RTCP packet, version unkwown"); + return; + } + processRtcpHeader(offset); + uint16_t RTCPlen = (ntohs(*(((uint16_t *)offset) + 1)) + 1) * 4; + offset += RTCPlen; + read += RTCPlen; + } +} + +// private +void RTCTransportProtocol::reset() { + // controller var + lastRoundBegin_ = std::chrono::steady_clock::now(); + currentState_ = RTC_SYNC_STATE; + + // cwin var + currentCWin_ = INITIAL_CWIN; + maxCWin_ = INITIAL_CWIN_MAX; + + // names/packets var + actualSegment_ = 0; + inflightInterestsCount_ = 0; + while (interestRetransmissions_.size() != 0) interestRetransmissions_.pop(); + nackedByProducer_.clear(); + nackedByProducerMaxSize_ = 512; + if (content_buffer_) content_buffer_->clear(); + + holes_.clear(); + lastReceived_ = 0; + + // stats + receivedBytes_ = 0; + sentInterest_ = 0; + receivedData_ = 0; + packetLost_ = 0; + avgPacketSize_ = INIT_PACKET_SIZE; + gotNack_ = false; + gotFutureNack_ = 0; + roundsWithoutNacks_ = 0; + pathTable_.clear(); + // roundCounter_ = 0; + // minRTTwin_.clear(); + // for (int i = 0; i < MIN_RTT_WIN; i++) + // minRTTwin_.push_back(UINT_MAX); + minRtt_ = UINT_MAX; + + // CC var + estimatedBw_ = 0.0; + lossRate_ = 0.0; + queuingDelay_ = 0.0; + protocolState_ = RTC_NORMAL_STATE; + + producerPathLabel_ = 0; + socket_->setSocketOption( + GeneralTransportOptions::INTEREST_LIFETIME, + (uint32_t) + RTC_INTEREST_LIFETIME); // XXX this should bedone by the application +} + +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::checkRound() { + uint32_t duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - lastRoundBegin_) + .count(); + if (duration >= ROUND_LEN) { + lastRoundBegin_ = std::chrono::steady_clock::now(); + updateStats(duration); // update stats and window + } +} + +void RTCTransportProtocol::updateDelayStats( + const ContentObject &content_object) { + uint32_t segmentNumber = content_object.getName().getSuffix(); + uint32_t pkt = segmentNumber & modMask_; + + if (inflightInterests_[pkt].transmissionTime == + 0) // this is always the case if we have a retransmitted packet (timeout + // or RTCP) + 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); + + // we collect OWD only for datapackets + if (content_object.getPayload().length() != NACK_HEADER_SIZE) { + uint64_t *senderTimeStamp = (uint64_t *)content_object.getPayload().data(); + + int64_t OWD = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count() - + *senderTimeStamp; + + pathTable_[pathLabel]->insertOwdSample(OWD); + } +} + +void RTCTransportProtocol::updateStats(uint32_t round_duration) { + if (receivedBytes_ != 0) { + double bytesPerSec = (double)(receivedBytes_ * ((double)MILLI_IN_A_SEC / + (double)round_duration)); + estimatedBw_ = (estimatedBw_ * ESTIMATED_BW_ALPHA) + + ((1 - ESTIMATED_BW_ALPHA) * bytesPerSec); + } + + auto it = pathTable_.find(producerPathLabel_); + if (it == pathTable_.end()) return; + + // double maxAvgRTT = it->second->getAverageRtt(); + // double minRTT = it->second->getMinRtt(); + minRtt_ = it->second->getMinRtt(); + queuingDelay_ = it->second->getQueuingDealy(); + + if (minRtt_ == 0) minRtt_ = 1; + + for (auto it = pathTable_.begin(); it != pathTable_.end(); it++) { + it->second->roundEnd(); + } + + // this is inefficient but the window is supposed to be small, so it + // probably makes sense to leave it like this + // if(minRTT == 0) + // minRTT = 1; + + // minRTTwin_[roundCounter_ % MIN_RTT_WIN] = minRTT; + // minRtt_ = minRTT; + // for (int i = 0; i < MIN_RTT_WIN; i++) + // if(minRtt_ > minRTTwin_[i]) + // minRtt_ = minRTTwin_[i]; + + // roundCounter_++; + + // std::cout << "min RTT " << minRtt_ << " queuing " << queuingDelay_ << + // std::endl; + + if (sentInterest_ != 0 && currentState_ == RTC_NORMAL_STATE) { + double lossRate = (double)((double)packetLost_ / (double)sentInterest_); + lossRate_ = lossRate_ * ESTIMATED_LOSSES_ALPHA + + (lossRate * (1 - ESTIMATED_LOSSES_ALPHA)); + } + + if (avgPacketSize_ == 0) avgPacketSize_ = INIT_PACKET_SIZE; + + uint32_t BDP = + ceil((estimatedBw_ * (double)((double)minRtt_ / (double)MILLI_IN_A_SEC) * + BANDWIDTH_SLACK_FACTOR) / + avgPacketSize_); + uint32_t BW = ceil(estimatedBw_); + computeMaxWindow(BW, BDP); + + // bound also by interest lifitime* production rate + if (!gotNack_) { + roundsWithoutNacks_++; + if (currentState_ == RTC_SYNC_STATE && + roundsWithoutNacks_ >= ROUNDS_IN_SYNC_BEFORE_SWITCH) { + currentState_ = RTC_NORMAL_STATE; + } + } else { + roundsWithoutNacks_ = 0; + } + + updateCCState(); + updateWindow(); + + // in any case we reset all the counters + + gotNack_ = false; + gotFutureNack_ = 0; + receivedBytes_ = 0; + sentInterest_ = 0; + receivedData_ = 0; + packetLost_ = 0; +} + +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 = ceil( + (productionRate / avgPacketSize_) * + (double)((double)(interestLifetime * INTEREST_LIFETIME_REDUCTION_FACTOR) / + (double)MILLI_IN_A_SEC)); + + if (currentState_ == RTC_SYNC_STATE) { + // in this case we do not limit the window with the BDP, beacuse most likly + // it is wrong + maxCWin_ = maxWaintingInterest; + return; + } + + // currentState = RTC_NORMAL_STATE + if (BDPWin != 0) { + maxCWin_ = ceil((double)BDPWin + ((double)BDPWin / 10.0)); // BDP + 10% + } else { + maxCWin_ = min(maxWaintingInterest, maxCWin_); + } +} + +void RTCTransportProtocol::updateWindow() { + if (currentState_ == RTC_SYNC_STATE) return; + + if (currentCWin_ < maxCWin_ * 0.7) { + currentCWin_ = min(maxCWin_, currentCWin_ * WIN_INCREASE_FACTOR); + } else if (currentCWin_ > maxCWin_) { + currentCWin_ = max(currentCWin_ * WIN_DECREASE_FACTOR, MIN_CWIN); + } +} + +void RTCTransportProtocol::decreaseWindow() { + // this is used only in SYNC mode + if (currentState_ == RTC_NORMAL_STATE) return; + + if (gotFutureNack_ == 1) + currentCWin_ = + min((currentCWin_ - 1), ceil((double)maxCWin_ * 0.66)); // 2/3 + else + currentCWin_--; + + currentCWin_ = max(currentCWin_, MIN_CWIN); +} + +void RTCTransportProtocol::increaseWindow() { + // this is used only in SYNC mode + if (currentState_ == RTC_NORMAL_STATE) return; + + // we need to be carefull to do not increase the window to much + if (currentCWin_ < ((double)maxCWin_ * 0.5)) { + currentCWin_ = currentCWin_ + 1; // exponential + } else { + currentCWin_ = min( + maxCWin_, ceil(currentCWin_ + (1.0 / (double)currentCWin_))); // linear + } +} + +void RTCTransportProtocol::sendInterest() { + Name interest_name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + interest_name); + bool isRTX = false; + // uint32_t sentInt = 0; + + if (interestRetransmissions_.size() > 0) { + // handle retransmission + // here we have two possibile retransmissions: retransmissions due to + // timeouts and retransmissions due to RTCP NACKs. we will send the interest + // anyway, even if it is pending (this is possible only in the second case) + uint32_t rtxSeg = interestRetransmissions_.front(); + interestRetransmissions_.pop(); + + std::unordered_map<uint32_t, uint64_t>::const_iterator res = + holes_.find(rtxSeg); + if (res != holes_.end()) { + // this packet is already managed by as an hole + // we don't need to send it again + return; + } + + // a packet recovery means that there was a loss + packetLost_++; + + uint32_t pkt = rtxSeg & modMask_; + interest_name.setSuffix(rtxSeg); + + // if the interest is not pending anymore we encrease the retrasnmission + // counter in order to avoid to handle a recovered packt as a normal one + if (!portal_->interestIsPending(interest_name)) { + inflightInterests_[pkt].retransmissions++; + } + + inflightInterests_[pkt].transmissionTime = 0; + isRTX = true; + } else { + // in this case we send the packet only if it is not pending yet + interest_name.setSuffix(actualSegment_); + if (portal_->interestIsPending(interest_name)) { + actualSegment_++; + return; + } + + // sentInt = actualSegment_; + uint32_t pkt = actualSegment_ & modMask_; + inflightInterests_[pkt].transmissionTime = + std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + inflightInterests_[pkt].retransmissions = 0; + actualSegment_++; + } + + auto interest = getInterest(); + interest->setName(interest_name); + + uint32_t interestLifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + interestLifetime); + interest->setLifetime(uint32_t(interestLifetime)); + + ConsumerInterestCallback on_interest_output = VOID_HANDLER; + + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + on_interest_output); + + if (on_interest_output != VOID_HANDLER) { + on_interest_output(*dynamic_cast<ConsumerSocket *>(socket_), *interest); + } + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); + + sentInterest_++; + + if (!isRTX) { + inflightInterestsCount_++; + } +} + +void RTCTransportProtocol::scheduleNextInterest() { + checkRound(); + if(!is_running_) + return; + + uint32_t MAX_RECOVER = + 40; // if the packet is more than MAX_RECOVER seq in the past we drop it + uint64_t TIME_BEFORE_RECOVERY = 10; // this should be proporsional to the RTT + + // holes are important only in NORMAL state + if (currentState_ == RTC_NORMAL_STATE) { + for (std::unordered_map<uint32_t, uint64_t>::iterator it = holes_.begin(); + it != holes_.end();) { + if (it->first < lastReceived_ - MAX_RECOVER) { + // the packet is to hold, remove it + it = holes_.erase(it); + } else { + uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); + uint64_t sinceLastTry = now - it->second; + + if (sinceLastTry > TIME_BEFORE_RECOVERY || it->second == 0) { + // a recovery means a packet lost + packetLost_++; + // update last sent time + it->second = now; + + Name interest_name; + socket_->getSocketOption(GeneralTransportOptions::NETWORK_NAME, + interest_name); + + uint32_t pkt = it->first & modMask_; + interest_name.setSuffix(it->first); + + if (!portal_->interestIsPending(interest_name)) { + inflightInterests_[pkt].retransmissions++; + } + + inflightInterests_[pkt].transmissionTime = 0; + // XXX + // code refactoring: + // from here on this is a copy and paste of the code inside + // sendInterest this should go inside an other method + auto interest = getInterest(); + uint32_t interestLifetime = default_values::interest_lifetime; + socket_->getSocketOption(GeneralTransportOptions::INTEREST_LIFETIME, + interestLifetime); + interest->setLifetime(uint32_t(interestLifetime)); + + ConsumerInterestCallback on_interest_output = VOID_HANDLER; + + socket_->getSocketOption(ConsumerCallbacksOptions::INTEREST_OUTPUT, + on_interest_output); + if (on_interest_output != VOID_HANDLER) + on_interest_output(*dynamic_cast<ConsumerSocket *>(socket_), + *interest); + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) return; + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); + + sentInterest_++; + } + ++it; + } + // as usual check the round at each packet + checkRound(); + } + } + + while (interestRetransmissions_.size() > 0) { + sendInterest(); + checkRound(); + } + + while (inflightInterestsCount_ < currentCWin_) { + sendInterest(); + checkRound(); + } +} + +void RTCTransportProtocol::scheduleAppNackRtx(std::vector<uint32_t> &nacks) { + for (uint32_t i = 0; i < nacks.size(); i++) { + if (nackedByProducer_.find(nacks[i]) != nackedByProducer_.end()) { + continue; + } + // packetLost_++; + // XXX here I need to avoid the retrasmission for packet that were nacked by + // the network + interestRetransmissions_.push(nacks[i]); + } + + scheduleNextInterest(); +} +void RTCTransportProtocol::onTimeout(Interest::Ptr &&interest) { + // packetLost_++; + + uint32_t segmentNumber = interest->getName().getSuffix(); + uint32_t pkt = segmentNumber & modMask_; + + if (inflightInterests_[pkt].retransmissions == 0) { + inflightInterestsCount_--; + } + + if (inflightInterests_[pkt].retransmissions < MAX_RTX) { + interestRetransmissions_.push(segmentNumber); + } + + scheduleNextInterest(); +} + +void RTCTransportProtocol::onNack(const ContentObject &content_object) { + uint32_t *payload = (uint32_t *)content_object.getPayload().data(); + uint32_t productionSeg = *payload; + uint32_t productionRate = *(++payload); + uint32_t nackSegment = content_object.getName().getSuffix(); + + // we synch the estimated production rate with the actual one + estimatedBw_ = (double)productionRate; + + // if(inflightInterests_[segmentNumber % + // default_values::default_buffer_size].retransmissions != 0){ ignore nacks for + // retransmissions + // return; + //} + + gotNack_ = true; + + if (productionSeg > nackSegment) { + // we are asking for stuff produced in the past + actualSegment_ = max(productionSeg + 1, actualSegment_); + if (currentState_ == RTC_NORMAL_STATE) { + currentState_ = RTC_SYNC_STATE; + // if we switch in SYNC mode we do not care about holes + // se we reset the data structure. going back to NORMAL + // mode will anable again the holes_ check. + holes_.clear(); + lastReceived_ = 0; + } + + computeMaxWindow(productionRate, 0); + increaseWindow(); + + if (nackedByProducer_.size() >= nackedByProducerMaxSize_) + nackedByProducer_.erase(nackedByProducer_.begin()); + nackedByProducer_.insert(nackSegment); + + } else if (productionSeg < nackSegment) { + gotFutureNack_++; + // we are asking stuff in the future + // example + // 10 12 13 14 15 16 17 + // ^ ^ ^ + // in prod nack actual + // in this example we sent up to segment 17 and we get a nack for segment 15 + // this means that we will get nack also for 16 17 + // and valid data for 13 14 + // so the next segment to ask is 15, because 13 and 14 will can back anyway + // we go back only in the case that the actual segment is really bigger than + // nack segment, other we do nothing + + actualSegment_ = min(actualSegment_, nackSegment); + + computeMaxWindow(productionRate, 0); + decreaseWindow(); + + if (currentState_ == RTC_SYNC_STATE) { + currentState_ = RTC_NORMAL_STATE; + } + } // equal should not happen +} + +void RTCTransportProtocol::onContentObject( + Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { + uint32_t payload_size = content_object->getPayload().length(); + uint32_t segmentNumber = content_object->getName().getSuffix(); + uint32_t pkt = segmentNumber & modMask_; + + // try to recover holes + // we can recover haoles with valid data, nacks or retransmitted packets + bool recoveredHole = false; + std::unordered_map<uint32_t, uint64_t>::const_iterator res = + holes_.find(segmentNumber); + if (res != holes_.end()) { + holes_.erase(res); + recoveredHole = true; + } + + if (payload_size == NACK_HEADER_SIZE) { + // Nacks always come form the producer, so we set the producerePathLabel_; + producerPathLabel_ = content_object->getPathLabel(); + if (inflightInterests_[pkt].retransmissions == 0) { + inflightInterestsCount_--; + onNack(*content_object); + updateDelayStats(*content_object); + } + + } else { + receivedData_++; + + avgPacketSize_ = + (ESTIMATED_PACKET_SIZE * avgPacketSize_) + + ((1 - ESTIMATED_PACKET_SIZE) * content_object->getPayload().length()); + + if (inflightInterests_[pkt].retransmissions == 0) { + inflightInterestsCount_--; + // we count only non retransmitted data in order to take into accunt only + // the transmition rate of the producer + receivedBytes_ += + content_object->headerSize() + content_object->payloadSize(); + updateDelayStats(*content_object); + + // handle holes + // the packet sequence make sense only in case of valid data (no nacks, no + // rtx) in RTC_NORMAL_STATE we should get all the packets in order, so if + // segmentNumber != lastReceived + 1 something happened + // if recoveredHole == true this is a packet recovered so we should do + // nothing + if (currentState_ == RTC_NORMAL_STATE && recoveredHole == false) { + if ((segmentNumber != lastReceived_ + 1) && + segmentNumber > lastReceived_) { + // we have holes in the sequence + for (uint32_t seq = lastReceived_ + 1; seq < segmentNumber; seq++) { + // the hole exists we do not insert it again + std::unordered_map<uint32_t, uint64_t>::const_iterator res = + holes_.find(seq); + if (res == holes_.end()) + holes_.insert(std::make_pair(seq, 0)); // 0 means never sent + } + } + } + + // this if should be always true + if (segmentNumber > lastReceived_) { + lastReceived_ = segmentNumber; + } + } + + returnContentToUser(*content_object); + increaseWindow(); + } + + scheduleNextInterest(); +} + +void RTCTransportProtocol::returnContentToUser( + const ContentObject &content_object) { + // return content to the user + Array a = content_object.getPayload(); + + uint8_t *start = ((uint8_t *)a.data()) + TIMESTAMP_SIZE; + unsigned size = a.length() - TIMESTAMP_SIZE; + + // set offset between hICN and RTP packets + uint16_t rtp_seq = ntohs(*(((uint16_t *)start) + 1)); + RTPhICN_offset_ = content_object.getName().getSuffix() - rtp_seq; + + content_buffer_->insert(content_buffer_->end(), start, start + size); + + ConsumerContentCallback on_payload = VOID_HANDLER; + socket_->getSocketOption(CONTENT_RETRIEVED, on_payload); + if (on_payload != VOID_HANDLER) { + on_payload(*dynamic_cast<ConsumerSocket *>(socket_), size, + std::make_error_code(std::errc(0))); + } +} + +uint32_t RTCTransportProtocol::hICN2RTP(uint32_t hicn_seq) { + return RTPhICN_offset_ - hicn_seq; +} + +uint32_t RTCTransportProtocol::RTP2hICN(uint32_t rtp_seq) { + return RTPhICN_offset_ + rtp_seq; +} + +void RTCTransportProtocol::processRtcpHeader(uint8_t *offset) { + uint8_t pkt_type = (*(offset + 1)); + switch (pkt_type) { + case RTCP_RR: // Receiver report + TRANSPORT_LOGI("got RR packet\n"); + break; + case RTCP_SR: // Sender report + TRANSPORT_LOGI("got SR packet\n"); + break; + case RTCP_SDES: // Description + processSDES(offset); + break; + case RTCP_RTPFB: // Transport layer FB message + processGenericNack(offset); + break; + case RTCP_PSFB: + processPli(offset); + break; + default: + errorParsingRtcpHeader(offset); + } +} + +void RTCTransportProtocol::errorParsingRtcpHeader(uint8_t *offset) { + uint8_t pt = (*(offset + 1)); + uint8_t code = ((*offset) & MASK_TYPE_CODE); + TRANSPORT_LOGE("Received unknwnon RTCP packet. Payload type = %u, code = %u", + pt, code); +} + +void RTCTransportProtocol::processSDES(uint8_t *offset) { + uint8_t code = ((*offset) & MASK_TYPE_CODE); + switch (code) { + case RTCP_SDES_CNAME: + TRANSPORT_LOGI("got SDES packet: CNAME\n"); + break; + default: + errorParsingRtcpHeader(offset); + } +} + +void RTCTransportProtocol::processPli(uint8_t *offset) { + if (((*offset) & MASK_TYPE_CODE) != RTCP_PSFB_PLI) { + errorParsingRtcpHeader(offset); + return; + } + + TRANSPORT_LOGI("got PLI packet\n"); +} + +void RTCTransportProtocol::processGenericNack(uint8_t *offset) { + if (((*offset) & MASK_TYPE_CODE) != RTCP_RTPFB_GENERIC_NACK) { + errorParsingRtcpHeader(offset); + return; + } + + std::vector<uint32_t> nacks; + + uint16_t header_lines = + ntohs(*(((uint16_t *)offset) + 1)) - + 2; // 2 is the number of header 32-bits words - 1 (RFC 4885) + uint8_t *payload = offset + RTPC_NACK_HEADER; // 12 bytes + for (uint16_t l = header_lines; l > 0; l--) { + nacks.push_back(RTP2hICN(ntohs(*((uint16_t *)payload)))); + + uint16_t BLP = ntohs(*(((uint16_t *)payload) + 1)); + + for (int bit = 0; bit < 15; bit++) { // 16 bits word to scan + if ((BLP >> bit) & 1) { + nacks.push_back(RTP2hICN((ntohs(*((uint16_t *)payload)) + bit + 1) % + MAX_RTCP_SEQ_NUMBER)); + } + } + + payload += 4; // go to the next line + } + + portal_->getIoService().post(std::bind( + &RTCTransportProtocol::scheduleAppNackRtx, this, std::move(nacks))); +} + +} // end namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc.h b/libtransport/src/hicn/transport/protocols/rtc.h new file mode 100755 index 000000000..249af6b99 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc.h @@ -0,0 +1,210 @@ +/* + * 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 <queue> +#include <set> +#include <unordered_map> + +#include <hicn/transport/protocols/protocol.h> +#include <hicn/transport/protocols/rtc_data_path.h> + +// algorithm state +#define RTC_SYNC_STATE 0 +#define RTC_NORMAL_STATE 1 +#define ROUNDS_IN_SYNC_BEFORE_SWITCH 3 + +// packet constants +#define INIT_PACKET_SIZE 1300 // bytes +#define HICN_PACKET_HEADER_SIZE 60 // bytes ipv6+tcp +#define NACK_HEADER_SIZE 8 // bytes +#define TIMESTAMP_SIZE 8 // bytes +#define RTC_INTEREST_LIFETIME 1000 // ms + +// controller constant +#define ROUND_LEN \ + 200 // ms interval of time on which we take decisions / measurements +#define MAX_RTX 128 +#define MIN_RTT_WIN 30 // rounds + +// cwin +#define INITIAL_CWIN 1 // packets +#define INITIAL_CWIN_MAX 100000 // packets +#define MIN_CWIN 5 // packets + +// statistics constants +#define BANDWIDTH_SLACK_FACTOR 1.5 +#define ESTIMATED_BW_ALPHA 0.7 +#define ESTIMATED_PACKET_SIZE 0.7 +#define ESTIMATED_LOSSES_ALPHA 0.8 +#define INTEREST_LIFETIME_REDUCTION_FACTOR 0.8 + +//#define MAX_LOSS_RATE 0.05 +//#define MAX_QUEUING_DELAY 200 //ms + +// cwin +#define INITIAL_CWIN 1 +#define MIN_CWIN 5 +#define WIN_DECREASE_FACTOR 0.8 +#define WIN_INCREASE_FACTOR 1.1 + +// protocol state +//#define RTC_CONGESTED_STATE 10 +//#define RTC_LOSSY_STATE 20 +//#define RTC_DELAY_STATE 30 +//#define RTC_NORMAL_STATE 40 + +// other constants +#define NANO_IN_A_SEC 1000000000 +#define MICRO_IN_A_SEC 1000000 +#define MILLI_IN_A_SEC 1000 + +// RTCP +#define MASK_RTCP_VERSION 192 +#define MASK_TYPE_CODE \ + 31 // this is RC in the RR/SR packet or FMT int the early feedback packets +#define RTPC_NACK_HEADER 12 // bytes +#define MAX_RTCP_SEQ_NUMBER 0xffff +#define RTCP_VERSION 2 +// RTCP TYPES +#define RTCP_SR 200 +#define RTCP_RR 201 +#define RTCP_SDES 202 +#define RTCP_RTPFB 205 +#define RTCP_PSFB 206 +// RTCP RC/FMT +#define RTCP_SDES_CNAME 1 +#define RTCP_RTPFB_GENERIC_NACK 1 +#define RTCP_PSFB_PLI 1 + +namespace transport { + +namespace protocol { + +struct sentInterest { + uint64_t transmissionTime; + uint8_t retransmissions; +}; + +class RTCTransportProtocol : public TransportProtocol { + public: + RTCTransportProtocol(interface::BaseSocket *icnet_socket); + + ~RTCTransportProtocol(); + + void start(utils::SharableVector<uint8_t> &content_buffer); + + void stop(); + + void resume(); + + void onRTCPPacket(uint8_t *packet, size_t len); + + private: + // algo functions + void reset(); + void checkRound(); + + // 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(); + void scheduleNextInterest(); + void scheduleAppNackRtx(std::vector<uint32_t> &nacks); + void onTimeout(Interest::Ptr &&interest); + void onNack(const ContentObject &content_object); + void onContentObject(Interest::Ptr &&interest, + ContentObject::Ptr &&content_object); + void returnContentToUser(const ContentObject &content_object); + + // RTCP functions + uint32_t hICN2RTP(uint32_t hicn_seq); + uint32_t RTP2hICN(uint32_t rtp_seq); + void processRtcpHeader(uint8_t *offset); + void errorParsingRtcpHeader(uint8_t *offset); + void processSDES(uint8_t *offset); + void processGenericNack(uint8_t *offset); + void processPli(uint8_t *offset); + + // controller var + std::chrono::steady_clock::time_point lastRoundBegin_; + // bool allPacketsInSync_; + // unsigned numberOfRoundsInSync_; + // unsigned numberOfCatchUpRounds_; + // bool catchUpPhase_; + unsigned currentState_; + + // uint32_t inProduction_; + + // cwin var + uint32_t currentCWin_; + uint32_t maxCWin_; + // uint32_t previousCWin_; + + // names/packets var + uint32_t actualSegment_; + int32_t RTPhICN_offset_; + uint32_t inflightInterestsCount_; + std::queue<uint32_t> interestRetransmissions_; + std::vector<sentInterest> inflightInterests_; + uint32_t nackedByProducerMaxSize_; + std::set<uint32_t> + nackedByProducer_; // this is used to avoid retransmissions from the + // application for pakets for which we already got a + // past NACK by the producer these packet are too old, + // they will never be retrived + std::shared_ptr<utils::SharableVector<uint8_t>> content_buffer_; + uint32_t modMask_; + + // stats + uint32_t receivedBytes_; + uint32_t sentInterest_; + uint32_t receivedData_; + uint32_t packetLost_; + double avgPacketSize_; + bool gotNack_; + uint32_t gotFutureNack_; + uint32_t roundsWithoutNacks_; + uint32_t producerPathLabel_; // XXX we pick only one path lable for the + // producer for now, assuming the usage of a + // single path this should be extended to a + // vector + std::unordered_map<uint32_t, std::shared_ptr<RTCDataPath>> pathTable_; + uint32_t roundCounter_; + // std::vector<uint64_t> minRTTwin_; + uint64_t minRtt_; + + std::unordered_map<uint32_t, uint64_t> holes_; + uint32_t lastReceived_; + + // CC var + double estimatedBw_; + double lossRate_; + double queuingDelay_; + unsigned protocolState_; +}; + +} // namespace protocol + +} // namespace transport diff --git a/libtransport/src/hicn/transport/protocols/rtc_data_path.cc b/libtransport/src/hicn/transport/protocols/rtc_data_path.cc new file mode 100755 index 000000000..6c9605fb2 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc_data_path.cc @@ -0,0 +1,85 @@ +/* + * 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/protocols/rtc_data_path.h> + +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(0.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; + + avg_owd = (avg_owd * (1 - ALPHA_RTC)) + (owd * ALPHA_RTC); +} + +void RTCDataPath::roundEnd() { + // compute queuing delay + queuing_delay = avg_owd - getMinOwd(); + + // 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; + } + + 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; + } + + OWDhistory_.pushBack(min_owd); + min_owd = INT_MAX; +} + +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/hicn/transport/protocols/rtc_data_path.h b/libtransport/src/hicn/transport/protocols/rtc_data_path.h new file mode 100755 index 000000000..ace16ff12 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/rtc_data_path.h @@ -0,0 +1,62 @@ +/* + * 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 <hicn/transport/utils/min_filter.h> +#include <climits> + +#define ALPHA_RTC 0.125 +#define HISTORY_LEN 30 + +namespace transport { + +namespace protocol { + +class RTCDataPath { + public: + RTCDataPath(); + + public: + void insertRttSample(uint64_t rtt); + void insertOwdSample(int64_t owd); + + uint64_t getMinRtt(); + + double getQueuingDealy(); + + 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; + + utils::MinFilter<uint64_t> RTThistory_; + utils::MinFilter<int64_t> OWDhistory_; +}; + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/test/CMakeLists.txt b/libtransport/src/hicn/transport/protocols/test/CMakeLists.txt new file mode 100755 index 000000000..6f9fdb9aa --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/test/CMakeLists.txt @@ -0,0 +1,10 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_transport_producer) + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach()
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc b/libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc new file mode 100755 index 000000000..204f2cbe2 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/test/test_transport_producer.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "../socket_producer.h" +#include "literals.h" + +#include <test.h> +#include <random> + +namespace transport { + +namespace protocol { + +namespace { +// The fixture for testing class Foo. +class ProducerTest : public ::testing::Test { + protected: + ProducerTest() : name_("b001::123|321"), producer_(io_service_) { + // You can do set-up work for each test here. + } + + virtual ~ProducerTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } + + Name name_; + asio::io_service io_service_; + ProducerSocket producer_; +}; + +} // namespace + +// Tests that the Foo::Bar() method does Abc. +TEST_F(ProducerTest, ProduceContent) { + std::string content(250000, '?'); + + producer_.registerPrefix(Prefix("b001::/64")); + producer_.produce(name_, reinterpret_cast<const uint8_t *>(content.data()), + content.size(), true); + producer_.setSocketOption(GeneralTransportOptions::CONTENT_OBJECT_EXPIRY_TIME, + 500000000_U32); + producer_.attach(); + producer_.serveForever(); +} + +} // namespace protocol + +} // namespace transport + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/vegas.cc b/libtransport/src/hicn/transport/protocols/vegas.cc new file mode 100755 index 000000000..b6d79bfcc --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas.cc @@ -0,0 +1,630 @@ +/* + * 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/errors/not_implemented_exception.h> +#include <hicn/transport/interfaces/socket_consumer.h> +#include <hicn/transport/protocols/vegas.h> +#include <hicn/transport/utils/literals.h> + +#include <cmath> + +namespace transport { + +namespace protocol { + +using namespace interface; + +VegasTransportProtocol::VegasTransportProtocol(BaseSocket *icnet_socket) + : TransportProtocol(icnet_socket), + is_final_block_number_discovered_(false), + final_block_number_(std::numeric_limits<uint32_t>::max()), + last_reassembled_segment_(0), + content_buffer_size_(0), + current_window_size_(default_values::min_window_size), + interests_in_flight_(0), + next_suffix_(0), + interest_retransmissions_(1 << default_values::log_2_default_buffer_size), + interest_timepoints_(1 << default_values::log_2_default_buffer_size), + retx_count_(0), + receive_buffer_(1 << default_values::log_2_default_buffer_size), + unverified_segments_(1 << default_values::log_2_default_buffer_size), + verified_manifests_(1 << default_values::log_2_default_buffer_size), + mask_((1 << default_values::log_2_default_buffer_size) - 1), + incremental_suffix_index_(0), + suffix_queue_completed_(false), + download_with_manifest_(false), + next_manifest_interval_(0_U16), + interest_tx_(0), + interest_count_(0), + byte_count_(0), + average_rtt_(0.0) { + portal_ = socket_->portal_; + incremental_suffix_index_++; +} + +VegasTransportProtocol::~VegasTransportProtocol() { stop(); } + +void VegasTransportProtocol::reset() { + portal_->setConsumerCallback(this); + + is_final_block_number_discovered_ = false; + interest_pool_index_ = 0; + final_block_number_ = std::numeric_limits<uint32_t>::max(); + next_suffix_ = 0; + interests_in_flight_ = 0; + last_reassembled_segment_ = 0; + content_buffer_size_ = 0; + content_buffer_->clear(); + interest_retransmissions_.clear(); + interest_retransmissions_.resize( + 1 << default_values::log_2_default_buffer_size, 0); + interest_timepoints_.clear(); + interest_timepoints_.resize(1 << default_values::log_2_default_buffer_size, + std::chrono::steady_clock::time_point()); + receive_buffer_.clear(); + unverified_segments_.clear(); + verified_manifests_.clear(); + next_manifest_interval_ = 0; + next_manifest_ = 0; + download_with_manifest_ = false; + incremental_suffix_index_ = 0; + + interest_tx_ = 0; + interest_count_ = 0; + byte_count_ = 0; + average_rtt_ = 0; + + // asio::io_service &io_service = portal_->getIoService(); + + // if (io_service.stopped()) { + // io_service.reset(); + // } +} + +void VegasTransportProtocol::start( + utils::SharableVector<uint8_t> &content_buffer) { + + if(is_running_) + return; + + socket_->t0_ = std::chrono::steady_clock::now(); + + is_running_ = true; + content_buffer_ = content_buffer.shared_from_this(); + + reset(); + + sendInterest(next_suffix_++); + portal_->runEventsLoop(); + removeAllPendingInterests(); + is_running_ = false; + +} + +void VegasTransportProtocol::resume(){ + if(is_running_) + return; + + is_running_ = true; + sendInterest(next_suffix_++); + portal_->runEventsLoop(); + removeAllPendingInterests(); + is_running_ = false; +} + +void VegasTransportProtocol::sendInterest(std::uint64_t next_suffix) { + auto interest = getInterest(); + socket_->network_name_.setSuffix(next_suffix); + interest->setName(socket_->network_name_); + + interest->setLifetime(uint32_t(socket_->interest_lifetime_)); + + if (socket_->on_interest_output_ != VOID_HANDLER) { + socket_->on_interest_output_(*socket_, *interest); + } + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + interests_in_flight_++; + interest_retransmissions_[next_suffix & mask_] = 0; + interest_timepoints_[next_suffix & mask_] = std::chrono::steady_clock::now(); + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); +} + +void VegasTransportProtocol::stop() { + is_running_ = false; + portal_->stopEventsLoop(); +} + +void VegasTransportProtocol::onContentSegment( + Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { + uint32_t incremental_suffix = content_object->getName().getSuffix(); + bool virtual_download = socket_->virtual_download_; + + if (verifyContentObject(*content_object)) { + byte_count_ += content_object->getPayload().length(); + + if (TRANSPORT_EXPECT_FALSE(content_object->testRst())) { + is_final_block_number_discovered_ = true; + final_block_number_ = incremental_suffix; + } + + if (!virtual_download) { + receive_buffer_.emplace( + std::make_pair(incremental_suffix, std::move(content_object))); + reassemble(); + } else if (TRANSPORT_EXPECT_FALSE(is_final_block_number_discovered_ && + incremental_suffix == + final_block_number_)) { + returnContentToUser(); + } + } else { + unverified_segments_.emplace( + std::make_pair(incremental_suffix, std::move(content_object))); + } +} + +void VegasTransportProtocol::afterContentReception( + const Interest &interest, const ContentObject &content_object) { + increaseWindow(); +} + +void VegasTransportProtocol::afterDataUnsatisfied(uint64_t segment) { + decreaseWindow(); +} + +void VegasTransportProtocol::scheduleNextInterests() { + if (is_running_) { + uint32_t next_suffix; + while (interests_in_flight_ < current_window_size_) { + if (download_with_manifest_) { + if (suffix_queue_.size() * 2 < current_window_size_ && + next_manifest_ < final_block_number_ && next_manifest_interval_) { + next_manifest_ += next_manifest_interval_; + sendInterest(next_manifest_); + continue; + } + + if (suffix_queue_.pop(next_suffix)) { + // next_suffix = suffix_queue_.front(); + sendInterest(next_suffix); + // suffix_queue_.pop_front(); + } else { + if (!suffix_queue_completed_) { + TRANSPORT_LOGE("Empty queue!!!!!!"); + } + break; + } + } else { + if (is_final_block_number_discovered_) { + if (next_suffix_ > final_block_number_) { + return; + } + } + + sendInterest(next_suffix_++); + } + } + } +} + +void VegasTransportProtocol::decreaseWindow() { + if (current_window_size_ > socket_->min_window_size_) { + current_window_size_ = std::ceil(current_window_size_ / 2); + socket_->current_window_size_ = current_window_size_; + } +} + +void VegasTransportProtocol::increaseWindow() { + if (current_window_size_ < socket_->max_window_size_) { + current_window_size_++; + socket_->max_window_size_ = current_window_size_; + } +}; + +void VegasTransportProtocol::changeInterestLifetime(uint64_t segment) { + std::chrono::steady_clock::duration duration = + std::chrono::steady_clock::now() - interest_timepoints_[segment]; + rtt_estimator_.addMeasurement( + std::chrono::duration_cast<std::chrono::microseconds>(duration)); + + RtoEstimator::Duration rto = rtt_estimator_.computeRto(); + std::chrono::milliseconds lifetime = + std::chrono::duration_cast<std::chrono::milliseconds>(rto); + + socket_->interest_lifetime_ = lifetime.count(); +} + +void VegasTransportProtocol::returnContentToUser() { + if (socket_->on_payload_retrieved_ != VOID_HANDLER) { + socket_->on_payload_retrieved_(*socket_, byte_count_, + std::make_error_code(std::errc(0))); + } + + stop(); +} + +void VegasTransportProtocol::onManifest( + std::unique_ptr<ContentObjectManifest> &&manifest) { + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + download_with_manifest_ = true; + + uint32_t segment = manifest->getName().getSuffix(); + + if (verifyManifest(*manifest)) { + manifest->decode(); + + if (TRANSPORT_EXPECT_TRUE(manifest->getVersion() == + core::ManifestVersion::VERSION_1)) { + switch (manifest->getManifestType()) { + case core::ManifestType::INLINE_MANIFEST: { + auto _it = manifest->getSuffixList().begin(); + auto _end = --manifest->getSuffixList().end(); + + if (TRANSPORT_EXPECT_FALSE(manifest->isFinalManifest())) { + _end++; + } + + // Get final block number + is_final_block_number_discovered_ = true; + final_block_number_ = manifest->getFinalBlockNumber(); + + for (; _it != _end; _it++) { + suffix_hash_map_[_it->first] = std::make_pair( + std::vector<uint8_t>(_it->second, _it->second + 32), + manifest->getHashAlgorithm()); + suffix_queue_.push(_it->first); + } + + next_manifest_interval_ = manifest->getSuffixList().size(); + + if (manifest->isFinalManifest()) { + suffix_queue_completed_ = true; + // Give it a try + if (verifier_thread_) { + asio::io_service &io_service = portal_->getIoService(); + io_service.post([this]() { scheduleNextInterests(); }); + } + } + + break; + } + case core::ManifestType::FLIC_MANIFEST: { + throw errors::NotImplementedException(); + } + case core::ManifestType::FINAL_CHUNK_NUMBER: { + throw errors::NotImplementedException(); + } + } + } + + if (!socket_->virtual_download_) { + receive_buffer_.emplace( + std::make_pair(segment, std::move(manifest->getPacket()))); + reassemble(); + } else { + if (segment >= final_block_number_) { + stop(); + } + } + } +} + +bool VegasTransportProtocol::verifyManifest( + const ContentObjectManifest &manifest) { + if (!socket_->verify_signature_) { + return true; + } + + bool is_data_secure = false; + + if (socket_->on_content_object_verification_ == VOID_HANDLER) { + is_data_secure = static_cast<bool>(socket_->verifier_.verify(manifest)); + } else if (socket_->on_content_object_verification_(*socket_, manifest)) { + is_data_secure = true; + } + + if (TRANSPORT_EXPECT_FALSE(!is_data_secure)) { + TRANSPORT_LOGE("Verification failed for %s\n", + manifest.getName().toString().c_str()); + } + + return is_data_secure; +} + +// TODO Add the name in the digest computation! +void VegasTransportProtocol::onContentObject( + Interest::Ptr &&interest, ContentObject::Ptr &&content_object) { + uint32_t incremental_suffix = content_object->getName().getSuffix(); + + std::chrono::microseconds rtt; + Time now = std::chrono::steady_clock::now(); + std::chrono::steady_clock::duration duration = + now - interest_timepoints_[incremental_suffix & mask_]; + rtt = std::chrono::duration_cast<std::chrono::microseconds>(duration); + + average_rtt_ = (0.7 * average_rtt_) + (0.3 * (double)rtt.count()); + + if (socket_->on_timer_expires_ != VOID_HANDLER) { + auto dt = std::chrono::duration_cast<TimeDuration>(now - socket_->t0_); + if (dt.count() > socket_->timer_interval_milliseconds_) { + socket_->on_timer_expires_(*socket_, byte_count_, dt, + current_window_size_, retx_count_, + std::round(average_rtt_)); + socket_->t0_ = std::chrono::steady_clock::now(); + } + } + + interests_in_flight_--; + + if (TRANSPORT_EXPECT_FALSE(!is_running_ || incremental_suffix == ~0_U64 || + receive_buffer_.find(incremental_suffix) != + receive_buffer_.end())) { + return; + } + + changeInterestLifetime(incremental_suffix); + + if (socket_->on_content_object_input_ != VOID_HANDLER) { + socket_->on_content_object_input_(*socket_, *content_object); + } + + if (socket_->on_interest_satisfied_ != VOID_HANDLER) { + socket_->on_interest_satisfied_(*socket_, *interest); + } + + if (!interest_retransmissions_[incremental_suffix & mask_]) { + afterContentReception(*interest, *content_object); + } + + if (TRANSPORT_EXPECT_FALSE(content_object->getPayloadType() == + PayloadType::MANIFEST)) { + // TODO Fix manifest!! + auto manifest = + std::make_unique<ContentObjectManifest>(std::move(content_object)); + + if (verifier_thread_ && incremental_suffix != 0) { + // verifier_thread_->add(std::bind(&VegasTransportProtocol::onManifest, + // this, std::move(manifest))); + } else { + onManifest(std::move(manifest)); + } + } else if (content_object->getPayloadType() == PayloadType::CONTENT_OBJECT) { + if (verifier_thread_) { + // verifier_thread_->add(std::bind(&VegasTransportProtocol::onContentSegment, + // this, std::move(content_object))); + } else { + onContentSegment(std::move(interest), std::move(content_object)); + } + } + + scheduleNextInterests(); +} + +bool VegasTransportProtocol::verifyContentObject( + const ContentObject &content_object) { + if (!dynamic_cast<ConsumerSocket *>(socket_)->verify_signature_) { + return true; + } + + uint64_t segment = content_object.getName().getSuffix(); + + bool ret = false; + + if (download_with_manifest_) { + auto it = suffix_hash_map_.find(segment); + if (it != suffix_hash_map_.end()) { + auto hash_type = static_cast<utils::CryptoHashType>(it->second.second); + auto data_packet_digest = content_object.computeDigest(it->second.second); + auto data_packet_digest_bytes = + data_packet_digest.getDigest<uint8_t>().data(); + std::vector<uint8_t> &manifest_digest_bytes = it->second.first; + + if (utils::CryptoHash::compareBinaryDigest(data_packet_digest_bytes, + manifest_digest_bytes.data(), + hash_type)) { + suffix_hash_map_.erase(it); + ret = true; + } else { + throw errors::RuntimeException( + "Verification failure policy has to be implemented."); + } + } + } else { + ret = static_cast<bool>( + dynamic_cast<ConsumerSocket *>(socket_)->verifier_.verify( + content_object)); + + if (!ret) { + throw errors::RuntimeException( + "Verification failure policy has to be implemented."); + } + } + + return ret; + ; +} + +void VegasTransportProtocol::onTimeout(Interest::Ptr &&interest) { + TRANSPORT_LOGW("Timeout on %s", interest->getName().toString().c_str()); + + if (TRANSPORT_EXPECT_FALSE(!is_running_)) { + return; + } + + interests_in_flight_--; + + uint64_t segment = interest->getName().getSuffix(); + + // Do not retransmit interests asking contents that do not exist. + if (is_final_block_number_discovered_) { + if (segment > final_block_number_) { + return; + } + } + + if (socket_->on_interest_timeout_ != VOID_HANDLER) { + socket_->on_interest_timeout_(*socket_, *interest); + } + + afterDataUnsatisfied(segment); + + if (TRANSPORT_EXPECT_TRUE(interest_retransmissions_[segment & mask_] < + socket_->max_retransmissions_)) { + retx_count_++; + + if (socket_->on_interest_retransmission_ != VOID_HANDLER) { + socket_->on_interest_retransmission_(*socket_, *interest); + } + + if (socket_->on_interest_output_ != VOID_HANDLER) { + socket_->on_interest_output_(*socket_, *interest); + } + + if (!is_running_) { + return; + } + + // retransmit + interests_in_flight_++; + interest_retransmissions_[segment & mask_]++; + + using namespace std::placeholders; + portal_->sendInterest(std::move(interest)); + } else { + TRANSPORT_LOGE("Stop: reached max retx limit."); + partialDownload(); + stop(); + } +} + +void VegasTransportProtocol::copyContent(const ContentObject &content_object) { + Array a = content_object.getPayload(); + + content_buffer_->insert(content_buffer_->end(), (uint8_t *)a.data(), + (uint8_t *)a.data() + a.length()); + + bool download_completed = + is_final_block_number_discovered_ && + content_object.getName().getSuffix() == final_block_number_; + + if (TRANSPORT_EXPECT_FALSE(download_completed || !is_running_)) { + // asio::io_service& io_service = portal_->getIoService(); + // io_service.post([this] () { + returnContentToUser(); + // }); + } +} + +void VegasTransportProtocol::reassemble() { + uint64_t index = last_reassembled_segment_; + auto it = receive_buffer_.find(index); + + do { + if (it->second->getPayloadType() == PayloadType::CONTENT_OBJECT) { + copyContent(*it->second); + receive_buffer_.erase(it); + } + + index = ++last_reassembled_segment_; + it = receive_buffer_.find(index); + } while (it != receive_buffer_.end()); +} + +void VegasTransportProtocol::partialDownload() { + if (!socket_->virtual_download_) { + reassemble(); + } + + if (socket_->on_payload_retrieved_ != VOID_HANDLER) { + socket_->on_payload_retrieved_( + *socket_, byte_count_, + std::make_error_code(std::errc(std::errc::io_error))); + } +} + +// TODO Check vegas protocol +// void VegasTransportProtocol::checkForFastRetransmission(const Interest +// &interest) { +// uint64_t segNumber = interest.getName().getSuffix(); +// received_segments_[segNumber] = true; +// fast_retransmitted_segments.erase(segNumber); + +// uint64_t possibly_lost_segment = 0; +// uint64_t highest_received_segment = received_segments_.rbegin()->first; + +// for (uint64_t i = 0; i <= highest_received_segment; i++) { +// if (received_segments_.find(i) == received_segments_.end()) { +// if (fast_retransmitted_segments.find(i) == +// fast_retransmitted_segments.end()) { +// possibly_lost_segment = i; +// uint8_t out_of_order_segments = 0; +// for (uint64_t j = i; j <= highest_received_segment; j++) { +// if (received_segments_.find(j) != received_segments_.end()) { +// out_of_order_segments++; +// if (out_of_order_segments >= +// default_values::max_out_of_order_segments) { +// fast_retransmitted_segments[possibly_lost_segment] = true; +// fastRetransmit(interest, possibly_lost_segment); +// } +// } +// } +// } +// } +// } +// } + +// void VegasTransportProtocol::fastRetransmit(const Interest &interest, +// uint32_t chunk_number) { +// if (interest_retransmissions_[chunk_number & mask_] < +// socket_->max_retransmissions_) { +// Name name = interest.getName(); +// name.setSuffix(chunk_number); + +// std::shared_ptr<Interest> retx_interest = +// std::make_shared<Interest>(name); + +// if (socket_->on_interest_retransmission_ != VOID_HANDLER) { +// socket_->on_interest_retransmission_(*socket_, *retx_interest); +// } + +// if (socket_->on_interest_output_ != VOID_HANDLER) { +// socket_->on_interest_output_(*socket_, *retx_interest); +// } + +// if (!is_running_) { +// return; +// } + +// interests_in_flight_++; +// interest_retransmissions_[chunk_number & mask_]++; + +// using namespace std::placeholders; +// portal_->sendInterest(std::move(retx_interest)); +// } +// } + +void VegasTransportProtocol::removeAllPendingInterests() { portal_->clear(); } + +} // end namespace protocol + +} // namespace transport diff --git a/libtransport/src/hicn/transport/protocols/vegas.h b/libtransport/src/hicn/transport/protocols/vegas.h new file mode 100755 index 000000000..7791ffc94 --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas.h @@ -0,0 +1,161 @@ +/* + * 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/protocols/protocol.h> +#include <hicn/transport/protocols/vegas_rto_estimator.h> +#include <hicn/transport/utils/event_thread.h> +#include <hicn/transport/utils/ring_buffer.h> +#include <hicn/transport/utils/sharable_vector.h> + +#include <map> + +namespace transport { + +namespace protocol { + +typedef utils::CircularFifo<uint32_t, 1024 * 128> SuffixQueue; +typedef std::chrono::time_point<std::chrono::steady_clock> Time; +typedef std::chrono::milliseconds TimeDuration; + +class VegasTransportProtocol : public TransportProtocol { + public: + VegasTransportProtocol(interface::BaseSocket *icnet_socket); + + virtual ~VegasTransportProtocol(); + + virtual void start(utils::SharableVector<uint8_t> &content_buffer) override; + + void stop() override; + + void resume() override; + + protected: + void reset(); + + void sendInterest(std::uint64_t next_suffix); + + void onContentSegment(Interest::Ptr &&interest, + ContentObject::Ptr &&content_object); + + bool verifyContentObject(const ContentObject &content_object); + + bool verifyManifest(const interface::ContentObjectManifest &manifest); + + virtual void onTimeout(Interest::Ptr &&interest) override; + + void onManifest(std::unique_ptr<interface::ContentObjectManifest> &&manifest); + + void onContentObject(Interest::Ptr &&interest, + ContentObject::Ptr &&content_object) override; + + virtual void changeInterestLifetime(uint64_t segment); + + void scheduleNextInterests(); + + virtual void decreaseWindow(); + + virtual void increaseWindow(); + + virtual void afterContentReception(const Interest &interest, + const ContentObject &content_object); + + virtual void afterDataUnsatisfied(uint64_t segment); + + void reassemble(); + + void returnContentToUser(); + + void partialDownload(); + + virtual void copyContent(const ContentObject &content_object); + + // virtual void checkForFastRetransmission(const Interest &interest); + + // void fastRetransmit(const Interest &interest, uint32_t chunk_number); + + void removeAllPendingInterests(); + + protected: + void handleTimeout(const std::error_code &ec); + + // reassembly variables + volatile bool is_final_block_number_discovered_; + std::atomic<uint64_t> final_block_number_; + uint64_t last_reassembled_segment_; + std::shared_ptr<utils::SharableVector<uint8_t>> content_buffer_; + size_t content_buffer_size_; + + // transmission variablesis_final_block_number_discovered_ + double current_window_size_; + double pending_window_size_; + uint64_t interests_in_flight_; + uint64_t next_suffix_; + std::vector<std::uint32_t> interest_retransmissions_; + std::vector<std::chrono::steady_clock::time_point> interest_timepoints_; + RtoEstimator rtt_estimator_; + + uint32_t retx_count_; + + // buffers + std::unordered_map<std::uint32_t, ContentObject::Ptr> + receive_buffer_; // verified segments by segment number + std::unordered_map<std::uint32_t, ContentObject::Ptr> + unverified_segments_; // used with embedded manifests + std::unordered_map<std::uint32_t, ContentObject::Ptr> + verified_manifests_; // by segment number + + std::uint16_t interest_pool_index_; + std::uint16_t mask_; + + // suffix randomization: since the suffixes in the manifests could not be in a + // sequential order, we need to map those suffixes into an ordered sequence. + std::unordered_map<std::uint64_t, std::uint64_t> + incremental_suffix_to_real_suffix_map_; + std::unordered_map<std::uint64_t, std::uint64_t> + real_suffix_to_incremental_suffix_map_; + std::uint32_t incremental_suffix_index_; + + // verification + std::unordered_map<uint32_t, std::pair<std::vector<uint8_t>, HashAlgorithm>> + suffix_hash_map_; + + // Fast Retransmission + std::map<uint64_t, bool> received_segments_; + std::unordered_map<uint64_t, bool> fast_retransmitted_segments; + + // Suffix queue + volatile bool suffix_queue_completed_; + SuffixQueue suffix_queue_; + + volatile bool download_with_manifest_; + uint32_t next_manifest_; + std::atomic<uint16_t> next_manifest_interval_; + + std::unique_ptr<utils::EventThread> verifier_thread_; + + uint32_t interest_tx_; + uint32_t interest_count_; + + uint64_t byte_count_; + double average_rtt_; + + std::unordered_map<uint32_t, uint64_t> sign_time_; +}; + +} // namespace protocol + +} // end namespace transport diff --git a/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc new file mode 100755 index 000000000..f5f797bbe --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.cc @@ -0,0 +1,57 @@ +/* + * 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_options_default_values.h> +#include <hicn/transport/protocols/vegas_rto_estimator.h> + +#include <algorithm> +#include <cmath> + +namespace transport { + +namespace protocol { + +using namespace interface; + +RtoEstimator::RtoEstimator(Duration min_rto) + : smoothed_rtt_(RtoEstimator::getInitialRtt().count()), + rtt_variation_(0), + first_measurement_(true), + last_rto_(min_rto.count()) {} + +void RtoEstimator::addMeasurement(Duration rtt) { + double duration = static_cast<double>(rtt.count()); + if (first_measurement_) { + smoothed_rtt_ = duration; + rtt_variation_ = duration / 2; + first_measurement_ = false; + } else { + rtt_variation_ = (1 - default_values::beta) * rtt_variation_ + + default_values::beta * std::abs(smoothed_rtt_ - duration); + smoothed_rtt_ = (1 - default_values::alpha) * smoothed_rtt_ + + default_values::alpha * duration; + } +} + +RtoEstimator::Duration RtoEstimator::computeRto() const { + double rto = smoothed_rtt_ + + std::max(double(default_values::clock_granularity.count()), + default_values::k* rtt_variation_); + return Duration(static_cast<Duration::rep>(rto)); +} + +} // end namespace protocol + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h new file mode 100755 index 000000000..e84afc49c --- /dev/null +++ b/libtransport/src/hicn/transport/protocols/vegas_rto_estimator.h @@ -0,0 +1,48 @@ +/* + * 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 <chrono> + +// Implementation inspired from RFC6298 +// (https://tools.ietf.org/search/rfc6298#ref-JK88) + +namespace transport { + +namespace protocol { + +class RtoEstimator { + public: + typedef std::chrono::microseconds Duration; + + static Duration getInitialRtt() { return std::chrono::seconds(1); } + + RtoEstimator(Duration min_rto = std::chrono::seconds(1)); + + void addMeasurement(Duration measure); + + Duration computeRto() const; + + private: + double smoothed_rtt_; + double rtt_variation_; + bool first_measurement_; + double last_rto_; +}; + +} // end namespace protocol + +} // end namespace transport
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/CMakeLists.txt b/libtransport/src/hicn/transport/utils/CMakeLists.txt new file mode 100755 index 000000000..088fb5862 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/CMakeLists.txt @@ -0,0 +1,76 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/uri.cc + ${CMAKE_CURRENT_SOURCE_DIR}/daemonizator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h + ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc + ${CMAKE_CURRENT_SOURCE_DIR}/identity.cc + ${CMAKE_CURRENT_SOURCE_DIR}/log.cc + ${CMAKE_CURRENT_SOURCE_DIR}/membuf.cc + ${CMAKE_CURRENT_SOURCE_DIR}/content_store.cc +) + + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/array.h + ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.h + ${CMAKE_CURRENT_SOURCE_DIR}/hash.h + ${CMAKE_CURRENT_SOURCE_DIR}/uri.h + ${CMAKE_CURRENT_SOURCE_DIR}/daemonizator.h + ${CMAKE_CURRENT_SOURCE_DIR}/sharable_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/branch_prediction.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h + ${CMAKE_CURRENT_SOURCE_DIR}/deadline_timer.h + ${CMAKE_CURRENT_SOURCE_DIR}/ring_buffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h + ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h + ${CMAKE_CURRENT_SOURCE_DIR}/stream_buffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/endianess.h + ${CMAKE_CURRENT_SOURCE_DIR}/literals.h + ${CMAKE_CURRENT_SOURCE_DIR}/signer.h + ${CMAKE_CURRENT_SOURCE_DIR}/verifier.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hasher.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.h + ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/identity.h + ${CMAKE_CURRENT_SOURCE_DIR}/conversions.h + ${CMAKE_CURRENT_SOURCE_DIR}/linux.h + ${CMAKE_CURRENT_SOURCE_DIR}/log.h + ${CMAKE_CURRENT_SOURCE_DIR}/event_thread.h + ${CMAKE_CURRENT_SOURCE_DIR}/object_pool.h + ${CMAKE_CURRENT_SOURCE_DIR}/membuf.h + ${CMAKE_CURRENT_SOURCE_DIR}/spinlock.h + ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h + ${CMAKE_CURRENT_SOURCE_DIR}/key_id.h +) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.h + ${CMAKE_CURRENT_SOURCE_DIR}/fd_deadline_timer.h + ) + + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.cc + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/libtransport/src/hicn/transport/utils/array.h b/libtransport/src/hicn/transport/utils/array.h new file mode 100755 index 000000000..a3a66e498 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/array.h @@ -0,0 +1,62 @@ +/* + * 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/portability/portability.h> + +#include <cstddef> + +namespace utils { + +template <typename T> +class Array { + public: + explicit Array(const T *array, size_t size) : array_(array), size_(size) { + this->array_ = array; + this->size_ = size; + } + + Array() : array_(nullptr), size_(0) { + this->array_ = nullptr; + this->size_ = 0; + } + + TRANSPORT_ALWAYS_INLINE const T *data() const { return array_; } + + TRANSPORT_ALWAYS_INLINE T *writableData() const { + return const_cast<T *>(array_); + } + + TRANSPORT_ALWAYS_INLINE std::size_t length() const { return size_; } + + TRANSPORT_ALWAYS_INLINE Array &setData(const T *data) { + array_ = data; + return *this; + } + + TRANSPORT_ALWAYS_INLINE Array &setSize(std::size_t size) { + size_ = size; + return *this; + } + + TRANSPORT_ALWAYS_INLINE bool empty() { return !size_; } + + private: + const T *array_; + std::size_t size_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/branch_prediction.h b/libtransport/src/hicn/transport/utils/branch_prediction.h new file mode 100755 index 000000000..b12282fe8 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/branch_prediction.h @@ -0,0 +1,22 @@ +/* + * 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 + +#undef TRANSPORT_EXPECT_TRUE +#undef TRANSPORT_EXPECT_FALSE + +#define TRANSPORT_EXPECT_TRUE(x) __builtin_expect((x), 1) +#define TRANSPORT_EXPECT_FALSE(x) __builtin_expect((x), 0)
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/content_store.cc b/libtransport/src/hicn/transport/utils/content_store.cc new file mode 100755 index 000000000..4c7637dad --- /dev/null +++ b/libtransport/src/hicn/transport/utils/content_store.cc @@ -0,0 +1,109 @@ +/* + * 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 <hicn/transport/core/interest.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/utils/content_store.h> + +namespace utils { + +ContentStore::ContentStore(std::size_t max_packets) + : max_content_store_size_(max_packets) {} + +ContentStore::~ContentStore() {} + +void ContentStore::insert( + const std::shared_ptr<ContentObject> &content_object) { + if (max_content_store_size_ == 0) { + return; + } + + std::unique_lock<std::mutex> lock(cs_mutex_); + + if (TRANSPORT_EXPECT_FALSE(content_store_hash_table_.size() != + lru_list_.size())) { + TRANSPORT_LOGW("Inconsistent size!!!!"); + TRANSPORT_LOGW("Hash Table: %zu |||| FIFO List: %zu", + content_store_hash_table_.size(), lru_list_.size()); + } + + // Check if the content can be cached + if (content_object->getLifetime() > 0) { + if (content_store_hash_table_.size() >= max_content_store_size_) { + content_store_hash_table_.erase(lru_list_.back()); + lru_list_.pop_back(); + } + + // Insert new item + + auto it = content_store_hash_table_.find(content_object->getName()); + if (it != content_store_hash_table_.end()) { + lru_list_.erase(it->second.second); + content_store_hash_table_.erase(content_object->getName()); + } + + lru_list_.push_front(std::cref(content_object->getName())); + auto pos = lru_list_.begin(); + content_store_hash_table_[content_object->getName()] = ContentStoreEntry( + ObjectTimeEntry(content_object, std::chrono::steady_clock::now()), pos); + } +} + +const std::shared_ptr<ContentObject> &ContentStore::find( + const Interest &interest) { + std::unique_lock<std::mutex> lock(cs_mutex_); + auto it = content_store_hash_table_.find(interest.getName()); + if (it != content_store_hash_table_.end()) { + // if (std::chrono::duration_cast<std::chrono::milliseconds>( + // std::chrono::steady_clock::now() - it->second.first.second).count() + // < it->second.first.first->getLifetime() || + // it->second.first.first->getLifetime() == + // default_values::never_expire_time) { + return it->second.first.first; + // } + } + + return empty_reference_; +} + +void ContentStore::erase(const Name &exact_name) { + std::unique_lock<std::mutex> lock(cs_mutex_); + auto it = content_store_hash_table_.find(exact_name); + lru_list_.erase(it->second.second); + content_store_hash_table_.erase(exact_name); +} + +void ContentStore::setLimit(size_t max_packets) { + max_content_store_size_ = max_packets; +} + +std::size_t ContentStore::getLimit() const { return max_content_store_size_; } + +std::size_t ContentStore::size() const { + return content_store_hash_table_.size(); +} + +void ContentStore::printContent() { + for (auto &item : content_store_hash_table_) { + if (item.second.first.first->getPayloadType() == + transport::core::PayloadType::MANIFEST) { + TRANSPORT_LOGI("Manifest: %s\n", + item.second.first.first->getName().toString().c_str()); + } + } +} + +} // end namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/content_store.h b/libtransport/src/hicn/transport/utils/content_store.h new file mode 100755 index 000000000..ab4963fff --- /dev/null +++ b/libtransport/src/hicn/transport/utils/content_store.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/interfaces/socket.h> + +#include <mutex> + +namespace transport { + +namespace core { +class Name; +class ContentObject; +class Interest; +} // namespace core + +} // namespace transport + +namespace utils { + +using Name = transport::core::Name; +using ContentObject = transport::core::ContentObject; +using Interest = transport::core::Interest; + +typedef std::pair<std::shared_ptr<ContentObject>, + std::chrono::steady_clock::time_point> + ObjectTimeEntry; +typedef std::pair<ObjectTimeEntry, + std::list<std::reference_wrapper<const Name>>::iterator> + ContentStoreEntry; +typedef std::list<std::reference_wrapper<const Name>> LRUList; +typedef std::unordered_map<Name, ContentStoreEntry> ContentStoreHashTable; + +class ContentStore { + public: + explicit ContentStore(std::size_t max_packets = 65536); + + ~ContentStore(); + + void insert(const std::shared_ptr<ContentObject> &content_object); + + const std::shared_ptr<ContentObject> &find(const Interest &interest); + + void erase(const Name &exact_name); + + void setLimit(size_t max_packets); + + size_t getLimit() const; + + size_t size() const; + + void printContent(); + + private: + ContentStoreHashTable content_store_hash_table_; + LRUList lru_list_; + std::shared_ptr<ContentObject> empty_reference_; + std::size_t max_content_store_size_; + std::mutex cs_mutex_; +}; + +} // end namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/conversions.h b/libtransport/src/hicn/transport/utils/conversions.h new file mode 100755 index 000000000..24b529206 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/conversions.h @@ -0,0 +1,37 @@ +/* + * 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/portability/portability.h> + +#include <stdio.h> +#include <cstdint> +#include <string> + +namespace utils { + +static TRANSPORT_ALWAYS_INLINE int convertStringToMacAddress( + const std::string& mac_address, uint8_t* mac_byte_array) { + const char* mac = mac_address.c_str(); + + sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac_byte_array[0], + &mac_byte_array[1], &mac_byte_array[2], &mac_byte_array[3], + &mac_byte_array[4], &mac_byte_array[5]); + + return 0; +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_hash.h b/libtransport/src/hicn/transport/utils/crypto_hash.h new file mode 100755 index 000000000..0c15c8bda --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_hash.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/array.h> +#include <hicn/transport/utils/crypto_hash_type.h> + +extern "C" { +#include <parc/security/parc_CryptoHash.h> +}; + +#include <cstring> +#include <unordered_map> + +namespace utils { + +class CryptoHasher; + +struct EnumClassHash { + template <typename T> + std::size_t operator()(T t) const { + return static_cast<std::size_t>(t); + } +}; + +static std::unordered_map<CryptoHashType, std::size_t, EnumClassHash> + hash_size_map = {{CryptoHashType::SHA_256, 32}, + {CryptoHashType::CRC32C, 4}, + {CryptoHashType::SHA_512, 64}}; + +class Signer; +class Verifier; + +class CryptoHash { + friend class CryptoHasher; + friend class Signer; + friend class Verifier; + + public: + CryptoHash() : hash_(nullptr) {} + + CryptoHash(const CryptoHash& other) { + if (other.hash_) { + hash_ = parcCryptoHash_Acquire(other.hash_); + } + } + + CryptoHash(CryptoHash&& other) { + if (other.hash_) { + hash_ = parcCryptoHash_Acquire(other.hash_); + } + } + + template <typename T> + CryptoHash(const T* buffer, std::size_t length, CryptoHashType hash_type) { + hash_ = parcCryptoHash_CreateFromArray( + static_cast<PARCCryptoHashType>(hash_type), buffer, length); + } + + ~CryptoHash() { + if (hash_) { + parcCryptoHash_Release(&hash_); + } + } + + CryptoHash& operator=(const CryptoHash& other) { + if (other.hash_) { + hash_ = parcCryptoHash_Acquire(other.hash_); + } + + return *this; + } + + template <typename T> + utils::Array<T> getDigest() const { + return utils::Array<T>( + static_cast<T*>(parcBuffer_Overlay(parcCryptoHash_GetDigest(hash_), 0)), + parcBuffer_Remaining(parcCryptoHash_GetDigest(hash_))); + } + + CryptoHashType getType() { + return static_cast<CryptoHashType>(parcCryptoHash_GetDigestType(hash_)); + } + + template <typename T> + static bool compareBinaryDigest(const T* digest1, const T* digest2, + CryptoHashType hash_type) { + if (hash_size_map.find(hash_type) == hash_size_map.end()) { + return false; + } + + return !static_cast<bool>( + std::memcmp(digest1, digest2, hash_size_map[hash_type])); + } + + private: + PARCCryptoHash* hash_; +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_hash_type.h b/libtransport/src/hicn/transport/utils/crypto_hash_type.h new file mode 100755 index 000000000..b7597e208 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_hash_type.h @@ -0,0 +1,31 @@ +/* + * 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 + +extern "C" { +#include <parc/security/parc_CryptoHashType.h> +}; + +namespace utils { + +enum class CryptoHashType : uint8_t { + SHA_256 = PARCCryptoHashType_SHA256, + SHA_512 = PARCCryptoHashType_SHA512, + CRC32C = PARCCryptoHashType_CRC32C, + NULL_HASH = PARCCryptoHashType_NULL +}; + +}
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_hasher.h b/libtransport/src/hicn/transport/utils/crypto_hasher.h new file mode 100755 index 000000000..c34a26fac --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_hasher.h @@ -0,0 +1,68 @@ +/* + * 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/crypto_hash.h> + +extern "C" { +#include <parc/security/parc_CryptoHasher.h> +}; + +namespace utils { + +class CryptoHasher { + public: + CryptoHasher(CryptoHashType hash_type) + : hasher_(parcCryptoHasher_Create( + static_cast<PARCCryptoHashType>(hash_type))), + managed_(true) {} + + CryptoHasher(PARCCryptoHasher* hasher) : hasher_(hasher), managed_(false) {} + + ~CryptoHasher() { + if (managed_) { + parcCryptoHasher_Release(&hasher_); + } + } + + CryptoHasher& init() { + if (parcCryptoHasher_Init(hasher_) == -1) { + throw errors::RuntimeException("Cryptohash init failed."); + } + + return *this; + } + + template <typename T> + CryptoHasher& updateBytes(const T* buffer, std::size_t length) { + if (parcCryptoHasher_UpdateBytes(hasher_, buffer, length) == -1) { + throw errors::RuntimeException("Cryptohash updateBytes failed."); + } + return *this; + } + + CryptoHash finalize() { + CryptoHash hash; + hash.hash_ = parcCryptoHasher_Finalize(hasher_); + return hash; + } + + private: + PARCCryptoHasher* hasher_; + bool managed_; +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/crypto_suite.h b/libtransport/src/hicn/transport/utils/crypto_suite.h new file mode 100755 index 000000000..8ae32b846 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/crypto_suite.h @@ -0,0 +1,35 @@ +/* + * 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 + +extern "C" { +#include <parc/security/parc_CryptoSuite.h> +}; + +namespace utils { + +enum class CryptoSuite : uint8_t { + RSA_SHA256 = PARCCryptoSuite_RSA_SHA256, + DSA_SHA256 = PARCCryptoSuite_DSA_SHA256, + RSA_SHA512 = PARCCryptoSuite_RSA_SHA512, + HMAC_SHA256 = PARCCryptoSuite_HMAC_SHA256, + HMAC_SHA512 = PARCCryptoSuite_HMAC_SHA512, + NULL_CRC32C = PARCCryptoSuite_NULL_CRC32C, + ECDSA_256K1 = PARCCryptoSuite_ECDSA_SHA256, + UNKNOWN = PARCCryptoSuite_UNKNOWN +}; + +} diff --git a/libtransport/src/hicn/transport/utils/daemonizator.cc b/libtransport/src/hicn/transport/utils/daemonizator.cc new file mode 100755 index 000000000..d9b3109af --- /dev/null +++ b/libtransport/src/hicn/transport/utils/daemonizator.cc @@ -0,0 +1,73 @@ +/* + * 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/errors/runtime_exception.h> +#include <hicn/transport/utils/daemonizator.h> +#include <hicn/transport/utils/log.h> + +#include <sys/stat.h> +#include <unistd.h> + +namespace utils { + +void Daemonizator::daemonize(bool close_fds) { + pid_t process_id = 0; + pid_t sid = 0; + + // Create child process + process_id = fork(); + + // Indication of fork() failure + if (process_id < 0) { + throw errors::RuntimeException("Fork failed."); + } + + // PARENT PROCESS. Need to kill it. + if (process_id > 0) { + TRANSPORT_LOGE("Process id of child process %d", process_id); + // return success in exit status + exit(EXIT_SUCCESS); + } + + // unmask the file mode + umask(0); + + // set new session + sid = setsid(); + if (sid < 0) { + // Return failure + exit(EXIT_FAILURE); + } + + // Change the current working directory to root. + int ret = chdir("/"); + + if (ret < 0) { + throw errors::RuntimeException("Error changing working directory to root"); + } + + // Close stdin. Redirect stdout and stderr to file if possible + + if (close_fds) { + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + close(STDIN_FILENO); + + // Really start application +} + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/daemonizator.h b/libtransport/src/hicn/transport/utils/daemonizator.h new file mode 100755 index 000000000..a21ce8a7b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/daemonizator.h @@ -0,0 +1,25 @@ +/* + * 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 <cstdlib> +namespace utils { + +class Daemonizator { + public: + static void daemonize(bool close_fds = true); +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/deadline_timer.h b/libtransport/src/hicn/transport/utils/deadline_timer.h new file mode 100755 index 000000000..61f906141 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/deadline_timer.h @@ -0,0 +1,114 @@ +/* + * 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/event_reactor.h> + +#include <chrono> +#include <cstddef> +#include <cstring> +#include <utility> + +namespace std { +namespace chrono { +namespace detail { + +template <typename From, typename To> +struct posix_duration_cast; + +// chrono -> timespec caster +template <typename Rep, typename Period> +struct posix_duration_cast<std::chrono::duration<Rep, Period>, + struct timespec> { + static struct timespec cast(std::chrono::duration<Rep, Period> const &d) { + struct timespec tv; + + std::chrono::seconds const sec = + std::chrono::duration_cast<std::chrono::seconds>(d); + + tv.tv_sec = sec.count(); + tv.tv_nsec = + std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count(); + + return tv; + } +}; + +// timespec -> chrono caster +template <typename Rep, typename Period> +struct posix_duration_cast<struct timespec, + std::chrono::duration<Rep, Period>> { + static std::chrono::duration<Rep, Period> cast(struct timespec const &tv) { + return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>( + std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec)); + } +}; + +} // namespace detail + +// chrono -> timespec +template <typename T, typename Rep, typename Period> +auto duration_cast(std::chrono::duration<Rep, Period> const &d) -> + typename std::enable_if<std::is_same<T, struct timespec>::value, + struct timespec>::type { + return detail::posix_duration_cast<std::chrono::duration<Rep, Period>, + timespec>::cast(d); +} + +// timespec -> chrono +template <typename Duration> +Duration duration_cast(struct timespec const &tv) { + return detail::posix_duration_cast<struct timespec, Duration>::cast(tv); +} + +} // namespace chrono +} // namespace std + +namespace utils { + +template <typename Implementation> +class DeadlineTimer { + public: + virtual ~DeadlineTimer() = default; + + template <typename WaitHandler> + void asyncWait(WaitHandler &&callback) { + static_cast<Implementation *>(this)->asyncWaitImpl( + std::forward<WaitHandler>(callback)); + } + + void wait() { static_cast<Implementation *>(this)->waitImpl(); } + + template <typename T, typename R> + void expiresFromNow(std::chrono::duration<T, R> &&duration) { + static_cast<Implementation *>(this)->expiresFromNowImpl( + std::forward<std::chrono::duration<T, R>>(duration)); + } + + template <typename TimePoint, + typename = typename std::enable_if< + std::is_same<std::remove_reference_t<TimePoint>, + std::chrono::steady_clock::time_point>::value, + TimePoint>::type> + void expiresAt(TimePoint &&time_point) { + static_cast<Implementation *>(this)->expiresAtImpl( + std::forward<TimePoint>(time_point)); + } + + void cancel() { static_cast<Implementation *>(this)->cancelImpl(); } +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/endianess.h b/libtransport/src/hicn/transport/utils/endianess.h new file mode 100755 index 000000000..a3ec21c90 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/endianess.h @@ -0,0 +1,136 @@ +/* + * 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/portability/portability.h> + +#include <arpa/inet.h> +#include <cstring> + +namespace utils { + +namespace { + +template <size_t Size> +struct uint_types_by_size; + +#define GENERATOR(sz, fn) \ + static TRANSPORT_ALWAYS_INLINE uint##sz##_t byteswap_gen(uint##sz##_t v) { \ + return fn(v); \ + } \ + template <> \ + struct uint_types_by_size<sz / 8> { \ + using type = uint##sz##_t; \ + }; + +GENERATOR(8, uint8_t) +#ifdef _MSC_VER +GENERATOR(64, _byteswap_uint64) +GENERATOR(32, _byteswap_ulong) +GENERATOR(16, _byteswap_ushort) +#else +GENERATOR(64, __builtin_bswap64) +GENERATOR(32, __builtin_bswap32) +GENERATOR(16, __builtin_bswap16) +#endif + +template <typename T> +struct EndianInt { + static_assert( + (std::is_integral<T>::value && !std::is_same<T, bool>::value) || + std::is_floating_point<T>::value, + "template type parameter must be non-bool integral or floating point"); + + static T swap(T x) { + // we implement this with memcpy because that is defined behavior in C++ + // we rely on compilers to optimize away the memcpy calls + constexpr auto s = sizeof(T); + using B = typename uint_types_by_size<s>::type; + B b; + std::memcpy(&b, &x, s); + b = byteswap_gen(b); + std::memcpy(&x, &b, s); + return x; + } + static T big(T x) { + return portability::little_endian_arch ? EndianInt::swap(x) : x; + } + static T little(T x) { + return portability::big_endian_arch ? EndianInt::swap(x) : x; + } +}; + +} // namespace + +// big* convert between native and big-endian representations +// little* convert between native and little-endian representations +// swap* convert between big-endian and little-endian representations +// +// ntohs, htons == big16 +// ntohl, htonl == big32 +#define GENERATOR1(fn, t, sz) \ + static t fn##sz(t x) { return fn<t>(x); } + +#define GENERATOR2(t, sz) \ + GENERATOR1(swap, t, sz) \ + GENERATOR1(big, t, sz) \ + GENERATOR1(little, t, sz) + +#define GENERATOR3(sz) \ + GENERATOR2(uint##sz##_t, sz) \ + GENERATOR2(int##sz##_t, sz) + +class Endian { + public: + enum class Order : uint8_t { LITTLE, BIG }; + + static constexpr Order order = + portability::little_endian_arch ? Order::LITTLE : Order::BIG; + + template <typename T> + static T swap(T x) { + return EndianInt<T>::swap(x); + } + + template <typename T> + static T big(T x) { + return EndianInt<T>::big(x); + } + + template <typename T> + static T little(T x) { + return EndianInt<T>::little(x); + } + +#if !defined(__ANDROID__) + GENERATOR3(64) + GENERATOR3(32) + GENERATOR3(16) + GENERATOR3(8) +#endif +}; + +template <typename T> +static TRANSPORT_ALWAYS_INLINE T ntoh(T x) { + return Endian::order == Endian::Order::LITTLE ? Endian::little(x) : x; +} + +template <typename T> +static TRANSPORT_ALWAYS_INLINE T hton(T x) { + return Endian::order == Endian::Order::LITTLE ? Endian::big(x) : x; +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc b/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc new file mode 100755 index 000000000..81b471857 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/utils/branch_prediction.h> +#include <hicn/transport/utils/epoll_event_reactor.h> +#include <hicn/transport/utils/fd_deadline_timer.h> + +#include <signal.h> +#include <unistd.h> +#include <iostream> + +namespace utils { + +EpollEventReactor::EpollEventReactor() + : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {} + +EpollEventReactor::~EpollEventReactor() { close(epoll_fd_); } + +int EpollEventReactor::addFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + TRANSPORT_LOGE("invalid fd %d", fd); + return -1; + } + + struct epoll_event evt; + std::memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) < + 0)) { + TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); + return -1; + } + + return 0; +} + +int EpollEventReactor::addFileDescriptor(int fd, uint32_t events, + EventCallback &callback) { + auto it = event_callback_map_.find(fd); + event_callback_map_[fd] = callback; + if (it != event_callback_map_.end()) { + event_callback_map_[fd] = callback; + } else { + return addFileDescriptor(fd, events); + } + + return 0; +} + +int EpollEventReactor::addFileDescriptor(int fd, uint32_t events, + EventCallback &&callback) { + auto it = event_callback_map_.find(fd); + event_callback_map_[fd] = callback; + if (it != event_callback_map_.end()) { + event_callback_map_[fd] = callback; + } else { + return addFileDescriptor(fd, events); + } + + return 0; +} + +int EpollEventReactor::modFileDescriptor(int fd, uint32_t events) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + TRANSPORT_LOGE("invalid fd %d", fd); + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + evt.events = events; + evt.data.fd = fd; + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) < + 0)) { + TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); + return -1; + } + + return 0; +} + +std::size_t EpollEventReactor::mapSize() { return event_callback_map_.size(); } + +int EpollEventReactor::delFileDescriptor(int fd) { + if (TRANSPORT_EXPECT_FALSE(fd < 0)) { + TRANSPORT_LOGE("invalid fd %d", fd); + return -1; + } + + struct epoll_event evt; + memset(&evt, 0, sizeof(evt)); + + if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) < + 0)) { + TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd); + return -1; + } + + event_callback_map_.erase(fd); + + return 0; +} + +void EpollEventReactor::runEventLoop(int timeout) { + Event evt[128]; + int en = 0; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + while (run_event_loop_) { + memset(&evt, 0, sizeof(evt)); + + en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset); + + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + TRANSPORT_LOGE("epoll_pwait: %s", strerror(errno)); + return; + } + + for (int i = 0; i < en; i++) { + if (evt[i].data.fd > 0) { + auto it = event_callback_map_.find(evt[i].data.fd); + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd); + } else { + event_callback_map_[evt[i].data.fd](evt[i]); + } + } else { + TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd); + } + } + } +} + +void EpollEventReactor::runOneEvent() { + Event evt; + int en = 0; + + // evt.events = EPOLLIN | EPOLLOUT; + sigset_t sigset; + sigemptyset(&sigset); + + memset(&evt, 0, sizeof(evt)); + + en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset); + + if (TRANSPORT_EXPECT_FALSE(en < 0)) { + TRANSPORT_LOGE("epoll_pwait: %s", strerror(errno)); + return; + } + + if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) { + auto it = event_callback_map_.find(evt.data.fd); + if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) { + TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd); + } else { + event_callback_map_[evt.data.fd](evt); + } + } else { + TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd); + } +} + +void EpollEventReactor::stop() { run_event_loop_ = false; } + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/epoll_event_reactor.h b/libtransport/src/hicn/transport/utils/epoll_event_reactor.h new file mode 100755 index 000000000..bb4db3ee7 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/epoll_event_reactor.h @@ -0,0 +1,65 @@ +/* + * 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/event_reactor.h> + +#include <sys/epoll.h> +#include <cstddef> +#include <functional> +#include <mutex> +#include <unordered_map> + +#define FD_NUMBER 20000 + +namespace utils { + +typedef struct epoll_event Event; +typedef std::function<int(const Event &)> EventCallback; +typedef std::unordered_map<int, EventCallback> EventCallbackMap; + +class EpollEventReactor : public EventReactor { + public: + explicit EpollEventReactor(); + + ~EpollEventReactor(); + + int addFileDescriptor(int fd, uint32_t events, EventCallback &callback); + + int addFileDescriptor(int fd, uint32_t events, EventCallback &&callback); + + int delFileDescriptor(int fd); + + int modFileDescriptor(int fd, uint32_t events); + + void runEventLoop(int timeout = -1) override; + + void runOneEvent() override; + + void stop() override; + + std::size_t mapSize(); + + private: + int addFileDescriptor(int fd, uint32_t events); + + int epoll_fd_; + volatile bool run_event_loop_; + EventCallbackMap event_callback_map_; + std::mutex event_callback_map_mutex_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/event_reactor.h b/libtransport/src/hicn/transport/utils/event_reactor.h new file mode 100755 index 000000000..4f8b58296 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/event_reactor.h @@ -0,0 +1,37 @@ +/* + * 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 <cstddef> +#include <functional> +#include <system_error> + +namespace utils { + +typedef std::function<void(const std::error_code &ec)> UserCallback; + +class EventReactor { + public: + virtual ~EventReactor() = default; + + virtual void runEventLoop(int timeout = -1) = 0; + + virtual void runOneEvent() = 0; + + virtual void stop() = 0; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/event_thread.h b/libtransport/src/hicn/transport/utils/event_thread.h new file mode 100755 index 000000000..3bf08c94b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/event_thread.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/errors/runtime_exception.h> +#include <memory> + +#include <asio.hpp> + +namespace utils { + +class EventThread { + private: + // No copies + EventThread(const EventThread&) = delete; // non construction-copyable + EventThread& operator=(const EventThread&) = delete; // non copyable + + public: + explicit EventThread(asio::io_service& io_service) + : internal_io_service_(nullptr), + io_service_(io_service), + work_(io_service_), + thread_(nullptr) { + run(); + } + + explicit EventThread() + : internal_io_service_(std::make_unique<asio::io_service>()), + io_service_(*internal_io_service_), + work_(io_service_), + thread_(nullptr) { + run(); + } + + ~EventThread() { stop(); } + + void run() { + if (stopped()) { + io_service_.reset(); + } + + thread_ = std::make_unique<std::thread>([this]() { io_service_.run(); }); + } + + std::thread::id getThreadId() const { + if (thread_) { + return thread_->get_id(); + } else { + throw errors::RuntimeException("Event thread is not running."); + } + } + + template <typename Func> + void add(Func&& f) { + // If the function f + // TODO USe post in mac os, asio->post in xenial + io_service_.post(std::forward<Func&&>(f)); + } + + template <typename Func> + void tryRunHandlerNow(Func&& f) { + io_service_.dispatch(std::forward<Func&&>(f)); + } + + void stop() { + TRANSPORT_LOGI("Stopping event thread!"); + + io_service_.stop(); + + if (thread_ && thread_->joinable()) { + thread_->join(); + } + + thread_.reset(); + } + + bool stopped() { return io_service_.stopped(); } + + asio::io_service& getIoService() { return io_service_; } + + private: + std::unique_ptr<asio::io_service> internal_io_service_; + asio::io_service& io_service_; + asio::io_service::work work_; + std::unique_ptr<std::thread> thread_; +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/fd_deadline_timer.h b/libtransport/src/hicn/transport/utils/fd_deadline_timer.h new file mode 100755 index 000000000..3ed4590bc --- /dev/null +++ b/libtransport/src/hicn/transport/utils/fd_deadline_timer.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/errors/runtime_exception.h> +#include <hicn/transport/utils/deadline_timer.h> +#include <hicn/transport/utils/epoll_event_reactor.h> +#include <hicn/transport/utils/log.h> + +#include <chrono> +#include <cstddef> + +#include <sys/timerfd.h> +#include <unistd.h> + +namespace utils { + +class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> { + public: + explicit FdDeadlineTimer(EpollEventReactor &reactor) + : reactor_(reactor), + timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), + flags_(0) { + if (timer_fd_ == -1) { + throw errors::RuntimeException("Impossible to create the timer!"); + } + } + + ~FdDeadlineTimer() { close(timer_fd_); } + + template <typename WaitHandler> + void asyncWaitImpl(WaitHandler &&callback) { + // ASIO_WAIT_HANDLER_CHECK(WaitHandler, callback) type_check; + + if (timerfd_settime(timer_fd_, flags_, &new_value_, NULL) == -1) { + throw errors::RuntimeException("Impossible to set the timer!"); + } + + uint32_t events = EPOLLIN; + + reactor_.addFileDescriptor( + timer_fd_, events, + [callback{move(callback)}](const Event &event) -> int { + uint64_t s = 0; + std::error_code ec; + + if (read(event.data.fd, &s, sizeof(s)) == -1) { + TRANSPORT_LOGE("Read error!!"); + } + + if (!(event.events & EPOLLIN)) { + ec = std::make_error_code(std::errc::operation_canceled); + } + + callback(ec); + + return 0; + }); + } + + void waitImpl() { + if (timerfd_settime(timer_fd_, flags_, &new_value_, NULL) == -1) { + throw errors::RuntimeException("Impossible to set the timer!"); + } + + uint64_t ret; + + if (read(timer_fd_, &ret, sizeof(ret)) == -1) { + throw errors::RuntimeException( + "Error while waiting for the timer expiration."); + } + } + + template <typename T, typename R> + void expiresFromNowImpl(std::chrono::duration<T, R> &&duration) { + std::memset(&new_value_, 0, sizeof(new_value_)); + new_value_.it_value = std::chrono::duration_cast<struct timespec>( + std::forward<std::chrono::duration<T, R>>(duration)); + } + + template <typename TimePoint, + typename = std::enable_if_t< + std::is_same<std::remove_reference_t<TimePoint>, + std::chrono::steady_clock::time_point>::value, + TimePoint>> + void expiresAtImpl(TimePoint &&time_point) { + std::memset(&new_value_, 0, sizeof(new_value_)); + + new_value_.it_value = std::chrono::duration_cast<struct timespec>( + time_point.time_since_epoch()); + flags_ |= TFD_TIMER_ABSTIME; + } + + void cancelImpl() { + std::memset(&new_value_, 0, sizeof(new_value_)); + + if (timerfd_settime(timer_fd_, 0, &new_value_, NULL) == -1) { + throw errors::RuntimeException("Impossible to cancel the timer!"); + } + + // reactor_.delFileDescriptor(timer_fd_); + } + + EventReactor &getEventReactor() { return reactor_; } + + private: + EpollEventReactor &reactor_; + int timer_fd_; + EventCallback callback_; + struct itimerspec new_value_; + int flags_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/hash.h b/libtransport/src/hicn/transport/utils/hash.h new file mode 100755 index 000000000..6815ca4bf --- /dev/null +++ b/libtransport/src/hicn/transport/utils/hash.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/portability/portability.h> + +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> + +namespace utils { + +namespace hash { + +/* + * Fowler / Noll / Vo (FNV) Hash + * http://www.isthe.com/chongo/tech/comp/fnv/ + */ + +const uint32_t FNV_32_HASH_START = 2166136261UL; +const uint64_t FNV_64_HASH_START = 14695981039346656037ULL; + +TRANSPORT_ALWAYS_INLINE uint32_t fnv32(const char *s, + uint32_t hash = FNV_32_HASH_START) { + for (; *s; ++s) { + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + hash ^= *s; + } + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint32_t fnv32_buf(const void *buf, size_t n, + uint32_t hash = FNV_32_HASH_START) { + // forcing signed char, since other platforms can use unsigned + const signed char *char_buf = reinterpret_cast<const signed char *>(buf); + + for (size_t i = 0; i < n; ++i) { + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + hash ^= char_buf[i]; + } + + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint32_t fnv32(const std::string &str, + uint32_t hash = FNV_32_HASH_START) { + return fnv32_buf(str.data(), str.size(), hash); +} + +TRANSPORT_ALWAYS_INLINE uint64_t fnv64(const char *s, + uint64_t hash = FNV_64_HASH_START) { + for (; *s; ++s) { + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + hash ^= *s; + } + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint64_t fnv64_buf(const void *buf, size_t n, + uint64_t hash = FNV_64_HASH_START) { + // forcing signed char, since other platforms can use unsigned + const signed char *char_buf = reinterpret_cast<const signed char *>(buf); + + for (size_t i = 0; i < n; ++i) { + hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + + (hash << 8) + (hash << 40); + hash ^= char_buf[i]; + } + return hash; +} + +TRANSPORT_ALWAYS_INLINE uint64_t fnv64(const std::string &str, + uint64_t hash = FNV_64_HASH_START) { + return fnv64_buf(str.data(), str.size(), hash); +} + +} // namespace hash + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/identity.cc b/libtransport/src/hicn/transport/utils/identity.cc new file mode 100755 index 000000000..bdf7f29f9 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/identity.cc @@ -0,0 +1,130 @@ +/* + * 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/identity.h> + +extern "C" { +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Security.h> +} + +namespace utils { + +Identity::Identity(const std::string &keystore_name, + const std::string &keystore_password, CryptoSuite suite, + unsigned int key_length, unsigned int validity_days, + const std::string &subject_name) { + parcSecurity_Init(); + + bool success = parcPkcs12KeyStore_CreateFile( + keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(), + parcCryptoSuite_GetSigningAlgorithm(static_cast<PARCCryptoSuite>(suite)), + key_length, validity_days); + + parcAssertTrue(success, + "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d) failed.", + keystore_name.c_str(), keystore_password.c_str(), + subject_name.c_str(), static_cast<int>(key_length), validity_days); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(keystore_name.c_str(), keystore_password.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, + parcCryptoSuite_GetCryptoHash(static_cast<PARCCryptoSuite>(suite))); + + signer_ = std::make_shared<Signer>(signer); + + signature_length_ = parcSigner_GetSignatureSize(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +Identity::Identity(const Identity &other) + : signer_(other.signer_), + hash_algorithm_(other.hash_algorithm_), + signature_length_(other.signature_length_) { + parcSecurity_Init(); + identity_ = parcIdentity_Acquire(other.identity_); +} + +Identity Identity::generateIdentity(const std::string &subject_name) { + std::string keystore_name = "keystore"; + std::string keystore_password = "password"; + std::size_t key_length = 1024; + unsigned int validity_days = 30; + CryptoSuite suite = CryptoSuite::RSA_SHA256; + + return utils::Identity(keystore_name, keystore_password, suite, key_length, + validity_days, subject_name); +} + +Identity::Identity(std::string &file_name, std::string &password, + transport::core::HashAlgorithm hash_algorithm) + : hash_algorithm_(hash_algorithm) { + parcSecurity_Init(); + + PARCIdentityFile *identity_file = + parcIdentityFile_Create(file_name.c_str(), password.c_str()); + + identity_ = + parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity); + + PARCSigner *signer = parcIdentity_CreateSigner( + identity_, static_cast<PARCCryptoHashType>(hash_algorithm)); + + signer_ = std::make_shared<Signer>(signer); + + signature_length_ = parcSigner_GetSignatureSize(signer); + + parcSigner_Release(&signer); + parcIdentityFile_Release(&identity_file); +} + +// Identity::Identity(Identity &&other) { +// identity_ = parcIdentity_Acquire(other.identity_); +//} + +// Identity& Identity::operator=(const Identity& other) { +// signer_ = other.signer_; +// hash_algorithm_ = other.hash_algorithm_; +// signature_length_ = other.signature_length_; +// identity_ = parcIdentity_Acquire(other.identity_); + +// parcSecurity_Init(); +// } + +Identity::~Identity() { + parcIdentity_Release(&identity_); + parcSecurity_Fini(); +} + +std::string Identity::getFileName() { + return std::string(parcIdentity_GetFileName(identity_)); +} + +std::string Identity::getPassword() { + return std::string(parcIdentity_GetPassWord(identity_)); +} + +Signer &Identity::getSigner() { return *signer_; } + +unsigned int Identity::getSignatureLength() const { return signature_length_; } + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/identity.h b/libtransport/src/hicn/transport/utils/identity.h new file mode 100755 index 000000000..018842ee3 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/identity.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/manifest_format.h> +#include <hicn/transport/utils/crypto_suite.h> +#include <hicn/transport/utils/signer.h> + +extern "C" { +#include <parc/security/parc_Identity.h> +#include <parc/security/parc_IdentityFile.h> +#include <parc/security/parc_Pkcs12KeyStore.h> +}; + +#include <string> + +namespace utils { + +class Identity { + public: + Identity(const std::string &keystore_name, + const std::string &keystore_password, CryptoSuite suite, + unsigned int signature_length, unsigned int validity_days, + const std::string &subject_name); + + // No copies + Identity(const Identity &other); + + Identity(std::string &file_name, std::string &password, + transport::core::HashAlgorithm hash_algorithm); + + ~Identity(); + + static Identity generateIdentity(const std::string &subject_name); + + std::string getFileName(); + + std::string getPassword(); + + Signer &getSigner(); + + unsigned int getSignatureLength() const; + + private: + PARCIdentity *identity_; + std::shared_ptr<Signer> signer_; + transport::core::HashAlgorithm hash_algorithm_; + unsigned int signature_length_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/key_id.h b/libtransport/src/hicn/transport/utils/key_id.h new file mode 100755 index 000000000..d67b73d7a --- /dev/null +++ b/libtransport/src/hicn/transport/utils/key_id.h @@ -0,0 +1,25 @@ +/* + * 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 <cstdint> +#include <utility> + +namespace utils { + +using KeyId = std::pair<uint8_t*, uint8_t>; + +} diff --git a/libtransport/src/hicn/transport/utils/linux.h b/libtransport/src/hicn/transport/utils/linux.h new file mode 100755 index 000000000..5820528e1 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/linux.h @@ -0,0 +1,64 @@ +/* + * 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 + +#ifdef __linux__ + +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/log.h> + +#include <arpa/inet.h> +#include <ifaddrs.h> +#include <netdb.h> +#include <stdio.h> +#include <sys/socket.h> +#include <string> + +#define LINK_LOCAL_PREFIX 0xfe80 + +namespace utils { + +static TRANSPORT_ALWAYS_INLINE int retrieveInterfaceAddress( + const std::string &interface_name, struct sockaddr_in6 *address) { + struct ifaddrs *ifap, *ifa; + char addr[INET6_ADDRSTRLEN]; + + getifaddrs(&ifap); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family == AF_INET6 && + strcmp(ifa->ifa_name, interface_name.c_str()) == 0) { + struct sockaddr_in6 *tmp = (struct sockaddr_in6 *)ifa->ifa_addr; + uint16_t prefix = 0; + memcpy(&prefix, tmp->sin6_addr.s6_addr, sizeof(uint16_t)); + + if (htons(LINK_LOCAL_PREFIX) != prefix) { + *address = *(struct sockaddr_in6 *)ifa->ifa_addr; + getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr, + sizeof(addr), NULL, 0, NI_NUMERICHOST); + TRANSPORT_LOGI("Interface: %s\tAddress: %s", ifa->ifa_name, addr); + } + } + } + + freeifaddrs(ifap); + + return 0; +} + +} // namespace utils + +#endif // __linux__
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/literals.h b/libtransport/src/hicn/transport/utils/literals.h new file mode 100755 index 000000000..bd00e0a58 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/literals.h @@ -0,0 +1,55 @@ +/* + * 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/portability/portability.h> + +#include <cstdint> + +TRANSPORT_ALWAYS_INLINE std::uint8_t operator"" _U8(unsigned long long value) { + return static_cast<std::uint8_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::uint16_t operator"" _U16( + unsigned long long value) { + return static_cast<std::uint16_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::uint32_t operator"" _U32( + unsigned long long value) { + return static_cast<std::uint32_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::uint64_t operator"" _U64( + unsigned long long value) { + return static_cast<std::uint64_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::int8_t operator"" _I8(unsigned long long value) { + return static_cast<std::int8_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::int16_t operator"" _I16(unsigned long long value) { + return static_cast<std::int16_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::int32_t operator"" _I32(unsigned long long value) { + return static_cast<std::int32_t>(value); +} + +TRANSPORT_ALWAYS_INLINE std::int64_t operator"" _I64(unsigned long long value) { + return static_cast<std::int64_t>(value); +}
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/log.cc b/libtransport/src/hicn/transport/utils/log.cc new file mode 100755 index 000000000..064625ec0 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/log.cc @@ -0,0 +1,1405 @@ +/* + * 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. + */ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 wonder-mice + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* When defined, Android log (android/log.h) will be used by default instead of + * stderr (ignored on non-Android platforms). Date, time, pid and tid (context) + * will be provided by Android log. Android log features will be used to output + * log level and tag. + */ +#ifdef TRANSPORT_LOG_USE_ANDROID_LOG +#undef TRANSPORT_LOG_USE_ANDROID_LOG +#if defined(__ANDROID__) +#define TRANSPORT_LOG_USE_ANDROID_LOG 1 +#else +#define TRANSPORT_LOG_USE_ANDROID_LOG 0 +#endif +#else +#define TRANSPORT_LOG_USE_ANDROID_LOG 0 +#endif +/* When defined, NSLog (uses Apple System Log) will be used instead of stderr + * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be + * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on + * non-public CFLog() function. Both use Apple System Log internally, but it's + * easier to call CFLog() from C than NSLog(). Current implementation doesn't + * support "%@" format specifier. + */ +#ifdef TRANSPORT_LOG_USE_NSLOG +#undef TRANSPORT_LOG_USE_NSLOG +#if defined(__APPLE__) && defined(__MACH__) +#define TRANSPORT_LOG_USE_NSLOG 1 +#else +#define TRANSPORT_LOG_USE_NSLOG 0 +#endif +#else +#define TRANSPORT_LOG_USE_NSLOG 0 +#endif +/* When defined, OutputDebugString() will be used instead of stderr (ignored on + * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with + * UTF-8 data. + */ +#ifdef TRANSPORT_LOG_USE_DEBUGSTRING +#undef TRANSPORT_LOG_USE_DEBUGSTRING +#if defined(_WIN32) || defined(_WIN64) +#define TRANSPORT_LOG_USE_DEBUGSTRING 1 +#else +#define TRANSPORT_LOG_USE_DEBUGSTRING 0 +#endif +#else +#define TRANSPORT_LOG_USE_DEBUGSTRING 0 +#endif +/* When defined, TRANSPORT_LOG library will not contain definition of tag prefix + * variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_TAG_PREFIX macro, for example: + * + * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "ProcessName"; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_TAG_PREFIX +#undef TRANSPORT_LOG_EXTERN_TAG_PREFIX +#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 1 +#else +#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 0 +#endif +/* When defined, TRANSPORT_LOG library will not contain definition of global + * format variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT macro, for example: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH}; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT +#undef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT +#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 1 +#else +#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 0 +#endif +/* When defined, transport_log library will not contain definition of global + * output variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD, + * custom_output_callback}; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT +#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 1 +#else +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 0 +#endif +/* When defined, transport_log library will not contain definition of global + * output level variable. In that case it must be defined elsewhere using + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = TRANSPORT_LOG_WARN; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL +#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1 +#else +#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0 +#endif +/* When defined, implementation will prefer smaller code size over speed. + * Very rough estimate is that code will be up to 2x smaller and up to 2x + * slower. Disabled by default. + */ +#ifdef TRANSPORT_LOG_OPTIMIZE_SIZE +#undef TRANSPORT_LOG_OPTIMIZE_SIZE +#define TRANSPORT_LOG_OPTIMIZE_SIZE 1 +#else +#define TRANSPORT_LOG_OPTIMIZE_SIZE 0 +#endif +/* Size of the log line buffer. The buffer is allocated on stack. It limits + * maximum length of a log line. + */ +#ifndef TRANSPORT_LOG_BUF_SZ +#define TRANSPORT_LOG_BUF_SZ 512 +#endif +/* Default number of bytes in one line of memory output. For large values + * TRANSPORT_LOG_BUF_SZ also must be increased. + */ +#ifndef TRANSPORT_LOG_MEM_WIDTH +#define TRANSPORT_LOG_MEM_WIDTH 32 +#endif +/* String to put in the end of each log line (can be empty). Its value used by + * stderr output callback. Its size used as a default value for + * TRANSPORT_LOG_EOL_SZ. + */ +#ifndef TRANSPORT_LOG_EOL +#define TRANSPORT_LOG_EOL "\n" +#endif +/* Default delimiter that separates parts of log message. Can NOT contain '%' + * or '\0'. + * + * Log message format specifications can override (or ignore) this value. For + * more details see TRANSPORT_LOG_MESSAGE_CTX_FORMAT, + * TRANSPORT_LOG_MESSAGE_SRC_FORMAT and TRANSPORT_LOG_MESSAGE_TAG_FORMAT. + */ +#ifndef TRANSPORT_LOG_DEF_DELIMITER +#define TRANSPORT_LOG_DEF_DELIMITER " " +#endif +/* Specifies log message context format. Log message context includes date, + * time, process id, thread id and message's log level. Custom information can + * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, + * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements), + * F_UINT(width, value). + * + * Must be defined as a tuple, for example: + * + * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, + * S(" > ")) + * + * In that case, resulting log message will be: + * + * 2016.12.22 > TAG function@filename.c:line Message text + * + * Note, that tag, source location and message text are not impacted by + * this setting. See TRANSPORT_LOG_MESSAGE_TAG_FORMAT and + * TRANSPORT_LOG_MESSAGE_SRC_FORMAT. + * + * If message context must be visually separated from the rest of the message, + * it must be reflected in context format (notice trailing S(" > ") in the + * example above). + * + * S(str) adds constant string str. String can NOT contain '%' or '\0'. + * + * F_INIT(statements) adds initialization statement(s) that will be evaluated + * once for each log message. All statements are evaluated in specified order. + * Several F_INIT() fields can be used in every log message format + * specification. Fields, like F_UINT(width, value), are allowed to use results + * of initialization statements. If statement introduces variables (or other + * names, like structures) they must be prefixed with "f_". Statements must be + * enclosed into additional "()". Example: + * + * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ + * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \ + * YEAR, S("."), MONTH, S("."), DAY, S(" "), \ + * F_UINT(5, f_ru.ru_nsignals), \ + * S(" ")) + * + * F_UINT(width, value) adds unsigned integer value extended with up to width + * spaces (for alignment purposes). Value can be any expression that evaluates + * to unsigned integer. If expression contains non-standard functions, they + * must be declared with F_INIT(). Example: + * + * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ + * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \ + * F_INIT(( unsigned tickcount(); )), \ + * F_UINT(5, tickcount()), \ + * S(" ")) + * + * Other log message format specifications follow same rules, but have a + * different set of supported fields. + */ +#ifndef TRANSPORT_LOG_MESSAGE_CTX_FORMAT +#define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \ + (MONTH, S("-"), DAY, S(TRANSPORT_LOG_DEF_DELIMITER), HOUR, S(":"), MINUTE, \ + S(":"), SECOND, S("."), MILLISECOND, S(TRANSPORT_LOG_DEF_DELIMITER), PID, \ + S(TRANSPORT_LOG_DEF_DELIMITER), TID, S(TRANSPORT_LOG_DEF_DELIMITER), LEVEL, \ + S(TRANSPORT_LOG_DEF_DELIMITER)) +#endif +/* Example: + */ +/* Specifies log message tag format. It includes tag prefix and tag. Custom + * information can be added as well. Supported fields: + * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements), + * F_UINT(width, value). + * + * TAG(prefix_delimiter, tag_delimiter) adds following string to log message: + * + * PREFIX<prefix_delimiter>TAG<tag_delimiter> + * + * Prefix delimiter will be used only when prefix is not empty. Tag delimiter + * will be used only when prefixed tag is not empty. Example: + * + * #define TRANSPORT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] ")) + * + * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#ifndef TRANSPORT_LOG_MESSAGE_TAG_FORMAT +#define TRANSPORT_LOG_MESSAGE_TAG_FORMAT (TAG(".", TRANSPORT_LOG_DEF_DELIMITER)) +#endif +/* Specifies log message source location format. It includes function name, + * file name and file line. Custom information can be added as well. Supported + * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements), + * F_UINT(width, value). + * + * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#ifndef TRANSPORT_LOG_MESSAGE_SRC_FORMAT +#define TRANSPORT_LOG_MESSAGE_SRC_FORMAT \ + (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(TRANSPORT_LOG_DEF_DELIMITER)) +#endif +/* Fields that can be used in log message format specifications (see above). + * Mentioning them here explicitly, so we know that nobody else defined them + * before us. See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#define YEAR YEAR +#define MONTH MONTH +#define DAY DAY +#define MINUTE MINUTE +#define SECOND SECOND +#define MILLISECOND MILLISECOND +#define PID PID +#define TID TID +#define LEVEL LEVEL +#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim) +#define FUNCTION FUNCTION +#define FILENAME FILENAME +#define FILELINE FILELINE +#define S(str) S(str) +#define F_INIT(statements) F_INIT(statements) +#define F_UINT(width, value) F_UINT(width, value) +/* Number of bytes to reserve for EOL in the log line buffer (must be >0). + * Must be larger than or equal to length of TRANSPORT_LOG_EOL with terminating + * null. + */ +#ifndef TRANSPORT_LOG_EOL_SZ +#define TRANSPORT_LOG_EOL_SZ sizeof(TRANSPORT_LOG_EOL) +#endif +/* Compile instrumented version of the library to facilitate unit testing. + */ +#ifndef TRANSPORT_LOG_INSTRUMENTED +#define TRANSPORT_LOG_INSTRUMENTED 0 +#endif + +#if defined(__linux__) +#if !defined(__ANDROID__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#endif +#if defined(__MINGW32__) +#ifdef __STRICT_ANSI__ +#undef __STRICT_ANSI__ +#endif +#endif + +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <hicn/transport/utils/log.h> + +#if defined(_WIN32) || defined(_WIN64) +#include <windows.h> +#else +#include <unistd.h> +#include <sys/time.h> +#if defined(__linux__) +#include <linux/limits.h> +#else +#include <sys/syslimits.h> +#endif +#endif + +#if defined(__linux__) +#include <sys/prctl.h> +#include <sys/types.h> +#if !defined(__ANDROID__) +#include <sys/syscall.h> +#endif +#endif +#if defined(__MACH__) +#include <pthread.h> +#endif + +#define INLINE _TRANSPORT_LOG_INLINE +#define VAR_UNUSED(var) (void)var +#define RETVAL_UNUSED(expr) \ + do { \ + while (expr) break; \ + } while (0) +#define STATIC_ASSERT(name, cond) typedef char assert_##name[(cond) ? 1 : -1] +#define ASSERT_UNREACHABLE(why) assert(!sizeof(why)) +#ifndef _countof +#define _countof(xs) (sizeof(xs) / sizeof((xs)[0])) +#endif + +#if TRANSPORT_LOG_INSTRUMENTED +#define INSTRUMENTED_CONST +#else +#define INSTRUMENTED_CONST const +#endif + +#define _PP_PASTE_2(a, b) a##b +#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b) + +#define _PP_PASTE_3(a, b, c) a##b##c +#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c) + +/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__ + * as a single token and requires additional expansion to realize that it's + * actually a list. If not for it, there would be no need in this extra + * expansion. + */ +#define _PP_ID(x) x +#define _PP_NARGS_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \ + _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, \ + _24, ...) \ + _24 +#define _PP_NARGS(...) \ + _PP_ID(_PP_NARGS_N(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, \ + 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) + +/* There is a more efficient way to implement this, but it requires + * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't + * have one. + */ +#define _PP_HEAD__(x, ...) x +#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~)) +#define _PP_HEAD(xs) _PP_HEAD_ xs +#define _PP_TAIL_(x, ...) (__VA_ARGS__) +#define _PP_TAIL(xs) _PP_TAIL_ xs +#define _PP_UNTUPLE_(...) __VA_ARGS__ +#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs + +/* Apply function macro to each element in tuple. Output is not + * enforced to be a tuple. + */ +#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs)) +#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs)) +#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs)) +#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs)) +#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs)) +#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs)) +#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs)) +#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs)) +#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs)) +#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs)) +#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs)) +#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs)) +#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs)) +#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs)) +#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs)) +#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs)) +#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs)) +#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs)) +#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs)) +#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs)) +#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs)) +#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs)) +#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs)) +#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs)) +#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs)(f, xs) + +/* Apply function macro to each element in tuple in reverse order. + * Output is not enforced to be a tuple. + */ +#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs)) +#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs)(f, xs) + +/* Used to implement _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All + * possible fields must be mentioned here. Not counting F_INIT() here because + * it's somewhat special and is handled spearatly (at least for now). + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__ (0 << 0) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__YEAR (1 << 1) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MONTH (1 << 2) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__DAY (1 << 3) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__HOUR (1 << 4) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1 << 5) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__SECOND (1 << 6) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1 << 7) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__PID (1 << 8) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TID (1 << 9) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1 << 10) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1 << 11) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1 << 12) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1 << 13) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1 << 14) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__S(s) (1 << 15) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0 << 16) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1 << 17) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_MASK_, _, field) + +/* Logical "or" of masks of fields used in specified format specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format) \ + (0 _PP_MAP(| _TRANSPORT_LOG_MESSAGE_FORMAT_MASK, format)) + +/* Expands to expressions that evaluates to true if field is used in + * specified format specification. Example: + * + * #if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, + * TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + * ... + * #endif + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \ + (_TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) & \ + _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format)) + +/* Same, but checks all supported format specifications. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \ + (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ + TRANSPORT_LOG_MESSAGE_TAG_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT)) + +#define _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED \ + (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT)) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#pragma warning(disable : 4204) /* nonstandard extension used: non-constant \ + aggregate initializer */ +#define memccpy _memccpy +#endif + +#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || defined(__MINGW64__) +#define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va) +static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) { + const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap); + return 0 < n ? n : (int)sz + 1; /* no need in _vscprintf() for now */ +} +#if TRANSPORT_LOG_OPTIMIZE_SIZE +#define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__) +static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) { + va_list va; + va_start(va, fmt); + const int n = fake_vsnprintf(s, sz, fmt, va); + va_end(va); + return n; +} +#endif +#endif + +typedef void (*time_cb)(struct tm *const tm, unsigned *const usec); +typedef void (*pid_cb)(int *const pid, int *const tid); +typedef void (*buffer_cb)(transport_log_message *msg, char *buf); + +typedef struct src_location { + const char *const func; + const char *const file; + const unsigned line; +} src_location; + +typedef struct mem_block { + const void *const d; + const unsigned d_sz; +} mem_block; + +static void time_callback(struct tm *const tm, unsigned *const usec); +static void pid_callback(int *const pid, int *const tid); +static void buffer_callback(transport_log_message *msg, char *buf); + +STATIC_ASSERT(eol_fits_eol_sz, + sizeof(TRANSPORT_LOG_EOL) <= TRANSPORT_LOG_EOL_SZ); +STATIC_ASSERT(eol_sz_greater_than_zero, 0 < TRANSPORT_LOG_EOL_SZ); +STATIC_ASSERT(eol_sz_less_than_buf_sz, + TRANSPORT_LOG_EOL_SZ < TRANSPORT_LOG_BUF_SZ); +#if !defined(_WIN32) && !defined(_WIN64) +STATIC_ASSERT(buf_sz_less_than_pipe_buf, TRANSPORT_LOG_BUF_SZ <= PIPE_BUF); +#endif +static const char c_hex[] = "0123456789abcdef"; + +static INSTRUMENTED_CONST unsigned g_buf_sz = + TRANSPORT_LOG_BUF_SZ - TRANSPORT_LOG_EOL_SZ; +static INSTRUMENTED_CONST time_cb g_time_cb = time_callback; +static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback; +static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback; + +#if TRANSPORT_LOG_USE_ANDROID_LOG +#include <android/log.h> + +static INLINE int android_lvl(const int lvl) { + switch (lvl) { + case TRANSPORT_LOG_VERBOSE: + return ANDROID_LOG_VERBOSE; + case TRANSPORT_LOG_DEBUG: + return ANDROID_LOG_DEBUG; + case TRANSPORT_LOG_INFO: + return ANDROID_LOG_INFO; + case TRANSPORT_LOG_WARN: + return ANDROID_LOG_WARN; + case TRANSPORT_LOG_ERROR: + return ANDROID_LOG_ERROR; + case TRANSPORT_LOG_FATAL: + return ANDROID_LOG_FATAL; + default: + ASSERT_UNREACHABLE("Bad log level"); + return ANDROID_LOG_UNKNOWN; + } +} + +static void out_android_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + *msg->p = 0; + const char *tag = msg->p; + if (msg->tag_e != msg->tag_b) { + tag = msg->tag_b; + *msg->tag_e = 0; + } + __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b); +} + +enum { OUT_ANDROID_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX }; +#define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback +#endif + +#if TRANSPORT_LOG_USE_NSLOG +#include <CoreFoundation/CoreFoundation.h> +CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); + +static INLINE int apple_lvl(const int lvl) { + switch (lvl) { + case TRANSPORT_LOG_VERBOSE: + return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */ + ; + case TRANSPORT_LOG_DEBUG: + return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */ + ; + case TRANSPORT_LOG_INFO: + return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */ + ; + case TRANSPORT_LOG_WARN: + return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */ + ; + case TRANSPORT_LOG_ERROR: + return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */ + ; + case TRANSPORT_LOG_FATAL: + return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */ + ; + default: + ASSERT_UNREACHABLE("Bad log level"); + return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */ + ; + } +} + +static void out_nslog_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + *msg->p = 0; + CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b); +} + +enum { OUT_NSLOG_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX }; +#define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback +#endif + +#if TRANSPORT_LOG_USE_DEBUGSTRING +#include <windows.h> + +static void out_debugstring_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + msg->p[0] = '\n'; + msg->p[1] = '\0'; + OutputDebugStringA(msg->buf); +} + +enum { OUT_DEBUGSTRING_MASK = TRANSPORT_LOG_PUT_STD }; +#define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback +#endif + +void transport_log_out_stderr_callback(const transport_log_message *const msg, + void *arg) { + VAR_UNUSED(arg); + const size_t eol_len = sizeof(TRANSPORT_LOG_EOL) - 1; + memcpy(msg->p, TRANSPORT_LOG_EOL, eol_len); +#if defined(_WIN32) || defined(_WIN64) + /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and + without FILE_WRITE_DATA */ + DWORD written; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf, + (DWORD)(msg->p - msg->buf + eol_len), &written, 0); +#else + /* write() is atomic for buffers less than or equal to PIPE_BUF. */ + RETVAL_UNUSED( + write(STDERR_FILENO, msg->buf, (size_t)(msg->p - msg->buf) + eol_len)); +#endif +} + +static const transport_log_output out_stderr = {TRANSPORT_LOG_OUT_STDERR}; + +#if !TRANSPORT_LOG_EXTERN_TAG_PREFIX +TRANSPORT_LOG_DEFINE_TAG_PREFIX = 0; +#endif + +#if !TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT +TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {TRANSPORT_LOG_MEM_WIDTH}; +#endif + +#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT +#if TRANSPORT_LOG_USE_ANDROID_LOG +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID}; +#elif TRANSPORT_LOG_USE_NSLOG +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG}; +#elif TRANSPORT_LOG_USE_DEBUGSTRING +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING}; +#else +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR}; +#endif +#endif + +#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL +TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0; +#endif + +const transport_log_spec _transport_log_stderr_spec = { + TRANSPORT_LOG_GLOBAL_FORMAT, + &out_stderr, +}; + +static const transport_log_spec global_spec = { + TRANSPORT_LOG_GLOBAL_FORMAT, + TRANSPORT_LOG_GLOBAL_OUTPUT, +}; + +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) +static char lvl_char(const int lvl) { + switch (lvl) { + case TRANSPORT_LOG_VERBOSE: + return 'V'; + case TRANSPORT_LOG_DEBUG: + return 'D'; + case TRANSPORT_LOG_INFO: + return 'I'; + case TRANSPORT_LOG_WARN: + return 'W'; + case TRANSPORT_LOG_ERROR: + return 'E'; + case TRANSPORT_LOG_FATAL: + return 'F'; + default: + ASSERT_UNREACHABLE("Bad log level"); + return '?'; + } +} +#endif + +#define GCCVER_LESS(MAJOR, MINOR, PATCH) \ + (__GNUC__ < MAJOR || (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \ + (__GNUC_MINOR__ == MINOR && \ + __GNUC_PATCHLEVEL__ < PATCH)))) + +#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4, 7, 0) +#define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0) +#define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n) +#define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n) +#define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n) +#define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n) +/* Note: will not store old value of *vp in *ep (non-standard behaviour) */ +#define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \ + __sync_bool_compare_and_swap(vp, *(ep), d) +#endif + +#if !TRANSPORT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64) +#define TCACHE +#define TCACHE_STALE (0x40000000) +#define TCACHE_FLUID (0x40000000 | 0x80000000) +static unsigned g_tcache_mode = TCACHE_STALE; +static struct timeval g_tcache_tv = {0, 0}; +static struct tm g_tcache_tm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static INLINE int tcache_get(const struct timeval *const tv, + struct tm *const tm) { + unsigned mode; + mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED); + if (0 == (mode & TCACHE_FLUID)) { + mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE); + if (0 == (mode & TCACHE_FLUID)) { + if (g_tcache_tv.tv_sec == tv->tv_sec) { + *tm = g_tcache_tm; + __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); + return !0; + } + __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED); + } + __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); + } + return 0; +} + +static INLINE void tcache_set(const struct timeval *const tv, + struct tm *const tm) { + unsigned stale = TCACHE_STALE; + if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, 0, + __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { + g_tcache_tv = *tv; + g_tcache_tm = *tm; + __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE); + } +} +#endif + +static void time_callback(struct tm *const tm, unsigned *const msec) { +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED + VAR_UNUSED(tm); + VAR_UNUSED(msec); +#else +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + GetLocalTime(&st); + tm->tm_year = st.wYear; + tm->tm_mon = st.wMonth - 1; + tm->tm_mday = st.wDay; + tm->tm_wday = st.wDayOfWeek; + tm->tm_hour = st.wHour; + tm->tm_min = st.wMinute; + tm->tm_sec = st.wSecond; + *msec = st.wMilliseconds; +#else + struct timeval tv; + gettimeofday(&tv, 0); +#ifndef TCACHE + localtime_r(&tv.tv_sec, tm); +#else + if (!tcache_get(&tv, tm)) { + localtime_r(&tv.tv_sec, tm); + tcache_set(&tv, tm); + } +#endif + *msec = (unsigned)tv.tv_usec / 1000; +#endif +#endif +} + +static void pid_callback(int *const pid, int *const tid) { +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(PID, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(pid); +#else +#if defined(_WIN32) || defined(_WIN64) + *pid = GetCurrentProcessId(); +#else + *pid = getpid(); +#endif +#endif + +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(tid); +#else +#if defined(_WIN32) || defined(_WIN64) + *tid = GetCurrentThreadId(); +#elif defined(__ANDROID__) + *tid = gettid(); +#elif defined(__linux__) + *tid = syscall(SYS_gettid); +#elif defined(__MACH__) + *tid = (int)pthread_mach_thread_np(pthread_self()); +#else +#define Platform not supported +#endif +#endif +} + +static void buffer_callback(transport_log_message *msg, char *buf) { + msg->e = (msg->p = msg->buf = buf) + g_buf_sz; +} + +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +static const char *funcname(const char *func) { return func ? func : ""; } +#endif + +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +static const char *filename(const char *file) { + const char *f = file; + for (const char *p = file; 0 != *p; ++p) { + if ('/' == *p || '\\' == *p) { + f = p + 1; + } + } + return f; +} +#endif + +static INLINE size_t nprintf_size(transport_log_message *const msg) { + // *nprintf() always puts 0 in the end when input buffer is not empty. This + // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which + // leaves space for one more character. Some put_xxx() functions don't use + // *nprintf() and could use that last character. In that case log line will + // have multiple (two) half-written parts which is confusing. To workaround + // that we allow *nprintf() to write its 0 in the eol area (which is always + // not empty). + return (size_t)(msg->e - msg->p + 1); +} + +static INLINE void put_nprintf(transport_log_message *const msg, const int n) { + if (0 < n) { + msg->p = n < msg->e - msg->p ? msg->p + n : msg->e; + } +} + +static INLINE char *put_padding_r(const unsigned w, const char wc, char *p, + char *e) { + for (char *const b = e - w; b < p; *--p = wc) { + } + return p; +} + +static char *put_integer_r(unsigned v, const int sign, const unsigned w, + const char wc, char *const e) { + static const char _signs[] = {'-', '0', '+'}; + static const char *const signs = _signs + 1; + char *p = e; + do { + *--p = '0' + v % 10; + } while (0 != (v /= 10)); + if (0 == sign) return put_padding_r(w, wc, p, e); + if ('0' != wc) { + *--p = signs[sign]; + return put_padding_r(w, wc, p, e); + } + p = put_padding_r(w, wc, p, e + 1); + *--p = signs[sign]; + return p; +} + +static INLINE char *put_uint_r(const unsigned v, const unsigned w, + const char wc, char *const e) { + return put_integer_r(v, 0, w, wc, e); +} + +static INLINE char *put_int_r(const int v, const unsigned w, const char wc, + char *const e) { + return 0 <= v ? put_integer_r((unsigned)v, 0, w, wc, e) + : put_integer_r((unsigned)-v, -1, w, wc, e); +} + +static INLINE char *put_stringn(const char *const s_p, const char *const s_e, + char *const p, char *const e) { + const ptrdiff_t m = e - p; + ptrdiff_t n = s_e - s_p; + if (n > m) { + n = m; + } + memcpy(p, s_p, n); + return p + n; +} + +static INLINE char *put_string(const char *s, char *p, char *const e) { + const ptrdiff_t n = e - p; + char *const c = (char *)memccpy(p, s, '\0', n); + return 0 != c ? c - 1 : e; +} + +static INLINE char *put_uint(unsigned v, const unsigned w, const char wc, + char *const p, char *const e) { + char buf[16]; + char *const se = buf + _countof(buf); + char *sp = put_uint_r(v, w, wc, se); + return put_stringn(sp, se, p, e); +} + +#define PUT_CSTR_R(p, STR) \ + do { \ + for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \ + *--(p) = (STR)[i]; \ + } \ + } \ + _TRANSPORT_LOG_ONCE + +#define PUT_CSTR_CHECKED(p, e, STR) \ + do { \ + for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \ + *(p)++ = (STR)[i]; \ + } \ + } \ + _TRANSPORT_LOG_ONCE + +/* F_INIT field support. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__YEAR +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MONTH +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__DAY +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__HOUR +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MINUTE +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__SECOND +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__PID +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TID +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__LEVEL +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FUNCTION +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILENAME +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILELINE +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__S(s) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT_, _, field) + +/* Implements generation of printf-like format string for log message + * format specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ "" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) "" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u" +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field) + +/* Implements generation of printf-like format parameters for log message + * format specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR \ + , (unsigned)(tm.tm_year + 1900) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH \ + , (unsigned)(tm.tm_mon + 1) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY , (unsigned)tm.tm_mday +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR , (unsigned)tm.tm_hour +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE , (unsigned)tm.tm_min +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND , (unsigned)tm.tm_sec +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND , (unsigned)msec +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID , pid +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID , tid +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL \ + , (char)lvl_char(msg->lvl) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION , funcname(src->func) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME , filename(src->file) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE , src->line +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) , v +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field) + +/* Implements generation of put_xxx_t statements for log message specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__YEAR \ + p = put_uint_r(tm.tm_year + 1900, 4, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MONTH \ + p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__DAY \ + p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__HOUR \ + p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE \ + p = put_uint_r((unsigned)tm.tm_min, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__SECOND \ + p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND \ + p = put_uint_r(msec, 3, '0', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) \ + p = put_uint_r(v, w, ' ', p); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R_, _, field) + +static void put_ctx(transport_log_message *const msg) { + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(msg); +#else +#if _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED + struct tm tm; + unsigned msec; + g_time_cb(&tm, &msec); +#endif +#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ + PID, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \ + _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \ + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + int pid, tid; + g_pid_cb(&pid, &tid); +#endif + +#if TRANSPORT_LOG_OPTIMIZE_SIZE + int n; + n = snprintf(msg->p, nprintf_size(msg), + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT, + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL, + TRANSPORT_LOG_MESSAGE_CTX_FORMAT)); + put_nprintf(msg, n); +#else + char buf[64]; + char *const e = buf + sizeof(buf); + char *p = e; + _PP_RMAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R, + TRANSPORT_LOG_MESSAGE_CTX_FORMAT) + msg->p = put_stringn(p, e, msg->p, msg->e); +#endif +#endif +} + +#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \ + do { \ + const char *ch; \ + msg->tag_b = msg->p; \ + if (0 != (ch = _transport_log_tag_prefix)) { \ + for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \ + } \ + } \ + if (0 != (ch = tag) && 0 != tag[0]) { \ + if (msg->tag_b != msg->p) { \ + PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \ + } \ + for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \ + } \ + } \ + msg->tag_e = msg->p; \ + if (msg->tag_b != msg->p) { \ + PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \ + } \ + } \ + _TRANSPORT_LOG_ONCE + +/* Implements simple put statements for log message specification. + */ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__ +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) \ + PUT_TAG(msg, tag, pd, td); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FUNCTION \ + msg->p = put_string(funcname(src->func), msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILENAME \ + msg->p = put_string(filename(src->file), msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILELINE \ + msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__S(s) \ + PUT_CSTR_CHECKED(msg->p, msg->e, s); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr) +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) \ + msg->p = put_uint(v, w, ' ', msg->p, msg->e); +#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT(field) \ + _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_, _, field) + +static void put_tag(transport_log_message *const msg, const char *const tag) { + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT) +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, \ + TRANSPORT_LOG_MESSAGE_TAG_FORMAT) + VAR_UNUSED(tag); +#endif +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_TAG_FORMAT) + VAR_UNUSED(msg); +#else + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT) +#endif +} + +static void put_src(transport_log_message *const msg, + const src_location *const src) { + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ + FUNCTION, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \ + !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \ + FILENAME, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \ + !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, \ + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) + VAR_UNUSED(src); +#endif +#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_SRC_FORMAT) + VAR_UNUSED(msg); +#else +#if TRANSPORT_LOG_OPTIMIZE_SIZE + int n; + n = snprintf(msg->p, nprintf_size(msg), + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT, + TRANSPORT_LOG_MESSAGE_SRC_FORMAT) + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL, + TRANSPORT_LOG_MESSAGE_SRC_FORMAT)); + put_nprintf(msg, n); +#else + _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) +#endif +#endif +} + +static void put_msg(transport_log_message *const msg, const char *const fmt, + va_list va) { + int n; + msg->msg_b = msg->p; + n = vsnprintf(msg->p, nprintf_size(msg), fmt, va); + put_nprintf(msg, n); +} + +static void output_mem(const transport_log_spec *log, + transport_log_message *const msg, + const mem_block *const mem) { + if (0 == mem->d || 0 == mem->d_sz) { + return; + } + const unsigned char *mem_p = (const unsigned char *)mem->d; + const unsigned char *const mem_e = mem_p + mem->d_sz; + const unsigned char *mem_cut; + const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width; + char *const hex_b = msg->msg_b; + char *const ascii_b = hex_b + 2 * mem_width + 2; + char *const ascii_e = ascii_b + mem_width; + if (msg->e < ascii_e) { + return; + } + while (mem_p != mem_e) { + char *hex = hex_b; + char *ascii = ascii_b; + for (mem_cut = mem_width < mem_e - mem_p ? mem_p + mem_width : mem_e; + mem_cut != mem_p; ++mem_p) { + const unsigned char ch = *mem_p; + *hex++ = c_hex[(0xf0 & ch) >> 4]; + *hex++ = c_hex[(0x0f & ch)]; + *ascii++ = isprint(ch) ? (char)ch : '?'; + } + while (hex != ascii_b) { + *hex++ = ' '; + } + msg->p = ascii; + log->output->callback(msg, log->output->arg); + } +} + +void transport_log_set_tag_prefix(const char *const prefix) { + _transport_log_tag_prefix = prefix; +} + +void transport_log_set_mem_width(const unsigned w) { + _transport_log_global_format.mem_width = w; +} + +void transport_log_set_output_level(const int lvl) { + _transport_log_global_output_lvl = lvl; +} + +void transport_log_set_output_v(const unsigned mask, void *const arg, + const transport_log_output_cb callback) { + _transport_log_global_output.mask = mask; + _transport_log_global_output.arg = arg; + _transport_log_global_output.callback = callback; +} + +static void _transport_log_write_imp(const transport_log_spec *log, + const src_location *const src, + const mem_block *const mem, const int lvl, + const char *const tag, + const char *const fmt, va_list va) { + transport_log_message msg; + char buf[TRANSPORT_LOG_BUF_SZ]; + const unsigned mask = log->output->mask; + msg.lvl = lvl; + msg.tag = tag; + g_buffer_cb(&msg, buf); + if (TRANSPORT_LOG_PUT_CTX & mask) { + put_ctx(&msg); + } + if (TRANSPORT_LOG_PUT_TAG & mask) { + put_tag(&msg, tag); + } + if (0 != src && TRANSPORT_LOG_PUT_SRC & mask) { + put_src(&msg, src); + } + if (TRANSPORT_LOG_PUT_MSG & mask) { + put_msg(&msg, fmt, va); + } + log->output->callback(&msg, log->output->arg); + if (0 != mem && TRANSPORT_LOG_PUT_MSG & mask) { + output_mem(log, &msg, mem); + } +} + +void _transport_log_write_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const char *const fmt, ...) { + const src_location src = {func, file, line}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_aux_d(const char *const func, const char *const file, + const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) { + const src_location src = {func, file, line}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, &src, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write(const int lvl, const char *const tag, + const char *const fmt, ...) { + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) { + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, 0, 0, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const void *const d, + const unsigned d_sz, const char *const fmt, + ...) { + const src_location src = {func, file, line}; + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem_aux_d(const char *const func, + const char *const file, const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) { + const src_location src = {func, file, line}; + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, &src, &mem, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem(const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) { + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va); + va_end(va); +} + +void _transport_log_write_mem_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) { + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _transport_log_write_imp(log, 0, &mem, lvl, tag, fmt, va); + va_end(va); +}
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/log.h b/libtransport/src/hicn/transport/utils/log.h new file mode 100755 index 000000000..17e47e7df --- /dev/null +++ b/libtransport/src/hicn/transport/utils/log.h @@ -0,0 +1,1057 @@ +/* + * 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. + */ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 wonder-mice + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +/* To detect incompatible changes you can define TRANSPORT_LOG_VERSION_REQUIRED + * to be the current value of TRANSPORT_LOG_VERSION before including this file + * (or via compiler command line): + * + * #define TRANSPORT_LOG_VERSION_REQUIRED 4 + * #include <hicn/transport_log.h> + * + * Compilation will fail when included file has different version. + */ +#define TRANSPORT_LOG_VERSION 4 +#if defined(TRANSPORT_LOG_VERSION_REQUIRED) +#if TRANSPORT_LOG_VERSION_REQUIRED != TRANSPORT_LOG_VERSION +#error different transport_log version required +#endif +#endif + +/* Log level guideline: + * - TRANSPORT_LOG_FATAL - happened something impossible and absolutely + * unexpected. Process can't continue and must be terminated. Example: division + * by zero, unexpected modifications from other thread. + * - TRANSPORT_LOG_ERROR - happened something possible, but highly unexpected. + * The process is able to recover and continue execution. Example: out of memory + * (could also be FATAL if not handled properly). + * - TRANSPORT_LOG_WARN - happened something that *usually* should not happen + * and significantly changes application behavior for some period of time. + * Example: configuration file not found, auth error. + * - TRANSPORT_LOG_INFO - happened significant life cycle event or major state + * transition. + * Example: app started, user logged in. + * - TRANSPORT_LOG_DEBUG - minimal set of events that could help to reconstruct + * the execution path. Usually disabled in release builds. + * - TRANSPORT_LOG_VERBOSE - all other events. Usually disabled in release + * builds. + * + * *Ideally*, log file of debugged, well tested, production ready application + * should be empty or very small. Choosing a right log level is as important as + * providing short and self descriptive log message. + */ +#define TRANSPORT_LOG_VERBOSE 1 +#define TRANSPORT_LOG_DEBUG 2 +#define TRANSPORT_LOG_INFO 3 +#define TRANSPORT_LOG_WARN 4 +#define TRANSPORT_LOG_ERROR 5 +#define TRANSPORT_LOG_FATAL 6 +#define TRANSPORT_LOG_NONE 0xFF + +/* "Current" log level is a compile time check and has no runtime overhead. Log + * level that is below current log level it said to be "disabled". Otherwise, + * it's "enabled". Log messages that are disabled has no runtime overhead - they + * are converted to no-op by preprocessor and then eliminated by compiler. + * Current log level is configured per compilation module (.c/.cpp/.m file) by + * defining TRANSPORT_LOG_DEF_LEVEL or TRANSPORT_LOG_LEVEL. TRANSPORT_LOG_LEVEL + * has higer priority and when defined overrides value provided by + * TRANSPORT_LOG_DEF_LEVEL. + * + * Common practice is to define default current log level with + * TRANSPORT_LOG_DEF_LEVEL in build script (e.g. Makefile, CMakeLists.txt, gyp, + * etc.) for the entire project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_LEVEL=TRANSPORT_LOG_INFO + * + * And when necessary to override it with TRANSPORT_LOG_LEVEL in .c/.cpp/.m + * files before including transport_log.h: + * + * #define TRANSPORT_LOG_LEVEL TRANSPORT_LOG_VERBOSE + * #include <hicn/transport_log.h> + * + * If both TRANSPORT_LOG_DEF_LEVEL and TRANSPORT_LOG_LEVEL are undefined, then + * TRANSPORT_LOG_INFO will be used for release builds (NDEBUG is defined) and + * TRANSPORT_LOG_DEBUG otherwise (NDEBUG is not defined). + */ +#if defined(TRANSPORT_LOG_LEVEL) +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_LEVEL +#elif defined(TRANSPORT_LOG_DEF_LEVEL) +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_DEF_LEVEL +#else +#ifdef NDEBUG +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_INFO +#else +#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_DEBUG +#endif +#endif + +/* "Output" log level is a runtime check. When log level is below output log + * level it said to be "turned off" (or just "off" for short). Otherwise it's + * "turned on" (or just "on"). Log levels that were "disabled" (see + * TRANSPORT_LOG_LEVEL and TRANSPORT_LOG_DEF_LEVEL) can't be "turned on", but + * "enabled" log levels could be "turned off". Only messages with log level + * which is "turned on" will reach output facility. All other messages will be + * ignored (and their arguments will not be evaluated). Output log level is a + * global property and configured per process using + * transport_log_set_output_level() function which can be called at any time. + * + * Though in some cases it could be useful to configure output log level per + * compilation module or per library. There are two ways to achieve that: + * - Define TRANSPORT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired + * output log level. + * - Copy transport_log.h and transport_log.c files into your library and build + * it with TRANSPORT_LOG_LIBRARY_PREFIX defined to library specific prefix. See + * TRANSPORT_LOG_LIBRARY_PREFIX for more details. + * + * When defined, TRANSPORT_LOG_OUTPUT_LEVEL must evaluate to integral value that + * corresponds to desired output log level. Use it only when compilation module + * is required to have output log level which is different from global output + * log level set by transport_log_set_output_level() function. For other cases, + * consider defining TRANSPORT_LOG_LEVEL or using + * transport_log_set_output_level() function. + * + * Example: + * + * #define TRANSPORT_LOG_OUTPUT_LEVEL g_module_log_level + * #include <hicn/transport_log.h> + * static int g_module_log_level = TRANSPORT_LOG_INFO; + * static void foo() { + * TRANSPORT_LOGI("Will check g_module_log_level for output log level"); + * } + * void debug_log(bool on) { + * g_module_log_level = on? TRANSPORT_LOG_DEBUG: TRANSPORT_LOG_INFO; + * } + * + * Note on performance. This expression will be evaluated each time message is + * logged (except when message log level is "disabled" - see TRANSPORT_LOG_LEVEL + * for details). Keep this expression as simple as possible, otherwise it will + * not only add runtime overhead, but also will increase size of call site + * (which will result in larger executable). The prefered way is to use integer + * variable (as in example above). If structure must be used, log_level field + * must be the first field in this structure: + * + * #define TRANSPORT_LOG_OUTPUT_LEVEL (g_config.log_level) + * #include <hicn/transport_log.h> + * struct config { + * int log_level; + * unsigned other_field; + * [...] + * }; + * static config g_config = {TRANSPORT_LOG_INFO, 0, ...}; + * + * This allows compiler to generate more compact load instruction (no need to + * specify offset since it's zero). Calling a function to get output log level + * is generaly a bad idea, since it will increase call site size and runtime + * overhead even further. + */ +#if defined(TRANSPORT_LOG_OUTPUT_LEVEL) +#define _TRANSPORT_LOG_OUTPUT_LEVEL TRANSPORT_LOG_OUTPUT_LEVEL +#else +#define _TRANSPORT_LOG_OUTPUT_LEVEL _transport_log_global_output_lvl +#endif + +/* "Tag" is a compound string that could be associated with a log message. It + * consists of tag prefix and tag (both are optional). + * + * Tag prefix is a global property and configured per process using + * transport_log_set_tag_prefix() function. Tag prefix identifies context in + * which component or module is running (e.g. process name). For example, the + * same library could be used in both client and server processes that work on + * the same machine. Tag prefix could be used to easily distinguish between + * them. For more details about tag prefix see transport_log_set_tag_prefix() + * function. Tag prefix + * + * Tag identifies component or module. It is configured per compilation module + * (.c/.cpp/.m file) by defining TRANSPORT_LOG_TAG or TRANSPORT_LOG_DEF_TAG. + * TRANSPORT_LOG_TAG has higer priority and when defined overrides value + * provided by TRANSPORT_LOG_DEF_TAG. When defined, value must evaluate to + * (const char *), so for strings double quotes must be used. + * + * Default tag could be defined with TRANSPORT_LOG_DEF_TAG in build script (e.g. + * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_TAG=\"MISC\" + * + * And when necessary could be overriden with TRANSPORT_LOG_TAG in .c/.cpp/.m + * files before including transport_log.h: + * + * #define TRANSPORT_LOG_TAG "MAIN" + * #include <hicn/transport_log.h> + * + * If both TRANSPORT_LOG_DEF_TAG and TRANSPORT_LOG_TAG are undefined no tag will + * be added to the log message (tag prefix still could be added though). + * + * Output example: + * + * 04-29 22:43:20.244 40059 1299 I hello.MAIN Number of arguments: 1 + * | | + * | +- tag (e.g. module) + * +- tag prefix (e.g. process name) + */ +#if defined(TRANSPORT_LOG_TAG) +#define _TRANSPORT_LOG_TAG TRANSPORT_LOG_TAG +#elif defined(TRANSPORT_LOG_DEF_TAG) +#define _TRANSPORT_LOG_TAG TRANSPORT_LOG_DEF_TAG +#else +#define _TRANSPORT_LOG_TAG 0 +#endif + +/* Source location is part of a log line that describes location (function or + * method name, file name and line number, e.g. "runloop@main.cpp:68") of a + * log statement that produced it. + * Source location formats are: + * - TRANSPORT_LOG_SRCLOC_NONE - don't add source location to log line. + * - TRANSPORT_LOG_SRCLOC_SHORT - add source location in short form (file and + * line number, e.g. "@main.cpp:68"). + * - TRANSPORT_LOG_SRCLOC_LONG - add source location in long form (function or + * method name, file and line number, e.g. "runloop@main.cpp:68"). + */ +#define TRANSPORT_LOG_SRCLOC_NONE 0 +#define TRANSPORT_LOG_SRCLOC_SHORT 1 +#define TRANSPORT_LOG_SRCLOC_LONG 2 + +/* Source location format is configured per compilation module (.c/.cpp/.m + * file) by defining TRANSPORT_LOG_DEF_SRCLOC or TRANSPORT_LOG_SRCLOC. + * TRANSPORT_LOG_SRCLOC has higer priority and when defined overrides value + * provided by TRANSPORT_LOG_DEF_SRCLOC. + * + * Common practice is to define default format with TRANSPORT_LOG_DEF_SRCLOC in + * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire + * project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_SRCLOC=TRANSPORT_LOG_SRCLOC_LONG + * + * And when necessary to override it with TRANSPORT_LOG_SRCLOC in .c/.cpp/.m + * files before including transport_log.h: + * + * #define TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_NONE + * #include <hicn/transport_log.h> + * + * If both TRANSPORT_LOG_DEF_SRCLOC and TRANSPORT_LOG_SRCLOC are undefined, then + * TRANSPORT_LOG_SRCLOC_NONE will be used for release builds (NDEBUG is defined) + * and TRANSPORT_LOG_SRCLOC_LONG otherwise (NDEBUG is not defined). + */ +#if defined(TRANSPORT_LOG_SRCLOC) +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC +#elif defined(TRANSPORT_LOG_DEF_SRCLOC) +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_DEF_SRCLOC +#else +#ifdef NDEBUG +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_NONE +#else +#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_LONG +#endif +#endif +#if TRANSPORT_LOG_SRCLOC_LONG == _TRANSPORT_LOG_SRCLOC +#define _TRANSPORT_LOG_SRCLOC_FUNCTION _TRANSPORT_LOG_FUNCTION +#else +#define _TRANSPORT_LOG_SRCLOC_FUNCTION 0 +#endif + +/* Censoring provides conditional logging of secret information, also known as + * Personally Identifiable Information (PII) or Sensitive Personal Information + * (SPI). Censoring can be either enabled (TRANSPORT_LOG_CENSORED) or disabled + * (TRANSPORT_LOG_UNCENSORED). When censoring is enabled, log statements marked + * as "secrets" will be ignored and will have zero overhead (arguments also will + * not be evaluated). + */ +#define TRANSPORT_LOG_CENSORED 1 +#define TRANSPORT_LOG_UNCENSORED 0 + +/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining + * TRANSPORT_LOG_DEF_CENSORING or TRANSPORT_LOG_CENSORING. + * TRANSPORT_LOG_CENSORING has higer priority and when defined overrides value + * provided by TRANSPORT_LOG_DEF_CENSORING. + * + * Common practice is to define default censoring with + * TRANSPORT_LOG_DEF_CENSORING in build script (e.g. Makefile, CMakeLists.txt, + * gyp, etc.) for the entire project or target: + * + * CC_ARGS := -DTRANSPORT_LOG_DEF_CENSORING=TRANSPORT_LOG_CENSORED + * + * And when necessary to override it with TRANSPORT_LOG_CENSORING in .c/.cpp/.m + * files before including transport_log.h (consider doing it only for debug + * purposes and be very careful not to push such temporary changes to source + * control): + * + * #define TRANSPORT_LOG_CENSORING TRANSPORT_LOG_UNCENSORED + * #include <hicn/transport_log.h> + * + * If both TRANSPORT_LOG_DEF_CENSORING and TRANSPORT_LOG_CENSORING are + * undefined, then TRANSPORT_LOG_CENSORED will be used for release builds + * (NDEBUG is defined) and TRANSPORT_LOG_UNCENSORED otherwise (NDEBUG is not + * defined). + */ +#if defined(TRANSPORT_LOG_CENSORING) +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_CENSORING +#elif defined(TRANSPORT_LOG_DEF_CENSORING) +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_DEF_CENSORING +#else +#ifdef NDEBUG +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_CENSORED +#else +#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_UNCENSORED +#endif +#endif + +/* Check censoring at compile time. Evaluates to true when censoring is disabled + * (i.e. when secrets will be logged). For example: + * + * #if TRANSPORT_LOG_SECRETS + * char ssn[16]; + * getSocialSecurityNumber(ssn); + * TRANSPORT_LOGI("Customer ssn: %s", ssn); + * #endif + * + * See TRANSPORT_LOG_SECRET() macro for a more convenient way of guarding single + * log statement. + */ +#define TRANSPORT_LOG_SECRETS \ + (TRANSPORT_LOG_UNCENSORED == _TRANSPORT_LOG_CENSORING) + +/* Static (compile-time) initialization support allows to configure logging + * before entering main() function. This mostly useful in C++ where functions + * and methods could be called during initialization of global objects. Those + * functions and methods could record log messages too and for that reason + * static initialization of logging configuration is customizable. + * + * Macros below allow to specify values to use for initial configuration: + * - TRANSPORT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none) + * - TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see + * TRANSPORT_LOG_MEM_WIDTH in transport_log.c) + * - TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default: + * stderr or platform specific, see TRANSPORT_LOG_USE_XXX macros in + * transport_log.c) + * - TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level + * (default: 0 - all levals are "turned on") + * + * For example, in log_config.c: + * + * #include <hicn/transport_log.h> + * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "MyApp"; + * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH}; + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD, + * custom_output_callback, 0}; TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = + * TRANSPORT_LOG_INFO; + * + * However, to use any of those macros transport_log library must be compiled + * with following macros defined: + * - to use TRANSPORT_LOG_DEFINE_TAG_PREFIX define + * TRANSPORT_LOG_EXTERN_TAG_PREFIX + * - to use TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT define + * TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT + * - to use TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT define + * TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT + * - to use TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define + * TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL + * + * When transport_log library compiled with one of TRANSPORT_LOG_EXTERN_XXX + * macros defined, corresponding TRANSPORT_LOG_DEFINE_XXX macro MUST be used + * exactly once somewhere. Otherwise build will fail with link error (undefined + * symbol). + */ +#define TRANSPORT_LOG_DEFINE_TAG_PREFIX const char *_transport_log_tag_prefix +#define TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT \ + transport_log_format _transport_log_global_format +#define TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT \ + transport_log_output _transport_log_global_output +#define TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL \ + int _transport_log_global_output_lvl + +/* Pointer to global format options. Direct modification is not allowed. Use + * transport_log_set_mem_width() instead. Could be used to initialize + * transport_log_spec structure: + * + * const transport_log_output g_output = {TRANSPORT_LOG_PUT_STD, + * output_callback, 0}; const transport_log_spec g_spec = + * {TRANSPORT_LOG_GLOBAL_FORMAT, &g_output}; TRANSPORT_LOGI_AUX(&g_spec, + * "Hello"); + */ +#define TRANSPORT_LOG_GLOBAL_FORMAT \ + ((const transport_log_format *)&_transport_log_global_format) + +/* Pointer to global output variable. Direct modification is not allowed. Use + * transport_log_set_output_v() or transport_log_set_output_p() instead. Could + * be used to initialize transport_log_spec structure: + * + * const transport_log_format g_format = {40}; + * const transport_log_spec g_spec = {g_format, TRANSPORT_LOG_GLOBAL_OUTPUT}; + * TRANSPORT_LOGI_AUX(&g_spec, "Hello"); + */ +#define TRANSPORT_LOG_GLOBAL_OUTPUT \ + ((const transport_log_output *)&_transport_log_global_output) + +/* When defined, all library symbols produced by linker will be prefixed with + * provided value. That allows to use transport_log library privately in another + * libraries without exposing transport_log symbols in their original form (to + * avoid possible conflicts with other libraries / components that also could + * use transport_log for logging). Value must be without quotes, for example: + * + * CC_ARGS := -DTRANSPORT_LOG_LIBRARY_PREFIX=my_lib_ + * + * Note, that in this mode TRANSPORT_LOG_LIBRARY_PREFIX must be defined when + * building transport_log library AND it also must be defined to the same value + * when building a library that uses it. For example, consider fictional + * KittyHttp library that wants to use transport_log for logging. First approach + * that could be taken is to add transport_log.h and transport_log.c to the + * KittyHttp's source code tree directly. In that case it will be enough just to + * define TRANSPORT_LOG_LIBRARY_PREFIX in KittyHttp's build script: + * + * // KittyHttp/CMakeLists.txt + * target_compile_definitions(KittyHttp PRIVATE + * "TRANSPORT_LOG_LIBRARY_PREFIX=KittyHttp_") + * + * If KittyHttp doesn't want to include transport_log source code in its source + * tree and wants to build transport_log as a separate library than + * transport_log library must be built with TRANSPORT_LOG_LIBRARY_PREFIX defined + * to KittyHttp_ AND KittyHttp library itself also needs to define + * TRANSPORT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do so either in its build + * script, as in example above, or by providing a wrapper header that KittyHttp + * library will need to use instead of transport_log.h: + * + * // KittyHttpLogging.h + * #define TRANSPORT_LOG_LIBRARY_PREFIX KittyHttp_ + * #include <hicn/transport_log.h> + * + * Regardless of the method chosen, the end result is that transport_log symbols + * will be prefixed with "KittyHttp_", so if a user of KittyHttp (say + * DogeBrowser) also uses transport_log for logging, they will not interferer + * with each other. Both will have their own log level, output facility, format + * options etc. + */ +#ifdef TRANSPORT_LOG_LIBRARY_PREFIX +#define _TRANSPORT_LOG_DECOR__(prefix, name) prefix##name +#define _TRANSPORT_LOG_DECOR_(prefix, name) _TRANSPORT_LOG_DECOR__(prefix, name) +#define _TRANSPORT_LOG_DECOR(name) \ + _TRANSPORT_LOG_DECOR_(TRANSPORT_LOG_LIBRARY_PREFIX, name) + +#define transport_log_set_tag_prefix \ + _TRANSPORT_LOG_DECOR(transport_log_set_tag_prefix) +#define transport_log_set_mem_width \ + _TRANSPORT_LOG_DECOR(transport_log_set_mem_width) +#define transport_log_set_output_level \ + _TRANSPORT_LOG_DECOR(transport_log_set_output_level) +#define transport_log_set_output_v \ + _TRANSPORT_LOG_DECOR(transport_log_set_output_v) +#define transport_log_set_output_p \ + _TRANSPORT_LOG_DECOR(transport_log_set_output_p) +#define transport_log_out_stderr_callback \ + _TRANSPORT_LOG_DECOR(transport_log_out_stderr_callback) +#define _transport_log_tag_prefix \ + _TRANSPORT_LOG_DECOR(_transport_log_tag_prefix) +#define _transport_log_global_format \ + _TRANSPORT_LOG_DECOR(_transport_log_global_format) +#define _transport_log_global_output \ + _TRANSPORT_LOG_DECOR(_transport_log_global_output) +#define _transport_log_global_output_lvl \ + _TRANSPORT_LOG_DECOR(_transport_log_global_output_lvl) +#define _transport_log_write_d _TRANSPORT_LOG_DECOR(_transport_log_write_d) +#define _transport_log_write_aux_d \ + _TRANSPORT_LOG_DECOR(_transport_log_write_aux_d) +#define _transport_log_write _TRANSPORT_LOG_DECOR(_transport_log_write) +#define _transport_log_write_aux _TRANSPORT_LOG_DECOR(_transport_log_write_aux) +#define _transport_log_write_mem_d \ + _TRANSPORT_LOG_DECOR(_transport_log_write_mem_d) +#define _transport_log_write_mem_aux_d \ + _TRANSPORT_LOG_DECOR(_transport_log_write_mem_aux_d) +#define _transport_log_write_mem _TRANSPORT_LOG_DECOR(_transport_log_write_mem) +#define _transport_log_write_mem_aux \ + _TRANSPORT_LOG_DECOR(_transport_log_write_mem_aux) +#define _transport_log_stderr_spec \ + _TRANSPORT_LOG_DECOR(_transport_log_stderr_spec) +#endif + +#if defined(__printflike) +#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __printflike(str_index, first_to_check) +#elif defined(__GNUC__) +#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __attribute__((format(__printf__, str_index, first_to_check))) +#else +#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) +#endif + +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__) +#define _TRANSPORT_LOG_FUNCTION __FUNCTION__ +#else +#define _TRANSPORT_LOG_FUNCTION __func__ +#endif + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#define _TRANSPORT_LOG_INLINE __inline +#define _TRANSPORT_LOG_IF(cond) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) if (cond) \ + __pragma(warning(pop)) +#define _TRANSPORT_LOG_WHILE(cond) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) while (cond) \ + __pragma(warning(pop)) +#else +#define _TRANSPORT_LOG_INLINE inline +#define _TRANSPORT_LOG_IF(cond) if (cond) +#define _TRANSPORT_LOG_WHILE(cond) while (cond) +#endif +#define _TRANSPORT_LOG_NEVER _TRANSPORT_LOG_IF(0) +#define _TRANSPORT_LOG_ONCE _TRANSPORT_LOG_WHILE(0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Set tag prefix. Prefix will be separated from the tag with dot ('.'). + * Use 0 or empty string to disable (default). Common use is to set it to + * the process (or build target) name (e.g. to separate client and server + * processes). Function will NOT copy provided prefix string, but will store the + * pointer. Hence specified prefix string must remain valid. See + * TRANSPORT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main() + * function. See TRANSPORT_LOG_TAG for more information about tag and tag + * prefix. + */ +void transport_log_set_tag_prefix(const char *const prefix); + +/* Set number of bytes per log line in memory (ASCII-HEX) output. Example: + * + * I hello.MAIN 4c6f72656d20697073756d20646f6c6f Lorem ipsum dolo + * |<- w bytes ->| |<- w chars ->| + * + * See TRANSPORT_LOGF_MEM and TRANSPORT_LOGF_MEM_AUX for more details. + */ +void transport_log_set_mem_width(const unsigned w); + +/* Set "output" log level. See TRANSPORT_LOG_LEVEL and + * TRANSPORT_LOG_OUTPUT_LEVEL for more info about log levels. + */ +void transport_log_set_output_level(const int lvl); + +/* Put mask is a set of flags that define what fields will be added to each + * log message. Default value is TRANSPORT_LOG_PUT_STD and other flags could be + * used to alter its behavior. See transport_log_set_output_v() for more + * details. + * + * Note about TRANSPORT_LOG_PUT_SRC: it will be added only in debug builds + * (NDEBUG is not defined). + */ +enum { + TRANSPORT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */ + TRANSPORT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */ + TRANSPORT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */ + TRANSPORT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */ + TRANSPORT_LOG_PUT_STD = 0xffff, /* everything (default) */ +}; + +typedef struct transport_log_message { + int lvl; /* Log level of the message */ + const char *tag; /* Associated tag (without tag prefix) */ + char *buf; /* Buffer start */ + char *e; /* Buffer end (last position where EOL with 0 could be written) */ + char *p; /* Buffer content end (append position) */ + char *tag_b; /* Prefixed tag start */ + char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */ + char *msg_b; /* Message start (expanded format string) */ +} transport_log_message; + +/* Type of output callback function. It will be called for each log line allowed + * by both "current" and "output" log levels ("enabled" and "turned on"). + * Callback function is allowed to modify content of the buffers pointed by the + * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg + * is UTF-8 encoded (no BOM mark). + */ +typedef void (*transport_log_output_cb)(const transport_log_message *msg, + void *arg); + +/* Format options. For more details see transport_log_set_mem_width(). + */ +typedef struct transport_log_format { + unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */ +} transport_log_format; + +/* Output facility. + */ +typedef struct transport_log_output { + unsigned + mask; /* What to put into log line buffer (see TRANSPORT_LOG_PUT_XXX) */ + void *arg; /* User provided output callback argument */ + transport_log_output_cb callback; /* Output callback function */ +} transport_log_output; + +/* Set output callback function. + * + * Mask allows to control what information will be added to the log line buffer + * before callback function is invoked. Default mask value is + * TRANSPORT_LOG_PUT_STD. + */ +void transport_log_set_output_v(const unsigned mask, void *const arg, + const transport_log_output_cb callback); +static _TRANSPORT_LOG_INLINE void transport_log_set_output_p( + const transport_log_output *const output) { + transport_log_set_output_v(output->mask, output->arg, output->callback); +} + +/* Used with _AUX macros and allows to override global format and output + * facility. Use TRANSPORT_LOG_GLOBAL_FORMAT and TRANSPORT_LOG_GLOBAL_OUTPUT for + * values from global configuration. Example: + * + * static const transport_log_output module_output = { + * TRANSPORT_LOG_PUT_STD, 0, custom_output_callback + * }; + * static const transport_log_spec module_spec = { + * TRANSPORT_LOG_GLOBAL_FORMAT, &module_output + * }; + * TRANSPORT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y); + * + * See TRANSPORT_LOGF_AUX and TRANSPORT_LOGF_MEM_AUX for details. + */ +typedef struct transport_log_spec { + const transport_log_format *format; + const transport_log_output *output; +} transport_log_spec; + +#ifdef __cplusplus +} +#endif + +/* Execute log statement if condition is true. Example: + * + * TRANSPORT_LOG_IF(1 < 2, TRANSPORT_LOGI("Log this")); + * TRANSPORT_LOG_IF(1 > 2, TRANSPORT_LOGI("Don't log this")); + * + * Keep in mind though, that if condition can't be evaluated at compile time, + * then it will be evaluated at run time. This will increase exectuable size + * and can have noticeable performance overhead. Try to limit conditions to + * expressions that can be evaluated at compile time. + */ +#define TRANSPORT_LOG_IF(cond, f) \ + do { \ + _TRANSPORT_LOG_IF((cond)) { f; } \ + } \ + _TRANSPORT_LOG_ONCE + +/* Mark log statement as "secret". Log statements that are marked as secrets + * will NOT be executed when censoring is enabled (see TRANSPORT_LOG_CENSORED). + * Example: + * + * TRANSPORT_LOG_SECRET(TRANSPORT_LOGI("Credit card: %s", credit_card)); + * TRANSPORT_LOG_SECRET(TRANSPORT_LOGD_MEM(cipher, cipher_sz, "Cipher + * bytes:")); + */ +#define TRANSPORT_LOG_SECRET(f) TRANSPORT_LOG_IF(TRANSPORT_LOG_SECRETS, f) + +/* Check "current" log level at compile time (ignoring "output" log level). + * Evaluates to true when specified log level is enabled. For example: + * + * #if TRANSPORT_LOG_ENABLED_DEBUG + * const char *const g_enum_strings[] = { + * "enum_value_0", "enum_value_1", "enum_value_2" + * }; + * #endif + * // ... + * #if TRANSPORT_LOG_ENABLED_DEBUG + * TRANSPORT_LOGD("enum value: %s", g_enum_strings[v]); + * #endif + * + * See TRANSPORT_LOG_LEVEL for details. + */ +#define TRANSPORT_LOG_ENABLED(lvl) ((lvl) >= _TRANSPORT_LOG_LEVEL) +#define TRANSPORT_LOG_ENABLED_VERBOSE \ + TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_VERBOSE) +#define TRANSPORT_LOG_ENABLED_DEBUG TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_DEBUG) +#define TRANSPORT_LOG_ENABLED_INFO TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_INFO) +#define TRANSPORT_LOG_ENABLED_WARN TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_WARN) +#define TRANSPORT_LOG_ENABLED_ERROR TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_ERROR) +#define TRANSPORT_LOG_ENABLED_FATAL TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_FATAL) + +/* Check "output" log level at run time (taking into account "current" log + * level as well). Evaluates to true when specified log level is turned on AND + * enabled. For example: + * + * if (TRANSPORT_LOG_ON_DEBUG) + * { + * char hash[65]; + * sha256(data_ptr, data_sz, hash); + * TRANSPORT_LOGD("data: len=%u, sha256=%s", data_sz, hash); + * } + * + * See TRANSPORT_LOG_OUTPUT_LEVEL for details. + */ +#define TRANSPORT_LOG_ON(lvl) \ + (TRANSPORT_LOG_ENABLED((lvl)) && (lvl) >= _TRANSPORT_LOG_OUTPUT_LEVEL) +#define TRANSPORT_LOG_ON_VERBOSE TRANSPORT_LOG_ON(TRANSPORT_LOG_VERBOSE) +#define TRANSPORT_LOG_ON_DEBUG TRANSPORT_LOG_ON(TRANSPORT_LOG_DEBUG) +#define TRANSPORT_LOG_ON_INFO TRANSPORT_LOG_ON(TRANSPORT_LOG_INFO) +#define TRANSPORT_LOG_ON_WARN TRANSPORT_LOG_ON(TRANSPORT_LOG_WARN) +#define TRANSPORT_LOG_ON_ERROR TRANSPORT_LOG_ON(TRANSPORT_LOG_ERROR) +#define TRANSPORT_LOG_ON_FATAL TRANSPORT_LOG_ON(TRANSPORT_LOG_FATAL) + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *_transport_log_tag_prefix; +extern transport_log_format _transport_log_global_format; +extern transport_log_output _transport_log_global_output; +extern int _transport_log_global_output_lvl; +extern const transport_log_spec _transport_log_stderr_spec; + +void _transport_log_write_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(6, 7); +void _transport_log_write_aux_d(const char *const func, const char *const file, + const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(7, 8); +void _transport_log_write(const int lvl, const char *const tag, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(3, 4); +void _transport_log_write_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(4, 5); +void _transport_log_write_mem_d(const char *const func, const char *const file, + const unsigned line, const int lvl, + const char *const tag, const void *const d, + const unsigned d_sz, const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(8, 9); +void _transport_log_write_mem_aux_d(const char *const func, + const char *const file, const unsigned line, + const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(9, 10); +void _transport_log_write_mem(const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(5, 6); +void _transport_log_write_mem_aux(const transport_log_spec *const log, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) + _TRANSPORT_LOG_PRINTFLIKE(6, 7); + +#ifdef __cplusplus +} +#endif + +/* Message logging macros: + * - TRANSPORT_LOGV("format string", args, ...) + * - TRANSPORT_LOGD("format string", args, ...) + * - TRANSPORT_LOGI("format string", args, ...) + * - TRANSPORT_LOGW("format string", args, ...) + * - TRANSPORT_LOGE("format string", args, ...) + * - TRANSPORT_LOGF("format string", args, ...) + * + * Memory logging macros: + * - TRANSPORT_LOGV_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...) + * - TRANSPORT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...) + * + * Auxiliary logging macros: + * - TRANSPORT_LOGV_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGD_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGI_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGW_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGE_AUX(&log_instance, "format string", args, ...) + * - TRANSPORT_LOGF_AUX(&log_instance, "format string", args, ...) + * + * Auxiliary memory logging macros: + * - TRANSPORT_LOGV_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", + * args, ...) + * + * Preformatted string logging macros: + * - TRANSPORT_LOGV_STR("preformatted string"); + * - TRANSPORT_LOGD_STR("preformatted string"); + * - TRANSPORT_LOGI_STR("preformatted string"); + * - TRANSPORT_LOGW_STR("preformatted string"); + * - TRANSPORT_LOGE_STR("preformatted string"); + * - TRANSPORT_LOGF_STR("preformatted string"); + * + * Explicit log level and tag macros: + * - TRANSPORT_LOG_WRITE(level, tag, "format string", args, ...) + * - TRANSPORT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string", + * args, ...) + * - TRANSPORT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args, + * ...) + * - TRANSPORT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz, + * "format string", args, ...) + * + * Format string follows printf() conventions. Both data_ptr and data_sz could + * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments + * match format specifiers in format string. + * + * Library assuming UTF-8 encoding for all strings (char *), including format + * string itself. + */ +#if TRANSPORT_LOG_SRCLOC_NONE == _TRANSPORT_LOG_SRCLOC +#define TRANSPORT_LOG_WRITE(lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) _transport_log_write(lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_AUX(log, lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_aux(log, lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#else +#define TRANSPORT_LOG_WRITE(lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, lvl, tag, d, d_sz, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_AUX(log, lvl, tag, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_aux_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, log, lvl, tag, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#define TRANSPORT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ + do { \ + if (TRANSPORT_LOG_ON(lvl)) \ + _transport_log_write_mem_aux_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, log, lvl, tag, d, d_sz, \ + __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE +#endif + +static _TRANSPORT_LOG_INLINE void _transport_log_unused(const int dummy, ...) { + (void)dummy; +} + +#define _TRANSPORT_LOG_UNUSED(...) \ + do { \ + _TRANSPORT_LOG_NEVER _transport_log_unused(0, __VA_ARGS__); \ + } \ + _TRANSPORT_LOG_ONCE + +#if TRANSPORT_LOG_ENABLED_VERBOSE +#define TRANSPORT_LOGV(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGV_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGV_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGV_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(log, TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGV(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGV_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGV_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGV_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_DEBUG +#define TRANSPORT_LOGD(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGD_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGD_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGD_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGD(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGD_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGD_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGD_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_INFO +#define TRANSPORT_LOGI(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGI_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGI_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGI_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGI(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGI_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGI_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGI_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_WARN +#define TRANSPORT_LOGW(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGW_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGW_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGW_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGW(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGW_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGW_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGW_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_ERROR +#define TRANSPORT_LOGE(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGE_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGE_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGE_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGE(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGE_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGE_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGE_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if TRANSPORT_LOG_ENABLED_FATAL +#define TRANSPORT_LOGF(...) \ + TRANSPORT_LOG_WRITE(TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, __VA_ARGS__) +#define TRANSPORT_LOGF_AUX(log, ...) \ + TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, \ + __VA_ARGS__) +#define TRANSPORT_LOGF_MEM(d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, d, d_sz, \ + __VA_ARGS__) +#define TRANSPORT_LOGF_MEM_AUX(log, d, d_sz, ...) \ + TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, d, \ + d_sz, __VA_ARGS__) +#else +#define TRANSPORT_LOGF(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGF_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGF_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#define TRANSPORT_LOGF_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__) +#endif + +#define TRANSPORT_LOGV_STR(s) TRANSPORT_LOGV("%s", (s)) +#define TRANSPORT_LOGD_STR(s) TRANSPORT_LOGD("%s", (s)) +#define TRANSPORT_LOGI_STR(s) TRANSPORT_LOGI("%s", (s)) +#define TRANSPORT_LOGW_STR(s) TRANSPORT_LOGW("%s", (s)) +#define TRANSPORT_LOGE_STR(s) TRANSPORT_LOGE("%s", (s)) +#define TRANSPORT_LOGF_STR(s) TRANSPORT_LOGF("%s", (s)) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Output to standard error stream. Library uses it by default, though in few + * cases it could be necessary to specify it explicitly. For example, when + * transport_log library is compiled with TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT, + * application must define and initialize global output variable: + * + * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR}; + * + * Another example is when using custom output, stderr could be used as a + * fallback when custom output facility failed to initialize: + * + * transport_log_set_output_v(TRANSPORT_LOG_OUT_STDERR); + */ +enum { TRANSPORT_LOG_OUT_STDERR_MASK = TRANSPORT_LOG_PUT_STD }; +void transport_log_out_stderr_callback(const transport_log_message *const msg, + void *arg); +#define TRANSPORT_LOG_OUT_STDERR \ + TRANSPORT_LOG_OUT_STDERR_MASK, 0, transport_log_out_stderr_callback + +/* Predefined spec for stderr. Uses global format options + * (TRANSPORT_LOG_GLOBAL_FORMAT) and TRANSPORT_LOG_OUT_STDERR. Could be used to + * force output to stderr for a particular message. Example: + * + * f = fopen("foo.log", "w"); + * if (!f) + * TRANSPORT_LOGE_AUX(TRANSPORT_LOG_STDERR, "Failed to open log file"); + */ +#define TRANSPORT_LOG_STDERR (&_transport_log_stderr_spec) + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/membuf.cc b/libtransport/src/hicn/transport/utils/membuf.cc new file mode 100755 index 000000000..0ab1a6044 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/membuf.cc @@ -0,0 +1,864 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2013-present Facebook, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The code in this file if adapated from the IOBuf of folly: + * https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h + */ + +#include <hicn/transport/utils/membuf.h> + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <stdexcept> +#include <vector> + +using std::unique_ptr; + +namespace { + +enum : uint16_t { + kHeapMagic = 0xa5a5, + // This memory segment contains an MemBuf that is still in use + kMemBufInUse = 0x01, + // This memory segment contains buffer data that is still in use + kDataInUse = 0x02, +}; + +enum : std::size_t { + // When create() is called for buffers less than kDefaultCombinedBufSize, + // we allocate a single combined memory segment for the MemBuf and the data + // together. See the comments for createCombined()/createSeparate() for more + // details. + // + // (The size of 1k is largely just a guess here. We could could probably do + // benchmarks of real applications to see if adjusting this number makes a + // difference. Callers that know their exact use case can also explicitly + // call createCombined() or createSeparate().) + kDefaultCombinedBufSize = 1024 +}; + +// Helper function for MemBuf::takeOwnership() +void takeOwnershipError(bool freeOnError, void* buf, + utils::MemBuf::FreeFunction freeFn, void* userData) { + if (!freeOnError) { + return; + } + if (!freeFn) { + free(buf); + return; + } + try { + freeFn(buf, userData); + } catch (...) { + // The user's free function is not allowed to throw. + // (We are already in the middle of throwing an exception, so + // we cannot let this exception go unhandled.) + abort(); + } +} + +} // namespace + +namespace utils { + +struct MemBuf::HeapPrefix { + explicit HeapPrefix(uint16_t flg) : magic(kHeapMagic), flags(flg) {} + ~HeapPrefix() { + // Reset magic to 0 on destruction. This is solely for debugging purposes + // to help catch bugs where someone tries to use HeapStorage after it has + // been deleted. + magic = 0; + } + + uint16_t magic; + std::atomic<uint16_t> flags; +}; + +struct MemBuf::HeapStorage { + HeapPrefix prefix; + // The MemBuf is last in the HeapStorage object. + // This way operator new will work even if allocating a subclass of MemBuf + // that requires more space. + utils::MemBuf buf; +}; + +struct MemBuf::HeapFullStorage { + // Make sure jemalloc allocates from the 64-byte class. Putting this here + // because HeapStorage is private so it can't be at namespace level. + static_assert(sizeof(HeapStorage) <= 64, + "MemBuf may not grow over 56 bytes!"); + + HeapStorage hs; + SharedInfo shared; + std::max_align_t align; +}; + +MemBuf::SharedInfo::SharedInfo() : freeFn(nullptr), userData(nullptr) { + // Use relaxed memory ordering here. Since we are creating a new SharedInfo, + // no other threads should be referring to it yet. + refcount.store(1, std::memory_order_relaxed); +} + +MemBuf::SharedInfo::SharedInfo(FreeFunction fn, void* arg) + : freeFn(fn), userData(arg) { + // Use relaxed memory ordering here. Since we are creating a new SharedInfo, + // no other threads should be referring to it yet. + refcount.store(1, std::memory_order_relaxed); +} + +void* MemBuf::operator new(size_t size) { + size_t fullSize = offsetof(HeapStorage, buf) + size; + auto* storage = static_cast<HeapStorage*>(malloc(fullSize)); + + new (&storage->prefix) HeapPrefix(kMemBufInUse); + return &(storage->buf); +} + +void* MemBuf::operator new(size_t /* size */, void* ptr) { return ptr; } + +void MemBuf::operator delete(void* ptr) { + auto* storageAddr = static_cast<uint8_t*>(ptr) - offsetof(HeapStorage, buf); + auto* storage = reinterpret_cast<HeapStorage*>(storageAddr); + releaseStorage(storage, kMemBufInUse); +} + +void MemBuf::operator delete(void* /* ptr */, void* /* placement */) { + // Provide matching operator for `MemBuf::new` to avoid MSVC compilation + // warning (C4291) about memory leak when exception is thrown in the + // constructor. +} + +void MemBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) { + // Use relaxed memory order here. If we are unlucky and happen to get + // out-of-date data the compare_exchange_weak() call below will catch + // it and load new data with memory_order_acq_rel. + auto flags = storage->prefix.flags.load(std::memory_order_acquire); + + while (true) { + uint16_t newFlags = uint16_t(flags & ~freeFlags); + if (newFlags == 0) { + // The storage space is now unused. Free it. + storage->prefix.HeapPrefix::~HeapPrefix(); + free(storage); + return; + } + + // This storage segment still contains portions that are in use. + // Just clear the flags specified in freeFlags for now. + auto ret = storage->prefix.flags.compare_exchange_weak( + flags, newFlags, std::memory_order_acq_rel); + if (ret) { + // We successfully updated the flags. + return; + } + + // We failed to update the flags. Some other thread probably updated them + // and cleared some of the other bits. Continue around the loop to see if + // we are the last user now, or if we need to try updating the flags again. + } +} + +void MemBuf::freeInternalBuf(void* /* buf */, void* userData) { + auto* storage = static_cast<HeapStorage*>(userData); + releaseStorage(storage, kDataInUse); +} + +MemBuf::MemBuf(CreateOp, std::size_t capacity) + : next_(this), + prev_(this), + data_(nullptr), + length_(0), + flags_and_shared_info_(0) { + SharedInfo* info; + allocExtBuffer(capacity, &buf_, &info, &capacity_); + setSharedInfo(info); + data_ = buf_; +} + +MemBuf::MemBuf(CopyBufferOp /* op */, const void* buf, std::size_t size, + std::size_t headroom, std::size_t min_tailroom) + : MemBuf(CREATE, headroom + size + min_tailroom) { + advance(headroom); + if (size > 0) { + assert(buf != nullptr); + memcpy(writableData(), buf, size); + append(size); + } +} + +unique_ptr<MemBuf> MemBuf::create(std::size_t capacity) { + // For smaller-sized buffers, allocate the MemBuf, SharedInfo, and the buffer + // all with a single allocation. + // + // We don't do this for larger buffers since it can be wasteful if the user + // needs to reallocate the buffer but keeps using the same MemBuf object. + // In this case we can't free the data space until the MemBuf is also + // destroyed. Callers can explicitly call createCombined() or + // createSeparate() if they know their use case better, and know if they are + // likely to reallocate the buffer later. + if (capacity <= kDefaultCombinedBufSize) { + return createCombined(capacity); + } + return createSeparate(capacity); +} + +unique_ptr<MemBuf> MemBuf::createCombined(std::size_t capacity) { + // To save a memory allocation, allocate space for the MemBuf object, the + // SharedInfo struct, and the data itself all with a single call to malloc(). + size_t requiredStorage = offsetof(HeapFullStorage, align) + capacity; + size_t mallocSize = requiredStorage; + auto* storage = static_cast<HeapFullStorage*>(malloc(mallocSize)); + + new (&storage->hs.prefix) HeapPrefix(kMemBufInUse | kDataInUse); + new (&storage->shared) SharedInfo(freeInternalBuf, storage); + + uint8_t* bufAddr = reinterpret_cast<uint8_t*>(&storage->align); + uint8_t* storageEnd = reinterpret_cast<uint8_t*>(storage) + mallocSize; + size_t actualCapacity = size_t(storageEnd - bufAddr); + unique_ptr<MemBuf> ret(new (&storage->hs.buf) MemBuf( + InternalConstructor(), packFlagsAndSharedInfo(0, &storage->shared), + bufAddr, actualCapacity, bufAddr, 0)); + return ret; +} + +unique_ptr<MemBuf> MemBuf::createSeparate(std::size_t capacity) { + return std::make_unique<MemBuf>(CREATE, capacity); +} + +unique_ptr<MemBuf> MemBuf::createChain(size_t totalCapacity, + std::size_t maxBufCapacity) { + unique_ptr<MemBuf> out = + create(std::min(totalCapacity, size_t(maxBufCapacity))); + size_t allocatedCapacity = out->capacity(); + + while (allocatedCapacity < totalCapacity) { + unique_ptr<MemBuf> newBuf = create( + std::min(totalCapacity - allocatedCapacity, size_t(maxBufCapacity))); + allocatedCapacity += newBuf->capacity(); + out->prependChain(std::move(newBuf)); + } + + return out; +} + +MemBuf::MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity, + std::size_t length, FreeFunction freeFn, void* userData, + bool freeOnError) + : next_(this), + prev_(this), + data_(static_cast<uint8_t*>(buf)), + buf_(static_cast<uint8_t*>(buf)), + length_(length), + capacity_(capacity), + flags_and_shared_info_( + packFlagsAndSharedInfo(flag_free_shared_info, nullptr)) { + try { + setSharedInfo(new SharedInfo(freeFn, userData)); + } catch (...) { + takeOwnershipError(freeOnError, buf, freeFn, userData); + throw; + } +} + +unique_ptr<MemBuf> MemBuf::takeOwnership(void* buf, std::size_t capacity, + std::size_t length, + FreeFunction freeFn, void* userData, + bool freeOnError) { + try { + // TODO: We could allocate the MemBuf object and SharedInfo all in a single + // memory allocation. We could use the existing HeapStorage class, and + // define a new kSharedInfoInUse flag. We could change our code to call + // releaseStorage(flag_free_shared_info) when this flag_free_shared_info, + // rather than directly calling delete. + // + // Note that we always pass freeOnError as false to the constructor. + // If the constructor throws we'll handle it below. (We have to handle + // allocation failures from std::make_unique too.) + return std::make_unique<MemBuf>(TAKE_OWNERSHIP, buf, capacity, length, + freeFn, userData, false); + } catch (...) { + takeOwnershipError(freeOnError, buf, freeFn, userData); + throw; + } +} + +MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept + : MemBuf(InternalConstructor(), 0, + // We cast away the const-ness of the buffer here. + // This is okay since MemBuf users must use unshare() to create a + // copy of this buffer before writing to the buffer. + static_cast<uint8_t*>(const_cast<void*>(buf)), capacity, + static_cast<uint8_t*>(const_cast<void*>(buf)), capacity) {} + +unique_ptr<MemBuf> MemBuf::wrapBuffer(const void* buf, std::size_t capacity) { + return std::make_unique<MemBuf>(WRAP_BUFFER, buf, capacity); +} + +MemBuf MemBuf::wrapBufferAsValue(const void* buf, + std::size_t capacity) noexcept { + return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity); +} + +MemBuf::MemBuf() noexcept {} + +MemBuf::MemBuf(MemBuf&& other) noexcept + : data_(other.data_), + buf_(other.buf_), + length_(other.length_), + capacity_(other.capacity_), + flags_and_shared_info_(other.flags_and_shared_info_) { + // Reset other so it is a clean state to be destroyed. + other.data_ = nullptr; + other.buf_ = nullptr; + other.length_ = 0; + other.capacity_ = 0; + other.flags_and_shared_info_ = 0; + + // If other was part of the chain, assume ownership of the rest of its chain. + // (It's only valid to perform move assignment on the head of a chain.) + if (other.next_ != &other) { + next_ = other.next_; + next_->prev_ = this; + other.next_ = &other; + + prev_ = other.prev_; + prev_->next_ = this; + other.prev_ = &other; + } +} + +MemBuf::MemBuf(const MemBuf& other) { *this = other.cloneAsValue(); } + +MemBuf::MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, + std::size_t capacity, uint8_t* data, std::size_t length) noexcept + : next_(this), + prev_(this), + data_(data), + buf_(buf), + length_(length), + capacity_(capacity), + flags_and_shared_info_(flagsAndSharedInfo) { + assert(data >= buf); + assert(data + length <= buf + capacity); +} + +MemBuf::~MemBuf() { + // Destroying an MemBuf destroys the entire chain. + // Users of MemBuf should only explicitly delete the head of any chain. + // The other elements in the chain will be automatically destroyed. + while (next_ != this) { + // Since unlink() returns unique_ptr() and we don't store it, + // it will automatically delete the unlinked element. + (void)next_->unlink(); + } + + decrementRefcount(); +} + +MemBuf& MemBuf::operator=(MemBuf&& other) noexcept { + if (this == &other) { + return *this; + } + + // If we are part of a chain, delete the rest of the chain. + while (next_ != this) { + // Since unlink() returns unique_ptr() and we don't store it, + // it will automatically delete the unlinked element. + (void)next_->unlink(); + } + + // Decrement our refcount on the current buffer + decrementRefcount(); + + // Take ownership of the other buffer's data + data_ = other.data_; + buf_ = other.buf_; + length_ = other.length_; + capacity_ = other.capacity_; + flags_and_shared_info_ = other.flags_and_shared_info_; + // Reset other so it is a clean state to be destroyed. + other.data_ = nullptr; + other.buf_ = nullptr; + other.length_ = 0; + other.capacity_ = 0; + other.flags_and_shared_info_ = 0; + + // If other was part of the chain, assume ownership of the rest of its chain. + // (It's only valid to perform move assignment on the head of a chain.) + if (other.next_ != &other) { + next_ = other.next_; + next_->prev_ = this; + other.next_ = &other; + + prev_ = other.prev_; + prev_->next_ = this; + other.prev_ = &other; + } + + return *this; +} + +MemBuf& MemBuf::operator=(const MemBuf& other) { + if (this != &other) { + *this = MemBuf(other); + } + return *this; +} + +bool MemBuf::empty() const { + const MemBuf* current = this; + do { + if (current->length() != 0) { + return false; + } + current = current->next_; + } while (current != this); + return true; +} + +size_t MemBuf::countChainElements() const { + size_t numElements = 1; + for (MemBuf* current = next_; current != this; current = current->next_) { + ++numElements; + } + return numElements; +} + +std::size_t MemBuf::computeChainDataLength() const { + std::size_t fullLength = length_; + for (MemBuf* current = next_; current != this; current = current->next_) { + fullLength += current->length_; + } + return fullLength; +} + +void MemBuf::prependChain(unique_ptr<MemBuf>&& iobuf) { + // Take ownership of the specified MemBuf + MemBuf* other = iobuf.release(); + + // Remember the pointer to the tail of the other chain + MemBuf* otherTail = other->prev_; + + // Hook up prev_->next_ to point at the start of the other chain, + // and other->prev_ to point at prev_ + prev_->next_ = other; + other->prev_ = prev_; + + // Hook up otherTail->next_ to point at us, + // and prev_ to point back at otherTail, + otherTail->next_ = this; + prev_ = otherTail; +} + +unique_ptr<MemBuf> MemBuf::clone() const { + return std::make_unique<MemBuf>(cloneAsValue()); +} + +unique_ptr<MemBuf> MemBuf::cloneOne() const { + return std::make_unique<MemBuf>(cloneOneAsValue()); +} + +unique_ptr<MemBuf> MemBuf::cloneCoalesced() const { + return std::make_unique<MemBuf>(cloneCoalescedAsValue()); +} + +unique_ptr<MemBuf> MemBuf::cloneCoalescedWithHeadroomTailroom( + std::size_t new_headroom, std::size_t new_tailroom) const { + return std::make_unique<MemBuf>( + cloneCoalescedAsValueWithHeadroomTailroom(new_headroom, new_tailroom)); +} + +MemBuf MemBuf::cloneAsValue() const { + auto tmp = cloneOneAsValue(); + + for (MemBuf* current = next_; current != this; current = current->next_) { + tmp.prependChain(current->cloneOne()); + } + + return tmp; +} + +MemBuf MemBuf::cloneOneAsValue() const { + if (SharedInfo* info = sharedInfo()) { + setFlags(flag_maybe_shared); + info->refcount.fetch_add(1, std::memory_order_acq_rel); + } + return MemBuf(InternalConstructor(), flags_and_shared_info_, buf_, capacity_, + data_, length_); +} + +MemBuf MemBuf::cloneCoalescedAsValue() const { + const std::size_t new_headroom = headroom(); + const std::size_t new_tailroom = prev()->tailroom(); + return cloneCoalescedAsValueWithHeadroomTailroom(new_headroom, new_tailroom); +} + +MemBuf MemBuf::cloneCoalescedAsValueWithHeadroomTailroom( + std::size_t new_headroom, std::size_t new_tailroom) const { + if (!isChained()) { + return cloneOneAsValue(); + } + // Coalesce into newBuf + const std::size_t new_length = computeChainDataLength(); + const std::size_t new_capacity = new_length + new_headroom + new_tailroom; + MemBuf newBuf{CREATE, new_capacity}; + newBuf.advance(new_headroom); + + auto current = this; + do { + if (current->length() > 0) { + memcpy(newBuf.writableTail(), current->data(), current->length()); + newBuf.append(current->length()); + } + current = current->next(); + } while (current != this); + + return newBuf; +} + +void MemBuf::unshareOneSlow() { + // Allocate a new buffer for the data + uint8_t* buf; + SharedInfo* sharedInfo; + std::size_t actualCapacity; + allocExtBuffer(capacity_, &buf, &sharedInfo, &actualCapacity); + + // Copy the data + // Maintain the same amount of headroom. Since we maintained the same + // minimum capacity we also maintain at least the same amount of tailroom. + std::size_t headlen = headroom(); + if (length_ > 0) { + assert(data_ != nullptr); + memcpy(buf + headlen, data_, length_); + } + + // Release our reference on the old buffer + decrementRefcount(); + // Make sure flag_maybe_shared and flag_free_shared_info are all cleared. + setFlagsAndSharedInfo(0, sharedInfo); + + // Update the buffer pointers to point to the new buffer + data_ = buf + headlen; + buf_ = buf; +} + +void MemBuf::unshareChained() { + // unshareChained() should only be called if we are part of a chain of + // multiple MemBufs. The caller should have already verified this. + assert(isChained()); + + MemBuf* current = this; + while (true) { + if (current->isSharedOne()) { + // we have to unshare + break; + } + + current = current->next_; + if (current == this) { + // None of the MemBufs in the chain are shared, + // so return without doing anything + return; + } + } + + // We have to unshare. Let coalesceSlow() do the work. + coalesceSlow(); +} + +void MemBuf::markExternallyShared() { + MemBuf* current = this; + do { + current->markExternallySharedOne(); + current = current->next_; + } while (current != this); +} + +void MemBuf::makeManagedChained() { + assert(isChained()); + + MemBuf* current = this; + while (true) { + current->makeManagedOne(); + current = current->next_; + if (current == this) { + break; + } + } +} + +void MemBuf::coalesceSlow() { + // coalesceSlow() should only be called if we are part of a chain of multiple + // MemBufs. The caller should have already verified this. + + // Compute the length of the entire chain + std::size_t new_length = 0; + MemBuf* end = this; + do { + new_length += end->length_; + end = end->next_; + } while (end != this); + + coalesceAndReallocate(new_length, end); + // We should be only element left in the chain now +} + +void MemBuf::coalesceSlow(size_t max_length) { + // coalesceSlow() should only be called if we are part of a chain of multiple + // MemBufs. The caller should have already verified this. + + // Compute the length of the entire chain + std::size_t new_length = 0; + MemBuf* end = this; + while (true) { + new_length += end->length_; + end = end->next_; + if (new_length >= max_length) { + break; + } + if (end == this) { + throw std::overflow_error( + "attempted to coalesce more data than " + "available"); + } + } + + coalesceAndReallocate(new_length, end); + // We should have the requested length now +} + +void MemBuf::coalesceAndReallocate(size_t new_headroom, size_t new_length, + MemBuf* end, size_t new_tailroom) { + std::size_t new_capacity = new_length + new_headroom + new_tailroom; + + // Allocate space for the coalesced buffer. + // We always convert to an external buffer, even if we happened to be an + // internal buffer before. + uint8_t* newBuf; + SharedInfo* newInfo; + std::size_t actualCapacity; + allocExtBuffer(new_capacity, &newBuf, &newInfo, &actualCapacity); + + // Copy the data into the new buffer + uint8_t* new_data = newBuf + new_headroom; + uint8_t* p = new_data; + MemBuf* current = this; + size_t remaining = new_length; + do { + if (current->length_ > 0) { + assert(current->length_ <= remaining); + assert(current->data_ != nullptr); + remaining -= current->length_; + memcpy(p, current->data_, current->length_); + p += current->length_; + } + current = current->next_; + } while (current != end); + assert(remaining == 0); + + // Point at the new buffer + decrementRefcount(); + + // Make sure flag_maybe_shared and flag_free_shared_info are all cleared. + setFlagsAndSharedInfo(0, newInfo); + + capacity_ = actualCapacity; + buf_ = newBuf; + data_ = new_data; + length_ = new_length; + + // Separate from the rest of our chain. + // Since we don't store the unique_ptr returned by separateChain(), + // this will immediately delete the returned subchain. + if (isChained()) { + (void)separateChain(next_, current->prev_); + } +} + +void MemBuf::decrementRefcount() { + // Externally owned buffers don't have a SharedInfo object and aren't managed + // by the reference count + SharedInfo* info = sharedInfo(); + if (!info) { + return; + } + + // Decrement the refcount + uint32_t newcnt = info->refcount.fetch_sub(1, std::memory_order_acq_rel); + // Note that fetch_sub() returns the value before we decremented. + // If it is 1, we were the only remaining user; if it is greater there are + // still other users. + if (newcnt > 1) { + return; + } + + // We were the last user. Free the buffer + freeExtBuffer(); + + // Free the SharedInfo if it was allocated separately. + // + // This is only used by takeOwnership(). + // + // To avoid this special case handling in decrementRefcount(), we could have + // takeOwnership() set a custom freeFn() that calls the user's free function + // then frees the SharedInfo object. (This would require that + // takeOwnership() store the user's free function with its allocated + // SharedInfo object.) However, handling this specially with a flag seems + // like it shouldn't be problematic. + if (flags() & flag_free_shared_info) { + delete sharedInfo(); + } +} + +void MemBuf::reserveSlow(std::size_t min_headroom, std::size_t min_tailroom) { + size_t new_capacity = (size_t)length_ + min_headroom + min_tailroom; + + // // reserveSlow() is dangerous if anyone else is sharing the buffer, as we + // may + // // reallocate and free the original buffer. It should only ever be called + // if + // // we are the only user of the buffer. + + // We'll need to reallocate the buffer. + // There are a few options. + // - If we have enough total room, move the data around in the buffer + // and adjust the data_ pointer. + // - If we're using an internal buffer, we'll switch to an external + // buffer with enough headroom and tailroom. + // - If we have enough headroom (headroom() >= min_headroom) but not too much + // (so we don't waste memory), we can try: + // - If we don't have too much to copy, we'll use realloc() (note that + // realloc might have to copy + // headroom + data + tailroom) + // - Otherwise, bite the bullet and reallocate. + if (headroom() + tailroom() >= min_headroom + min_tailroom) { + uint8_t* new_data = writableBuffer() + min_headroom; + std::memmove(new_data, data_, length_); + data_ = new_data; + return; + } + + size_t new_allocated_capacity = 0; + uint8_t* new_buffer = nullptr; + std::size_t new_headroom = 0; + std::size_t old_headroom = headroom(); + + // If we have a buffer allocated with malloc and we just need more tailroom, + // try to use realloc()/xallocx() to grow the buffer in place. + SharedInfo* info = sharedInfo(); + if (info && (info->freeFn == nullptr) && length_ != 0 && + old_headroom >= min_headroom) { + size_t head_slack = old_headroom - min_headroom; + new_allocated_capacity = goodExtBufferSize(new_capacity + head_slack); + + size_t copySlack = capacity() - length_; + if (copySlack * 2 <= length_) { + void* p = realloc(buf_, new_allocated_capacity); + if (TRANSPORT_EXPECT_FALSE(p == nullptr)) { + throw std::bad_alloc(); + } + new_buffer = static_cast<uint8_t*>(p); + new_headroom = old_headroom; + } + } + + // None of the previous reallocation strategies worked (or we're using + // an internal buffer). malloc/copy/free. + if (new_buffer == nullptr) { + new_allocated_capacity = goodExtBufferSize(new_capacity); + new_buffer = static_cast<uint8_t*>(malloc(new_allocated_capacity)); + if (length_ > 0) { + assert(data_ != nullptr); + memcpy(new_buffer + min_headroom, data_, length_); + } + if (sharedInfo()) { + freeExtBuffer(); + } + new_headroom = min_headroom; + } + + std::size_t cap; + initExtBuffer(new_buffer, new_allocated_capacity, &info, &cap); + + if (flags() & flag_free_shared_info) { + delete sharedInfo(); + } + + setFlagsAndSharedInfo(0, info); + capacity_ = cap; + buf_ = new_buffer; + data_ = new_buffer + new_headroom; + // length_ is unchanged +} + +void MemBuf::freeExtBuffer() { + SharedInfo* info = sharedInfo(); + + if (info->freeFn) { + try { + info->freeFn(buf_, info->userData); + } catch (...) { + // The user's free function should never throw. Otherwise we might + // throw from the MemBuf destructor. Other code paths like coalesce() + // also assume that decrementRefcount() cannot throw. + abort(); + } + } else { + free(buf_); + } +} + +void MemBuf::allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn, + SharedInfo** infoReturn, + std::size_t* capacityReturn) { + size_t mallocSize = goodExtBufferSize(minCapacity); + uint8_t* buf = static_cast<uint8_t*>(malloc(mallocSize)); + initExtBuffer(buf, mallocSize, infoReturn, capacityReturn); + *bufReturn = buf; +} + +size_t MemBuf::goodExtBufferSize(std::size_t minCapacity) { + // Determine how much space we should allocate. We'll store the SharedInfo + // for the external buffer just after the buffer itself. (We store it just + // after the buffer rather than just before so that the code can still just + // use free(buf_) to free the buffer.) + size_t minSize = static_cast<size_t>(minCapacity) + sizeof(SharedInfo); + // Add room for padding so that the SharedInfo will be aligned on an 8-byte + // boundary. + minSize = (minSize + 7) & ~7; + + // Use goodMallocSize() to bump up the capacity to a decent size to request + // from malloc, so we can use all of the space that malloc will probably give + // us anyway. + return minSize; +} + +void MemBuf::initExtBuffer(uint8_t* buf, size_t mallocSize, + SharedInfo** infoReturn, + std::size_t* capacityReturn) { + // Find the SharedInfo storage at the end of the buffer + // and construct the SharedInfo. + uint8_t* infoStart = (buf + mallocSize) - sizeof(SharedInfo); + SharedInfo* sharedInfo = new (infoStart) SharedInfo; + + *capacityReturn = std::size_t(infoStart - buf); + *infoReturn = sharedInfo; +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/membuf.h b/libtransport/src/hicn/transport/utils/membuf.h new file mode 100755 index 000000000..944237e2b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/membuf.h @@ -0,0 +1,916 @@ +/* + * Copyright 2013-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The code in this file if adapated from the IOBuf of folly: + * https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h + */ + +#pragma once + +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/branch_prediction.h> + +#include <atomic> +#include <cassert> +#include <cinttypes> +#include <cstddef> +#include <cstring> +#include <iterator> +#include <limits> +#include <memory> +#include <type_traits> +#include <vector> + +// Ignore shadowing warnings within this file, so includers can use -Wshadow. +TRANSPORT_GNU_DISABLE_WARNING("-Wshadow") + +namespace utils { + +class MemBuf { + public: + enum CreateOp { CREATE }; + enum WrapBufferOp { WRAP_BUFFER }; + enum TakeOwnershipOp { TAKE_OWNERSHIP }; + enum CopyBufferOp { COPY_BUFFER }; + + typedef void (*FreeFunction)(void* buf, void* userData); + + static std::unique_ptr<MemBuf> create(std::size_t capacity); + MemBuf(CreateOp, std::size_t capacity); + + /** + * Create a new MemBuf, using a single memory allocation to allocate space + * for both the MemBuf object and the data storage space. + * + * This saves one memory allocation. However, it can be wasteful if you + * later need to grow the buffer using reserve(). If the buffer needs to be + * reallocated, the space originally allocated will not be freed() until the + * MemBuf object itself is also freed. (It can also be slightly wasteful in + * some cases where you clone this MemBuf and then free the original MemBuf.) + */ + static std::unique_ptr<MemBuf> createCombined(std::size_t capacity); + + /** + * Create a new IOBuf, using separate memory allocations for the IOBuf object + * for the IOBuf and the data storage space. + * + * This requires two memory allocations, but saves space in the long run + * if you know that you will need to reallocate the data buffer later. + */ + static std::unique_ptr<MemBuf> createSeparate(std::size_t capacity); + + /** + * Allocate a new MemBuf chain with the requested total capacity, allocating + * no more than maxBufCapacity to each buffer. + */ + static std::unique_ptr<MemBuf> createChain(size_t totalCapacity, + std::size_t maxBufCapacity); + + static std::unique_ptr<MemBuf> takeOwnership(void* buf, std::size_t capacity, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true) { + return takeOwnership(buf, capacity, capacity, freeFn, userData, + freeOnError); + } + + MemBuf(TakeOwnershipOp op, void* buf, std::size_t capacity, + FreeFunction freeFn = nullptr, void* userData = nullptr, + bool freeOnError = true) + : MemBuf(op, buf, capacity, capacity, freeFn, userData, freeOnError) {} + + static std::unique_ptr<MemBuf> takeOwnership(void* buf, std::size_t capacity, + std::size_t length, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + + MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity, std::size_t length, + FreeFunction freeFn = nullptr, void* userData = nullptr, + bool freeOnError = true); + + static std::unique_ptr<MemBuf> wrapBuffer(const void* buf, + std::size_t capacity); + + static MemBuf wrapBufferAsValue(const void* buf, + std::size_t capacity) noexcept; + + MemBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept; + + /** + * Convenience function to create a new MemBuf object that copies data from a + * user-supplied buffer, optionally allocating a given amount of + * headroom and tailroom. + */ + static std::unique_ptr<MemBuf> copyBuffer(const void* buf, std::size_t size, + std::size_t headroom = 0, + std::size_t minTailroom = 0); + + MemBuf(CopyBufferOp op, const void* buf, std::size_t size, + std::size_t headroom = 0, std::size_t minTailroom = 0); + + /** + * Convenience function to free a chain of MemBufs held by a unique_ptr. + */ + static void destroy(std::unique_ptr<MemBuf>&& data) { + auto destroyer = std::move(data); + } + + ~MemBuf(); + + bool empty() const; + + const uint8_t* data() const { return data_; } + + uint8_t* writableData() { return data_; } + + const uint8_t* tail() const { return data_ + length_; } + + uint8_t* writableTail() { return data_ + length_; } + + std::size_t length() const { return length_; } + + std::size_t headroom() const { return std::size_t(data_ - buffer()); } + + std::size_t tailroom() const { return std::size_t(bufferEnd() - tail()); } + + const uint8_t* buffer() const { return buf_; } + + uint8_t* writableBuffer() { return buf_; } + + const uint8_t* bufferEnd() const { return buf_ + capacity_; } + + std::size_t capacity() const { return capacity_; } + + MemBuf* next() { return next_; } + + const MemBuf* next() const { return next_; } + + MemBuf* prev() { return prev_; } + + const MemBuf* prev() const { return prev_; } + + /** + * Shift the data forwards in the buffer. + * + * This shifts the data pointer forwards in the buffer to increase the + * headroom. This is commonly used to increase the headroom in a newly + * allocated buffer. + * + * The caller is responsible for ensuring that there is sufficient + * tailroom in the buffer before calling advance(). + * + * If there is a non-zero data length, advance() will use memmove() to shift + * the data forwards in the buffer. In this case, the caller is responsible + * for making sure the buffer is unshared, so it will not affect other MemBufs + * that may be sharing the same underlying buffer. + */ + void advance(std::size_t amount) { + // In debug builds, assert if there is a problem. + assert(amount <= tailroom()); + + if (length_ > 0) { + memmove(data_ + amount, data_, length_); + } + data_ += amount; + } + + /** + * Shift the data backwards in the buffer. + * + * The caller is responsible for ensuring that there is sufficient headroom + * in the buffer before calling retreat(). + * + * If there is a non-zero data length, retreat() will use memmove() to shift + * the data backwards in the buffer. In this case, the caller is responsible + * for making sure the buffer is unshared, so it will not affect other MemBufs + * that may be sharing the same underlying buffer. + */ + void retreat(std::size_t amount) { + // In debug builds, assert if there is a problem. + assert(amount <= headroom()); + + if (length_ > 0) { + memmove(data_ - amount, data_, length_); + } + data_ -= amount; + } + + void prepend(std::size_t amount) { + data_ -= amount; + length_ += amount; + } + + void append(std::size_t amount) { length_ += amount; } + + void trimStart(std::size_t amount) { + data_ += amount; + length_ -= amount; + } + + void trimEnd(std::size_t amount) { length_ -= amount; } + + void clear() { + data_ = writableBuffer(); + length_ = 0; + } + + void reserve(std::size_t minHeadroom, std::size_t minTailroom) { + // Maybe we don't need to do anything. + if (headroom() >= minHeadroom && tailroom() >= minTailroom) { + return; + } + // If the buffer is empty but we have enough total room (head + tail), + // move the data_ pointer around. + if (length() == 0 && headroom() + tailroom() >= minHeadroom + minTailroom) { + data_ = writableBuffer() + minHeadroom; + return; + } + // Bah, we have to do actual work. + reserveSlow(minHeadroom, minTailroom); + } + + bool isChained() const { + assert((next_ == this) == (prev_ == this)); + return next_ != this; + } + + size_t countChainElements() const; + + std::size_t computeChainDataLength() const; + + void prependChain(std::unique_ptr<MemBuf>&& iobuf); + + void appendChain(std::unique_ptr<MemBuf>&& iobuf) { + // Just use prependChain() on the next element in our chain + next_->prependChain(std::move(iobuf)); + } + + std::unique_ptr<MemBuf> unlink() { + next_->prev_ = prev_; + prev_->next_ = next_; + prev_ = this; + next_ = this; + return std::unique_ptr<MemBuf>(this); + } + + /** + * Remove this MemBuf from its current chain and return a unique_ptr to + * the MemBuf that formerly followed it in the chain. + */ + std::unique_ptr<MemBuf> pop() { + MemBuf* next = next_; + next_->prev_ = prev_; + prev_->next_ = next_; + prev_ = this; + next_ = this; + return std::unique_ptr<MemBuf>((next == this) ? nullptr : next); + } + + /** + * Remove a subchain from this chain. + * + * Remove the subchain starting at head and ending at tail from this chain. + * + * Returns a unique_ptr pointing to head. (In other words, ownership of the + * head of the subchain is transferred to the caller.) If the caller ignores + * the return value and lets the unique_ptr be destroyed, the subchain will + * be immediately destroyed. + * + * The subchain referenced by the specified head and tail must be part of the + * same chain as the current MemBuf, but must not contain the current MemBuf. + * However, the specified head and tail may be equal to each other (i.e., + * they may be a subchain of length 1). + */ + std::unique_ptr<MemBuf> separateChain(MemBuf* head, MemBuf* tail) { + assert(head != this); + assert(tail != this); + + head->prev_->next_ = tail->next_; + tail->next_->prev_ = head->prev_; + + head->prev_ = tail; + tail->next_ = head; + + return std::unique_ptr<MemBuf>(head); + } + + /** + * Return true if at least one of the MemBufs in this chain are shared, + * or false if all of the MemBufs point to unique buffers. + * + * Use isSharedOne() to only check this MemBuf rather than the entire chain. + */ + bool isShared() const { + const MemBuf* current = this; + while (true) { + if (current->isSharedOne()) { + return true; + } + current = current->next_; + if (current == this) { + return false; + } + } + } + + /** + * Return true if all MemBufs in this chain are managed by the usual + * refcounting mechanism (and so the lifetime of the underlying memory + * can be extended by clone()). + */ + bool isManaged() const { + const MemBuf* current = this; + while (true) { + if (!current->isManagedOne()) { + return false; + } + current = current->next_; + if (current == this) { + return true; + } + } + } + + /** + * Return true if this MemBuf is managed by the usual refcounting mechanism + * (and so the lifetime of the underlying memory can be extended by + * cloneOne()). + */ + bool isManagedOne() const { return sharedInfo(); } + + /** + * Return true if other MemBufs are also pointing to the buffer used by this + * MemBuf, and false otherwise. + * + * If this MemBuf points at a buffer owned by another (non-MemBuf) part of the + * code (i.e., if the MemBuf was created using wrapBuffer(), or was cloned + * from such an MemBuf), it is always considered shared. + * + * This only checks the current MemBuf, and not other MemBufs in the chain. + */ + bool isSharedOne() const { + // If this is a user-owned buffer, it is always considered shared + if ((TRANSPORT_EXPECT_FALSE(!sharedInfo()))) { + return true; + } + + if ((TRANSPORT_EXPECT_FALSE(sharedInfo()->externallyShared))) { + return true; + } + + if ((TRANSPORT_EXPECT_TRUE(!(flags() & flag_maybe_shared)))) { + return false; + } + + // flag_maybe_shared is set, so we need to check the reference count. + // (Checking the reference count requires an atomic operation, which is why + // we prefer to only check flag_maybe_shared if possible.) + bool shared = sharedInfo()->refcount.load(std::memory_order_acquire) > 1; + if (!shared) { + // we're the last one left + clearFlags(flag_maybe_shared); + } + return shared; + } + + /** + * Ensure that this MemBuf has a unique buffer that is not shared by other + * MemBufs. + * + * unshare() operates on an entire chain of MemBuf objects. If the chain is + * shared, it may also coalesce the chain when making it unique. If the + * chain is coalesced, subsequent MemBuf objects in the current chain will be + * automatically deleted. + * + * Note that buffers owned by other (non-MemBuf) users are automatically + * considered shared. + * + * Throws std::bad_alloc on error. On error the MemBuf chain will be + * unmodified. + * + * Currently unshare may also throw std::overflow_error if it tries to + * coalesce. (TODO: In the future it would be nice if unshare() were smart + * enough not to coalesce the entire buffer if the data is too large. + * However, in practice this seems unlikely to become an issue.) + */ + void unshare() { + if (isChained()) { + unshareChained(); + } else { + unshareOne(); + } + } + + /** + * Ensure that this MemBuf has a unique buffer that is not shared by other + * MemBufs. + * + * unshareOne() operates on a single MemBuf object. This MemBuf will have a + * unique buffer after unshareOne() returns, but other MemBufs in the chain + * may still be shared after unshareOne() returns. + * + * Throws std::bad_alloc on error. On error the MemBuf will be unmodified. + */ + void unshareOne() { + if (isSharedOne()) { + unshareOneSlow(); + } + } + + /** + * Mark the underlying buffers in this chain as shared with external memory + * management mechanism. This will make isShared() always returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an MemBuf, before it has been shared with other threads. + */ + void markExternallyShared(); + + /** + * Mark the underlying buffer that this MemBuf refers to as shared with + * external memory management mechanism. This will make isSharedOne() always + * returns true. + * + * This function is not thread-safe, and only safe to call immediately after + * creating an MemBuf, before it has been shared with other threads. + */ + void markExternallySharedOne() { + SharedInfo* info = sharedInfo(); + if (info) { + info->externallyShared = true; + } + } + + /** + * Ensure that the memory that MemBufs in this chain refer to will continue to + * be allocated for as long as the MemBufs of the chain (or any clone()s + * created from this point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManaged() { + if (isChained()) { + makeManagedChained(); + } else { + makeManagedOne(); + } + } + + /** + * Ensure that the memory that this MemBuf refers to will continue to be + * allocated for as long as this MemBuf (or any clone()s created from this + * point onwards) is alive. + * + * This only has an effect for user-owned buffers (created with the + * WRAP_BUFFER constructor or wrapBuffer factory function), in which case + * those buffers are unshared. + */ + void makeManagedOne() { + if (!isManagedOne()) { + // We can call the internal function directly; unmanaged implies shared. + unshareOneSlow(); + } + } + + // /** + // * Coalesce this MemBuf chain into a single buffer. + // * + // * This method moves all of the data in this MemBuf chain into a single + // * contiguous buffer, if it is not already in one buffer. After coalesce() + // * returns, this MemBuf will be a chain of length one. Other MemBufs in + // the + // * chain will be automatically deleted. + // * + // * After coalescing, the MemBuf will have at least as much headroom as the + // * first MemBuf in the chain, and at least as much tailroom as the last + // MemBuf + // * in the chain. + // * + // * Throws std::bad_alloc on error. On error the MemBuf chain will be + // * unmodified. + // * + // * Returns ByteRange that points to the data MemBuf stores. + // */ + // ByteRange coalesce() { + // const std::size_t newHeadroom = headroom(); + // const std::size_t newTailroom = prev()->tailroom(); + // return coalesceWithHeadroomTailroom(newHeadroom, newTailroom); + // } + + // /** + // * This is similar to the coalesce() method, except this allows to set a + // * headroom and tailroom after coalescing. + // * + // * Returns ByteRange that points to the data MemBuf stores. + // */ + // ByteRange coalesceWithHeadroomTailroom( + // std::size_t newHeadroom, + // std::size_t newTailroom) { + // if (isChained()) { + // coalesceAndReallocate( + // newHeadroom, computeChainDataLength(), this, newTailroom); + // } + // return ByteRange(data_, length_); + // } + + /** + * Ensure that this chain has at least maxLength bytes available as a + * contiguous memory range. + * + * This method coalesces whole buffers in the chain into this buffer as + * necessary until this buffer's length() is at least maxLength. + * + * After coalescing, the MemBuf will have at least as much headroom as the + * first MemBuf in the chain, and at least as much tailroom as the last MemBuf + * that was coalesced. + * + * Throws std::bad_alloc or std::overflow_error on error. On error the MemBuf + * chain will be unmodified. Throws std::overflow_error if maxLength is + * longer than the total chain length. + * + * Upon return, either enough of the chain was coalesced into a contiguous + * region, or the entire chain was coalesced. That is, + * length() >= maxLength || !isChained() is true. + */ + void gather(std::size_t maxLength) { + if (!isChained() || length_ >= maxLength) { + return; + } + coalesceSlow(maxLength); + } + + /** + * Return a new MemBuf chain sharing the same data as this chain. + * + * The new MemBuf chain will normally point to the same underlying data + * buffers as the original chain. (The one exception to this is if some of + * the MemBufs in this chain contain small internal data buffers which cannot + * be shared.) + */ + std::unique_ptr<MemBuf> clone() const; + + /** + * Similar to clone(). But returns MemBuf by value rather than heap-allocating + * it. + */ + MemBuf cloneAsValue() const; + + /** + * Return a new MemBuf with the same data as this MemBuf. + * + * The new MemBuf returned will not be part of a chain (even if this MemBuf is + * part of a larger chain). + */ + std::unique_ptr<MemBuf> cloneOne() const; + + /** + * Similar to cloneOne(). But returns MemBuf by value rather than + * heap-allocating it. + */ + MemBuf cloneOneAsValue() const; + + /** + * Return a new unchained MemBuf that may share the same data as this chain. + * + * If the MemBuf chain is not chained then the new MemBuf will point to the + * same underlying data buffer as the original chain. Otherwise, it will clone + * and coalesce the MemBuf chain. + * + * The new MemBuf will have at least as much headroom as the first MemBuf in + * the chain, and at least as much tailroom as the last MemBuf in the chain. + * + * Throws std::bad_alloc on error. + */ + std::unique_ptr<MemBuf> cloneCoalesced() const; + + /** + * This is similar to the cloneCoalesced() method, except this allows to set a + * headroom and tailroom for the new MemBuf. + */ + std::unique_ptr<MemBuf> cloneCoalescedWithHeadroomTailroom( + std::size_t newHeadroom, std::size_t newTailroom) const; + + /** + * Similar to cloneCoalesced(). But returns MemBuf by value rather than + * heap-allocating it. + */ + MemBuf cloneCoalescedAsValue() const; + + /** + * This is similar to the cloneCoalescedAsValue() method, except this allows + * to set a headroom and tailroom for the new MemBuf. + */ + MemBuf cloneCoalescedAsValueWithHeadroomTailroom( + std::size_t newHeadroom, std::size_t newTailroom) const; + + /** + * Similar to Clone(). But use other as the head node. Other nodes in the + * chain (if any) will be allocted on heap. + */ + void cloneInto(MemBuf& other) const { other = cloneAsValue(); } + + /** + * Similar to CloneOne(). But to fill an existing MemBuf instead of a new + * MemBuf. + */ + void cloneOneInto(MemBuf& other) const { other = cloneOneAsValue(); } + + /** + * Return an iovector suitable for e.g. writev() + * + * auto iov = buf->getIov(); + * auto xfer = writev(fd, iov.data(), iov.size()); + * + * Naturally, the returned iovector is invalid if you modify the buffer + * chain. + */ + std::vector<struct iovec> getIov() const; + + /** + * Update an existing iovec array with the MemBuf data. + * + * New iovecs will be appended to the existing vector; anything already + * present in the vector will be left unchanged. + * + * Naturally, the returned iovec data will be invalid if you modify the + * buffer chain. + */ + void appendToIov(std::vector<struct iovec>* iov) const; + + /** + * Fill an iovec array with the MemBuf data. + * + * Returns the number of iovec filled. If there are more buffer than + * iovec, returns 0. This version is suitable to use with stack iovec + * arrays. + * + * Naturally, the filled iovec data will be invalid if you modify the + * buffer chain. + */ + size_t fillIov(struct iovec* iov, size_t len) const; + + /** + * A helper that wraps a number of iovecs into an MemBuf chain. If count == + * 0, then a zero length buf is returned. This function never returns + * nullptr. + */ + static std::unique_ptr<MemBuf> wrapIov(const iovec* vec, size_t count); + + /** + * A helper that takes ownerships a number of iovecs into an MemBuf chain. If + * count == 0, then a zero length buf is returned. This function never + * returns nullptr. + */ + static std::unique_ptr<MemBuf> takeOwnershipIov(const iovec* vec, + size_t count, + FreeFunction freeFn = nullptr, + void* userData = nullptr, + bool freeOnError = true); + + /* + * Overridden operator new and delete. + * These perform specialized memory management to help support + * createCombined(), which allocates MemBuf objects together with the buffer + * data. + */ + void* operator new(size_t size); + void* operator new(size_t size, void* ptr); + void operator delete(void* ptr); + void operator delete(void* ptr, void* placement); + + // /** + // * Iteration support: a chain of MemBufs may be iterated through using + // * STL-style iterators over const ByteRanges. Iterators are only + // invalidated + // * if the MemBuf that they currently point to is removed. + // */ + // Iterator cbegin() const; + // Iterator cend() const; + // Iterator begin() const; + // Iterator end() const; + + /** + * Allocate a new null buffer. + * + * This can be used to allocate an empty MemBuf on the stack. It will have no + * space allocated for it. This is generally useful only to later use move + * assignment to fill out the MemBuf. + */ + MemBuf() noexcept; + + /** + * Move constructor and assignment operator. + * + * In general, you should only ever move the head of an MemBuf chain. + * Internal nodes in an MemBuf chain are owned by the head of the chain, and + * should not be moved from. (Technically, nothing prevents you from moving + * a non-head node, but the moved-to node will replace the moved-from node in + * the chain. This has implications for ownership, since non-head nodes are + * owned by the chain head. You are then responsible for relinquishing + * ownership of the moved-to node, and manually deleting the moved-from + * node.) + * + * With the move assignment operator, the destination of the move should be + * the head of an MemBuf chain or a solitary MemBuf not part of a chain. If + * the move destination is part of a chain, all other MemBufs in the chain + * will be deleted. + */ + MemBuf(MemBuf&& other) noexcept; + MemBuf& operator=(MemBuf&& other) noexcept; + + MemBuf(const MemBuf& other); + MemBuf& operator=(const MemBuf& other); + + private: + enum FlagsEnum : uintptr_t { + // Adding any more flags would not work on 32-bit architectures, + // as these flags are stashed in the least significant 2 bits of a + // max-align-aligned pointer. + flag_free_shared_info = 0x1, + flag_maybe_shared = 0x2, + flag_mask = flag_free_shared_info | flag_maybe_shared + }; + + struct SharedInfo { + SharedInfo(); + SharedInfo(FreeFunction fn, void* arg); + + // A pointer to a function to call to free the buffer when the refcount + // hits 0. If this is null, free() will be used instead. + FreeFunction freeFn; + void* userData; + std::atomic<uint32_t> refcount; + bool externallyShared{false}; + }; + // Helper structs for use by operator new and delete + struct HeapPrefix; + struct HeapStorage; + struct HeapFullStorage; + + /** + * Create a new MemBuf pointing to an external buffer. + * + * The caller is responsible for holding a reference count for this new + * MemBuf. The MemBuf constructor does not automatically increment the + * reference count. + */ + struct InternalConstructor {}; // avoid conflicts + MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf, + std::size_t capacity, uint8_t* data, std::size_t length) noexcept; + + void unshareOneSlow(); + void unshareChained(); + void makeManagedChained(); + void coalesceSlow(); + void coalesceSlow(size_t maxLength); + // newLength must be the entire length of the buffers between this and + // end (no truncation) + void coalesceAndReallocate(size_t newHeadroom, size_t newLength, MemBuf* end, + size_t newTailroom); + void coalesceAndReallocate(size_t newLength, MemBuf* end) { + coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom()); + } + void decrementRefcount(); + void reserveSlow(std::size_t minHeadroom, std::size_t minTailroom); + void freeExtBuffer(); + + static size_t goodExtBufferSize(std::size_t minCapacity); + static void initExtBuffer(uint8_t* buf, size_t mallocSize, + SharedInfo** infoReturn, + std::size_t* capacityReturn); + static void allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn, + SharedInfo** infoReturn, + std::size_t* capacityReturn); + static void releaseStorage(HeapStorage* storage, uint16_t freeFlags); + static void freeInternalBuf(void* buf, void* userData); + + /* + * Member variables + */ + + /* + * Links to the next and the previous MemBuf in this chain. + * + * The chain is circularly linked (the last element in the chain points back + * at the head), and next_ and prev_ can never be null. If this MemBuf is the + * only element in the chain, next_ and prev_ will both point to this. + */ + MemBuf* next_{this}; + MemBuf* prev_{this}; + + /* + * A pointer to the start of the data referenced by this MemBuf, and the + * length of the data. + * + * This may refer to any subsection of the actual buffer capacity. + */ + uint8_t* data_{nullptr}; + uint8_t* buf_{nullptr}; + std::size_t length_{0}; + std::size_t capacity_{0}; + + // Pack flags in least significant 2 bits, sharedInfo in the rest + mutable uintptr_t flags_and_shared_info_{0}; + + static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags, + SharedInfo* info) { + uintptr_t uinfo = reinterpret_cast<uintptr_t>(info); + return flags | uinfo; + } + + inline SharedInfo* sharedInfo() const { + return reinterpret_cast<SharedInfo*>(flags_and_shared_info_ & ~flag_mask); + } + + inline void setSharedInfo(SharedInfo* info) { + uintptr_t uinfo = reinterpret_cast<uintptr_t>(info); + flags_and_shared_info_ = (flags_and_shared_info_ & flag_mask) | uinfo; + } + + inline uintptr_t flags() const { return flags_and_shared_info_ & flag_mask; } + + // flags_ are changed from const methods + inline void setFlags(uintptr_t flags) const { + flags_and_shared_info_ |= flags; + } + + inline void clearFlags(uintptr_t flags) const { + flags_and_shared_info_ &= ~flags; + } + + inline void setFlagsAndSharedInfo(uintptr_t flags, SharedInfo* info) { + flags_and_shared_info_ = packFlagsAndSharedInfo(flags, info); + } + + struct DeleterBase { + virtual ~DeleterBase() {} + virtual void dispose(void* p) = 0; + }; + + template <class UniquePtr> + struct UniquePtrDeleter : public DeleterBase { + typedef typename UniquePtr::pointer Pointer; + typedef typename UniquePtr::deleter_type Deleter; + + explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {} + void dispose(void* p) override { + try { + deleter_(static_cast<Pointer>(p)); + delete this; + } catch (...) { + abort(); + } + } + + private: + Deleter deleter_; + }; + + static void freeUniquePtrBuffer(void* ptr, void* userData) { + static_cast<DeleterBase*>(userData)->dispose(ptr); + } +}; + +// template <class UniquePtr> +// typename std::enable_if< +// detail::IsUniquePtrToSL<UniquePtr>::value, +// std::unique_ptr<MemBuf>>::type +// MemBuf::takeOwnership(UniquePtr&& buf, size_t count) { +// size_t size = count * sizeof(typename UniquePtr::element_type); +// auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter()); +// return takeOwnership( +// buf.release(), size, &MemBuf::freeUniquePtrBuffer, deleter); +// } + +inline std::unique_ptr<MemBuf> MemBuf::copyBuffer(const void* data, + std::size_t size, + std::size_t headroom, + std::size_t minTailroom) { + std::size_t capacity = headroom + size + minTailroom; + std::unique_ptr<MemBuf> buf = MemBuf::create(capacity); + buf->advance(headroom); + if (size != 0) { + memcpy(buf->writableData(), data, size); + } + buf->append(size); + return buf; +} + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/min_filter.h b/libtransport/src/hicn/transport/utils/min_filter.h new file mode 100755 index 000000000..acb081edc --- /dev/null +++ b/libtransport/src/hicn/transport/utils/min_filter.h @@ -0,0 +1,56 @@ +/* + * 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.sudo make instamake install + */ + +#pragma once + +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/log.h> + +#include <deque> +#include <iostream> +#include <set> +#include <type_traits> +#include <vector> + +namespace utils { + +template <typename T> +class MinFilter { + public: + MinFilter(std::size_t size) : size_(size) {} + + std::size_t size() { return by_arrival_.size(); } + + template <typename R> + TRANSPORT_ALWAYS_INLINE void pushBack(R&& value) { + if (by_arrival_.size() > size_) { + by_order_.erase(by_arrival_.back()); + by_arrival_.pop_back(); + } + + by_arrival_.push_front(by_order_.insert(std::forward<R>(value))); + } + + TRANSPORT_ALWAYS_INLINE const T& begin() { return *by_order_.cbegin(); } + + TRANSPORT_ALWAYS_INLINE const T& rBegin() { return *by_order_.crbegin(); } + + private: + std::multiset<T> by_order_; + std::deque<typename std::multiset<T>::const_iterator> by_arrival_; + std::size_t size_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/object_pool.h b/libtransport/src/hicn/transport/utils/object_pool.h new file mode 100755 index 000000000..d4d8e18d6 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/object_pool.h @@ -0,0 +1,76 @@ +/* + * 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 + +// TODO +#include <hicn/transport/utils/spinlock.h> + +#include <deque> +#include <memory> +#include <mutex> + +namespace utils { + +template <typename T> +class ObjectPool { + class ObjectDeleter { + public: + ObjectDeleter(ObjectPool<T> *pool = nullptr) : pool_(pool) {} + + void operator()(T *t) { + if (pool_) { + pool_->add(t); + } else { + delete t; + } + } + + private: + ObjectPool<T> *pool_; + }; + + public: + using Ptr = std::unique_ptr<T, ObjectDeleter>; + + ObjectPool() {} + + std::pair<bool, Ptr> get() { + if (object_pool_.empty()) { + return std::make_pair<bool, Ptr>(false, makePtr(nullptr)); + } + + utils::SpinLock::Acquire locked(object_pool_lock_); + auto ret = std::move(object_pool_.front()); + object_pool_.pop_front(); + return std::make_pair<bool, Ptr>(true, std::move(ret)); + } + + void add(T *object) { + utils::SpinLock::Acquire locked(object_pool_lock_); + object_pool_.emplace_back(makePtr(object)); + } + + Ptr makePtr(T *object) { return Ptr(object, ObjectDeleter(this)); } + + private: + // No copies + ObjectPool(const ObjectPool &other) = delete; + + utils::SpinLock object_pool_lock_; + std::deque<Ptr> object_pool_; +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/ring_buffer.h b/libtransport/src/hicn/transport/utils/ring_buffer.h new file mode 100755 index 000000000..52bcd81c4 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/ring_buffer.h @@ -0,0 +1,129 @@ +/* + * 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 <atomic> +#include <cstddef> + +namespace utils { + +/** + * NOTE: Single consumer single producer ring buffer + */ +template <typename Element, std::size_t Size> +class CircularFifo { + public: + enum { Capacity = Size + 1 }; + + CircularFifo() : tail_(0), head_(0), size_(0) {} + virtual ~CircularFifo() {} + + bool push(const Element& item); + bool push(Element&& item); + bool pop(Element& item); + + bool wasEmpty() const; + bool wasFull() const; + bool isLockFree() const; + std::size_t size() const; + + private: + std::size_t increment(std::size_t idx) const; + std::atomic<std::size_t> tail_; // tail(input) index + Element array_[Capacity]; + std::atomic<std::size_t> head_; // head(output) index + std::atomic<std::size_t> size_; +}; + +template <typename Element, std::size_t Size> +bool CircularFifo<Element, Size>::push(const Element& item) { + const auto current_tail = tail_.load(std::memory_order_relaxed); + const auto next_tail = increment(current_tail); + if (next_tail != head_.load(std::memory_order_acquire)) { + array_[current_tail] = item; + tail_.store(next_tail, std::memory_order_release); + size_++; + return true; + } + + // full queue + return false; +} + +/** + * Push by move + */ +template <typename Element, std::size_t Size> +bool CircularFifo<Element, Size>::push(Element&& item) { + const auto current_tail = tail_.load(std::memory_order_relaxed); + const auto next_tail = increment(current_tail); + if (next_tail != head_.load(std::memory_order_acquire)) { + array_[current_tail] = std::move(item); + tail_.store(next_tail, std::memory_order_release); + size_++; + return true; + } + + // full queue + return false; +} + +// Pop by Consumer can only update the head +// (load with relaxed, store with release) +// the tail must be accessed with at least acquire +template <typename Element, std::size_t Size> +bool CircularFifo<Element, Size>::pop(Element& item) { + const auto current_head = head_.load(std::memory_order_relaxed); + if (current_head == tail_.load(std::memory_order_acquire)) { + return false; // empty queue + } + + item = std::move(array_[current_head]); + head_.store(increment(current_head), std::memory_order_release); + size_--; + return true; +} + +template <typename Element, std::size_t Size> +bool CircularFifo<Element, Size>::wasEmpty() const { + // snapshot with acceptance of that this comparison operation is not atomic + return (head_.load() == tail_.load()); +} + +// snapshot with acceptance that this comparison is not atomic +template <typename Element, std::size_t Size> +bool CircularFifo<Element, Size>::wasFull() const { + const auto next_tail = + increment(tail_.load()); // acquire, we dont know who call + return (next_tail == head_.load()); +} + +template <typename Element, std::size_t Size> +bool CircularFifo<Element, Size>::isLockFree() const { + return (tail_.is_lock_free() && head_.is_lock_free()); +} + +template <typename Element, std::size_t Size> +std::size_t CircularFifo<Element, Size>::increment(std::size_t idx) const { + return (idx + 1) % Capacity; +} + +template <typename Element, std::size_t Size> +std::size_t CircularFifo<Element, Size>::size() const { + return size_.load(std::memory_order_relaxed); +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/sharable_vector.h b/libtransport/src/hicn/transport/utils/sharable_vector.h new file mode 100755 index 000000000..31adff1ad --- /dev/null +++ b/libtransport/src/hicn/transport/utils/sharable_vector.h @@ -0,0 +1,30 @@ +/* + * 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 <memory> +#include <vector> + +namespace utils { + +template <class T> +class SharableVector : public std::vector<T>, + public std::enable_shared_from_this<SharableVector<T>> { + public: + virtual ~SharableVector(){}; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/signer.cc b/libtransport/src/hicn/transport/utils/signer.cc new file mode 100755 index 000000000..c11d5e183 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/signer.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright 2017 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/errors/malformed_ahpacket_exception.h> +#include <hicn/transport/utils/endianess.h> +#include <hicn/transport/utils/membuf.h> +#include <hicn/transport/utils/signer.h> +#include <hicn/transport/utils/key_id.h> + + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include <hicn/hicn.h> +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Security.h> +#include <parc/security/parc_SymmetricKeySigner.h> +} + +#include <chrono> + +#define ALLOW_UNALIGNED_READS 1 + +namespace utils { + +uint8_t Signer::zeros[200] = {0}; + +/*One signer_ per Private Key*/ +Signer::Signer(PARCKeyStore *keyStore, PARCCryptoSuite suite) { + switch (suite) { + case PARCCryptoSuite_NULL_CRC32C: + break; + case PARCCryptoSuite_ECDSA_SHA256: + case PARCCryptoSuite_RSA_SHA256: + case PARCCryptoSuite_DSA_SHA256: + case PARCCryptoSuite_RSA_SHA512: + this->signer_ = + parcSigner_Create(parcPublicKeySigner_Create(keyStore, suite), + PARCPublicKeySignerAsSigner); + this->key_id_ = parcSigner_CreateKeyId(this->signer_); + break; + + case PARCCryptoSuite_HMAC_SHA512: + case PARCCryptoSuite_HMAC_SHA256: + default: + this->signer_ = parcSigner_Create( + parcSymmetricKeySigner_Create((PARCSymmetricKeyStore *)keyStore, + parcCryptoSuite_GetCryptoHash(suite)), + PARCSymmetricKeySignerAsSigner); + this->key_id_ = parcSigner_CreateKeyId(this->signer_); + break; + } +} + +Signer::Signer(const PARCSigner *signer) + : signer_(parcSigner_Acquire(signer)), + key_id_(parcSigner_CreateKeyId(this->signer_)) {} + +Signer::~Signer() { + parcSigner_Release(&signer_); + parcKeyId_Release(&key_id_); +} + +void Signer::sign(Packet &packet) { + // header chain points to the IP + TCP hicn header + utils::MemBuf *header_chain = packet.header_head_; + utils::MemBuf * payload_chain = packet.payload_head_; + uint8_t *hicn_packet = header_chain->writableData(); + Packet::Format format = packet.getFormat(); + std::size_t sign_len_bytes = parcSigner_GetSignatureSize(signer_); + + if (!(format & HFO_AH)) { + throw errors::MalformedAHPacketException(); + } + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + if (format & HFO_INET) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v6_hdr_t)); + } + + std::size_t header_len = Packet::getHeaderSizeFromFormat(format); + + packet.resetForHash(); + packet.setSignatureSize(sign_len_bytes); + + /* Fill the hicn_ah header */ + using namespace std::chrono; + auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); + packet.setSignatureTimestamp(now); + // *reinterpret_cast<uint64_t*>(ah->signTime) = utils::hton<uint64_t>(now); + // // std::memcpy(&ah->hicn_ah.signTime, &sign_time, sizeof(ah->hicn_ah.signTime)); + + packet.setValidationAlgorithm(CryptoSuite(parcSigner_GetCryptoSuite(this->signer_))); + // ah->validationAlgorithm = parcSigner_GetCryptoSuite(this->signer_); + + KeyId key_id; + key_id.first = (uint8_t *)parcBuffer_Overlay((PARCBuffer *) parcKeyId_GetKeyId(this->key_id_), 0); + packet.setKeyId(key_id); + + // memcpy(ah->keyId, + // parcBuffer_Overlay((PARCBuffer *) parcKeyId_GetKeyId(this->key_id_), 0), + // sizeof(_ah_header_t::keyId)); + + // Calculate hash + utils::CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_)); + hasher.init(); + hasher.updateBytes(hicn_packet, header_len); + hasher.updateBytes(zeros, sign_len_bytes); + + for (utils::MemBuf *current = payload_chain; current != header_chain; current = current->next()) { + hasher.updateBytes(current->data(), current->length()); + } + + utils::CryptoHash hash = hasher.finalize(); + + PARCSignature *signature = parcSigner_SignDigest(this->signer_, hash.hash_); + PARCBuffer *buffer = parcSignature_GetSignature(signature); + + PARCByteArray * byte_array = parcBuffer_Array(buffer); + uint8_t * bytes = parcByteArray_Array(byte_array); + size_t bytes_len = parcBuffer_Remaining(buffer); + + if (bytes_len > sign_len_bytes) { + throw errors::MalformedAHPacketException(); + } + + /* Restore the resetted fields */ + if (format & HFO_INET) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v6_hdr_t)); + } + + int offset = sign_len_bytes - bytes_len; + + std::unique_ptr<utils::MemBuf> signature_buffer; + std::unique_ptr<utils::MemBuf> tmp_buf = utils::MemBuf::takeOwnership( + bytes, + bytes_len, + bytes_len, + [](void* buf, void* userData){ parcSignature_Release((PARCSignature **)&userData); }, + signature, + true); + + if (offset) { + signature_buffer = utils::MemBuf::create(offset); + memset(signature_buffer->writableData(), 0, offset); + signature_buffer->append(offset); + signature_buffer->appendChain(std::move(tmp_buf)); + } else { + signature_buffer = std::move(tmp_buf); + } + + packet.setSignature(std::move(signature_buffer)); +} + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/signer.h b/libtransport/src/hicn/transport/utils/signer.h new file mode 100755 index 000000000..7b54b63c8 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/signer.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/packet.h> + +extern "C" { +#include <parc/security/parc_CryptoHashType.h> +#include <parc/security/parc_CryptoSuite.h> +#include <parc/security/parc_KeyStore.h> +#include <parc/security/parc_Signer.h> +} + +namespace utils { + +using Packet = transport::core::Packet; + +/** + * A signer can use a single key (asymmetric or symmetric) to sign a packet. + */ +class Signer { + friend class Identity; + + public: + /** + * Create a Signer + * + * @param keyStore A keystore containing a private key or simmetric key to + * use to sign packet with this Signer. + * @param suite CryptoSuite to use to verify the signature + */ + Signer(PARCKeyStore *keyStore, PARCCryptoSuite suite); + + Signer(const PARCSigner *signer); + + ~Signer(); + + /** + * @brief Sign a packet + * + * This method is general and must be used for Public-private key signature, + * HMAC and CRC. + * + * @param packet A pointer to the header of the packet to sign. Mutable + * field in the packet must be set to 0. + * @param key_id Indentifier of the key to use to generate the signature. + */ + void sign(Packet &packet); + + private: + PARCSigner *signer_; + PARCKeyId *key_id_; + static uint8_t zeros[200]; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/socket.h b/libtransport/src/hicn/transport/utils/socket.h new file mode 100755 index 000000000..ab8578f91 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/socket.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/config.h> +#include <hicn/transport/core/content_object.h> +#include <hicn/transport/core/facade.h> +#include <hicn/transport/core/interest.h> +#include <hicn/transport/core/manifest_format_fixed.h> +#include <hicn/transport/core/manifest_inline.h> +#include <hicn/transport/core/name.h> +#include <hicn/transport/transport/download_observer.h> +#include <hicn/transport/transport/socket_options_default_values.h> +#include <hicn/transport/transport/socket_options_keys.h> +#include <hicn/transport/utils/crypto_suite.h> +#include <hicn/transport/utils/identity.h> +#include <hicn/transport/utils/verifier.h> + +#define SOCKET_OPTION_GET 0 +#define SOCKET_OPTION_NOT_GET 1 +#define SOCKET_OPTION_SET 2 +#define SOCKET_OPTION_NOT_SET 3 +#define SOCKET_OPTION_DEFAULT 12345 + +#define VOID_HANDLER 0 + +namespace transport { + +namespace transport { + +template <typename PortalType> +class Socket; +class ConsumerSocket; +class ProducerSocket; + +using Interest = core::Interest; +using ContentObject = core::ContentObject; +using Name = core::Name; +using ContentObjectManifest = core::ManifestInline<ContentObject, core::Fixed>; +using InterestManifest = core::ManifestInline<Interest, core::Fixed>; +using HashAlgorithm = core::HashAlgorithm; +using CryptoSuite = utils::CryptoSuite; +using Identity = utils::Identity; +using Verifier = utils::Verifier; + +using HicnForwarderPortal = core::HicnForwarderPortal; + +#ifdef __linux__ +#ifndef __ANDROID__ +using RawSocketPortal = core::RawSocketPortal; +#endif +#endif + +#ifdef __vpp__ +using VPPForwarderPortal = core::VPPForwarderPortal; +using BaseSocket = Socket<VPPForwarderPortal>; +using BasePortal = VPPForwarderPortal; +#else +using BaseSocket = Socket<HicnForwarderPortal>; +using BasePortal = HicnForwarderPortal; +#endif + +using PayloadType = core::PayloadType; +using Prefix = core::Prefix; +using Array = utils::Array<uint8_t>; + +using ConsumerInterestCallback = + std::function<void(ConsumerSocket &, const Interest &)>; + +using ConsumerContentCallback = + std::function<void(ConsumerSocket &, std::size_t, const std::error_code &)>; + +using ConsumerTimerCallback = + std::function<void(ConsumerSocket &, std::size_t, + std::chrono::milliseconds &, float, uint32_t, uint32_t)>; + +using ProducerContentCallback = std::function<void( + ProducerSocket &, const std::error_code &, uint64_t bytes_written)>; + +using ConsumerContentObjectCallback = + std::function<void(ConsumerSocket &, const ContentObject &)>; + +using ConsumerContentObjectVerificationCallback = + std::function<bool(ConsumerSocket &, const ContentObject &)>; + +using ConsumerManifestCallback = + std::function<void(ConsumerSocket &, const ContentObjectManifest &)>; + +using ProducerContentObjectCallback = + std::function<void(ProducerSocket &, ContentObject &)>; + +using ProducerInterestCallback = + std::function<void(ProducerSocket &, const Interest &)>; + +using ProducerInterestCallback = + std::function<void(ProducerSocket &, const Interest &)>; + +template <typename PortalType> +class Socket { + static_assert(std::is_same<PortalType, HicnForwarderPortal>::value +#ifdef __linux__ +#ifndef __ANDROID__ + || std::is_same<PortalType, RawSocketPortal>::value +#ifdef __vpp__ + || std::is_same<PortalType, VPPForwarderPortal>::value +#endif +#endif + , +#else + , + +#endif + "This class is not allowed as Portal"); + + public: + typedef PortalType Portal; + + virtual asio::io_service &getIoService() = 0; + + virtual void connect() = 0; + + virtual int setSocketOption(int socket_option_key, + uint32_t socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + double socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + bool socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + Name socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + std::list<Prefix> socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ProducerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ProducerContentCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback socket_option_value) = 0; + + virtual int setSocketOption( + int socket_option_key, + ConsumerContentObjectCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerInterestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerContentCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerManifestCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + IcnObserver *socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + HashAlgorithm socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + CryptoSuite socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const Identity &socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + ConsumerTimerCallback socket_option_value) = 0; + + virtual int setSocketOption(int socket_option_key, + const std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + uint32_t &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + double &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + bool &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + Name &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::list<Prefix> &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ProducerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ProducerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectVerificationCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, + ConsumerContentObjectCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerInterestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption( + int socket_option_key, ConsumerManifestCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ProducerContentCallback &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::shared_ptr<Portal> &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + IcnObserver **socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + HashAlgorithm &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + CryptoSuite &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + Identity &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + std::string &socket_option_value) = 0; + + virtual int getSocketOption(int socket_option_key, + ConsumerTimerCallback &socket_option_value) = 0; + + protected: + virtual ~Socket(){}; + + protected: + std::string output_interface_; +}; + +} // namespace transport + +} // namespace transport diff --git a/libtransport/src/hicn/transport/utils/spinlock.h b/libtransport/src/hicn/transport/utils/spinlock.h new file mode 100755 index 000000000..33e5cda85 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/spinlock.h @@ -0,0 +1,53 @@ +/* + * 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 <atomic> + +namespace utils { + +class SpinLock : private std::atomic_flag { + public: + class Acquire { + public: + Acquire(SpinLock& spin_lock) : spin_lock_(spin_lock) { spin_lock_.lock(); } + + ~Acquire() { spin_lock_.unlock(); } + + // No copies + Acquire& operator=(const Acquire&) = delete; + Acquire(const Acquire&) = delete; + + private: + SpinLock& spin_lock_; + }; + + SpinLock() : std::atomic_flag(false) {} + + void lock() { + // busy-wait + while (std::atomic_flag::test_and_set(std::memory_order_acquire)) + ; + } + + void unlock() { clear(std::memory_order_release); } + + bool tryLock() { + return std::atomic_flag::test_and_set(std::memory_order_acquire); + } +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/stream_buffer.h b/libtransport/src/hicn/transport/utils/stream_buffer.h new file mode 100755 index 000000000..adfb696f2 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/stream_buffer.h @@ -0,0 +1,31 @@ +/* + * 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 <streambuf> + +namespace utils { + +template <typename char_type> +struct ostreambuf + : public std::basic_streambuf<char_type, std::char_traits<char_type> > { + ostreambuf(char_type* buffer, std::streamsize buffer_length) { + // set the "put" pointer the start of the buffer and record it's length. + setp(buffer, buffer + buffer_length); + } +}; + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/string_tokenizer.cc b/libtransport/src/hicn/transport/utils/string_tokenizer.cc new file mode 100755 index 000000000..9d1911080 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/string_tokenizer.cc @@ -0,0 +1,47 @@ +/* + * 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/errors/errors.h> +#include <hicn/transport/utils/string_tokenizer.h> + +namespace utils { + +StringTokenizer::StringTokenizer(const std::string &str) + : str_(str), delimiter_(" ") {} + +StringTokenizer::StringTokenizer(const std::string &str, + const std::string &delim) + : str_(str), delimiter_(delim) {} + +bool StringTokenizer::hasMoreTokens() { + return str_.find(delimiter_) != std::string::npos || !str_.empty(); +} + +std::string StringTokenizer::nextToken() { + unsigned long pos = str_.find(delimiter_); + + bool token_found = std::string::npos != pos; + + if (!token_found && str_.empty()) { + throw errors::TokenizerException(); + } + + std::string token = str_.substr(0, pos); + str_.erase(0, token_found ? pos + delimiter_.length() : pos); + + return token; +} + +} // namespace utils
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/string_tokenizer.h b/libtransport/src/hicn/transport/utils/string_tokenizer.h new file mode 100755 index 000000000..36630eb58 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/string_tokenizer.h @@ -0,0 +1,35 @@ +/* + * 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 <string> + +namespace utils { + +class StringTokenizer { + public: + StringTokenizer(const std::string &str); + StringTokenizer(const std::string &str, const std::string &delim); + + bool hasMoreTokens(); + std::string nextToken(); + + private: + std::string str_; + std::string delimiter_; +}; + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/test.h b/libtransport/src/hicn/transport/utils/test.h new file mode 100755 index 000000000..e3dd619ac --- /dev/null +++ b/libtransport/src/hicn/transport/utils/test.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 <sstream> + +namespace testing { + +namespace internal { + +enum GTestColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW }; + +extern void ColoredPrintf(GTestColor color, const char *fmt, ...); + +} // namespace internal + +} // namespace testing + +#define PRINTF(...) \ + do { \ + testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN, \ + "[ ] "); \ + testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, \ + __VA_ARGS__); \ + } while (0) + +// C++ stream interface +class TestCout : public std::stringstream { + public: + ~TestCout() {} +}; + +#define TEST_COUT TestCout()
\ No newline at end of file diff --git a/libtransport/src/hicn/transport/utils/uri.cc b/libtransport/src/hicn/transport/utils/uri.cc new file mode 100755 index 000000000..33eb8b45b --- /dev/null +++ b/libtransport/src/hicn/transport/utils/uri.cc @@ -0,0 +1,122 @@ +/* + * 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/errors/runtime_exception.h> +#include <hicn/transport/utils/uri.h> + +namespace utils { + +Uri::Uri() {} + +Uri &Uri::parse(const std::string &uri) { + if (uri.length() == 0) { + throw errors::RuntimeException("Malformed URI."); + } + + iterator_t uri_end = uri.end(); + + // get query start + iterator_t query_start = std::find(uri.begin(), uri_end, '?'); + + // protocol + iterator_t protocol_start = uri.begin(); + iterator_t protocol_end = std::find(protocol_start, uri_end, ':'); //"://"); + + if (protocol_end != uri_end) { + std::string prot = &*(protocol_end); + if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) { + protocol_ = std::string(protocol_start, protocol_end); + protocol_end += 3; // :// + } else { + protocol_end = uri.begin(); // no protocol + } + } else { + protocol_end = uri.begin(); // no protocol + } + // host + iterator_t host_start = protocol_end; + iterator_t path_start = + std::find(host_start, uri_end, '/'); // get path_start + + iterator_t host_end = std::find( + protocol_end, (path_start != uri_end) ? path_start : query_start, + ':'); // check for port + + locator_ = std::string(host_start, host_end); + + // port + if ((host_end != uri_end) && ((&*(host_end))[0] == ':')) { + host_end++; + iterator_t port_end = (path_start != uri_end) ? path_start : query_start; + port_ = std::string(host_end, port_end); + } + + // path + if (path_start != uri_end) { + path_ = std::string(path_start, query_start); + } + // query + if (query_start != uri_end) { + query_string_ = std::string(query_start, uri.end()); + } + + return *this; +} + +Uri &Uri::parseProtocolAndLocator(const std::string &locator) { + iterator_t total_end = locator.end(); + + // protocol + iterator_t protocol_start = locator.begin(); + iterator_t protocol_end = + std::find(protocol_start, total_end, ':'); //"://"); + + if (protocol_end != total_end) { + std::string prot = &*(protocol_end); + if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) { + protocol_ = std::string(protocol_start, protocol_end); + protocol_end += 3; // :// + } else { + throw errors::RuntimeException("Malformed locator. (Missing \"://\")"); + } + } else { + throw errors::RuntimeException("Malformed locator. No protocol specified."); + } + + // locator + iterator_t host_start = protocol_end; + iterator_t host_end = std::find(protocol_end, total_end, '/'); + + if (host_start == host_end) { + throw errors::RuntimeException( + "Malformed locator. Locator name is missing"); + } + + locator_ = std::string(host_start, host_end); + + return *this; +} + +std::string Uri::getLocator() { return locator_; } + +std::string Uri::getPath() { return path_; } + +std::string Uri::getPort() { return port_; } + +std::string Uri::getProtocol() { return protocol_; } + +std::string Uri::getQueryString() { return query_string_; } + +} // end namespace utils diff --git a/libtransport/src/hicn/transport/utils/uri.h b/libtransport/src/hicn/transport/utils/uri.h new file mode 100755 index 000000000..7c28e8552 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/uri.h @@ -0,0 +1,47 @@ +/* + * 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 <algorithm> // find +#include <string> + +namespace utils { + +class Uri { + typedef std::string::const_iterator iterator_t; + + public: + Uri(); + + Uri &parse(const std::string &uri); + + Uri &parseProtocolAndLocator(const std::string &locator); + + std::string getQueryString(); + + std::string getPath(); + + std::string getProtocol(); + + std::string getLocator(); + + std::string getPort(); + + private: + std::string query_string_, path_, protocol_, locator_, port_; +}; // uri + +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/verifier.cc b/libtransport/src/hicn/transport/utils/verifier.cc new file mode 100755 index 000000000..9a3de43c1 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/verifier.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/transport/core/packet.h> +#include <hicn/transport/errors/malformed_ahpacket_exception.h> +#include <hicn/transport/portability/portability.h> +#include <hicn/transport/utils/key_id.h> +#include <hicn/transport/utils/log.h> +#include <hicn/transport/utils/verifier.h> + +extern "C" { +TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat") +#include <hicn/hicn.h> +#include <parc/security/parc_CertificateFactory.h> +#include <parc/security/parc_InMemoryVerifier.h> +#include <parc/security/parc_Security.h> +} + +#include <sys/stat.h> + +namespace utils { + +TRANSPORT_ALWAYS_INLINE bool file_exists(const std::string &name) { + struct stat buffer; + return (stat(name.c_str(), &buffer) == 0); +} + +uint8_t Verifier::zeros[200] = {0}; + +Verifier::Verifier() { + parcSecurity_Init(); + PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create(); + this->verifier_ = + parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier); + parcInMemoryVerifier_Release(&in_memory_verifier); +} + +Verifier::~Verifier() { + parcVerifier_Release(&verifier_); + parcSecurity_Fini(); +} + +/* + * TODO: Unsupported in libparc + */ +bool Verifier::hasKey(PARCKeyId *keyId) { return false; } + +/* + * TODO: signal errors without trap. + */ +bool Verifier::addKey(PARCKey *key) { + parcVerifier_AddKey(this->verifier_, key); + return true; +} + +PARCKeyId * Verifier::addKeyFromCertificate(const std::string &file_name) { + PARCCertificateFactory *factory = parcCertificateFactory_Create(PARCCertificateType_X509, + PARCContainerEncoding_PEM); + parcAssertNotNull(factory, "Expected non-NULL factory"); + + if (!file_exists(file_name)) { + TRANSPORT_LOGW("Warning! The certificate %s file does not exist", + file_name.c_str()); + return nullptr; + } + + PARCCertificate *certificate = + parcCertificateFactory_CreateCertificateFromFile( + factory, (char *)file_name.c_str(), NULL); + + PARCKey *key = parcCertificate_GetPublicKey(certificate); + addKey(key); + + PARCKeyId *ret = parcKeyId_Acquire(parcKey_GetKeyId(key)); + + // parcKey_Release(&key); + // parcCertificate_Release(&certificate); + // parcCertificateFactory_Release(&factory); + + return ret; +} + +int Verifier::verify(const Packet &packet) { + bool valid = false; + + // header chain points to the IP + TCP hicn header + utils::MemBuf *header_chain = packet.header_head_; + utils::MemBuf *payload_chain = packet.payload_head_; + uint8_t *hicn_packet = header_chain->writableData(); + Packet::Format format = packet.getFormat(); + + if (!(packet.format_ & HFO_AH)) { + throw errors::MalformedAHPacketException(); + } + + // Copy IP+TCP/ICMP header before zeroing them + hicn_header_t header_copy; + if (format & HFO_INET) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(&header_copy, hicn_packet, sizeof(hicn_v6_hdr_t)); + } + + std::size_t header_len = Packet::getHeaderSizeFromFormat(format); + + PARCCryptoSuite suite = + static_cast<PARCCryptoSuite>(packet.getValidationAlgorithm()); + KeyId _key_id = packet.getKeyId(); + PARCBuffer *buffer = + parcBuffer_Wrap(_key_id.first, _key_id.second, 0, _key_id.second); + PARCKeyId *key_id = parcKeyId_Create(buffer); + parcBuffer_Release(&buffer); + + int ah_payload_len = header_chain->next()->length(); + uint8_t *signature = header_chain->next()->writableData(); + + // Reset fields that should not appear in the signature + const_cast<Packet &>(packet).resetForHash(); + + PARCCryptoHashType hashtype = parcCryptoSuite_GetCryptoHash(suite); + utils::CryptoHasher hasher( + parcVerifier_GetCryptoHasher(verifier_, key_id, hashtype)); + + hasher.init() + .updateBytes(hicn_packet, header_len) + .updateBytes(zeros, ah_payload_len); + + for (utils::MemBuf *current = payload_chain; current != header_chain; + current = current->next()) { + hasher.updateBytes(current->data(), current->length()); + } + + utils::CryptoHash hash = hasher.finalize(); + PARCCryptoHash *hash_computed_locally = hash.hash_; + + PARCBuffer *bits = + parcBuffer_Wrap(signature, ah_payload_len, 0, ah_payload_len); + parcBuffer_Rewind(bits); + + /* IF the signature algo is ECDSA, the signature might be shorter than the + * signature field */ + PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite); + while (algo == PARCSigningAlgorithm_ECDSA && parcBuffer_HasRemaining(bits) && + parcBuffer_GetUint8(bits) == 0) + ; + + if (algo == PARCSigningAlgorithm_ECDSA) { + parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1); + } + + if (!parcBuffer_HasRemaining(bits)) { + parcKeyId_Release(&key_id); + parcBuffer_Release(&bits); + return valid; + } + + PARCSignature *signatureToVerify = parcSignature_Create( + parcCryptoSuite_GetSigningAlgorithm(suite), hashtype, bits); + + if (algo == PARCSigningAlgorithm_RSA) { + parcBuffer_SetPosition(bits, 0); + } + + valid = parcVerifier_VerifyDigestSignature( + verifier_, key_id, hash_computed_locally, suite, signatureToVerify); + + /* Restore the resetted fields */ + if (format & HFO_INET) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v4_hdr_t)); + } else if (format & HFO_INET6) { + memcpy(hicn_packet, &header_copy, sizeof(hicn_v6_hdr_t)); + } + + parcKeyId_Release(&key_id); + + parcBuffer_Release(&bits); + parcSignature_Release(&signatureToVerify); + + return valid; +} +} // namespace utils diff --git a/libtransport/src/hicn/transport/utils/verifier.h b/libtransport/src/hicn/transport/utils/verifier.h new file mode 100755 index 000000000..6313a7240 --- /dev/null +++ b/libtransport/src/hicn/transport/utils/verifier.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <hicn/transport/core/packet.h> + +extern "C" { +#include <parc/security/parc_KeyId.h> +#include <parc/security/parc_Verifier.h> +} + +namespace utils { + +using Packet = transport::core::Packet; + +/** + * A verifier holds a crypto cache that contains all the keys to use for + * verify signatures/hmacs. + */ +class Verifier { + public: + Verifier(); + + ~Verifier(); + + /** + * @brief Check if a key is already in this Verifier. + * + * A PARCVerifier contains a CryptoCache with a set of key to use for + * verification purposes. + * + * @param keyId Identifier of the key to match in the CryptoCache of the + * Verifier. + * @return true if the key is found, false otherwise. + */ + bool hasKey(PARCKeyId *keyId); + + /** + * @brief Add a key to this Verifier + * + * @param key to add + * @return true if the key was added successfully, false otherwise. + */ + bool addKey(PARCKey *key); + + PARCKeyId *addKeyFromCertificate(const std::string &file_name); + + /** + * @brief Verify a Signature + * + * This method is general and must be used for Public-private key signature, + * HMAC and CRC. + * + * @param signature A pointer to the buffer holding the signature + * @param sign_len Lenght of the signature (must be consistent with the type + * of the key) + * @param bufferSigned A pointer to the packet header signed with + * signature. Mutable fields and the signature field in the packet must be + * set to 0 + * @param buf_len Lenght of bufferSigned + * @param suite CryptoSuite to use to verify the signature + * @param key_id Indentifier of the key to use to verify the signature. The + * key must be already present in the Verifier. + */ + int verify(const Packet &packet); + + private: + PARCVerifier *verifier_; + static uint8_t zeros[200]; +}; + +} // namespace utils |